Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 274 additions & 0 deletions docs/architecture/acp-pr8-goal-plan-design.zh-CN.md

Large diffs are not rendered by default.

150 changes: 150 additions & 0 deletions packages/cli/src/__tests__/acp-agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe('Maka ACP agent', () => {
agentCapabilities: {
loadSession: true,
sessionCapabilities: { list: {}, resume: {}, close: {} },
_meta: { '_maka/goalPlan': { version: 1 } },
},
authMethods: [],
agentInfo: { name: 'maka', title: 'Maka', version: '0.2.0' },
Expand Down Expand Up @@ -98,6 +99,137 @@ describe('Maka ACP agent', () => {
);
});

test('decodes all Goal/Plan extension inputs with Host specifications', async () => {
const controls: unknown[] = [];
const starts: unknown[] = [];
const registry = {
...fakeSessionRegistry(),
goalQuery: async (input: unknown) => ({
sessionId: (input as { sessionId: string }).sessionId,
goal: null,
}),
goalArm: async (input: unknown) => {
controls.push(input);
return { sessionId: 'session-1', goal: {} } as never;
},
goalControl: async (input: unknown) => {
controls.push(input);
return { sessionId: 'session-1', goal: {} } as never;
},
planQuery: async (input: unknown) => {
controls.push(input);
return {
kind: 'page' as const,
sessionId: 'session-1',
storeVersion: 0,
latestProposalId: null,
activeExecutionId: null,
items: [],
nextCursor: null,
};
},
planControl: async (input: unknown) => {
controls.push(input);
return {
sessionId: 'session-1',
storeVersion: 1,
eventType: 'plan_abandoned' as const,
proposalId: null,
executionId: null,
};
},
planTurnStart: async (input: unknown) => {
starts.push(input);
return { plan: {}, turn: {} } as never;
},
};
await client({ name: 'test-client' }).connectWith(
createMakaAcpAgent({ version: '0.2.0', sessionRegistry: registry }),
async (agent) => {
await agent.request(methods.agent.initialize, { protocolVersion: 1 });
assert.deepEqual(await agent.request('_maka/goal/query', { sessionId: 'session-1' }), {
sessionId: 'session-1',
goal: null,
});
await agent.request('_maka/goal/arm', {
sessionId: 'session-1',
condition: 'Finish',
maxIterations: null,
tokenBudget: null,
});
await agent.request('_maka/goal/control', {
sessionId: 'session-1',
goalId: 'goal-1',
expectedRevision: 1,
action: 'pause',
});
await agent.request('_maka/plan/query', { kind: 'list_start', sessionId: 'session-1' });
for (const input of [
{
kind: 'request_revision',
sessionId: 'session-1',
proposalId: 'proposal-1',
operationId: 'op-1',
},
{
kind: 'abandon_proposal',
sessionId: 'session-1',
proposalId: 'proposal-1',
operationId: 'op-2',
},
{
kind: 'approve_proposal',
sessionId: 'session-1',
proposalId: 'proposal-1',
expectedRevision: 1,
expectedStoreVersion: 1,
operationId: 'op-3',
},
{
kind: 'resume_execution',
sessionId: 'session-1',
executionId: 'exec-1',
operationId: 'op-4',
},
{
kind: 'cancel_execution',
sessionId: 'session-1',
executionId: 'exec-1',
operationId: 'op-5',
},
])
await agent.request('_maka/plan/control', input);
await agent.request('_maka/plan/turn/start', {
kind: 'approve_proposal',
sessionId: 'session-1',
proposalId: 'proposal-1',
expectedRevision: 1,
expectedStoreVersion: 1,
turnId: 'turn-1',
});
await agent.request('_maka/plan/turn/start', {
kind: 'resume_execution',
sessionId: 'session-1',
executionId: 'execution-1',
turnId: 'turn-2',
});
assert.equal(controls.length, 8);
assert.equal(starts.length, 2);
await assert.rejects(
agent.request('_maka/plan/turn/start', {
kind: 'approve_proposal',
sessionId: 'session-1',
}),
(error: unknown) => error instanceof RequestError && error.code === -32602,
);
await assert.rejects(
agent.request('_maka/goal/arm', { sessionId: 'session-1', condition: 'Finish' }),
(error: unknown) => error instanceof RequestError && error.code === -32602,
);
},
);
});

test('routes bounded copy-source queries and rejects invalid input through the SDK', async () => {
await client({ name: 'test-client' }).connectWith(
createMakaAcpAgent({ version: '0.2.0', sessionRegistry: fakeSessionRegistry() }),
Expand Down Expand Up @@ -300,6 +432,24 @@ function fakeSessionRegistry(
reason: 'resume_candidate_missing' as const,
},
}),
goalQuery: async () => {
throw new Error('unused Goal query');
},
goalArm: async () => {
throw new Error('unused Goal arm');
},
goalControl: async () => {
throw new Error('unused Goal control');
},
planQuery: async () => {
throw new Error('unused Plan query');
},
planControl: async () => {
throw new Error('unused Plan control');
},
planTurnStart: async () => {
throw new Error('unused Plan Turn start');
},
queryCopySource: async () => ({
sessionId: 'session-1',
expectedSourceRevision: 1,
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/__tests__/acp-child-process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ describe('Maka ACP child process', () => {
agentCapabilities: {
loadSession: true,
sessionCapabilities: { list: {}, resume: {}, close: {} },
_meta: { '_maka/goalPlan': { version: 1 } },
},
authMethods: [],
agentInfo: { name: 'maka', title: 'Maka', version: '0.2.0' },
Expand Down
Loading