Merge remote-tracking branch 'origin/master' into codex/trim-ai-prose
# Conflicts: # docs/config-catalog.md # docs/cordis-catalog/services.md # docs/rfc/proposed/feature/2026-07-08-interactive-side-sessions.md # docs/rfc/proposed/simplification/2026-06-20-unify-agent-and-session-id.md # examples/coding-agent/tests/keyless-smoke.e2e.ts # packages/ui/acp/src/index.ts
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Multi-session ACP server bridge over JSON-RPC stdio. Creates or resumes
|
||||
* agents, routes their events, settles prompts by turn, and answers approvals.
|
||||
* Stdout is reserved for protocol frames.
|
||||
* Each session keeps independent presentation and prompt-correlation state so
|
||||
* concurrent streams cannot cross. Stdout is reserved for protocol frames.
|
||||
* @module @deepseek-ai/dsh-acp
|
||||
*/
|
||||
|
||||
@@ -72,7 +73,8 @@ import {
|
||||
} from './codec.ts'
|
||||
|
||||
export const name = 'acp'
|
||||
// Interface services required by advertised load, presentation, and interaction capabilities.
|
||||
// Interface services back advertised loading, tool-owned presentation with a generic fallback, and interaction.
|
||||
// TODO(acp-session-inject): remove `sessions`; the bridge never reads it, and ownership is already behind `agents`.
|
||||
export const inject = ['agents', 'sessions', 'sessionPersistence', 'tools', 'userInteraction']
|
||||
|
||||
/** Build an ACP invalid-params error with visible human detail. */
|
||||
@@ -227,7 +229,10 @@ interface SessionRecord {
|
||||
reject: (error: Error) => void
|
||||
turn: number | undefined
|
||||
} | undefined
|
||||
/** Idle config changes awaiting a turn-enclosed log anchor; last write wins. */
|
||||
/**
|
||||
* Idle config changes awaiting a turn-enclosed log anchor; last write wins.
|
||||
* Responses overlay them, but a restart before the next turn restores the logged fold.
|
||||
*/
|
||||
pendingSwitches: { sandboxMode?: SandboxMode; approvalPolicy?: ApprovalPolicy }
|
||||
}
|
||||
|
||||
@@ -238,7 +243,7 @@ interface SessionRecord {
|
||||
* correlation in a `finally` so presentation failure cannot starve settlement.
|
||||
*/
|
||||
export function apply(ctx: Context, config: AcpConfig): void {
|
||||
// Capture injected services while executing inside this plugin's fiber.
|
||||
// Handlers run later outside this injection scope, so capture services now.
|
||||
const agents = ctx.agents
|
||||
const sessionPersistence = ctx.sessionPersistence
|
||||
const logger = ctx.logger
|
||||
@@ -247,7 +252,9 @@ export function apply(ctx: Context, config: AcpConfig): void {
|
||||
// Presenter failures are logged and contained per session or replay.
|
||||
const makePresenter = (agent?: Agent): ToolPresenter => new ToolPresenter(tools, (message) => { logger.warn(message) }, agent)
|
||||
|
||||
// Keep forward and reverse session indexes in lockstep.
|
||||
// TODO(derive-acp-session-id): derive event ids from `agent.session`, verify ownership, then remove the reverse map.
|
||||
// Agent events currently carry only the Agent, so retain `SessionRecord.sessionId` and update both indexes together.
|
||||
// Dropping the forward record lets the weak reverse entry expire.
|
||||
const sessions = new Map<SessionId, SessionRecord>()
|
||||
const bySession = new WeakMap<Agent, SessionId>()
|
||||
// Reserve ids across asynchronous resume; distinct ids still load concurrently.
|
||||
@@ -1025,6 +1032,7 @@ export function streamSessionEventUpdate(
|
||||
|
||||
/**
|
||||
* Map a whole harness todo list to an ACP plan, assigning medium priority.
|
||||
* Statuses map directly and ACP replaces its whole plan on each update.
|
||||
* @param todos - the harness todo list (the whole list, not a diff).
|
||||
* @returns the ACP plan body, one entry per todo.
|
||||
*/
|
||||
@@ -1045,6 +1053,7 @@ const noTerminalRendering: TerminalRendering = { enabled: false, cwd: undefined
|
||||
/**
|
||||
* Resolve tool-owned call/result views with generic fallbacks. Per-session
|
||||
* call-id state supplies the tool name and arguments omitted from result events.
|
||||
* Each entry is consumed by its result; any remainder dies with the session.
|
||||
*/
|
||||
export class ToolPresenter {
|
||||
private readonly pending = new Map<CallId, { name: string; args: unknown; card: ToolCallView['card'] }>()
|
||||
|
||||
Reference in New Issue
Block a user