wip: first run of implementation
This commit is contained in:
117
tests/engine/agentic/protocol/responseBuilder.test.ts
Normal file
117
tests/engine/agentic/protocol/responseBuilder.test.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { ProtocolResponseBuilder } from '../../../../src/main/agentic/protocol/responseBuilder';
|
||||
|
||||
describe('ProtocolResponseBuilder', () => {
|
||||
it('builds canonical envelope from mixed text + AGUI payload', () => {
|
||||
const builder = new ProtocolResponseBuilder();
|
||||
|
||||
const raw = [
|
||||
'I found weak months.',
|
||||
'```json',
|
||||
'{"specVersion":"1","elements":[{"type":"chart","chartType":"bar","series":[{"label":"Jan","value":10}]}]}',
|
||||
'```',
|
||||
].join('\n');
|
||||
|
||||
const result = builder.build({
|
||||
rawAssistantOutput: raw,
|
||||
surface: 'tab',
|
||||
capabilities: {
|
||||
widgets: ['chart'],
|
||||
actions: ['openPost'],
|
||||
tools: ['search_posts'],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.envelope.ui?.elements).toHaveLength(1);
|
||||
expect(result.envelope.assistantText).toContain('I found weak months');
|
||||
expect(result.envelope.protocolVersion).toBe('2.0');
|
||||
expect(result.repairAttempted).toBe(false);
|
||||
});
|
||||
|
||||
it('repairs non-canonical envelope keys and validates output', () => {
|
||||
const builder = new ProtocolResponseBuilder();
|
||||
|
||||
const raw = JSON.stringify({
|
||||
protocol_version: '2.0',
|
||||
assistant_text: 'Need more details',
|
||||
intent: 'ask_input',
|
||||
needs_input: {
|
||||
required: true,
|
||||
fields: [{ key: 'date', label: 'Date', inputType: 'date' }],
|
||||
},
|
||||
actions: [],
|
||||
confidence: 0.8,
|
||||
trace_id: 'trace-manual',
|
||||
});
|
||||
|
||||
const result = builder.build({
|
||||
rawAssistantOutput: raw,
|
||||
surface: 'sidebar',
|
||||
capabilities: {
|
||||
widgets: ['form'],
|
||||
actions: ['openPost'],
|
||||
tools: ['search_posts'],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.repairAttempted).toBe(true);
|
||||
expect(result.envelope.assistantText).toBe('Need more details');
|
||||
expect(result.envelope.needsInput.required).toBe(true);
|
||||
expect(result.envelope.needsInput.fields).toHaveLength(1);
|
||||
expect(result.validationError).toBeUndefined();
|
||||
});
|
||||
|
||||
it('falls back to safe summarize envelope when payload is invalid', () => {
|
||||
const builder = new ProtocolResponseBuilder();
|
||||
|
||||
const raw = '{"specVersion":"9","elements":[]}';
|
||||
|
||||
const result = builder.build({
|
||||
rawAssistantOutput: raw,
|
||||
surface: 'tab',
|
||||
capabilities: {
|
||||
widgets: ['chart'],
|
||||
actions: ['openPost'],
|
||||
tools: ['search_posts'],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.envelope.intent).toBe('summarize');
|
||||
expect(result.envelope.ui).toBeUndefined();
|
||||
expect(result.envelope.assistantText).toContain('specVersion');
|
||||
expect(result.traceId.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('blocks actions that are unavailable for the active surface capabilities', () => {
|
||||
const builder = new ProtocolResponseBuilder();
|
||||
|
||||
const raw = JSON.stringify({
|
||||
protocolVersion: '2.0',
|
||||
assistantText: 'Open settings?',
|
||||
intent: 'propose_action',
|
||||
needsInput: { required: false, fields: [] },
|
||||
actions: [{
|
||||
id: 'a1',
|
||||
action: 'openSettings',
|
||||
label: 'Open Settings',
|
||||
policy: 'confirm',
|
||||
requiresConfirmation: true,
|
||||
}],
|
||||
confidence: 0.7,
|
||||
traceId: 'trace-abc',
|
||||
});
|
||||
|
||||
const result = builder.build({
|
||||
rawAssistantOutput: raw,
|
||||
surface: 'tab',
|
||||
capabilities: {
|
||||
widgets: ['chart'],
|
||||
actions: ['openPost'],
|
||||
tools: ['search_posts'],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.envelope.actions).toHaveLength(0);
|
||||
expect(result.warnings.some((warning) => warning.includes('openSettings'))).toBe(true);
|
||||
});
|
||||
});
|
||||
74
tests/engine/agentic/protocol/validator.test.ts
Normal file
74
tests/engine/agentic/protocol/validator.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
validateProtocolRequestEnvelope,
|
||||
validateProtocolResponseEnvelope,
|
||||
type ProtocolResponseEnvelope,
|
||||
} from '../../../../src/main/agentic/protocol/validator';
|
||||
|
||||
describe('agentic protocol validator', () => {
|
||||
it('validates canonical response envelope', () => {
|
||||
const envelope: ProtocolResponseEnvelope = {
|
||||
protocolVersion: '2.0',
|
||||
assistantText: 'Done',
|
||||
intent: 'summarize',
|
||||
needsInput: {
|
||||
required: false,
|
||||
fields: [],
|
||||
},
|
||||
actions: [],
|
||||
confidence: 0.82,
|
||||
traceId: 'trace-abc',
|
||||
};
|
||||
|
||||
const result = validateProtocolResponseEnvelope(envelope);
|
||||
expect(result.ok).toBe(true);
|
||||
expect(result.error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('rejects response envelope with unknown properties in strict mode', () => {
|
||||
const result = validateProtocolResponseEnvelope({
|
||||
protocolVersion: '2.0',
|
||||
assistantText: 'Done',
|
||||
intent: 'summarize',
|
||||
needsInput: { required: false, fields: [] },
|
||||
actions: [],
|
||||
confidence: 0.8,
|
||||
traceId: 'trace-abc',
|
||||
extra: 'nope',
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.error?.code).toBe('AGUI_PROTOCOL_VALIDATION_ERROR');
|
||||
});
|
||||
|
||||
it('rejects needsInput.required=true without fields', () => {
|
||||
const result = validateProtocolResponseEnvelope({
|
||||
protocolVersion: '2.0',
|
||||
assistantText: 'Need details',
|
||||
intent: 'ask_input',
|
||||
needsInput: { required: true, fields: [] },
|
||||
actions: [],
|
||||
confidence: 0.9,
|
||||
traceId: 'trace-xyz',
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.error?.message).toContain('needsInput.fields');
|
||||
});
|
||||
|
||||
it('validates canonical request envelope with capabilities', () => {
|
||||
const result = validateProtocolRequestEnvelope({
|
||||
protocolVersion: '2.0',
|
||||
surface: 'tab',
|
||||
messages: [{ role: 'user', content: 'Create a chart' }],
|
||||
context: { projectId: 'project-1' },
|
||||
capabilities: {
|
||||
widgets: ['chart', 'form'],
|
||||
actions: ['openPost'],
|
||||
tools: ['search_posts'],
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user