@ai-sdk/harness-acp 1.0.9 → 1.0.11

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @ai-sdk/harness-acp
2
2
 
3
+ ## 1.0.11
4
+
5
+ ### Patch Changes
6
+
7
+ - 62a9c2a: feat(harness): add support for structured output to `HarnessAgent` via `output` property
8
+ - Updated dependencies [62a9c2a]
9
+ - Updated dependencies [d25cae2]
10
+ - @ai-sdk/harness@1.0.73
11
+
12
+ ## 1.0.10
13
+
14
+ ### Patch Changes
15
+
16
+ - 8d05a55: fix(harness-acp): fix incorrect `finish-step` emission timing
17
+
3
18
  ## 1.0.9
4
19
 
5
20
  ### Patch Changes
@@ -12,7 +12,7 @@ import {
12
12
  ListToolsRequestSchema,
13
13
  McpError
14
14
  } from "@modelcontextprotocol/sdk/types.js";
15
- var VERSION = true ? "1.0.9" : "0.0.0-test";
15
+ var VERSION = true ? "1.0.11" : "0.0.0-test";
16
16
  function createHostToolMCPServer({
17
17
  tools: tools2,
18
18
  revision = 1,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/v1/bridge/host-tool-mcp.ts","../../src/v1/bridge/host-tool-mcp-server.ts","../../src/v1/bridge/host-tool-relay-client.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { env } from 'node:process';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { HarnessV1BridgeToolWire } from '@ai-sdk/harness';\nimport {\n createHostToolMCPServer,\n type HostToolMCPInvocationResult,\n} from './host-tool-mcp-server';\nimport { postHostToolRelay } from './host-tool-relay-client';\n\nconst catalogPath = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOLS_FILE',\n});\nconst relayUrl = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOL_RELAY_URL',\n});\nconst relayCredential = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL',\n});\nconst tools = await readToolCatalog({ path: catalogPath });\nconst hostToolServer = createHostToolMCPServer({\n tools,\n invoke: async ({ toolName, input, catalogRevision }) =>\n validateInvocationResult({\n value: await postRelay({\n path: '/invoke',\n body: {\n requestId: randomUUID(),\n toolName,\n input,\n catalogRevision,\n },\n }),\n }),\n onListTools: async ({ revision }) => {\n await postRelay({\n path: '/catalog/seen',\n body: { revision },\n });\n },\n});\n\nawait hostToolServer.server.connect(new StdioServerTransport());\nvoid watchCatalog({\n initialRevision: 1,\n updateCatalog: hostToolServer.updateCatalog,\n}).catch(error => {\n process.stderr.write(\n `Host tool catalog synchronization failed: ${\n error instanceof Error ? error.message : String(error)\n }\\n`,\n );\n void hostToolServer.server.close();\n process.exitCode = 1;\n});\n\nasync function watchCatalog({\n initialRevision,\n updateCatalog,\n}: {\n initialRevision: number;\n updateCatalog: (options: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n }) => Promise<void>;\n}): Promise<void> {\n let revision = initialRevision;\n for (;;) {\n const value = await postRelay({\n path: '/catalog/next',\n body: { afterRevision: revision },\n });\n if (isRecord(value) && value.closed === true) return;\n if (\n !isRecord(value) ||\n !Number.isSafeInteger(value.revision) ||\n (value.revision as number) < revision\n ) {\n throw new Error('Invalid host tool catalog poll response.');\n }\n const nextRevision = value.revision as number;\n if (nextRevision === revision) continue;\n const nextTools = validateToolCatalog({ value: value.tools });\n await updateCatalog({ revision: nextRevision, tools: nextTools });\n revision = nextRevision;\n }\n}\n\nasync function postRelay({\n path,\n body,\n}: {\n path: string;\n body: Readonly<Record<string, unknown>>;\n}): Promise<unknown> {\n const response = await postHostToolRelay({\n relayUrl,\n relayCredential,\n path,\n body,\n });\n const { value } = response;\n if (!response.ok) {\n throw new Error(readErrorMessage({ value, status: response.status }));\n }\n return value;\n}\n\nfunction validateToolCatalog({\n value,\n}: {\n value: unknown;\n}): ReadonlyArray<HarnessV1BridgeToolWire> {\n if (!Array.isArray(value) || !value.every(isTool)) {\n throw new Error('Invalid host tool catalog.');\n }\n return value;\n}\n\nasync function readToolCatalog({\n path,\n}: {\n path: string;\n}): Promise<ReadonlyArray<HarnessV1BridgeToolWire>> {\n const text = await readFile(path, 'utf8');\n const value = await new Response(text, {\n headers: { 'content-type': 'application/json' },\n }).json();\n return validateToolCatalog({ value });\n}\n\nfunction isTool(value: unknown): value is HarnessV1BridgeToolWire {\n return (\n isRecord(value) &&\n typeof value.name === 'string' &&\n (value.description === undefined ||\n typeof value.description === 'string') &&\n (value.inputSchema === undefined ||\n (isRecord(value.inputSchema) && !Array.isArray(value.inputSchema)))\n );\n}\n\nfunction validateInvocationResult({\n value,\n}: {\n value: unknown;\n}): HostToolMCPInvocationResult {\n if (\n !isRecord(value) ||\n typeof value.correlationToken !== 'string' ||\n (value.isError !== undefined && typeof value.isError !== 'boolean')\n ) {\n throw new Error('Invalid host tool relay response.');\n }\n return {\n output: value.output,\n ...(value.isError ? { isError: true } : {}),\n correlationToken: value.correlationToken,\n };\n}\n\nfunction readErrorMessage({\n value,\n status,\n}: {\n value: unknown;\n status: number;\n}): string {\n return isRecord(value) && typeof value.error === 'string'\n ? value.error\n : `Host tool relay returned HTTP ${status}.`;\n}\n\nfunction requireEnvironmentVariable({ name }: { name: string }): string {\n const value = env[name];\n if (value == null || value.length === 0) {\n throw new Error(`Missing ${name}.`);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return value != null && typeof value === 'object' && !Array.isArray(value);\n}\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n type CallToolResult,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { HarnessV1BridgeToolWire } from '@ai-sdk/harness';\n\ndeclare const __PACKAGE_VERSION__: string | undefined;\n\nconst VERSION: string =\n typeof __PACKAGE_VERSION__ !== 'undefined'\n ? __PACKAGE_VERSION__\n : '0.0.0-test';\n\nexport type HostToolMCPInvocationResult = {\n readonly output: unknown;\n readonly isError?: boolean;\n readonly correlationToken: string;\n};\n\nexport type HostToolMCPServer = {\n readonly server: Server;\n updateCatalog(options: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n }): Promise<void>;\n};\n\nexport function createHostToolMCPServer({\n tools,\n revision = 1,\n invoke,\n onListTools,\n}: {\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n revision?: number;\n invoke: (options: {\n toolName: string;\n input: Readonly<Record<string, unknown>>;\n catalogRevision: number;\n }) => Promise<HostToolMCPInvocationResult>;\n onListTools?: (options: { revision: number }) => Promise<void>;\n}): HostToolMCPServer {\n let catalog = createCatalog({ revision, tools });\n let catalogAcknowledgmentError: unknown;\n const server = new Server(\n {\n name: '@ai-sdk/harness-acp-host-tools',\n version: VERSION,\n },\n { capabilities: { tools: { listChanged: true } } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n if (catalogAcknowledgmentError != null) {\n throw catalogAcknowledgmentError;\n }\n const current = catalog;\n if (onListTools != null) {\n setImmediate(() => {\n void onListTools({ revision: current.revision }).catch(error => {\n catalogAcknowledgmentError = error;\n });\n });\n }\n return { tools: current.tools.map(toMCPTool) };\n });\n server.setRequestHandler(CallToolRequestSchema, async request => {\n const current = catalog;\n const tool = current.byName.get(request.params.name);\n if (tool == null) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Unknown host tool: ${request.params.name}`,\n );\n }\n const input = request.params.arguments ?? {};\n const result = await invoke({\n toolName: tool.name,\n input,\n catalogRevision: current.revision,\n });\n return toCallToolResult({ result });\n });\n\n return {\n server,\n updateCatalog: async ({ revision: nextRevision, tools: nextTools }) => {\n catalog = createCatalog({\n revision: nextRevision,\n tools: nextTools,\n });\n await server.sendToolListChanged();\n },\n };\n}\n\nfunction createCatalog({\n revision,\n tools,\n}: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n}): {\n readonly revision: number;\n readonly tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n readonly byName: ReadonlyMap<string, HarnessV1BridgeToolWire>;\n} {\n return {\n revision,\n tools: [...tools],\n byName: new Map(tools.map(tool => [tool.name, tool])),\n };\n}\n\nfunction toMCPTool(tool: HarnessV1BridgeToolWire): Tool {\n return {\n name: tool.name,\n ...(tool.description == null ? {} : { description: tool.description }),\n inputSchema: requireObjectSchema({\n toolName: tool.name,\n schema: tool.inputSchema ?? { type: 'object' },\n }),\n };\n}\n\nfunction requireObjectSchema({\n toolName,\n schema,\n}: {\n toolName: string;\n schema: unknown;\n}): Tool['inputSchema'] {\n if (\n schema == null ||\n typeof schema !== 'object' ||\n Array.isArray(schema) ||\n (Reflect.get(schema, 'type') !== undefined &&\n Reflect.get(schema, 'type') !== 'object')\n ) {\n throw new Error(\n `Host tool ${toolName} must use an object JSON Schema for MCP.`,\n );\n }\n return schema as Tool['inputSchema'];\n}\n\nfunction toCallToolResult({\n result,\n}: {\n result: HostToolMCPInvocationResult;\n}): CallToolResult {\n return {\n content: [\n {\n type: 'text',\n text: stringifyOutput({ output: result.output }),\n },\n ],\n ...(result.isError ? { isError: true } : {}),\n _meta: {\n 'ai-sdk-harness-acp-correlation': result.correlationToken,\n },\n };\n}\n\nfunction stringifyOutput({ output }: { output: unknown }): string {\n if (output === undefined) return 'null';\n try {\n return JSON.stringify(output);\n } catch {\n return JSON.stringify({\n error: 'Host tool output could not be serialized as JSON.',\n });\n }\n}\n","import { Agent, request } from 'node:http';\n\n/*\n * A host-tool call can remain pending while a person reviews an approval.\n * The loopback connection therefore must not impose an inactivity deadline.\n */\nconst relayAgent = new Agent({ keepAlive: true, timeout: 0 });\n\nexport async function postHostToolRelay({\n relayUrl,\n relayCredential,\n path,\n body,\n}: {\n relayUrl: string;\n relayCredential: string;\n path: string;\n body: Readonly<Record<string, unknown>>;\n}): Promise<{ status: number; ok: boolean; value: unknown }> {\n const serializedBody = JSON.stringify(body);\n\n return new Promise((resolve, reject) => {\n const relayRequest = request(\n new URL(path, relayUrl),\n {\n agent: relayAgent,\n method: 'POST',\n headers: {\n authorization: `Bearer ${relayCredential}`,\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(serializedBody),\n },\n },\n response => {\n const chunks: Buffer[] = [];\n response.on('data', (chunk: Buffer) => chunks.push(chunk));\n response.on('error', reject);\n response.on('end', () => {\n void new Response(Buffer.concat(chunks)).json().then(value => {\n const status = response.statusCode ?? 0;\n resolve({\n status,\n ok: status >= 200 && status < 300,\n value,\n });\n }, reject);\n });\n },\n );\n relayRequest.on('error', reject);\n relayRequest.end(serializedBody);\n });\n}\n"],"mappings":"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","names":["tools","request","relayUrl","relayCredential"]}
