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
+15 -4
View File
@@ -450,6 +450,7 @@ async function grantScopedDataCapability(input: {
magicNotesAccess: MagicNotesCapabilityAccess
configAccess?: MagicNotesCapabilityAccess
obsidian?: { settings: ObsidianSettings; access: 'read' | 'write' }
storyGraph?: import('./agent/knowledge-mcp-gateway').StoryGraphBinding
workspacePath?: string
browserConversationId?: string
browserTabId?: BrowserTabId
@@ -462,6 +463,7 @@ async function grantScopedDataCapability(input: {
}): Promise<ScopedDataCapability> {
const enabledServers = new Set(input.enabledServers)
const obsidian = enabledServers.has('obsidian') ? input.obsidian : undefined
const storyGraph = enabledServers.has('story-graph') ? input.storyGraph : undefined
const libraryIds = enabledServers.has('knowledge-base')
? input.libraryIds
: []
@@ -480,7 +482,7 @@ async function grantScopedDataCapability(input: {
magicNotesAccess === 'none' &&
configAccess === 'none' &&
!browserConversationId &&
!obsidian)
!obsidian && !storyGraph)
) {
return { toolNames: [] }
}
@@ -506,7 +508,10 @@ async function grantScopedDataCapability(input: {
authorizeApply: input.authorizeConfigApply
}
: undefined
const token = obsidian
const token = storyGraph
? input.gateway.grant(input.requestId, libraryIds, input.signal, magicNotesAccess, config,
browserConversationId, browserTabId, browserUsageLease, obsidian, storyGraph)
: obsidian
? input.gateway.grant(
input.requestId,
libraryIds,
@@ -2280,6 +2285,8 @@ export function registerIpcHandlers(
)
: []
const notesCapability = await grantScopedDataCapability({
storyGraph: requestRuntimeTarget && (await applicationSettingsStore?.get())?.heartbeatEnabled
? { runtimeTarget: requestRuntimeTarget, projectId: schedule.projectId ?? undefined } : undefined,
obsidian: enabledBuiltinMcpServers.includes('obsidian')
? {
settings: await capabilityService.getObsidianSettings(),
@@ -2397,7 +2404,8 @@ export function registerIpcHandlers(
}),
trustedInstructions,
...(knowledgeCapabilityToken
? { knowledgeCapabilityToken }
? { knowledgeCapabilityToken, ...(notesCapability.toolNames.includes('story_graph_search')
? { storyGraphBinding: knowledgeGateway?.bindRemoteStoryGraph(knowledgeCapabilityToken) } : {}) }
: {}),
...(notesCapability.browserTabId
? { browserTabId: notesCapability.browserTabId }
@@ -3807,6 +3815,8 @@ export function registerIpcHandlers(
: undefined
const imageToolAvailable = Boolean(await imageToolBinding?.describe())
const scopedCapability = await grantScopedDataCapability({
storyGraph: selectedRuntimeTarget && applicationSettings?.heartbeatEnabled
? { runtimeTarget: selectedRuntimeTarget, projectId: enrichedRequest.projectId } : undefined,
obsidian: enabledBuiltinMcpServers.includes('obsidian')
? {
settings: await capabilityService.getObsidianSettings(),
@@ -3873,7 +3883,8 @@ export function registerIpcHandlers(
}
: enrichedRequest
const request: AgentExecutionRequest = knowledgeCapabilityToken
? { ...baseRequest, knowledgeCapabilityToken }
? { ...baseRequest, knowledgeCapabilityToken, ...(scopedCapability.toolNames.includes('story_graph_search')
? { storyGraphBinding: knowledgeGateway?.bindRemoteStoryGraph(knowledgeCapabilityToken) } : {}) }
: baseRequest
if (scopedCapability.browserTabId) {
request.browserTabId = scopedCapability.browserTabId