@tanstack/ai-acp 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"translate.js","sources":["../../../src/stream/translate.ts"],"sourcesContent":["import { EventType, buildBaseUsage } from '@tanstack/ai'\nimport type { StreamChunk, TokenUsage } from '@tanstack/ai'\nimport type {\n AcpSessionUpdate,\n AcpStopReason,\n AcpToolCallUpdate,\n AcpUsage,\n} from '../types/acp-types'\n\nexport const BRIDGED_MCP_SERVER_NAME = 'tanstack'\n\nexport type AcpStreamEvent =\n | { kind: 'session'; sessionId: string }\n | { kind: 'update'; update: AcpSessionUpdate }\n | { kind: 'done'; stopReason: AcpStopReason; usage?: AcpUsage }\n\nexport interface AcpTranslateLabels {\n sessionIdEvent: string\n planEvent?: string\n refusalMessage?: string\n /**\n * When set, non-text agent message content (image / audio / resource /\n * resource_link blocks) is surfaced as a CUSTOM event under this name instead\n * of being dropped. Each event's `value` is `{ content: <ACP content block> }`.\n * Omit to keep the text-only behavior.\n */\n contentEvent?: string\n}\n\nexport interface TranslateContext {\n model: string\n runId: string\n threadId: string\n parentRunId?: string\n genId: () => string\n labels: AcpTranslateLabels\n bridgedToolNames?: ReadonlySet<string>\n onAcpEvent?: (event: AcpStreamEvent) => void\n}\n\nexport function matchBridgedToolName(\n title: string | null | undefined,\n bridgedToolNames: ReadonlySet<string> | undefined,\n): string | undefined {\n if (!title || !bridgedToolNames) return undefined\n if (bridgedToolNames.has(title)) return title\n for (const name of bridgedToolNames) {\n if (title.startsWith(`${name} (`)) return name\n }\n return undefined\n}\n\nfunction resolveToolName(\n update: AcpToolCallUpdate,\n bridgedToolNames: ReadonlySet<string> | undefined,\n): string {\n return (\n matchBridgedToolName(update.title, bridgedToolNames) ??\n update.kind ??\n 'tool'\n )\n}\n\nfunction stringifyToolOutput(update: AcpToolCallUpdate): string {\n if (update.rawOutput !== undefined) {\n return typeof update.rawOutput === 'string'\n ? update.rawOutput\n : JSON.stringify(update.rawOutput)\n }\n const blocks = update.content ?? []\n const text = blocks\n .map((block) =>\n block.content && typeof block.content.text === 'string'\n ? block.content.text\n : '',\n )\n .join('')\n // Preserve non-text tool content (diff / terminal / image / resource blocks)\n // by serializing the structured array, rather than collapsing to a stub.\n const hasNonText = blocks.some(\n (block) =>\n block.type !== 'content' ||\n (block.content !== undefined && block.content.type !== 'text'),\n )\n if (hasNonText) return JSON.stringify(blocks)\n if (text !== '') return text\n return JSON.stringify({ status: update.status ?? 'completed' })\n}\n\nfunction buildUsage(usage: AcpUsage | undefined): TokenUsage | undefined {\n if (!usage) return undefined\n const promptTokens = usage.inputTokens ?? 0\n const completionTokens = usage.outputTokens ?? 0\n const result = buildBaseUsage({\n promptTokens,\n completionTokens,\n totalTokens: usage.totalTokens ?? promptTokens + completionTokens,\n })\n if (usage.cachedReadTokens) {\n result.promptTokensDetails = { cachedTokens: usage.cachedReadTokens }\n }\n if (usage.thoughtTokens) {\n result.completionTokensDetails = { reasoningTokens: usage.thoughtTokens }\n }\n return result\n}\n\nexport async function* translateAcpStream(\n events: AsyncIterable<AcpStreamEvent>,\n ctx: TranslateContext,\n): AsyncIterable<StreamChunk> {\n const { model, runId, threadId, genId, labels } = ctx\n const now = () => Date.now()\n\n let runStarted = false\n const unresolvedToolCalls = new Set<string>()\n const knownToolCalls = new Set<string>()\n\n let textMessageId: string | null = null\n let textContent = ''\n let reasoningId: string | null = null\n\n function* startRun(): Generator<StreamChunk> {\n if (runStarted) return\n runStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: now(),\n ...(ctx.parentRunId !== undefined && { parentRunId: ctx.parentRunId }),\n }\n }\n\n function* closeText(): Generator<StreamChunk> {\n if (textMessageId !== null) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: textMessageId,\n model,\n timestamp: now(),\n }\n }\n textMessageId = null\n textContent = ''\n }\n\n function* closeReasoning(): Generator<StreamChunk> {\n if (reasoningId !== null) {\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n }\n reasoningId = null\n }\n\n function* synthesizeUnresolvedResults(): Generator<StreamChunk> {\n for (const toolCallId of unresolvedToolCalls) {\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId,\n messageId: genId(),\n model,\n timestamp: now(),\n content: JSON.stringify({ status: 'interrupted' }),\n }\n }\n unresolvedToolCalls.clear()\n }\n\n function* openToolCall(update: AcpToolCallUpdate): Generator<StreamChunk> {\n if (knownToolCalls.has(update.toolCallId)) return\n knownToolCalls.add(update.toolCallId)\n const toolCallName = resolveToolName(update, ctx.bridgedToolNames)\n const input = {\n ...(update.title != null && { title: update.title }),\n ...(update.rawInput !== undefined && update.rawInput !== null\n ? typeof update.rawInput === 'object'\n ? (update.rawInput as Record<string, unknown>)\n : { input: update.rawInput }\n : {}),\n }\n const args = JSON.stringify(input)\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: update.toolCallId,\n toolCallName,\n toolName: toolCallName,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: update.toolCallId,\n model,\n timestamp: now(),\n delta: args,\n args,\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: update.toolCallId,\n toolCallName,\n toolName: toolCallName,\n model,\n timestamp: now(),\n input,\n }\n unresolvedToolCalls.add(update.toolCallId)\n }\n\n function* resolveToolCall(update: AcpToolCallUpdate): Generator<StreamChunk> {\n yield* openToolCall(update)\n unresolvedToolCalls.delete(update.toolCallId)\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId: update.toolCallId,\n messageId: genId(),\n model,\n timestamp: now(),\n content: stringifyToolOutput(update),\n ...(update.status === 'failed' && { state: 'output-error' as const }),\n }\n }\n\n function* handleUpdate(update: AcpSessionUpdate): Generator<StreamChunk> {\n if (update.sessionUpdate === 'agent_message_chunk') {\n yield* closeReasoning()\n // Non-text content (image / audio / resource / resource_link): surface it\n // as a CUSTOM event when the harness opted in, instead of dropping it.\n if (update.content.type !== 'text') {\n if (labels.contentEvent !== undefined) {\n yield* closeText()\n yield {\n type: EventType.CUSTOM,\n model,\n timestamp: now(),\n name: labels.contentEvent,\n value: { content: update.content },\n }\n }\n return\n }\n const text =\n typeof update.content.text === 'string' ? update.content.text : ''\n if (text === '') return\n if (textMessageId === null) {\n textMessageId = genId()\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: textMessageId,\n model,\n timestamp: now(),\n role: 'assistant',\n }\n }\n textContent += text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: textMessageId,\n model,\n timestamp: now(),\n delta: text,\n content: textContent,\n }\n } else if (update.sessionUpdate === 'agent_thought_chunk') {\n yield* closeText()\n const thought =\n typeof update.content.text === 'string' ? update.content.text : ''\n if (thought === '') return\n if (reasoningId === null) {\n reasoningId = genId()\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningId,\n role: 'reasoning' as const,\n model,\n timestamp: now(),\n }\n }\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningId,\n delta: thought,\n model,\n timestamp: now(),\n }\n } else if (update.sessionUpdate === 'tool_call') {\n yield* closeText()\n yield* closeReasoning()\n yield* openToolCall(update)\n if (update.status === 'completed' || update.status === 'failed') {\n yield* resolveToolCall(update)\n }\n } else if (update.sessionUpdate === 'tool_call_update') {\n if (update.status === 'completed' || update.status === 'failed') {\n yield* resolveToolCall(update)\n } else if (\n update.status === 'in_progress' &&\n update.rawInput !== undefined\n ) {\n yield* closeText()\n yield* closeReasoning()\n if (!knownToolCalls.has(update.toolCallId)) {\n yield* openToolCall(update)\n } else {\n const input = {\n ...(update.title != null && { title: update.title }),\n ...(typeof update.rawInput === 'object' && update.rawInput !== null\n ? (update.rawInput as Record<string, unknown>)\n : { input: update.rawInput }),\n }\n const args = JSON.stringify(input)\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: update.toolCallId,\n model,\n timestamp: now(),\n delta: args,\n args,\n }\n }\n }\n } else if (\n update.sessionUpdate === 'plan' &&\n labels.planEvent !== undefined\n ) {\n yield {\n type: EventType.CUSTOM,\n model,\n timestamp: now(),\n name: labels.planEvent,\n value: { entries: update.entries },\n }\n }\n }\n\n try {\n for await (const event of events) {\n ctx.onAcpEvent?.(event)\n\n if (event.kind === 'session') {\n yield* startRun()\n yield {\n type: EventType.CUSTOM,\n model,\n timestamp: now(),\n name: labels.sessionIdEvent,\n value: { sessionId: event.sessionId },\n }\n } else if (event.kind === 'update') {\n yield* startRun()\n yield* handleUpdate(event.update)\n } else {\n yield* startRun()\n yield* closeText()\n yield* closeReasoning()\n yield* synthesizeUnresolvedResults()\n\n if (event.stopReason === 'refusal') {\n const message =\n labels.refusalMessage ?? 'The harness refused the request.'\n yield {\n type: EventType.RUN_ERROR,\n model,\n timestamp: now(),\n message,\n code: 'refusal',\n error: { message, code: 'refusal' },\n }\n } else {\n const usage = buildUsage(event.usage)\n const finishReason =\n event.stopReason === 'max_tokens' ||\n event.stopReason === 'max_turn_requests'\n ? ('length' as const)\n : ('stop' as const)\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: now(),\n finishReason,\n ...(usage !== undefined && { usage }),\n }\n }\n }\n }\n } catch (error) {\n yield* closeText()\n yield* closeReasoning()\n yield* synthesizeUnresolvedResults()\n throw error\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"translate.js","names":[],"sources":["../../../src/stream/translate.ts"],"sourcesContent":["import { EventType, buildBaseUsage } from '@tanstack/ai'\nimport type { StreamChunk, TokenUsage } from '@tanstack/ai'\nimport type {\n AcpSessionUpdate,\n AcpStopReason,\n AcpToolCallUpdate,\n AcpUsage,\n} from '../types/acp-types'\n\nexport const BRIDGED_MCP_SERVER_NAME = 'tanstack'\n\nexport type AcpStreamEvent =\n | { kind: 'session'; sessionId: string }\n | { kind: 'update'; update: AcpSessionUpdate }\n | { kind: 'done'; stopReason: AcpStopReason; usage?: AcpUsage }\n\nexport interface AcpTranslateLabels {\n sessionIdEvent: string\n planEvent?: string\n refusalMessage?: string\n /**\n * When set, non-text agent message content (image / audio / resource /\n * resource_link blocks) is surfaced as a CUSTOM event under this name instead\n * of being dropped. Each event's `value` is `{ content: <ACP content block> }`.\n * Omit to keep the text-only behavior.\n */\n contentEvent?: string\n}\n\nexport interface TranslateContext {\n model: string\n runId: string\n threadId: string\n parentRunId?: string\n genId: () => string\n labels: AcpTranslateLabels\n bridgedToolNames?: ReadonlySet<string>\n onAcpEvent?: (event: AcpStreamEvent) => void\n}\n\nexport function matchBridgedToolName(\n title: string | null | undefined,\n bridgedToolNames: ReadonlySet<string> | undefined,\n): string | undefined {\n if (!title || !bridgedToolNames) return undefined\n if (bridgedToolNames.has(title)) return title\n for (const name of bridgedToolNames) {\n if (title.startsWith(`${name} (`)) return name\n }\n return undefined\n}\n\nfunction resolveToolName(\n update: AcpToolCallUpdate,\n bridgedToolNames: ReadonlySet<string> | undefined,\n): string {\n return (\n matchBridgedToolName(update.title, bridgedToolNames) ??\n update.kind ??\n 'tool'\n )\n}\n\nfunction stringifyToolOutput(update: AcpToolCallUpdate): string {\n if (update.rawOutput !== undefined) {\n return typeof update.rawOutput === 'string'\n ? update.rawOutput\n : JSON.stringify(update.rawOutput)\n }\n const blocks = update.content ?? []\n const text = blocks\n .map((block) =>\n block.content && typeof block.content.text === 'string'\n ? block.content.text\n : '',\n )\n .join('')\n // Preserve non-text tool content (diff / terminal / image / resource blocks)\n // by serializing the structured array, rather than collapsing to a stub.\n const hasNonText = blocks.some(\n (block) =>\n block.type !== 'content' ||\n (block.content !== undefined && block.content.type !== 'text'),\n )\n if (hasNonText) return JSON.stringify(blocks)\n if (text !== '') return text\n return JSON.stringify({ status: update.status ?? 'completed' })\n}\n\nfunction buildUsage(usage: AcpUsage | undefined): TokenUsage | undefined {\n if (!usage) return undefined\n const promptTokens = usage.inputTokens ?? 0\n const completionTokens = usage.outputTokens ?? 0\n const result = buildBaseUsage({\n promptTokens,\n completionTokens,\n totalTokens: usage.totalTokens ?? promptTokens + completionTokens,\n })\n if (usage.cachedReadTokens) {\n result.promptTokensDetails = { cachedTokens: usage.cachedReadTokens }\n }\n if (usage.thoughtTokens) {\n result.completionTokensDetails = { reasoningTokens: usage.thoughtTokens }\n }\n return result\n}\n\nexport async function* translateAcpStream(\n events: AsyncIterable<AcpStreamEvent>,\n ctx: TranslateContext,\n): AsyncIterable<StreamChunk> {\n const { model, runId, threadId, genId, labels } = ctx\n const now = () => Date.now()\n\n let runStarted = false\n const unresolvedToolCalls = new Set<string>()\n const knownToolCalls = new Set<string>()\n\n let textMessageId: string | null = null\n let textContent = ''\n let reasoningId: string | null = null\n\n function* startRun(): Generator<StreamChunk> {\n if (runStarted) return\n runStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId,\n threadId,\n model,\n timestamp: now(),\n ...(ctx.parentRunId !== undefined && { parentRunId: ctx.parentRunId }),\n }\n }\n\n function* closeText(): Generator<StreamChunk> {\n if (textMessageId !== null) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: textMessageId,\n model,\n timestamp: now(),\n }\n }\n textMessageId = null\n textContent = ''\n }\n\n function* closeReasoning(): Generator<StreamChunk> {\n if (reasoningId !== null) {\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n }\n reasoningId = null\n }\n\n function* synthesizeUnresolvedResults(): Generator<StreamChunk> {\n for (const toolCallId of unresolvedToolCalls) {\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId,\n messageId: genId(),\n model,\n timestamp: now(),\n content: JSON.stringify({ status: 'interrupted' }),\n }\n }\n unresolvedToolCalls.clear()\n }\n\n function* openToolCall(update: AcpToolCallUpdate): Generator<StreamChunk> {\n if (knownToolCalls.has(update.toolCallId)) return\n knownToolCalls.add(update.toolCallId)\n const toolCallName = resolveToolName(update, ctx.bridgedToolNames)\n const input = {\n ...(update.title != null && { title: update.title }),\n ...(update.rawInput !== undefined && update.rawInput !== null\n ? typeof update.rawInput === 'object'\n ? (update.rawInput as Record<string, unknown>)\n : { input: update.rawInput }\n : {}),\n }\n const args = JSON.stringify(input)\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: update.toolCallId,\n toolCallName,\n toolName: toolCallName,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: update.toolCallId,\n model,\n timestamp: now(),\n delta: args,\n args,\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: update.toolCallId,\n toolCallName,\n toolName: toolCallName,\n model,\n timestamp: now(),\n input,\n }\n unresolvedToolCalls.add(update.toolCallId)\n }\n\n function* resolveToolCall(update: AcpToolCallUpdate): Generator<StreamChunk> {\n yield* openToolCall(update)\n unresolvedToolCalls.delete(update.toolCallId)\n yield {\n type: EventType.TOOL_CALL_RESULT,\n toolCallId: update.toolCallId,\n messageId: genId(),\n model,\n timestamp: now(),\n content: stringifyToolOutput(update),\n ...(update.status === 'failed' && { state: 'output-error' as const }),\n }\n }\n\n function* handleUpdate(update: AcpSessionUpdate): Generator<StreamChunk> {\n if (update.sessionUpdate === 'agent_message_chunk') {\n yield* closeReasoning()\n // Non-text content (image / audio / resource / resource_link): surface it\n // as a CUSTOM event when the harness opted in, instead of dropping it.\n if (update.content.type !== 'text') {\n if (labels.contentEvent !== undefined) {\n yield* closeText()\n yield {\n type: EventType.CUSTOM,\n model,\n timestamp: now(),\n name: labels.contentEvent,\n value: { content: update.content },\n }\n }\n return\n }\n const text =\n typeof update.content.text === 'string' ? update.content.text : ''\n if (text === '') return\n if (textMessageId === null) {\n textMessageId = genId()\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: textMessageId,\n model,\n timestamp: now(),\n role: 'assistant',\n }\n }\n textContent += text\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: textMessageId,\n model,\n timestamp: now(),\n delta: text,\n content: textContent,\n }\n } else if (update.sessionUpdate === 'agent_thought_chunk') {\n yield* closeText()\n const thought =\n typeof update.content.text === 'string' ? update.content.text : ''\n if (thought === '') return\n if (reasoningId === null) {\n reasoningId = genId()\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningId,\n model,\n timestamp: now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningId,\n role: 'reasoning' as const,\n model,\n timestamp: now(),\n }\n }\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningId,\n delta: thought,\n model,\n timestamp: now(),\n }\n } else if (update.sessionUpdate === 'tool_call') {\n yield* closeText()\n yield* closeReasoning()\n yield* openToolCall(update)\n if (update.status === 'completed' || update.status === 'failed') {\n yield* resolveToolCall(update)\n }\n } else if (update.sessionUpdate === 'tool_call_update') {\n if (update.status === 'completed' || update.status === 'failed') {\n yield* resolveToolCall(update)\n } else if (\n update.status === 'in_progress' &&\n update.rawInput !== undefined\n ) {\n yield* closeText()\n yield* closeReasoning()\n if (!knownToolCalls.has(update.toolCallId)) {\n yield* openToolCall(update)\n } else {\n const input = {\n ...(update.title != null && { title: update.title }),\n ...(typeof update.rawInput === 'object' && update.rawInput !== null\n ? (update.rawInput as Record<string, unknown>)\n : { input: update.rawInput }),\n }\n const args = JSON.stringify(input)\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: update.toolCallId,\n model,\n timestamp: now(),\n delta: args,\n args,\n }\n }\n }\n } else if (\n update.sessionUpdate === 'plan' &&\n labels.planEvent !== undefined\n ) {\n yield {\n type: EventType.CUSTOM,\n model,\n timestamp: now(),\n name: labels.planEvent,\n value: { entries: update.entries },\n }\n }\n }\n\n try {\n for await (const event of events) {\n ctx.onAcpEvent?.(event)\n\n if (event.kind === 'session') {\n yield* startRun()\n yield {\n type: EventType.CUSTOM,\n model,\n timestamp: now(),\n name: labels.sessionIdEvent,\n value: { sessionId: event.sessionId },\n }\n } else if (event.kind === 'update') {\n yield* startRun()\n yield* handleUpdate(event.update)\n } else {\n yield* startRun()\n yield* closeText()\n yield* closeReasoning()\n yield* synthesizeUnresolvedResults()\n\n if (event.stopReason === 'refusal') {\n const message =\n labels.refusalMessage ?? 'The harness refused the request.'\n yield {\n type: EventType.RUN_ERROR,\n model,\n timestamp: now(),\n message,\n code: 'refusal',\n error: { message, code: 'refusal' },\n }\n } else {\n const usage = buildUsage(event.usage)\n const finishReason =\n event.stopReason === 'max_tokens' ||\n event.stopReason === 'max_turn_requests'\n ? ('length' as const)\n : ('stop' as const)\n yield {\n type: EventType.RUN_FINISHED,\n runId,\n threadId,\n model,\n timestamp: now(),\n finishReason,\n ...(usage !== undefined && { usage }),\n }\n }\n }\n }\n } catch (error) {\n yield* closeText()\n yield* closeReasoning()\n yield* synthesizeUnresolvedResults()\n throw error\n }\n}\n"],"mappings":"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"}
@@ -1,34 +1,23 @@
1
+ //#region src/transport/resolve.ts
2
+ /**
3
+ * Pick stdio vs WebSocket for ACP based on sandbox capabilities and caller
4
+ * preference.
5
+ */
1
6
  function resolveAcpTransportMode(sandbox, preference = "auto") {
2
- if (preference === "stdio") {
3
- if (sandbox.capabilities.writableStdin !== true) {
4
- throw new Error(
5
- `${sandbox.provider}: ACP stdio transport requires capabilities.writableStdin, but this sandbox cannot write to process stdin. Use transport: 'websocket' or 'auto'.`
6
- );
7
- }
8
- return "stdio";
9
- }
10
- if (preference === "websocket") {
11
- if (sandbox.capabilities.ports !== true) {
12
- throw new Error(
13
- `${sandbox.provider}: ACP WebSocket transport requires capabilities.ports so the harness server can be reached from the orchestrator.`
14
- );
15
- }
16
- if (sandbox.capabilities.backgroundProcesses !== true) {
17
- throw new Error(
18
- `${sandbox.provider}: ACP WebSocket transport requires capabilities.backgroundProcesses to run the in-sandbox harness server.`
19
- );
20
- }
21
- return "websocket";
22
- }
23
- if (sandbox.capabilities.writableStdin === true) return "stdio";
24
- if (sandbox.capabilities.ports === true && sandbox.capabilities.backgroundProcesses === true) {
25
- return "websocket";
26
- }
27
- throw new Error(
28
- `${sandbox.provider}: cannot run ACP — no writable stdin and no port-exposed background-process support. Use a Docker/local-process sandbox or enable ports on the provider.`
29
- );
7
+ if (preference === "stdio") {
8
+ if (sandbox.capabilities.writableStdin !== true) throw new Error(`${sandbox.provider}: ACP stdio transport requires capabilities.writableStdin, but this sandbox cannot write to process stdin. Use transport: 'websocket' or 'auto'.`);
9
+ return "stdio";
10
+ }
11
+ if (preference === "websocket") {
12
+ if (sandbox.capabilities.ports !== true) throw new Error(`${sandbox.provider}: ACP WebSocket transport requires capabilities.ports so the harness server can be reached from the orchestrator.`);
13
+ if (sandbox.capabilities.backgroundProcesses !== true) throw new Error(`${sandbox.provider}: ACP WebSocket transport requires capabilities.backgroundProcesses to run the in-sandbox harness server.`);
14
+ return "websocket";
15
+ }
16
+ if (sandbox.capabilities.writableStdin === true) return "stdio";
17
+ if (sandbox.capabilities.ports === true && sandbox.capabilities.backgroundProcesses === true) return "websocket";
18
+ throw new Error(`${sandbox.provider}: cannot run ACP — no writable stdin and no port-exposed background-process support. Use a Docker/local-process sandbox or enable ports on the provider.`);
30
19
  }
31
- export {
32
- resolveAcpTransportMode
33
- };
34
- //# sourceMappingURL=resolve.js.map
20
+ //#endregion
21
+ export { resolveAcpTransportMode };
22
+
23
+ //# sourceMappingURL=resolve.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sources":["../../../src/transport/resolve.ts"],"sourcesContent":["import type { SandboxHandle } from '@tanstack/ai-sandbox'\nimport type { AcpTransportPreference } from './types'\n\n/**\n * Pick stdio vs WebSocket for ACP based on sandbox capabilities and caller\n * preference.\n */\nexport function resolveAcpTransportMode(\n sandbox: SandboxHandle,\n preference: AcpTransportPreference = 'auto',\n): 'stdio' | 'websocket' {\n if (preference === 'stdio') {\n if (sandbox.capabilities.writableStdin !== true) {\n throw new Error(\n `${sandbox.provider}: ACP stdio transport requires capabilities.writableStdin, but this sandbox cannot write to process stdin. Use transport: 'websocket' or 'auto'.`,\n )\n }\n return 'stdio'\n }\n\n if (preference === 'websocket') {\n if (sandbox.capabilities.ports !== true) {\n throw new Error(\n `${sandbox.provider}: ACP WebSocket transport requires capabilities.ports so the harness server can be reached from the orchestrator.`,\n )\n }\n if (sandbox.capabilities.backgroundProcesses !== true) {\n throw new Error(\n `${sandbox.provider}: ACP WebSocket transport requires capabilities.backgroundProcesses to run the in-sandbox harness server.`,\n )\n }\n return 'websocket'\n }\n\n if (sandbox.capabilities.writableStdin === true) return 'stdio'\n if (\n sandbox.capabilities.ports === true &&\n sandbox.capabilities.backgroundProcesses === true\n ) {\n return 'websocket'\n }\n\n throw new Error(\n `${sandbox.provider}: cannot run ACP — no writable stdin and no port-exposed background-process support. Use a Docker/local-process sandbox or enable ports on the provider.`,\n )\n}\n"],"names":[],"mappings":"AAOO,SAAS,wBACd,SACA,aAAqC,QACd;AACvB,MAAI,eAAe,SAAS;AAC1B,QAAI,QAAQ,aAAa,kBAAkB,MAAM;AAC/C,YAAM,IAAI;AAAA,QACR,GAAG,QAAQ,QAAQ;AAAA,MAAA;AAAA,IAEvB;AACA,WAAO;AAAA,EACT;AAEA,MAAI,eAAe,aAAa;AAC9B,QAAI,QAAQ,aAAa,UAAU,MAAM;AACvC,YAAM,IAAI;AAAA,QACR,GAAG,QAAQ,QAAQ;AAAA,MAAA;AAAA,IAEvB;AACA,QAAI,QAAQ,aAAa,wBAAwB,MAAM;AACrD,YAAM,IAAI;AAAA,QACR,GAAG,QAAQ,QAAQ;AAAA,MAAA;AAAA,IAEvB;AACA,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,aAAa,kBAAkB,KAAM,QAAO;AACxD,MACE,QAAQ,aAAa,UAAU,QAC/B,QAAQ,aAAa,wBAAwB,MAC7C;AACA,WAAO;AAAA,EACT;AAEA,QAAM,IAAI;AAAA,IACR,GAAG,QAAQ,QAAQ;AAAA,EAAA;AAEvB;"}
1
+ {"version":3,"file":"resolve.js","names":[],"sources":["../../../src/transport/resolve.ts"],"sourcesContent":["import type { SandboxHandle } from '@tanstack/ai-sandbox'\nimport type { AcpTransportPreference } from './types'\n\n/**\n * Pick stdio vs WebSocket for ACP based on sandbox capabilities and caller\n * preference.\n */\nexport function resolveAcpTransportMode(\n sandbox: SandboxHandle,\n preference: AcpTransportPreference = 'auto',\n): 'stdio' | 'websocket' {\n if (preference === 'stdio') {\n if (sandbox.capabilities.writableStdin !== true) {\n throw new Error(\n `${sandbox.provider}: ACP stdio transport requires capabilities.writableStdin, but this sandbox cannot write to process stdin. Use transport: 'websocket' or 'auto'.`,\n )\n }\n return 'stdio'\n }\n\n if (preference === 'websocket') {\n if (sandbox.capabilities.ports !== true) {\n throw new Error(\n `${sandbox.provider}: ACP WebSocket transport requires capabilities.ports so the harness server can be reached from the orchestrator.`,\n )\n }\n if (sandbox.capabilities.backgroundProcesses !== true) {\n throw new Error(\n `${sandbox.provider}: ACP WebSocket transport requires capabilities.backgroundProcesses to run the in-sandbox harness server.`,\n )\n }\n return 'websocket'\n }\n\n if (sandbox.capabilities.writableStdin === true) return 'stdio'\n if (\n sandbox.capabilities.ports === true &&\n sandbox.capabilities.backgroundProcesses === true\n ) {\n return 'websocket'\n }\n\n throw new Error(\n `${sandbox.provider}: cannot run ACP — no writable stdin and no port-exposed background-process support. Use a Docker/local-process sandbox or enable ports on the provider.`,\n )\n}\n"],"mappings":";;;;;AAOA,SAAgB,wBACd,SACA,aAAqC,QACd;CACvB,IAAI,eAAe,SAAS;EAC1B,IAAI,QAAQ,aAAa,kBAAkB,MACzC,MAAM,IAAI,MACR,GAAG,QAAQ,SAAS,iJACtB;EAEF,OAAO;CACT;CAEA,IAAI,eAAe,aAAa;EAC9B,IAAI,QAAQ,aAAa,UAAU,MACjC,MAAM,IAAI,MACR,GAAG,QAAQ,SAAS,kHACtB;EAEF,IAAI,QAAQ,aAAa,wBAAwB,MAC/C,MAAM,IAAI,MACR,GAAG,QAAQ,SAAS,0GACtB;EAEF,OAAO;CACT;CAEA,IAAI,QAAQ,aAAa,kBAAkB,MAAM,OAAO;CACxD,IACE,QAAQ,aAAa,UAAU,QAC/B,QAAQ,aAAa,wBAAwB,MAE7C,OAAO;CAGT,MAAM,IAAI,MACR,GAAG,QAAQ,SAAS,yJACtB;AACF"}
@@ -1,51 +1,42 @@
1
+ //#region src/transport/stdio.ts
1
2
  function spawnHandleToAcpTransport(handle) {
2
- const encoder = new TextEncoder();
3
- const decoder = new TextDecoder();
4
- const readable = new ReadableStream({
5
- async start(controller) {
6
- try {
7
- for await (const chunk of handle.stdout) {
8
- controller.enqueue(encoder.encode(chunk));
9
- }
10
- controller.close();
11
- } catch (error) {
12
- controller.error(error);
13
- }
14
- }
15
- });
16
- const writable = new WritableStream({
17
- write(chunk) {
18
- return handle.stdin.write(decoder.decode(chunk));
19
- },
20
- close() {
21
- return handle.stdin.end();
22
- }
23
- });
24
- let tail = "";
25
- void (async () => {
26
- try {
27
- for await (const chunk of handle.stderr) {
28
- tail = (tail + chunk).slice(-4096);
29
- }
30
- } catch {
31
- }
32
- })();
33
- const exited = handle.wait().then((code) => {
34
- throw new Error(
35
- `ACP harness process exited unexpectedly (code ${code}).${tail.trim() !== "" ? `
36
- stderr: ${tail.trim()}` : ""}`
37
- );
38
- });
39
- void exited.catch(() => void 0);
40
- return {
41
- writable,
42
- readable,
43
- exited,
44
- stderrTail: () => tail,
45
- kill: () => handle.kill()
46
- };
3
+ const encoder = new TextEncoder();
4
+ const decoder = new TextDecoder();
5
+ const readable = new ReadableStream({ async start(controller) {
6
+ try {
7
+ for await (const chunk of handle.stdout) controller.enqueue(encoder.encode(chunk));
8
+ controller.close();
9
+ } catch (error) {
10
+ controller.error(error);
11
+ }
12
+ } });
13
+ const writable = new WritableStream({
14
+ write(chunk) {
15
+ return handle.stdin.write(decoder.decode(chunk));
16
+ },
17
+ close() {
18
+ return handle.stdin.end();
19
+ }
20
+ });
21
+ let tail = "";
22
+ (async () => {
23
+ try {
24
+ for await (const chunk of handle.stderr) tail = (tail + chunk).slice(-4096);
25
+ } catch {}
26
+ })();
27
+ const exited = handle.wait().then((code) => {
28
+ throw new Error(`ACP harness process exited unexpectedly (code ${code}).${tail.trim() !== "" ? `\nstderr: ${tail.trim()}` : ""}`);
29
+ });
30
+ exited.catch(() => void 0);
31
+ return {
32
+ writable,
33
+ readable,
34
+ exited,
35
+ stderrTail: () => tail,
36
+ kill: () => handle.kill()
37
+ };
47
38
  }
48
- export {
49
- spawnHandleToAcpTransport
50
- };
51
- //# sourceMappingURL=stdio.js.map
39
+ //#endregion
40
+ export { spawnHandleToAcpTransport };
41
+
42
+ //# sourceMappingURL=stdio.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","sources":["../../../src/transport/stdio.ts"],"sourcesContent":["/**\n * Adapt a sandbox {@link SpawnHandle} into byte streams for ACP stdio\n * (newline-delimited JSON-RPC).\n */\nimport type { SpawnHandle } from '@tanstack/ai-sandbox'\nimport type { AcpByteTransport } from './types'\n\nexport function spawnHandleToAcpTransport(\n handle: SpawnHandle,\n): AcpByteTransport {\n const encoder = new TextEncoder()\n const decoder = new TextDecoder()\n\n const readable = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n for await (const chunk of handle.stdout) {\n controller.enqueue(encoder.encode(chunk))\n }\n controller.close()\n } catch (error) {\n controller.error(error)\n }\n },\n })\n\n const writable = new WritableStream<Uint8Array>({\n write(chunk) {\n return handle.stdin.write(decoder.decode(chunk))\n },\n close() {\n return handle.stdin.end()\n },\n })\n\n let tail = ''\n void (async () => {\n try {\n for await (const chunk of handle.stderr) {\n tail = (tail + chunk).slice(-4096)\n }\n } catch {\n // stderr closed\n }\n })()\n\n const exited: Promise<never> = handle.wait().then((code) => {\n throw new Error(\n `ACP harness process exited unexpectedly (code ${code}).${\n tail.trim() !== '' ? `\\nstderr: ${tail.trim()}` : ''\n }`,\n )\n })\n void exited.catch(() => undefined)\n\n return {\n writable,\n readable,\n exited,\n stderrTail: () => tail,\n kill: () => handle.kill(),\n }\n}\n"],"names":[],"mappings":"AAOO,SAAS,0BACd,QACkB;AAClB,QAAM,UAAU,IAAI,YAAA;AACpB,QAAM,UAAU,IAAI,YAAA;AAEpB,QAAM,WAAW,IAAI,eAA2B;AAAA,IAC9C,MAAM,MAAM,YAAY;AACtB,UAAI;AACF,yBAAiB,SAAS,OAAO,QAAQ;AACvC,qBAAW,QAAQ,QAAQ,OAAO,KAAK,CAAC;AAAA,QAC1C;AACA,mBAAW,MAAA;AAAA,MACb,SAAS,OAAO;AACd,mBAAW,MAAM,KAAK;AAAA,MACxB;AAAA,IACF;AAAA,EAAA,CACD;AAED,QAAM,WAAW,IAAI,eAA2B;AAAA,IAC9C,MAAM,OAAO;AACX,aAAO,OAAO,MAAM,MAAM,QAAQ,OAAO,KAAK,CAAC;AAAA,IACjD;AAAA,IACA,QAAQ;AACN,aAAO,OAAO,MAAM,IAAA;AAAA,IACtB;AAAA,EAAA,CACD;AAED,MAAI,OAAO;AACX,QAAM,YAAY;AAChB,QAAI;AACF,uBAAiB,SAAS,OAAO,QAAQ;AACvC,gBAAQ,OAAO,OAAO,MAAM,KAAK;AAAA,MACnC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF,GAAA;AAEA,QAAM,SAAyB,OAAO,KAAA,EAAO,KAAK,CAAC,SAAS;AAC1D,UAAM,IAAI;AAAA,MACR,iDAAiD,IAAI,KACnD,KAAK,KAAA,MAAW,KAAK;AAAA,UAAa,KAAK,MAAM,KAAK,EACpD;AAAA,IAAA;AAAA,EAEJ,CAAC;AACD,OAAK,OAAO,MAAM,MAAM,MAAS;AAEjC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,MAAM;AAAA,IAClB,MAAM,MAAM,OAAO,KAAA;AAAA,EAAK;AAE5B;"}
1
+ {"version":3,"file":"stdio.js","names":[],"sources":["../../../src/transport/stdio.ts"],"sourcesContent":["/**\n * Adapt a sandbox {@link SpawnHandle} into byte streams for ACP stdio\n * (newline-delimited JSON-RPC).\n */\nimport type { SpawnHandle } from '@tanstack/ai-sandbox'\nimport type { AcpByteTransport } from './types'\n\nexport function spawnHandleToAcpTransport(\n handle: SpawnHandle,\n): AcpByteTransport {\n const encoder = new TextEncoder()\n const decoder = new TextDecoder()\n\n const readable = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n for await (const chunk of handle.stdout) {\n controller.enqueue(encoder.encode(chunk))\n }\n controller.close()\n } catch (error) {\n controller.error(error)\n }\n },\n })\n\n const writable = new WritableStream<Uint8Array>({\n write(chunk) {\n return handle.stdin.write(decoder.decode(chunk))\n },\n close() {\n return handle.stdin.end()\n },\n })\n\n let tail = ''\n void (async () => {\n try {\n for await (const chunk of handle.stderr) {\n tail = (tail + chunk).slice(-4096)\n }\n } catch {\n // stderr closed\n }\n })()\n\n const exited: Promise<never> = handle.wait().then((code) => {\n throw new Error(\n `ACP harness process exited unexpectedly (code ${code}).${\n tail.trim() !== '' ? `\\nstderr: ${tail.trim()}` : ''\n }`,\n )\n })\n void exited.catch(() => undefined)\n\n return {\n writable,\n readable,\n exited,\n stderrTail: () => tail,\n kill: () => handle.kill(),\n }\n}\n"],"mappings":";AAOA,SAAgB,0BACd,QACkB;CAClB,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,UAAU,IAAI,YAAY;CAEhC,MAAM,WAAW,IAAI,eAA2B,EAC9C,MAAM,MAAM,YAAY;EACtB,IAAI;GACF,WAAW,MAAM,SAAS,OAAO,QAC/B,WAAW,QAAQ,QAAQ,OAAO,KAAK,CAAC;GAE1C,WAAW,MAAM;EACnB,SAAS,OAAO;GACd,WAAW,MAAM,KAAK;EACxB;CACF,EACF,CAAC;CAED,MAAM,WAAW,IAAI,eAA2B;EAC9C,MAAM,OAAO;GACX,OAAO,OAAO,MAAM,MAAM,QAAQ,OAAO,KAAK,CAAC;EACjD;EACA,QAAQ;GACN,OAAO,OAAO,MAAM,IAAI;EAC1B;CACF,CAAC;CAED,IAAI,OAAO;CACX,CAAM,YAAY;EAChB,IAAI;GACF,WAAW,MAAM,SAAS,OAAO,QAC/B,QAAQ,OAAO,MAAA,CAAO,MAAM,KAAK;EAErC,QAAQ,CAER;CACF,EAAA,CAAG;CAEH,MAAM,SAAyB,OAAO,KAAK,CAAC,CAAC,MAAM,SAAS;EAC1D,MAAM,IAAI,MACR,iDAAiD,KAAK,IACpD,KAAK,KAAK,MAAM,KAAK,aAAa,KAAK,KAAK,MAAM,IAEtD;CACF,CAAC;CACD,OAAY,YAAY,KAAA,CAAS;CAEjC,OAAO;EACL;EACA;EACA;EACA,kBAAkB;EAClB,YAAY,OAAO,KAAK;CAC1B;AACF"}
@@ -1,135 +1,136 @@
1
1
  import { ndJsonStream } from "@agentclientprotocol/sdk";
2
+ //#region src/transport/websocket.ts
2
3
  function waitForWebSocketOpen(ws, signal) {
3
- if (ws.readyState === WebSocket.OPEN) return Promise.resolve();
4
- return new Promise((resolve, reject) => {
5
- const onOpen = () => {
6
- cleanup();
7
- resolve();
8
- };
9
- const onError = () => {
10
- cleanup();
11
- reject(new Error("WebSocket connection failed"));
12
- };
13
- const onAbort = () => {
14
- cleanup();
15
- ws.close();
16
- reject(signal?.reason ?? new Error("WebSocket connection aborted"));
17
- };
18
- const cleanup = () => {
19
- ws.removeEventListener("open", onOpen);
20
- ws.removeEventListener("error", onError);
21
- signal?.removeEventListener("abort", onAbort);
22
- };
23
- ws.addEventListener("open", onOpen);
24
- ws.addEventListener("error", onError);
25
- signal?.addEventListener("abort", onAbort, { once: true });
26
- });
4
+ if (ws.readyState === WebSocket.OPEN) return Promise.resolve();
5
+ return new Promise((resolve, reject) => {
6
+ const onOpen = () => {
7
+ cleanup();
8
+ resolve();
9
+ };
10
+ const onError = () => {
11
+ cleanup();
12
+ reject(/* @__PURE__ */ new Error("WebSocket connection failed"));
13
+ };
14
+ const onAbort = () => {
15
+ cleanup();
16
+ ws.close();
17
+ reject(signal?.reason ?? /* @__PURE__ */ new Error("WebSocket connection aborted"));
18
+ };
19
+ const cleanup = () => {
20
+ ws.removeEventListener("open", onOpen);
21
+ ws.removeEventListener("error", onError);
22
+ signal?.removeEventListener("abort", onAbort);
23
+ };
24
+ ws.addEventListener("open", onOpen);
25
+ ws.addEventListener("error", onError);
26
+ signal?.addEventListener("abort", onAbort, { once: true });
27
+ });
27
28
  }
29
+ /**
30
+ * Wrap a stream controller so its terminal calls are idempotent. A WebSocket can
31
+ * fire `close` after `error` (or after the reader cancels), and closing/erroring
32
+ * an already-settled controller throws `ERR_INVALID_STATE` out of the listener.
33
+ */
28
34
  function idempotentController(controller) {
29
- let settled = false;
30
- return {
31
- enqueue: (chunk) => {
32
- if (!settled) controller.enqueue(chunk);
33
- },
34
- close: () => {
35
- if (settled) return;
36
- settled = true;
37
- controller.close();
38
- },
39
- error: (reason) => {
40
- if (settled) return;
41
- settled = true;
42
- controller.error(reason);
43
- }
44
- };
35
+ let settled = false;
36
+ return {
37
+ enqueue: (chunk) => {
38
+ if (!settled) controller.enqueue(chunk);
39
+ },
40
+ close: () => {
41
+ if (settled) return;
42
+ settled = true;
43
+ controller.close();
44
+ },
45
+ error: (reason) => {
46
+ if (settled) return;
47
+ settled = true;
48
+ controller.error(reason);
49
+ }
50
+ };
45
51
  }
52
+ /**
53
+ * One JSON-RPC object per WebSocket text frame (e.g. `grok agent serve`).
54
+ */
46
55
  function webSocketFrameToAcpStream(ws) {
47
- const decoder = new TextDecoder();
48
- const readable = new ReadableStream({
49
- start(rawController) {
50
- const controller = idempotentController(rawController);
51
- ws.addEventListener("message", (event) => {
52
- const text = typeof event.data === "string" ? event.data : decoder.decode(event.data);
53
- const trimmed = text.trim();
54
- if (trimmed === "") return;
55
- try {
56
- controller.enqueue(JSON.parse(trimmed));
57
- } catch (error) {
58
- controller.error(
59
- error instanceof Error ? error : new Error(String(error))
60
- );
61
- }
62
- });
63
- ws.addEventListener("close", () => controller.close());
64
- ws.addEventListener(
65
- "error",
66
- () => controller.error(new Error("WebSocket connection error"))
67
- );
68
- },
69
- cancel() {
70
- ws.close();
71
- }
72
- });
73
- const writable = new WritableStream({
74
- write(message) {
75
- ws.send(JSON.stringify(message));
76
- },
77
- close() {
78
- ws.close();
79
- }
80
- });
81
- return { readable, writable };
56
+ const decoder = new TextDecoder();
57
+ return {
58
+ readable: new ReadableStream({
59
+ start(rawController) {
60
+ const controller = idempotentController(rawController);
61
+ ws.addEventListener("message", (event) => {
62
+ const trimmed = (typeof event.data === "string" ? event.data : decoder.decode(event.data)).trim();
63
+ if (trimmed === "") return;
64
+ try {
65
+ controller.enqueue(JSON.parse(trimmed));
66
+ } catch (error) {
67
+ controller.error(error instanceof Error ? error : new Error(String(error)));
68
+ }
69
+ });
70
+ ws.addEventListener("close", () => controller.close());
71
+ ws.addEventListener("error", () => controller.error(/* @__PURE__ */ new Error("WebSocket connection error")));
72
+ },
73
+ cancel() {
74
+ ws.close();
75
+ }
76
+ }),
77
+ writable: new WritableStream({
78
+ write(message) {
79
+ ws.send(JSON.stringify(message));
80
+ },
81
+ close() {
82
+ ws.close();
83
+ }
84
+ })
85
+ };
82
86
  }
83
87
  function webSocketNdjsonToAcpStream(ws) {
84
- const encoder = new TextEncoder();
85
- const decoder = new TextDecoder();
86
- const readable = new ReadableStream({
87
- start(rawController) {
88
- const controller = idempotentController(rawController);
89
- ws.addEventListener("message", (event) => {
90
- const text = typeof event.data === "string" ? event.data : decoder.decode(event.data);
91
- controller.enqueue(encoder.encode(text));
92
- });
93
- ws.addEventListener("close", () => controller.close());
94
- ws.addEventListener(
95
- "error",
96
- () => controller.error(new Error("WebSocket connection error"))
97
- );
98
- },
99
- cancel() {
100
- ws.close();
101
- }
102
- });
103
- const writable = new WritableStream({
104
- write(chunk) {
105
- ws.send(chunk);
106
- },
107
- close() {
108
- ws.close();
109
- }
110
- });
111
- return ndJsonStream(writable, readable);
88
+ const encoder = new TextEncoder();
89
+ const decoder = new TextDecoder();
90
+ const readable = new ReadableStream({
91
+ start(rawController) {
92
+ const controller = idempotentController(rawController);
93
+ ws.addEventListener("message", (event) => {
94
+ const text = typeof event.data === "string" ? event.data : decoder.decode(event.data);
95
+ controller.enqueue(encoder.encode(text));
96
+ });
97
+ ws.addEventListener("close", () => controller.close());
98
+ ws.addEventListener("error", () => controller.error(/* @__PURE__ */ new Error("WebSocket connection error")));
99
+ },
100
+ cancel() {
101
+ ws.close();
102
+ }
103
+ });
104
+ return ndJsonStream(new WritableStream({
105
+ write(chunk) {
106
+ ws.send(chunk);
107
+ },
108
+ close() {
109
+ ws.close();
110
+ }
111
+ }), readable);
112
112
  }
113
+ /**
114
+ * Open a WebSocket to an in-sandbox ACP server and adapt it for
115
+ * {@link ClientSideConnection}.
116
+ */
113
117
  function openWebSocket(url, headers) {
114
- if (headers === void 0) return new WebSocket(url);
115
- return Reflect.construct(WebSocket, [url, { headers }]);
118
+ if (headers === void 0) return new WebSocket(url);
119
+ return Reflect.construct(WebSocket, [url, { headers }]);
116
120
  }
117
121
  async function connectAcpWebSocket(url, options = {}) {
118
- const ws = openWebSocket(url, options.headers);
119
- await waitForWebSocketOpen(ws, options.signal);
120
- const framing = options.framing ?? "frame";
121
- const stream = framing === "ndjson" ? webSocketNdjsonToAcpStream(ws) : webSocketFrameToAcpStream(ws);
122
- return {
123
- stream,
124
- close: () => ws.close()
125
- };
122
+ const ws = openWebSocket(url, options.headers);
123
+ await waitForWebSocketOpen(ws, options.signal);
124
+ return {
125
+ stream: (options.framing ?? "frame") === "ndjson" ? webSocketNdjsonToAcpStream(ws) : webSocketFrameToAcpStream(ws),
126
+ close: () => ws.close()
127
+ };
126
128
  }
129
+ /** Convert an HTTP sandbox channel URL to a WebSocket base URL. */
127
130
  function httpChannelUrlToWsBase(channelUrl) {
128
- return channelUrl.replace(/^http/i, "ws").replace(/\/$/, "");
131
+ return channelUrl.replace(/^http/i, "ws").replace(/\/$/, "");
129
132
  }
130
- export {
131
- connectAcpWebSocket,
132
- httpChannelUrlToWsBase,
133
- webSocketFrameToAcpStream
134
- };
135
- //# sourceMappingURL=websocket.js.map
133
+ //#endregion
134
+ export { connectAcpWebSocket, httpChannelUrlToWsBase, webSocketFrameToAcpStream };
135
+
136
+ //# sourceMappingURL=websocket.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.js","sources":["../../../src/transport/websocket.ts"],"sourcesContent":["import { ndJsonStream } from '@agentclientprotocol/sdk'\nimport type { AcpJsonRpcStream, AcpMessageFraming } from './types'\n\nfunction waitForWebSocketOpen(\n ws: WebSocket,\n signal?: AbortSignal,\n): Promise<void> {\n if (ws.readyState === WebSocket.OPEN) return Promise.resolve()\n return new Promise((resolve, reject) => {\n const onOpen = (): void => {\n cleanup()\n resolve()\n }\n const onError = (): void => {\n cleanup()\n reject(new Error('WebSocket connection failed'))\n }\n const onAbort = (): void => {\n cleanup()\n ws.close()\n reject(signal?.reason ?? new Error('WebSocket connection aborted'))\n }\n const cleanup = (): void => {\n ws.removeEventListener('open', onOpen)\n ws.removeEventListener('error', onError)\n signal?.removeEventListener('abort', onAbort)\n }\n ws.addEventListener('open', onOpen)\n ws.addEventListener('error', onError)\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/**\n * Wrap a stream controller so its terminal calls are idempotent. A WebSocket can\n * fire `close` after `error` (or after the reader cancels), and closing/erroring\n * an already-settled controller throws `ERR_INVALID_STATE` out of the listener.\n */\nfunction idempotentController<T>(\n controller: ReadableStreamDefaultController<T>,\n): {\n enqueue: (chunk: T) => void\n close: () => void\n error: (reason: unknown) => void\n} {\n let settled = false\n return {\n enqueue: (chunk) => {\n if (!settled) controller.enqueue(chunk)\n },\n close: () => {\n if (settled) return\n settled = true\n controller.close()\n },\n error: (reason) => {\n if (settled) return\n settled = true\n controller.error(reason)\n },\n }\n}\n\n/**\n * One JSON-RPC object per WebSocket text frame (e.g. `grok agent serve`).\n */\nexport function webSocketFrameToAcpStream(ws: WebSocket): AcpJsonRpcStream {\n const decoder = new TextDecoder()\n\n const readable = new ReadableStream<unknown>({\n start(rawController) {\n const controller = idempotentController(rawController)\n ws.addEventListener('message', (event) => {\n const text =\n typeof event.data === 'string'\n ? event.data\n : decoder.decode(event.data as ArrayBuffer)\n const trimmed = text.trim()\n if (trimmed === '') return\n try {\n controller.enqueue(JSON.parse(trimmed))\n } catch (error) {\n controller.error(\n error instanceof Error ? error : new Error(String(error)),\n )\n }\n })\n ws.addEventListener('close', () => controller.close())\n ws.addEventListener('error', () =>\n controller.error(new Error('WebSocket connection error')),\n )\n },\n cancel() {\n ws.close()\n },\n })\n\n const writable = new WritableStream({\n write(message) {\n ws.send(JSON.stringify(message))\n },\n close() {\n ws.close()\n },\n })\n\n return { readable, writable } as AcpJsonRpcStream\n}\n\nfunction webSocketNdjsonToAcpStream(ws: WebSocket): AcpJsonRpcStream {\n const encoder = new TextEncoder()\n const decoder = new TextDecoder()\n\n const readable = new ReadableStream<Uint8Array>({\n start(rawController) {\n const controller = idempotentController(rawController)\n ws.addEventListener('message', (event) => {\n const text =\n typeof event.data === 'string'\n ? event.data\n : decoder.decode(event.data as ArrayBuffer)\n controller.enqueue(encoder.encode(text))\n })\n ws.addEventListener('close', () => controller.close())\n ws.addEventListener('error', () =>\n controller.error(new Error('WebSocket connection error')),\n )\n },\n cancel() {\n ws.close()\n },\n })\n\n const writable = new WritableStream<Uint8Array>({\n write(chunk) {\n ws.send(chunk)\n },\n close() {\n ws.close()\n },\n })\n\n return ndJsonStream(writable, readable)\n}\n\nexport interface ConnectAcpWebSocketOptions {\n headers?: Record<string, string>\n signal?: AbortSignal\n framing?: AcpMessageFraming\n}\n\nexport interface AcpWebSocketConnection {\n stream: AcpJsonRpcStream\n close: () => void\n}\n\n/**\n * Open a WebSocket to an in-sandbox ACP server and adapt it for\n * {@link ClientSideConnection}.\n */\nfunction openWebSocket(\n url: string,\n headers?: Record<string, string>,\n): WebSocket {\n if (headers === undefined) return new WebSocket(url)\n // Node/ws accepts `{ headers }`; DOM lib constructor types omit this overload.\n return Reflect.construct(WebSocket, [url, { headers }]) as WebSocket\n}\n\nexport async function connectAcpWebSocket(\n url: string,\n options: ConnectAcpWebSocketOptions = {},\n): Promise<AcpWebSocketConnection> {\n const ws = openWebSocket(url, options.headers)\n await waitForWebSocketOpen(ws, options.signal)\n\n const framing = options.framing ?? 'frame'\n const stream =\n framing === 'ndjson'\n ? webSocketNdjsonToAcpStream(ws)\n : webSocketFrameToAcpStream(ws)\n\n return {\n stream,\n close: () => ws.close(),\n }\n}\n\n/** Convert an HTTP sandbox channel URL to a WebSocket base URL. */\nexport function httpChannelUrlToWsBase(channelUrl: string): string {\n return channelUrl.replace(/^http/i, 'ws').replace(/\\/$/, '')\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"websocket.js","names":[],"sources":["../../../src/transport/websocket.ts"],"sourcesContent":["import { ndJsonStream } from '@agentclientprotocol/sdk'\nimport type { AcpJsonRpcStream, AcpMessageFraming } from './types'\n\nfunction waitForWebSocketOpen(\n ws: WebSocket,\n signal?: AbortSignal,\n): Promise<void> {\n if (ws.readyState === WebSocket.OPEN) return Promise.resolve()\n return new Promise((resolve, reject) => {\n const onOpen = (): void => {\n cleanup()\n resolve()\n }\n const onError = (): void => {\n cleanup()\n reject(new Error('WebSocket connection failed'))\n }\n const onAbort = (): void => {\n cleanup()\n ws.close()\n reject(signal?.reason ?? new Error('WebSocket connection aborted'))\n }\n const cleanup = (): void => {\n ws.removeEventListener('open', onOpen)\n ws.removeEventListener('error', onError)\n signal?.removeEventListener('abort', onAbort)\n }\n ws.addEventListener('open', onOpen)\n ws.addEventListener('error', onError)\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/**\n * Wrap a stream controller so its terminal calls are idempotent. A WebSocket can\n * fire `close` after `error` (or after the reader cancels), and closing/erroring\n * an already-settled controller throws `ERR_INVALID_STATE` out of the listener.\n */\nfunction idempotentController<T>(\n controller: ReadableStreamDefaultController<T>,\n): {\n enqueue: (chunk: T) => void\n close: () => void\n error: (reason: unknown) => void\n} {\n let settled = false\n return {\n enqueue: (chunk) => {\n if (!settled) controller.enqueue(chunk)\n },\n close: () => {\n if (settled) return\n settled = true\n controller.close()\n },\n error: (reason) => {\n if (settled) return\n settled = true\n controller.error(reason)\n },\n }\n}\n\n/**\n * One JSON-RPC object per WebSocket text frame (e.g. `grok agent serve`).\n */\nexport function webSocketFrameToAcpStream(ws: WebSocket): AcpJsonRpcStream {\n const decoder = new TextDecoder()\n\n const readable = new ReadableStream<unknown>({\n start(rawController) {\n const controller = idempotentController(rawController)\n ws.addEventListener('message', (event) => {\n const text =\n typeof event.data === 'string'\n ? event.data\n : decoder.decode(event.data as ArrayBuffer)\n const trimmed = text.trim()\n if (trimmed === '') return\n try {\n controller.enqueue(JSON.parse(trimmed))\n } catch (error) {\n controller.error(\n error instanceof Error ? error : new Error(String(error)),\n )\n }\n })\n ws.addEventListener('close', () => controller.close())\n ws.addEventListener('error', () =>\n controller.error(new Error('WebSocket connection error')),\n )\n },\n cancel() {\n ws.close()\n },\n })\n\n const writable = new WritableStream({\n write(message) {\n ws.send(JSON.stringify(message))\n },\n close() {\n ws.close()\n },\n })\n\n return { readable, writable } as AcpJsonRpcStream\n}\n\nfunction webSocketNdjsonToAcpStream(ws: WebSocket): AcpJsonRpcStream {\n const encoder = new TextEncoder()\n const decoder = new TextDecoder()\n\n const readable = new ReadableStream<Uint8Array>({\n start(rawController) {\n const controller = idempotentController(rawController)\n ws.addEventListener('message', (event) => {\n const text =\n typeof event.data === 'string'\n ? event.data\n : decoder.decode(event.data as ArrayBuffer)\n controller.enqueue(encoder.encode(text))\n })\n ws.addEventListener('close', () => controller.close())\n ws.addEventListener('error', () =>\n controller.error(new Error('WebSocket connection error')),\n )\n },\n cancel() {\n ws.close()\n },\n })\n\n const writable = new WritableStream<Uint8Array>({\n write(chunk) {\n // TS 5.7+ types Uint8Array as generic over its buffer (ArrayBufferLike),\n // which no longer structurally matches the DOM `BufferSource` param.\n // Runtime accepts any typed array; narrow to the lib's expected type.\n ws.send(chunk as BufferSource)\n },\n close() {\n ws.close()\n },\n })\n\n return ndJsonStream(writable, readable)\n}\n\nexport interface ConnectAcpWebSocketOptions {\n headers?: Record<string, string>\n signal?: AbortSignal\n framing?: AcpMessageFraming\n}\n\nexport interface AcpWebSocketConnection {\n stream: AcpJsonRpcStream\n close: () => void\n}\n\n/**\n * Open a WebSocket to an in-sandbox ACP server and adapt it for\n * {@link ClientSideConnection}.\n */\nfunction openWebSocket(\n url: string,\n headers?: Record<string, string>,\n): WebSocket {\n if (headers === undefined) return new WebSocket(url)\n // Node/ws accepts `{ headers }`; DOM lib constructor types omit this overload.\n return Reflect.construct(WebSocket, [url, { headers }]) as WebSocket\n}\n\nexport async function connectAcpWebSocket(\n url: string,\n options: ConnectAcpWebSocketOptions = {},\n): Promise<AcpWebSocketConnection> {\n const ws = openWebSocket(url, options.headers)\n await waitForWebSocketOpen(ws, options.signal)\n\n const framing = options.framing ?? 'frame'\n const stream =\n framing === 'ndjson'\n ? webSocketNdjsonToAcpStream(ws)\n : webSocketFrameToAcpStream(ws)\n\n return {\n stream,\n close: () => ws.close(),\n }\n}\n\n/** Convert an HTTP sandbox channel URL to a WebSocket base URL. */\nexport function httpChannelUrlToWsBase(channelUrl: string): string {\n return channelUrl.replace(/^http/i, 'ws').replace(/\\/$/, '')\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-acp",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Shared Agent Client Protocol (ACP) transport, session, and AG-UI translation for TanStack AI harness adapters.",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -36,21 +36,21 @@
36
36
  "@agentclientprotocol/sdk": "^0.25.0"
37
37
  },
38
38
  "peerDependencies": {
39
- "@tanstack/ai": "^0.41.0",
40
- "@tanstack/ai-sandbox": "^0.2.3"
39
+ "@tanstack/ai": "^0.43.0",
40
+ "@tanstack/ai-sandbox": "^0.3.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@vitest/coverage-v8": "4.0.14",
44
- "@tanstack/ai": "0.41.0",
45
- "@tanstack/ai-sandbox": "0.2.3",
46
- "@tanstack/ai-sandbox-local-process": "0.2.0"
44
+ "@tanstack/ai": "0.43.0",
45
+ "@tanstack/ai-sandbox": "0.3.0",
46
+ "@tanstack/ai-sandbox-local-process": "0.2.1"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "vite build",
50
50
  "clean": "premove ./build ./dist",
51
- "lint:fix": "eslint ./src --fix",
51
+ "lint:fix": "oxlint src --type-aware --fix",
52
52
  "test:build": "publint --strict",
53
- "test:eslint": "eslint ./src",
53
+ "test:oxlint": "oxlint src --type-aware",
54
54
  "test:lib": "vitest",
55
55
  "test:lib:dev": "pnpm test:lib --watch",
56
56
  "test:types": "tsc"