feat(agent): add Hermes transitional capability matrix
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
This commit is contained in:
@@ -30,6 +30,28 @@ const transport = (capabilities = ['session.list', 'session.tree']): HermesRunti
|
|||||||
terminate: vi.fn(),
|
terminate: vi.fn(),
|
||||||
});
|
});
|
||||||
describe('HermesRuntimeProvider normalization boundary', () => {
|
describe('HermesRuntimeProvider normalization boundary', () => {
|
||||||
|
it('returns an exhaustive fail-closed transitional capability matrix', async () => {
|
||||||
|
const provider = new HermesRuntimeProvider(transport());
|
||||||
|
|
||||||
|
await expect(provider.transitionalCapabilityMatrix(scope)).resolves.toEqual([
|
||||||
|
{ capability: 'kanban', status: 'unsupported' },
|
||||||
|
{ capability: 'skills', status: 'unsupported' },
|
||||||
|
{ capability: 'memory', status: 'unsupported' },
|
||||||
|
{ capability: 'tools', status: 'unsupported' },
|
||||||
|
{ capability: 'cron', status: 'unsupported' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('denies unsupported transitional capabilities without calling Hermes', async () => {
|
||||||
|
const hermes = transport();
|
||||||
|
const provider = new HermesRuntimeProvider(hermes);
|
||||||
|
|
||||||
|
await expect(provider.assertTransitionalCapability('memory', scope)).rejects.toMatchObject({
|
||||||
|
code: 'capability_unsupported',
|
||||||
|
});
|
||||||
|
expect(hermes.capabilities).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('normalizes legacy sessions without exposing legacy fields', async () => {
|
it('normalizes legacy sessions without exposing legacy fields', async () => {
|
||||||
const provider = new HermesRuntimeProvider(transport());
|
const provider = new HermesRuntimeProvider(transport());
|
||||||
await expect(provider.listSessions(scope)).resolves.toEqual(
|
await expect(provider.listSessions(scope)).resolves.toEqual(
|
||||||
|
|||||||
@@ -11,9 +11,19 @@ import type {
|
|||||||
RuntimeSessionState,
|
RuntimeSessionState,
|
||||||
RuntimeSessionTree,
|
RuntimeSessionTree,
|
||||||
RuntimeStreamEvent,
|
RuntimeStreamEvent,
|
||||||
|
TransitionalCapabilityInventoryEntry,
|
||||||
|
TransitionalCapabilityInventoryProvider,
|
||||||
|
TransitionalRuntimeCapability,
|
||||||
} from '@mosaicstack/types';
|
} from '@mosaicstack/types';
|
||||||
|
|
||||||
const HERMES_PROVIDER_ID = 'runtime.hermes';
|
const HERMES_PROVIDER_ID = 'runtime.hermes';
|
||||||
|
const TRANSITIONAL_CAPABILITIES: readonly TransitionalRuntimeCapability[] = [
|
||||||
|
'kanban',
|
||||||
|
'skills',
|
||||||
|
'memory',
|
||||||
|
'tools',
|
||||||
|
'cron',
|
||||||
|
];
|
||||||
const RUNTIME_CAPABILITIES: readonly RuntimeCapability[] = [
|
const RUNTIME_CAPABILITIES: readonly RuntimeCapability[] = [
|
||||||
'session.list',
|
'session.list',
|
||||||
'session.tree',
|
'session.tree',
|
||||||
@@ -65,11 +75,38 @@ export class HermesRuntimeProviderError extends Error {
|
|||||||
* Transitional Hermes adapter. Legacy identifiers and schemas do not cross this
|
* Transitional Hermes adapter. Legacy identifiers and schemas do not cross this
|
||||||
* boundary: callers only observe Mosaic AgentRuntimeProvider contracts.
|
* boundary: callers only observe Mosaic AgentRuntimeProvider contracts.
|
||||||
*/
|
*/
|
||||||
export class HermesRuntimeProvider implements AgentRuntimeProvider {
|
export class HermesRuntimeProvider
|
||||||
|
implements AgentRuntimeProvider, TransitionalCapabilityInventoryProvider
|
||||||
|
{
|
||||||
readonly id = HERMES_PROVIDER_ID;
|
readonly id = HERMES_PROVIDER_ID;
|
||||||
|
|
||||||
constructor(private readonly transport: HermesRuntimeTransport) {}
|
constructor(private readonly transport: HermesRuntimeTransport) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full AC-TESS-05 migration inventory. These operations are deliberately
|
||||||
|
* unsupported until their Mosaic-owned plugin contracts exist.
|
||||||
|
*/
|
||||||
|
async transitionalCapabilityMatrix(
|
||||||
|
_scope: RuntimeScope,
|
||||||
|
): Promise<TransitionalCapabilityInventoryEntry[]> {
|
||||||
|
return TRANSITIONAL_CAPABILITIES.map((capability) => ({ capability, status: 'unsupported' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async assertTransitionalCapability(
|
||||||
|
capability: TransitionalRuntimeCapability,
|
||||||
|
scope: RuntimeScope,
|
||||||
|
): Promise<void> {
|
||||||
|
const entry = (await this.transitionalCapabilityMatrix(scope)).find(
|
||||||
|
(candidate) => candidate.capability === capability,
|
||||||
|
);
|
||||||
|
if (!entry || entry.status !== 'supported') {
|
||||||
|
throw new HermesRuntimeProviderError(
|
||||||
|
'capability_unsupported',
|
||||||
|
`Hermes transitional capability is unsupported: ${capability}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async capabilities(scope: RuntimeScope): Promise<RuntimeCapabilitySet> {
|
async capabilities(scope: RuntimeScope): Promise<RuntimeCapabilitySet> {
|
||||||
const legacyCapabilities = await this.transport.capabilities(scope);
|
const legacyCapabilities = await this.transport.capabilities(scope);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -6,6 +6,24 @@ export type RuntimeCapability =
|
|||||||
| 'session.attach'
|
| 'session.attach'
|
||||||
| 'session.terminate';
|
| 'session.terminate';
|
||||||
export type RuntimeSessionState = 'starting' | 'active' | 'idle' | 'stopped' | 'failed';
|
export type RuntimeSessionState = 'starting' | 'active' | 'idle' | 'stopped' | 'failed';
|
||||||
|
|
||||||
|
/** Transitional capability inventory is normalized; provider legacy vocabularies never enter core. */
|
||||||
|
export type TransitionalRuntimeCapability = 'kanban' | 'skills' | 'memory' | 'tools' | 'cron';
|
||||||
|
export type TransitionalCapabilityStatus = 'supported' | 'unsupported';
|
||||||
|
export interface TransitionalCapabilityInventoryEntry {
|
||||||
|
capability: TransitionalRuntimeCapability;
|
||||||
|
status: TransitionalCapabilityStatus;
|
||||||
|
}
|
||||||
|
/** Optional extension for transitional adapters; not every runtime has Hermes inventory. */
|
||||||
|
export interface TransitionalCapabilityInventoryProvider {
|
||||||
|
transitionalCapabilityMatrix(
|
||||||
|
scope: RuntimeScope,
|
||||||
|
): Promise<TransitionalCapabilityInventoryEntry[]>;
|
||||||
|
assertTransitionalCapability(
|
||||||
|
capability: TransitionalRuntimeCapability,
|
||||||
|
scope: RuntimeScope,
|
||||||
|
): Promise<void>;
|
||||||
|
}
|
||||||
export type RuntimeAttachMode = 'read' | 'control';
|
export type RuntimeAttachMode = 'read' | 'control';
|
||||||
|
|
||||||
/** Server-derived immutable authority context. Client identity fields are intentionally absent. */
|
/** Server-derived immutable authority context. Client identity fields are intentionally absent. */
|
||||||
|
|||||||
Reference in New Issue
Block a user