Files
goodbuddy/src/main/agent/remote-runtime-channel.ts
T
mesalogo 66f77b83c2 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.
2026-09-28 12:38:00 +08:00

199 lines
6.3 KiB
TypeScript

import type {
AcpBindingCursors,
AcpCompletePromptRequest,
AcpCompletePromptResult,
AcpEscalateCancellationRequest,
AcpReconcilePromptRequest,
AcpReconcilePromptResult
} from '../../shared/agent-protocol'
import type {
RemoteOwnedPromptStartRequest,
RemoteOwnedPromptStartResult,
RemotePromptOperationAcceptance,
RemotePromptOperationPreparation,
remoteOwnedPromptAttachRequestSchema,
remoteSemanticTranscriptAckRequestSchema,
remoteSemanticTranscriptAckResultSchema,
remoteSemanticTranscriptPageRequestSchema,
remoteSemanticTranscriptPageResultSchema,
} from '../../shared/remote-agent-contracts'
import type { z } from 'zod'
import type { ImageToolBinding } from './image-tool-binding'
import type { RemoteQuestionResponse } from '../../shared/remote-question-contracts'
import type {
ModelBridgePolicy
} from '../../shared/model-bridge-contracts'
export type RemoteRuntimeChannelCapabilities = {
cancellationEscalation: boolean
promptOperationReconciliation: boolean
ownedPrompt?: boolean
modelBridge?: boolean
}
export type RemoteOwnedPromptAttachRequest = z.infer<
typeof remoteOwnedPromptAttachRequestSchema
>
export type RemoteSemanticTranscriptPageRequest = z.infer<
typeof remoteSemanticTranscriptPageRequestSchema
>
export type RemoteSemanticTranscriptPageResult = z.infer<
typeof remoteSemanticTranscriptPageResultSchema
>
export type RemoteSemanticTranscriptAckRequest = z.infer<
typeof remoteSemanticTranscriptAckRequestSchema
>
export type RemoteSemanticTranscriptAckResult = z.infer<
typeof remoteSemanticTranscriptAckResultSchema
>
export type RuntimeSessionBindingCursors = AcpBindingCursors
export type RemoteCancellationEscalation = Omit<
AcpEscalateCancellationRequest,
'reason'
> & {
reason: unknown
}
export type RemotePromptOperationReconciliation =
AcpReconcilePromptRequest
export type RemotePromptOperationReconciliationResult =
AcpReconcilePromptResult
export type RemotePromptOperationCompletion = AcpCompletePromptRequest
export type RemotePromptOperationCompletionResult =
AcpCompletePromptResult
export type RemoteModelBridgeOpenRequest = {
bindingId: string
promptOperationId: string
requestId: string
policy: ModelBridgePolicy
}
export type RemoteModelBridgeCloseResult = {
clean: boolean
poisoned: boolean
}
export interface RemoteModelBridgeSession {
readonly version: 'goodbuddy-model-bridge-v1'
readonly channelId: string
readonly channelEpoch: string
readonly policy: ModelBridgePolicy
readonly closed: Promise<RemoteModelBridgeCloseResult>
close(reason?: string): Promise<RemoteModelBridgeCloseResult>
}
/**
* A process-neutral, already authenticated ACP virtual channel.
*
* Implementations own framing, journaling and transport generation checks.
* The runtime intentionally receives no SSH client, socket or command API.
*/
export interface RemoteRuntimeChannel {
respondToQuestion?(request: RemoteQuestionResponse): Promise<void>
readonly input: ReadableStream<Uint8Array>
readonly output: WritableStream<Uint8Array>
readonly generation: number
readonly channelEpoch: string
/**
* Signed Runtime capability identity advertised by the Agent before the
* untrusted Runtime process starts.
*/
readonly advertisedAcpCapabilitiesDigest: string
readonly capabilities: RemoteRuntimeChannelCapabilities
readonly closed: Promise<void>
isCurrentGeneration(): boolean
getBindingCursors(
bindingId: string
): Promise<RuntimeSessionBindingCursors>
/** Accepts the prompt and starts its Ask or Execute Runtime process. */
preparePrompt(
preparation: RemotePromptOperationPreparation,
imageToolBinding?: ImageToolBinding,
waitSignal?: AbortSignal,
storyGraphBinding?: import('./knowledge-mcp-gateway').StoryGraphRemoteBinding
): Promise<RemotePromptOperationAcceptance>
/** Starts an Agent-owned ACP prompt exactly once for its stable operation. */
startOwnedPrompt?(
request: RemoteOwnedPromptStartRequest
): Promise<RemoteOwnedPromptStartResult>
/** Reattaches to an already accepted Agent-owned prompt without replaying it. */
attachOwnedPrompt?(
request: RemoteOwnedPromptAttachRequest
): Promise<RemoteOwnedPromptStartResult>
pageOwnedPromptTranscript?(
request: RemoteSemanticTranscriptPageRequest
): Promise<RemoteSemanticTranscriptPageResult>
ackOwnedPromptTranscript?(
request: RemoteSemanticTranscriptAckRequest
): Promise<RemoteSemanticTranscriptAckResult>
/**
* Bounds same-daemon stream recovery to the signed prompt lifetime and
* caller cancellation. Implementations that cannot recover may omit it.
*/
setRecoveryBoundary?(
deadlineAt: string,
signal: AbortSignal
): void
clearRecoveryBoundary?(): void
/**
* Allocates and registers a fresh Main-owned model channel for one prompt.
* Legacy channels may omit this when the runtime is configured without a
* model bridge.
*/
openModelBridge?(
request: RemoteModelBridgeOpenRequest
): Promise<RemoteModelBridgeSession>
completePromptOperation(
operation: RemotePromptOperationCompletion
): Promise<RemotePromptOperationCompletionResult>
/**
* Stops or resumes inbound ACP delivery. Pausing must take effect before
* the returned promise resolves and must not advance the Main ACK cursor.
*/
setInboundPaused(paused: boolean): Promise<void>
escalateCancellation(
escalation: RemoteCancellationEscalation
): Promise<void>
reconcilePromptOperation(
operation: RemotePromptOperationReconciliation
): Promise<RemotePromptOperationReconciliationResult>
close(): Promise<void>
}
export class StaleRemoteRuntimeGenerationError extends Error {
constructor() {
super('远端 Runtime 连接代际已失效')
this.name = 'StaleRemoteRuntimeGenerationError'
}
}
export function isDefinitiveRemoteRuntimeRequestRejection(
error: unknown,
method: string
): boolean {
if (
error === null ||
typeof error !== 'object' ||
!('remoteRequestOutcome' in error) ||
error.remoteRequestOutcome !== 'rejected' ||
!('remoteMethod' in error) ||
error.remoteMethod !== method
) {
return false
}
return true
}
export function assertCurrentRemoteRuntimeGeneration(
channel: RemoteRuntimeChannel
): void {
if (!channel.isCurrentGeneration()) {
throw new StaleRemoteRuntimeGenerationError()
}
}