feat: let agents read supervisor story graphs

Expose saved decisions, timelines and source evidence through read-only Story Graph tools across Model, Harness and local/remote OpenCode and Continue. Supervisor enablement and runtime assignments govern discovery and calls; reads do not invoke models or modify stored evidence. Remote use requires matching Desktop and Agent changes.

Make Continue discussion show the complete editable message with a separated destination and shared action layout. Sending uses the visible draft without reattaching removed context.

Validation: existing focused MCP, runtime, controlled Linux Host, discussion component and Electron layout checks plus typechecks; staged diff check passed. Cross-scope fact reuse, historical reconstruction and full package acceptance remain deferred.

Release note: Agents can consult saved supervisor decisions and their sources when continuing work. Continue discussion now lets you review and edit the complete message before sending.
This commit is contained in:
mesalogo
2026-09-28 12:38:00 +08:00
parent 83a3ea261e
commit 66f77b83c2
61 changed files with 1527 additions and 150 deletions
+26
View File
@@ -9211,6 +9211,32 @@ describe('registerIpcHandlers agent terminal state', () => {
}
})
it.each(['model', 'opencode', 'continue', 'deepseek-harness'])('binds Story Graph to the current project for %s and omits it when Supervisor is off', async runtimeId => {
const received: AgentExecutionRequest[] = []
const binding = { available: vi.fn(async () => true), call: vi.fn() }
const gateway = { grant: vi.fn(() => 'graph-capability'), getAvailableToolNames: vi.fn(() => ['story_graph_search', 'story_graph_get_context', 'story_graph_read_source']),
bindRemoteStoryGraph: vi.fn(() => binding), drainReferences: vi.fn(() => []), revoke: vi.fn() }
const harness = createHarness({ runtimeId, capability: 'chat', supportsToolExecution: true,
async *run(request: AgentExecutionRequest) { received.push(request); yield { requestId: request.requestId, type: 'done' } }
}, undefined, 'always', undefined, false, undefined, undefined, gateway, false, undefined, {
getEnabledBuiltinMcpServerIds: vi.fn(async () => ['story-graph'])
})
try {
const projectId = '00000000-0000-4000-8000-000000000088'
for (const enabled of [true, false]) {
harness.getApplicationSettings.mockResolvedValue({ heartbeatEnabled: enabled })
const requestId = crypto.randomUUID()
await harness.handler!(trustedEvent(harness.webContents), { requestId, projectId, conversationId: 'graph-grant', prompt: 'Continue work', workMode: 'ask', knowledgeLibraryIds: [] })
await vi.waitFor(() => expect(harness.assistantDatabase.updateTaskStatus).toHaveBeenCalledWith(requestId, 'completed'))
}
expect(gateway.grant).toHaveBeenCalledExactlyOnceWith(expect.any(String), [], expect.any(AbortSignal), 'none',
undefined, undefined, undefined, undefined, undefined, { projectId, runtimeTarget: runtimeId })
expect(received[0]).toMatchObject({ knowledgeCapabilityToken: 'graph-capability', storyGraphBinding: binding })
expect(received[1]).not.toHaveProperty('storyGraphBinding')
expect(gateway.bindRemoteStoryGraph).toHaveBeenCalledOnce()
} finally { await harness.dispose() }
})
it('grants read-only Magic Notes tools in Ask and write tools in Execute', async () => {
const runtime = {
runtimeId: 'model',