Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions packages/acp-bridge/src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,50 @@ function broadcastPromptCancelledOnce(
broadcastPromptCancelled(entry, sessionId, originatorClientId, reason);
}

function broadcastTurnComplete(
Comment thread
chiga0 marked this conversation as resolved.
entry: SessionEntry,
sessionId: string,
promptResult: { stopReason?: string; [k: string]: unknown },
promptId: string | undefined,
originatorClientId: string | undefined,
): void {
entry.events.publish({
type: 'turn_complete',
data: {
sessionId,
stopReason: promptResult.stopReason ?? 'end_turn',
...(promptId ? { promptId } : {}),
},
...(originatorClientId ? { originatorClientId } : {}),
});
}

function broadcastTurnError(
entry: SessionEntry,
sessionId: string,
err: unknown,
Comment thread
chiga0 marked this conversation as resolved.
promptId: string | undefined,
originatorClientId: string | undefined,
): void {
const message = err instanceof Error ? err.message : String(err);
const code =
err instanceof Error &&
'code' in err &&
typeof (err as Error & { code?: unknown }).code === 'string'
? (err as Error & { code: string }).code
: undefined;
entry.events.publish({
type: 'turn_error',
data: {
sessionId,
message,
...(code ? { code } : {}),
...(promptId ? { promptId } : {}),
},
...(originatorClientId ? { originatorClientId } : {}),
});
}

function hasControlCharacter(value: string): boolean {
for (let i = 0; i < value.length; i += 1) {
const code = value.charCodeAt(i);
Expand Down Expand Up @@ -2263,6 +2307,28 @@ export function createHttpAcpBridge(opts: BridgeOptions): HttpAcpBridge {
}
return racedPromise;
});
const promptId = context?.promptId;
result.then(
(promptResult) => {
broadcastTurnComplete(
entry,
sessionId,
promptResult,
promptId,
originatorClientId,
);
},
(err) => {
if (err instanceof DOMException && err.name === 'AbortError') return;
Comment thread
chiga0 marked this conversation as resolved.
broadcastTurnError(
entry,
sessionId,
err,
promptId,
originatorClientId,
);
},
);
// Tail swallows failures so subsequent prompts still run. The caller
// still sees rejections on its own `result` reference.
entry.promptQueue = result.then(
Expand Down Expand Up @@ -2339,6 +2405,12 @@ export function createHttpAcpBridge(opts: BridgeOptions): HttpAcpBridge {
return entry.events.subscribe(subOpts);
},

getSessionLastEventId(sessionId) {
const entry = byId.get(sessionId);
if (!entry) throw new SessionNotFoundError(sessionId);
return entry.events.lastEventId;
},

respondToPermission(requestId, response, context) {
// F3 Commit 3 — legacy workspace-level vote route. Look up the
// session via mediator's resolved+pending peek, forward to
Expand Down
14 changes: 14 additions & 0 deletions packages/acp-bridge/src/bridgeTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ export interface BridgeClientRequestContext {
* dedicated daemon or `designated` policy instead).
*/
fromLoopback?: boolean;
/**
* Caller-generated correlation id for non-blocking prompt mode.
* When present, the bridge stamps `turn_complete` / `turn_error` events
* with this id so the SDK's `prompt()` can match the SSE event to the
* pending HTTP 202 request.
*/
promptId?: string;
}

/**
Expand Down Expand Up @@ -191,6 +198,13 @@ export interface HttpAcpBridge {
opts?: SubscribeOptions,
): AsyncIterable<BridgeEvent>;

/**
* Return the most recent monotonic event id for this session's bus.
* Used by non-blocking prompt responses to tell the client where to
* start SSE replay so no events are missed.
*/
getSessionLastEventId(sessionId: string): number;

/**
* Explicitly close a live session. Force-closes even when other clients
* are attached. Throws `SessionNotFoundError` for unknown ids.
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/serve/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ export const SERVE_CAPABILITY_REGISTRY = {
},
prompt_absolute_deadline: { since: 'v1' },
writer_idle_timeout: { since: 'v1' },
non_blocking_prompt: { since: 'v1' },
} as const satisfies Record<string, ServeCapabilityDescriptor>;

export type ServeFeature = keyof typeof SERVE_CAPABILITY_REGISTRY;
Expand Down
Loading