1
+ {"version":3,"sources":["../../src/v1/bridge/host-tool-mcp.ts","../../src/v1/bridge/host-tool-mcp-server.ts","../../src/v1/bridge/host-tool-relay-client.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\nimport { env } from 'node:process';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport type { HarnessV1BridgeToolWire } from '@ai-sdk/harness';\nimport {\n createHostToolMCPServer,\n type HostToolMCPInvocationResult,\n} from './host-tool-mcp-server';\nimport { postHostToolRelay } from './host-tool-relay-client';\n\nconst catalogPath = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOLS_FILE',\n});\nconst relayUrl = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOL_RELAY_URL',\n});\nconst relayCredential = requireEnvironmentVariable({\n name: 'AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL',\n});\nconst tools = await readToolCatalog({ path: catalogPath });\nconst hostToolServer = createHostToolMCPServer({\n tools,\n invoke: async ({ toolName, input, catalogRevision }) =>\n validateInvocationResult({\n value: await postRelay({\n path: '/invoke',\n body: {\n requestId: randomUUID(),\n toolName,\n input,\n catalogRevision,\n },\n }),\n }),\n onListTools: async ({ revision }) => {\n await postRelay({\n path: '/catalog/seen',\n body: { revision },\n });\n },\n});\n\nawait hostToolServer.server.connect(new StdioServerTransport());\nvoid watchCatalog({\n initialRevision: 1,\n updateCatalog: hostToolServer.updateCatalog,\n}).catch(error => {\n process.stderr.write(\n `Host tool catalog synchronization failed: ${\n error instanceof Error ? error.message : String(error)\n }\\n`,\n );\n void hostToolServer.server.close();\n process.exitCode = 1;\n});\n\nasync function watchCatalog({\n initialRevision,\n updateCatalog,\n}: {\n initialRevision: number;\n updateCatalog: (options: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n }) => Promise<void>;\n}): Promise<void> {\n let revision = initialRevision;\n for (;;) {\n const value = await postRelay({\n path: '/catalog/next',\n body: { afterRevision: revision },\n });\n if (isRecord(value) && value.closed === true) return;\n if (\n !isRecord(value) ||\n !Number.isSafeInteger(value.revision) ||\n (value.revision as number) < revision\n ) {\n throw new Error('Invalid host tool catalog poll response.');\n }\n const nextRevision = value.revision as number;\n if (nextRevision === revision) continue;\n const nextTools = validateToolCatalog({ value: value.tools });\n await updateCatalog({ revision: nextRevision, tools: nextTools });\n revision = nextRevision;\n }\n}\n\nasync function postRelay({\n path,\n body,\n}: {\n path: string;\n body: Readonly<Record<string, unknown>>;\n}): Promise<unknown> {\n const response = await postHostToolRelay({\n relayUrl,\n relayCredential,\n path,\n body,\n });\n const { value } = response;\n if (!response.ok) {\n throw new Error(readErrorMessage({ value, status: response.status }));\n }\n return value;\n}\n\nfunction validateToolCatalog({\n value,\n}: {\n value: unknown;\n}): ReadonlyArray<HarnessV1BridgeToolWire> {\n if (!Array.isArray(value) || !value.every(isTool)) {\n throw new Error('Invalid host tool catalog.');\n }\n return value;\n}\n\nasync function readToolCatalog({\n path,\n}: {\n path: string;\n}): Promise<ReadonlyArray<HarnessV1BridgeToolWire>> {\n const text = await readFile(path, 'utf8');\n const value = await new Response(text, {\n headers: { 'content-type': 'application/json' },\n }).json();\n return validateToolCatalog({ value });\n}\n\nfunction isTool(value: unknown): value is HarnessV1BridgeToolWire {\n return (\n isRecord(value) &&\n typeof value.name === 'string' &&\n (value.description === undefined ||\n typeof value.description === 'string') &&\n (value.inputSchema === undefined ||\n (isRecord(value.inputSchema) && !Array.isArray(value.inputSchema)))\n );\n}\n\nfunction validateInvocationResult({\n value,\n}: {\n value: unknown;\n}): HostToolMCPInvocationResult {\n if (\n !isRecord(value) ||\n typeof value.correlationToken !== 'string' ||\n (value.isError !== undefined && typeof value.isError !== 'boolean')\n ) {\n throw new Error('Invalid host tool relay response.');\n }\n return {\n output: value.output,\n ...(value.isError ? { isError: true } : {}),\n correlationToken: value.correlationToken,\n };\n}\n\nfunction readErrorMessage({\n value,\n status,\n}: {\n value: unknown;\n status: number;\n}): string {\n return isRecord(value) && typeof value.error === 'string'\n ? value.error\n : `Host tool relay returned HTTP ${status}.`;\n}\n\nfunction requireEnvironmentVariable({ name }: { name: string }): string {\n const value = env[name];\n if (value == null || value.length === 0) {\n throw new Error(`Missing ${name}.`);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return value != null && typeof value === 'object' && !Array.isArray(value);\n}\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n type CallToolResult,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type { HarnessV1BridgeToolWire } from '@ai-sdk/harness';\n\ndeclare const __PACKAGE_VERSION__: string | undefined;\n\nconst VERSION: string =\n typeof __PACKAGE_VERSION__ !== 'undefined'\n ? __PACKAGE_VERSION__\n : '0.0.0-test';\n\nexport type HostToolMCPInvocationResult = {\n readonly output: unknown;\n readonly isError?: boolean;\n readonly correlationToken: string;\n};\n\nexport type HostToolMCPServer = {\n readonly server: Server;\n updateCatalog(options: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n }): Promise<void>;\n};\n\nexport function createHostToolMCPServer({\n tools,\n revision = 1,\n invoke,\n onListTools,\n}: {\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n revision?: number;\n invoke: (options: {\n toolName: string;\n input: Readonly<Record<string, unknown>>;\n catalogRevision: number;\n }) => Promise<HostToolMCPInvocationResult>;\n onListTools?: (options: { revision: number }) => Promise<void>;\n}): HostToolMCPServer {\n let catalog = createCatalog({ revision, tools });\n let catalogAcknowledgmentError: unknown;\n const server = new Server(\n {\n name: '@ai-sdk/harness-acp-host-tools',\n version: VERSION,\n },\n { capabilities: { tools: { listChanged: true } } },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n if (catalogAcknowledgmentError != null) {\n throw catalogAcknowledgmentError;\n }\n const current = catalog;\n if (onListTools != null) {\n setImmediate(() => {\n void onListTools({ revision: current.revision }).catch(error => {\n catalogAcknowledgmentError = error;\n });\n });\n }\n return { tools: current.tools.map(toMCPTool) };\n });\n server.setRequestHandler(CallToolRequestSchema, async request => {\n const current = catalog;\n const tool = current.byName.get(request.params.name);\n if (tool == null) {\n throw new McpError(\n ErrorCode.InvalidParams,\n `Unknown host tool: ${request.params.name}`,\n );\n }\n const input = request.params.arguments ?? {};\n const result = await invoke({\n toolName: tool.name,\n input,\n catalogRevision: current.revision,\n });\n return toCallToolResult({ result });\n });\n\n return {\n server,\n updateCatalog: async ({ revision: nextRevision, tools: nextTools }) => {\n catalog = createCatalog({\n revision: nextRevision,\n tools: nextTools,\n });\n await server.sendToolListChanged();\n },\n };\n}\n\nfunction createCatalog({\n revision,\n tools,\n}: {\n revision: number;\n tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n}): {\n readonly revision: number;\n readonly tools: ReadonlyArray<HarnessV1BridgeToolWire>;\n readonly byName: ReadonlyMap<string, HarnessV1BridgeToolWire>;\n} {\n return {\n revision,\n tools: [...tools],\n byName: new Map(tools.map(tool => [tool.name, tool])),\n };\n}\n\nfunction toMCPTool(tool: HarnessV1BridgeToolWire): Tool {\n return {\n name: tool.name,\n ...(tool.description == null ? {} : { description: tool.description }),\n inputSchema: requireObjectSchema({\n toolName: tool.name,\n schema: tool.inputSchema ?? { type: 'object' },\n }),\n };\n}\n\nfunction requireObjectSchema({\n toolName,\n schema,\n}: {\n toolName: string;\n schema: unknown;\n}): Tool['inputSchema'] {\n if (\n schema == null ||\n typeof schema !== 'object' ||\n Array.isArray(schema) ||\n (Reflect.get(schema, 'type') !== undefined &&\n Reflect.get(schema, 'type') !== 'object')\n ) {\n throw new Error(\n `Host tool ${toolName} must use an object JSON Schema for MCP.`,\n );\n }\n return schema as Tool['inputSchema'];\n}\n\nfunction toCallToolResult({\n result,\n}: {\n result: HostToolMCPInvocationResult;\n}): CallToolResult {\n return {\n content: [\n {\n type: 'text',\n text: stringifyOutput({ output: result.output }),\n },\n ],\n ...(result.isError ? { isError: true } : {}),\n _meta: {\n 'ai-sdk-harness-acp-correlation': result.correlationToken,\n },\n };\n}\n\nfunction stringifyOutput({ output }: { output: unknown }): string {\n if (output === undefined) return 'null';\n try {\n return JSON.stringify(output);\n } catch {\n return JSON.stringify({\n error: 'Host tool output could not be serialized as JSON.',\n });\n }\n}\n","import { Agent, request } from 'node:http';\n\n/*\n * A host-tool call can remain pending while a person reviews an approval.\n * The loopback connection therefore must not impose an inactivity deadline.\n */\nconst relayAgent = new Agent({ keepAlive: true, timeout: 0 });\n\nexport async function postHostToolRelay({\n relayUrl,\n relayCredential,\n path,\n body,\n}: {\n relayUrl: string;\n relayCredential: string;\n path: string;\n body: Readonly<Record<string, unknown>>;\n}): Promise<{ status: number; ok: boolean; value: unknown }> {\n const serializedBody = JSON.stringify(body);\n\n return new Promise((resolve, reject) => {\n const relayRequest = request(\n new URL(path, relayUrl),\n {\n agent: relayAgent,\n method: 'POST',\n headers: {\n authorization: `Bearer ${relayCredential}`,\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(serializedBody),\n },\n },\n response => {\n const chunks: Buffer[] = [];\n response.on('data', (chunk: Buffer) => chunks.push(chunk));\n response.on('error', reject);\n response.on('end', () => {\n void new Response(Buffer.concat(chunks)).json().then(value => {\n const status = response.statusCode ?? 0;\n resolve({\n status,\n ok: status >= 200 && status < 300,\n value,\n });\n }, reject);\n });\n },\n );\n relayRequest.on('error', reject);\n relayRequest.end(serializedBody);\n });\n}\n"],"mappings":"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","names":["tools","request","relayUrl","relayCredential"]}
@@ -69,7 +69,6 @@ async function runBridge(options) {
69
69
  let currentBoundPort = 0;
70
70
  let currentTurnState = "init";
71
71
  let activeSocket;
72
- let activeSocketReadyForLiveEvents = false;
73
72
  let isFirstTurn = true;
74
73
  let turnAbort;
75
74
  let currentUserMessages;
@@ -153,14 +152,6 @@ async function runBridge(options) {
153
152
  } catch {
154
153
  }
155
154
  };
