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
@@ -91,4 +91,18 @@ describe('native terminal client', () => {
expect(config.mcp.tools.url).toBe('http://127.0.0.1:12345/mcp')
expect(await readFile(new URL(config.plugin[0]), 'utf8')).toContain('execute')
})
it('allows only the bound read tools through native OpenCode Ask permissions and execution hook', async () => {
const { client, input } = await fixture()
await client.open(1, { ...input, mcpServers: [{ name: 'goodbuddy-0', url: 'http://127.0.0.1:12345/mcp', headers: {},
readOnlyTools: ['story_graph_search', 'story_graph_get_context', 'story_graph_read_source'] }] })
const config = JSON.parse(launches[0]!.spawnSpec.env.OPENCODE_CONFIG_CONTENT!)
expect(config.mcp['goodbuddy-0']).toBeDefined()
const hooks = await (await import(/* @vite-ignore */ new URL(config.plugin[0]).href)).default()
for (const name of ['story_graph_search', 'story_graph_get_context', 'story_graph_read_source']) {
expect(config.permission[`goodbuddy-0_${name}`]).toBe('allow')
await expect(hooks['tool.execute.before']({ tool: `goodbuddy-0_${name}` })).resolves.toBeUndefined()
}
await expect(hooks['tool.execute.before']({ tool: 'goodbuddy-0_generate_image' })).rejects.toThrow('read-only')
})
})