156
- const sendControl = (msg) => {
157
- if (activeSocket?.readyState === WS_OPEN) {
158
- try {
159
- activeSocket.send(JSON.stringify(msg));
160
- } catch {
161
- }
162
- }
163
- };
164
155
  const emit = (event) => {
165
156
  const seq = ++seqCounter;
166
157
  const line = JSON.stringify({ ...event, seq });
@@ -168,7 +159,7 @@ async function runBridge(options) {
168
159
  diskBuffer += `${line}
169
160
  `;
170
161
  scheduleEventFlush();
171
- if (activeSocketReadyForLiveEvents && activeSocket?.readyState === WS_OPEN) {
162
+ if (activeSocket?.readyState === WS_OPEN) {
172
163
  try {
173
164
  activeSocket.send(line);
174
165
  } catch {
@@ -271,8 +262,7 @@ async function runBridge(options) {
271
262
  const handleInbound = async (msg, ws) => {
272
263
  switch (msg.type) {
273
264
  case "start": {
274
- if (activeSocket !== ws) return;
275
- activeSocketReadyForLiveEvents = true;
265
+ activeSocket = ws;
276
266
  const firstTurn = isFirstTurn;
277
267
  isFirstTurn = false;
278
268
  eventLog = [];
@@ -352,9 +342,8 @@ async function runBridge(options) {
352
342
  turnAbort?.abort();
353
343
  return;
354
344
  case "resume":
355
- if (activeSocket !== ws) return;
345
+ activeSocket = ws;
356
346
  replay(ws, msg.lastSeenEventId);
357
- activeSocketReadyForLiveEvents = true;
358
347
  return;
359
348
  case "destroy":
360
349
  currentTurnState = "done";
@@ -366,7 +355,7 @@ async function runBridge(options) {
366
355
  currentTurnState = "done";
367
356
  void writeBridgeMeta("done");
368
357
  const data = await onStop?.() ?? {};
369
- sendControl({ type: "bridge-stop", data });
358
+ sendControl(ws, { type: "bridge-stop", data });
370
359
  drainThenExit(ws, 1e3, "stop");
371
360
  return;
372
361
  }
@@ -419,9 +408,7 @@ async function runBridge(options) {
419
408
  ws.close(1008, "unauthorized");
420
409
  return;
421
410
  }
422
- activeSocket = ws;
423
- activeSocketReadyForLiveEvents = false;
424
- sendControl({
411
+ sendControl(ws, {
425
412
  type: "bridge-hello",
426
413
  state: currentTurnState,
427
414
  lastSeq: seqCounter
@@ -432,7 +419,7 @@ async function runBridge(options) {
432
419
  const text = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
433
420
  parsed = JSON.parse(text);
434
421
  } catch (err) {
435
- sendControl({
422
+ sendControl(ws, {
436
423
  type: "error",
437
424
  error: `protocol parse error: ${err.message}`
438
425
  });
@@ -443,7 +430,6 @@ async function runBridge(options) {
443
430
  ws.on("close", () => {
444
431
  if (activeSocket === ws) {
445
432
  activeSocket = void 0;
446
- activeSocketReadyForLiveEvents = false;
447
433
  }
448
434
  });
449
435
  ws.on("error", () => {
@@ -470,6 +456,14 @@ async function runBridge(options) {
470
456
  })
471
457
  };
472
458
  }
459
+ function sendControl(socket, message) {
460
+ if (socket?.readyState === WS_OPEN) {
461
+ try {
462
+ socket.send(JSON.stringify(message));
463
+ } catch {
464
+ }
465
+ }
466
+ }
473
467
  function serialiseError(err) {
474
468
  if (err instanceof Error) {
475
469
  return { name: err.name, message: err.message, stack: err.stack };
@@ -897,7 +891,6 @@ function createACPStreamTranslator({
897
891
  let openBlock;
898
892
  let blockCounter = 0;
899
893
  let stepOpen = false;
900
- let completedToolStepEligible = false;
901
894
  let finished = false;
902
895
  const blockIdCounts = /* @__PURE__ */ new Map();
903
896
  const toolStates = /* @__PURE__ */ new Map();
@@ -953,10 +946,9 @@ function createACPStreamTranslator({
953
946
  harnessMetadata: { acp: { inferredStep: true } }
954
947
  });
955
948
  stepOpen = false;
956
- completedToolStepEligible = false;
957
949
  };
958
- const flushCompletedToolStep = () => {
959
- if (!stepOpen || !completedToolStepEligible || pendingToolCallIds.size > 0) {
950
+ const finishCompletedToolStep = () => {
951
+ if (!stepOpen || pendingToolCallIds.size > 0) {
960
952
  return;
961
953
  }
962
954
  emitInferredStep({
@@ -1020,7 +1012,6 @@ function createACPStreamTranslator({
1020
1012
  state.emittedCall = true;
1021
1013
  stepOpen = true;
1022
1014
  pendingToolCallIds.add(state.toolCallId);
1023
- completedToolStepEligible = false;
1024
1015
  }
1025
1016
  emitFileChanges({ state, emit });
1026
1017
  if (!state.emittedResult && (state.values.status === "completed" || state.values.status === "failed")) {
@@ -1033,7 +1024,7 @@ function createACPStreamTranslator({
1033
1024
  });
1034
1025
  state.emittedResult = true;
1035
1026
  pendingToolCallIds.delete(state.toolCallId);
1036
- completedToolStepEligible = pendingToolCallIds.size === 0;
1027
+ finishCompletedToolStep();
1037
1028
  }
1038
1029
  };
1039
1030
  const hostToolCall = ({
@@ -1051,7 +1042,6 @@ function createACPStreamTranslator({
1051
1042
  });
1052
1043
  stepOpen = true;
1053
1044
  pendingToolCallIds.add(toolCallId);
1054
- completedToolStepEligible = false;
1055
1045
  };
1056
1046
  const hostToolResult = ({
1057
1047
  toolCallId,
@@ -1068,7 +1058,7 @@ function createACPStreamTranslator({
1068
1058
  ...isError ? { isError: true } : {}
1069
1059
  });
1070
1060
  pendingToolCallIds.delete(toolCallId);
1071
- completedToolStepEligible = pendingToolCallIds.size === 0;
1061
+ finishCompletedToolStep();
1072
1062
  };
1073
1063
  return {
1074
1064
  update: (options) => {
@@ -1082,7 +1072,6 @@ function createACPStreamTranslator({
1082
1072
  emit({ type: "raw", rawValue: preservedUpdate });
1083
1073
  }
1084
1074
  if (update.sessionUpdate === "agent_message_chunk" && update.content?.type === "text" && typeof update.content.text === "string") {
1085
- flushCompletedToolStep();
1086
1075
  emitContent({
1087
1076
  type: "text",
1088
1077
  text: update.content.text,
@@ -1091,7 +1080,6 @@ function createACPStreamTranslator({
1091
1080
  return;
1092
1081
  }
1093
1082
  if (update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string") {
1094
- flushCompletedToolStep();
1095
1083
  emitContent({
1096
1084
  type: "reasoning",
1097
1085
  text: update.content.text,
@@ -2746,6 +2734,27 @@ function createACPRecoveredSession({
2746
2734
  );
2747
2735
  return response;
2748
2736
  },
2737
+ promptWithMeta: async ({ prompt, meta }) => {
2738
+ if (disposed) {
2739
+ throw new Error("Recovered ACP session is disposed.");
2740
+ }
2741
+ updates.clearErrors();
2742
+ const response = agent.request(
2743
+ acp2.methods.agent.session.prompt,
2744
+ { sessionId, prompt, _meta: meta }
2745
+ );
2746
+ void response.then(
2747
+ (value) => {
2748
+ updates.enqueue({
2749
+ kind: "stop",
2750
+ response: value,
2751
+ stopReason: value.stopReason
2752
+ });
2753
+ },
2754
+ (error) => updates.reject(error)
2755
+ );
2756
+ return response;
2757
+ },
2749
2758
  nextUpdate: () => updates.next(),
2750
2759
  dispose: () => {
2751
2760
  if (disposed) return;
@@ -3047,7 +3056,13 @@ async function runTurn(start, turn) {
3047
3056
  };
3048
3057
  hostToolRelay?.bindTurn({ turn: relayTurn });
3049
3058
  try {
3050
- void activeSession.prompt(start.prompt);
3059
+ const promptMeta = createOutputSchemaPromptMeta({ start });
3060
+ void promptActiveSession({
3061
+ session: activeSession,
3062
+ agent: connection.agent,
3063
+ prompt: start.prompt,
3064
+ meta: promptMeta
3065
+ });
3051
3066
  if (turn.abortSignal.aborted) {
3052
3067
  await cancel();
3053
3068
  } else {
@@ -3434,6 +3449,60 @@ function createChildEnvironment({
3434
3449
  function isRecord7(value) {
3435
3450
  return value != null && typeof value === "object" && !Array.isArray(value);
3436
3451
  }
3452
+ function createOutputSchemaPromptMeta({
3453
+ start
3454
+ }) {
3455
+ if (start.responseFormat?.type !== "json" || start.responseFormat.schema == null || start.outputSchemaMapping?.type !== "session-prompt-meta") {
3456
+ return void 0;
3457
+ }
3458
+ const root = {};
3459
+ let target = root;
3460
+ const path = start.outputSchemaMapping.path;
3461
+ for (let index = 0; index < path.length - 1; index++) {
3462
+ const child2 = {};
3463
+ target[path[index]] = child2;
3464
+ target = child2;
3465
+ }
3466
+ target[path[path.length - 1]] = start.responseFormat.schema;
3467
+ return root;
3468
+ }
3469
+ function promptActiveSession({
3470
+ session: session2,
3471
+ agent,
3472
+ prompt,
3473
+ meta
3474
+ }) {
3475
+ if (meta == null) return session2.prompt(prompt);
3476
+ if (session2.promptWithMeta != null) {
3477
+ return session2.promptWithMeta({ prompt, meta });
3478
+ }
3479
+ const updates = session2.updates;
3480
+ if (updates == null) {
3481
+ throw new Error(
3482
+ "The installed ACP SDK cannot send session prompt metadata while preserving streamed updates."
3483
+ );
3484
+ }
3485
+ updates.clearErrors();
3486
+ const response = agent.request(
3487
+ acp4.methods.agent.session.prompt,
3488
+ {
3489
+ sessionId: session2.sessionId,
3490
+ prompt,
3491
+ _meta: meta
3492
+ }
3493
+ );
3494
+ void response.then(
3495
+ (value) => {
3496
+ updates.enqueue({
3497
+ kind: "stop",
3498
+ response: value,
3499
+ stopReason: value.stopReason
3500
+ });
3501
+ },
3502
+ (error) => updates.reject(error)
3503
+ );
3504
+ return response;
3505
+ }
3437
3506
  async function readImplementationDescriptor({
3438
3507
  path
3439
3508
  }) {