@acprotocol/server 0.1.1 → 0.1.3
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.
|
@@ -68,6 +68,8 @@ function buildSystemPrompt(manifest) {
|
|
|
68
68
|
parts.push(
|
|
69
69
|
[
|
|
70
70
|
"## Rules",
|
|
71
|
+
"- When the user provides information that matches available fields, IMMEDIATELY fill those fields using tools \u2014 do not wait for an explicit request to fill the form.",
|
|
72
|
+
"- Your primary job is to operate the UI. Whenever you can act, act \u2014 don't just acknowledge.",
|
|
71
73
|
'- Use `fill_field` with animate="typewriter" so the user can see values being entered.',
|
|
72
74
|
"- ALWAYS call `ask_confirm` before clicking any action marked [REQUIRES_CONFIRMATION].",
|
|
73
75
|
"- If the user's request is missing essential information, ask briefly and generically \u2014 do NOT list specific field names.",
|
|
@@ -175,12 +177,13 @@ function manifestToTools(manifest) {
|
|
|
175
177
|
screenIDs.push(id);
|
|
176
178
|
screenLabels.push(`${id} (${s.label})`);
|
|
177
179
|
}
|
|
178
|
-
const
|
|
180
|
+
const allFields = collectFields(manifest);
|
|
181
|
+
const allFieldIDs = allFields.map((f) => f.id);
|
|
179
182
|
const allActionIDs = collectActionIDs(manifest);
|
|
180
183
|
const allModalIDs = collectModalIDs(manifest);
|
|
181
184
|
const tools = [
|
|
182
185
|
navigateTool(screenIDs, screenLabels),
|
|
183
|
-
fillFieldTool(
|
|
186
|
+
fillFieldTool(allFields),
|
|
184
187
|
clearFieldTool(allFieldIDs),
|
|
185
188
|
clickActionTool(allActionIDs),
|
|
186
189
|
highlightTool(allFieldIDs),
|
|
@@ -263,15 +266,25 @@ function navigateTool(screenIDs, screenLabels) {
|
|
|
263
266
|
}
|
|
264
267
|
);
|
|
265
268
|
}
|
|
266
|
-
function fillFieldTool(
|
|
269
|
+
function fillFieldTool(fields) {
|
|
270
|
+
const fieldDescriptions = fields.map((f) => {
|
|
271
|
+
let desc = `${f.id} (${f.type})`;
|
|
272
|
+
if (f.options?.length) {
|
|
273
|
+
const opts = f.options.map((o) => o.value).join(", ");
|
|
274
|
+
desc += ` \u2014 valid values: [${opts}]`;
|
|
275
|
+
}
|
|
276
|
+
if (f.required) desc += " REQUIRED";
|
|
277
|
+
return desc;
|
|
278
|
+
});
|
|
267
279
|
return makeTool(
|
|
268
280
|
"fill_field",
|
|
269
|
-
`Fill a form field with a value.
|
|
281
|
+
`Fill a form field with a value. Available fields:
|
|
282
|
+
${fieldDescriptions.join("\n")}`,
|
|
270
283
|
{
|
|
271
284
|
type: "object",
|
|
272
285
|
properties: {
|
|
273
286
|
field: { type: "string", description: "Field ID to fill" },
|
|
274
|
-
value: { description: "Value to set
|
|
287
|
+
value: { description: "Value to set. For select fields, use one of the valid option values listed above." },
|
|
275
288
|
animate: {
|
|
276
289
|
type: "string",
|
|
277
290
|
enum: ["typewriter", "count_up", "fade_in", "none"],
|
|
@@ -368,18 +381,18 @@ function showToastTool() {
|
|
|
368
381
|
required: ["message"]
|
|
369
382
|
});
|
|
370
383
|
}
|
|
371
|
-
function
|
|
384
|
+
function collectFields(m) {
|
|
372
385
|
const seen = /* @__PURE__ */ new Set();
|
|
373
|
-
const
|
|
386
|
+
const fields = [];
|
|
374
387
|
for (const s of Object.values(m.screens)) {
|
|
375
388
|
for (const f of s.fields ?? []) {
|
|
376
389
|
if (!seen.has(f.id)) {
|
|
377
|
-
|
|
390
|
+
fields.push(f);
|
|
378
391
|
seen.add(f.id);
|
|
379
392
|
}
|
|
380
393
|
}
|
|
381
394
|
}
|
|
382
|
-
return
|
|
395
|
+
return fields;
|
|
383
396
|
}
|
|
384
397
|
function collectActionIDs(m) {
|
|
385
398
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -726,4 +739,4 @@ export {
|
|
|
726
739
|
runAgentLoop,
|
|
727
740
|
createServer
|
|
728
741
|
};
|
|
729
|
-
//# sourceMappingURL=chunk-
|
|
742
|
+
//# sourceMappingURL=chunk-4TQWR52X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/prompt.ts","../src/session.ts","../src/tools.ts","../src/agent.ts","../src/server.ts"],"sourcesContent":["import type { ManifestMessage } from \"./types.js\";\n\n/**\n * Builds the LLM system prompt from an ACP manifest.\n *\n * The prompt includes the following sections:\n * 1. **Identity** — from `persona.name` and `persona.role`, or a generic fallback\n * 2. **Instructions** — from `persona.instructions` (if present)\n * 3. **User context** — from `user.name`, `user.org`, `user.role`\n * 4. **Application context** — from `manifest.context` (JSON)\n * 5. **Screens & capabilities** — fields (with types, required flags, options), actions, modals\n * 6. **Rules** — 10 behavioral rules for UI control\n *\n * @param manifest - The ACP manifest message describing the application UI.\n * @returns A multi-section system prompt string for the LLM.\n *\n * @example\n * ```ts\n * import { buildSystemPrompt } from \"@acprotocol/server\";\n *\n * const prompt = buildSystemPrompt(manifest);\n * // => \"You are Aria, CRM assistant.\\n\\n...\"\n * ```\n */\nexport function buildSystemPrompt(manifest: ManifestMessage): string {\n const parts: string[] = [];\n\n // Identity\n if (manifest.persona?.name) {\n let identity = `You are ${manifest.persona.name}`;\n if (manifest.persona.role) identity += `, ${manifest.persona.role}`;\n identity += \".\";\n parts.push(identity);\n } else {\n parts.push(\n \"You are an AI assistant embedded in a software application.\",\n );\n }\n\n // Persona instructions\n if (manifest.persona?.instructions) {\n parts.push(manifest.persona.instructions);\n }\n\n // User context\n if (manifest.user) {\n const lines: string[] = [\"## User\"];\n if (manifest.user.name) lines.push(`- Name: ${manifest.user.name}`);\n if (manifest.user.org) lines.push(`- Organization: ${manifest.user.org}`);\n if (manifest.user.role) lines.push(`- Role: ${manifest.user.role}`);\n if (lines.length > 1) parts.push(lines.join(\"\\n\"));\n }\n\n // App context\n if (manifest.context && Object.keys(manifest.context).length > 0) {\n parts.push(\n \"## Application Context\\n\" + JSON.stringify(manifest.context, null, 2),\n );\n }\n\n // Screens & capabilities\n const screenLines: string[] = [\n \"## Application Screens\",\n \"You can control the application UI using the available tools. Here are the screens and their fields:\",\n \"\",\n ];\n\n for (const [id, screen] of Object.entries(manifest.screens)) {\n screenLines.push(`### Screen: ${id} (${screen.label})`);\n if (screen.route) screenLines.push(`Route: ${screen.route}`);\n\n if (screen.fields?.length) {\n screenLines.push(\"Fields:\");\n for (const f of screen.fields) {\n const req = f.required ? \" [REQUIRED]\" : \"\";\n screenLines.push(` - \\`${f.id}\\` (${f.type}): ${f.label}${req}`);\n if (f.options?.length) {\n const opts = f.options.map((o) => `${o.value}=${o.label}`).join(\", \");\n screenLines.push(` Options: ${opts}`);\n }\n }\n }\n\n if (screen.actions?.length) {\n screenLines.push(\"Actions:\");\n for (const act of screen.actions) {\n let flags = \"\";\n if (act.requiresConfirmation) flags += \" [REQUIRES_CONFIRMATION]\";\n if (act.destructive) flags += \" [DESTRUCTIVE]\";\n screenLines.push(` - \\`${act.id}\\`: ${act.label}${flags}`);\n }\n }\n\n if (screen.modals?.length) {\n screenLines.push(\"Modals:\");\n for (const md of screen.modals) {\n screenLines.push(` - \\`${md.id}\\`: ${md.label}`);\n }\n }\n\n screenLines.push(\"\");\n }\n parts.push(screenLines.join(\"\\n\"));\n\n // Rules\n parts.push(\n [\n \"## Rules\",\n \"- When the user provides information that matches available fields, IMMEDIATELY fill those fields using tools — do not wait for an explicit request to fill the form.\",\n \"- Your primary job is to operate the UI. Whenever you can act, act — don't just acknowledge.\",\n '- Use `fill_field` with animate=\"typewriter\" so the user can see values being entered.',\n \"- ALWAYS call `ask_confirm` before clicking any action marked [REQUIRES_CONFIRMATION].\",\n \"- If the user's request is missing essential information, ask briefly and generically — do NOT list specific field names.\",\n \"- Do NOT narrate individual fields being filled. Just confirm the action briefly when done.\",\n \"- If a command fails (you'll see the error in the next message), explain and try to fix it.\",\n \"- Respond in the same language the user speaks.\",\n \"- Be concise. Keep responses short — prefer brief confirmations.\",\n \"- Navigate to the correct screen before filling fields.\",\n \"- When filling multiple fields on the same screen, combine ALL fill_field calls in a single response.\",\n \"- Do NOT fill one field at a time — batch them together.\",\n ].join(\"\\n\"),\n );\n\n return parts.join(\"\\n\\n\");\n}\n","import type { ChatCompletionMessageParam } from \"openai/resources/chat/completions\";\nimport type { ManifestMessage } from \"./types.js\";\nimport { buildSystemPrompt } from \"./prompt.js\";\n\n/** Maximum number of messages to keep in the sliding window. */\nconst MAX_HISTORY = 40;\n\n/**\n * Per-connection session state.\n *\n * Manages the manifest, current screen, message history (with a sliding window),\n * and a monotonically increasing sequence counter for command/result correlation.\n *\n * @example\n * ```ts\n * import { Session } from \"@acprotocol/server\";\n *\n * const session = new Session(\"session-id\");\n * session.setManifest(manifest);\n * session.addMessage({ role: \"user\", content: \"Hello\" });\n * const history = session.getHistory(); // returns a copy\n * const seq = session.nextSeq(); // 0, 1, 2, ...\n * ```\n */\nexport class Session {\n /** Unique session identifier. */\n readonly id: string;\n\n /** The current ACP manifest, or `null` if not yet received. */\n manifest: ManifestMessage | null = null;\n\n /** The current screen ID. */\n currentScreen = \"\";\n\n private history: ChatCompletionMessageParam[] = [];\n private _seq = 0;\n\n constructor(id: string) {\n this.id = id;\n }\n\n /**\n * Sets the manifest and rebuilds the system prompt.\n *\n * If the manifest includes a `currentScreen`, it is applied.\n * The system prompt is built from the manifest and added or replaced\n * in the message history.\n *\n * @param manifest - The ACP manifest message describing the application UI.\n */\n setManifest(manifest: ManifestMessage): void {\n this.manifest = manifest;\n if (manifest.currentScreen) {\n this.currentScreen = manifest.currentScreen;\n }\n this.updateSystemPrompt(buildSystemPrompt(manifest));\n }\n\n /**\n * Updates the current screen ID.\n * @param screen - The screen ID to switch to.\n */\n setScreen(screen: string): void {\n this.currentScreen = screen;\n }\n\n /**\n * Adds a message to the conversation history.\n *\n * If the history exceeds {@link MAX_HISTORY} (40), the oldest messages\n * after the system prompt are trimmed to maintain a sliding window.\n *\n * @param msg - An OpenAI-compatible chat message.\n */\n addMessage(msg: ChatCompletionMessageParam): void {\n this.history.push(msg);\n if (this.history.length > MAX_HISTORY) {\n // Keep system prompt at index 0, trim oldest after it\n const system = this.history[0];\n const start = this.history.length - MAX_HISTORY + 1;\n this.history = [system, ...this.history.slice(start)];\n }\n }\n\n /**\n * Returns a shallow copy of the message history.\n * Safe to iterate without affecting the session's internal state.\n */\n getHistory(): ChatCompletionMessageParam[] {\n return [...this.history];\n }\n\n /**\n * Replaces the first system message in history, or prepends one.\n * @param prompt - The new system prompt content.\n */\n updateSystemPrompt(prompt: string): void {\n const idx = this.history.findIndex((m) => m.role === \"system\");\n if (idx >= 0) {\n this.history[idx] = { role: \"system\", content: prompt };\n } else {\n this.history.unshift({ role: \"system\", content: prompt });\n }\n }\n\n /**\n * Returns and increments the sequence counter.\n *\n * Each command sent to the client carries a `seq` number which the client\n * echoes back in the `result` or `confirm` message. This counter is\n * monotonically increasing per session.\n */\n nextSeq(): number {\n return this._seq++;\n }\n}\n","import type { ChatCompletionTool } from \"openai/resources/chat/completions\";\nimport type { ManifestMessage, UIAction, FieldDescriptor } from \"./types.js\";\n\n/**\n * Converts an ACP manifest into OpenAI-compatible tool definitions.\n *\n * Generates 8 base tools (navigate, fill_field, clear_field, click_action,\n * highlight, focus, ask_confirm, show_toast) plus 2 modal tools\n * (open_modal, close_modal) when the manifest contains modal descriptors.\n *\n * Field, action, and modal IDs are deduplicated across all screens.\n * Screen IDs and labels are included as enums in the navigate tool.\n *\n * @param manifest - The ACP manifest describing available screens and UI elements.\n * @returns An array of OpenAI-compatible tool definitions.\n *\n * @example\n * ```ts\n * const tools = manifestToTools(manifest);\n * // Pass to OpenAI: openai.chat.completions.create({ tools, ... })\n * ```\n */\nexport function manifestToTools(manifest: ManifestMessage): ChatCompletionTool[] {\n const screenIDs: string[] = [];\n const screenLabels: string[] = [];\n for (const [id, s] of Object.entries(manifest.screens)) {\n screenIDs.push(id);\n screenLabels.push(`${id} (${s.label})`);\n }\n\n const allFields = collectFields(manifest);\n const allFieldIDs = allFields.map((f) => f.id);\n const allActionIDs = collectActionIDs(manifest);\n const allModalIDs = collectModalIDs(manifest);\n\n const tools: ChatCompletionTool[] = [\n navigateTool(screenIDs, screenLabels),\n fillFieldTool(allFields),\n clearFieldTool(allFieldIDs),\n clickActionTool(allActionIDs),\n highlightTool(allFieldIDs),\n focusTool(allFieldIDs),\n askConfirmTool(),\n showToastTool(),\n ];\n\n if (allModalIDs.length > 0) {\n tools.push(openModalTool(allModalIDs), closeModalTool());\n }\n\n return tools;\n}\n\n/**\n * Converts an OpenAI tool call into an ACP UIAction.\n *\n * Supports all 10 tool names: navigate, fill_field, clear_field, click_action,\n * highlight, focus, open_modal, close_modal, ask_confirm, show_toast.\n *\n * @param name - The tool function name from the LLM response.\n * @param argsJSON - The JSON-encoded arguments string from the LLM response.\n * @returns A UIAction object ready to be sent in a command message.\n * @throws {Error} If the tool name is not recognized.\n *\n * @example\n * ```ts\n * const action = toolCallToUIAction(\"fill_field\", '{\"field\":\"name\",\"value\":\"Alice\"}');\n * // => { do: \"fill\", field: \"name\", value: \"Alice\", animate: \"typewriter\" }\n * ```\n */\nexport function toolCallToUIAction(name: string, argsJSON: string): UIAction {\n let args: Record<string, unknown>;\n try {\n args = JSON.parse(argsJSON);\n } catch {\n args = {};\n }\n\n switch (name) {\n case \"navigate\":\n return { do: \"navigate\", screen: str(args.screen) };\n\n case \"fill_field\":\n return {\n do: \"fill\",\n field: str(args.field),\n value: args.value,\n animate: (str(args.animate) as UIAction[\"animate\"]) || \"typewriter\",\n speed: num(args.speed) || undefined,\n };\n\n case \"clear_field\":\n return { do: \"clear\", field: str(args.field) };\n\n case \"click_action\":\n return { do: \"click\", action: str(args.action) };\n\n case \"highlight\":\n return {\n do: \"highlight\",\n field: str(args.field),\n duration: num(args.duration) || undefined,\n };\n\n case \"focus\":\n return { do: \"focus\", field: str(args.field) };\n\n case \"open_modal\":\n return {\n do: \"open_modal\",\n modal: str(args.modal),\n query: str(args.query) || undefined,\n };\n\n case \"close_modal\":\n return { do: \"close_modal\" };\n\n case \"ask_confirm\":\n return { do: \"ask_confirm\", message: str(args.message) };\n\n case \"show_toast\":\n return {\n do: \"show_toast\",\n message: str(args.message),\n level: (str(args.level) as UIAction[\"level\"]) || undefined,\n duration: num(args.duration) || undefined,\n };\n\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n}\n\n// ── Tool Builders ───────────────────────────────────────────────────────────\n\nfunction makeTool(name: string, description: string, parameters: object): ChatCompletionTool {\n return {\n type: \"function\",\n function: { name, description, parameters },\n } as ChatCompletionTool;\n}\n\nfunction navigateTool(screenIDs: string[], screenLabels: string[]): ChatCompletionTool {\n return makeTool(\n \"navigate\",\n `Navigate to a screen. Available: ${screenLabels.join(\", \")}`,\n {\n type: \"object\",\n properties: {\n screen: { type: \"string\", enum: screenIDs, description: \"Screen ID to navigate to\" },\n },\n required: [\"screen\"],\n },\n );\n}\n\nfunction fillFieldTool(fields: FieldDescriptor[]): ChatCompletionTool {\n const fieldDescriptions = fields.map((f) => {\n let desc = `${f.id} (${f.type})`;\n if (f.options?.length) {\n const opts = f.options.map((o) => o.value).join(\", \");\n desc += ` — valid values: [${opts}]`;\n }\n if (f.required) desc += \" REQUIRED\";\n return desc;\n });\n\n return makeTool(\n \"fill_field\",\n `Fill a form field with a value. Available fields:\\n${fieldDescriptions.join(\"\\n\")}`,\n {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to fill\" },\n value: { description: \"Value to set. For select fields, use one of the valid option values listed above.\" },\n animate: {\n type: \"string\",\n enum: [\"typewriter\", \"count_up\", \"fade_in\", \"none\"],\n default: \"typewriter\",\n description: \"Animation style for filling\",\n },\n },\n required: [\"field\", \"value\"],\n },\n );\n}\n\nfunction clearFieldTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\"clear_field\", \"Clear a form field value\", {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to clear\" },\n },\n required: [\"field\"],\n });\n}\n\nfunction clickActionTool(actionIDs: string[]): ChatCompletionTool {\n return makeTool(\n \"click_action\",\n `Click a button or trigger an action. Available: ${actionIDs.join(\", \")}. IMPORTANT: if the action has requiresConfirmation=true, you MUST call ask_confirm first and wait for the user's response before clicking.`,\n {\n type: \"object\",\n properties: {\n action: { type: \"string\", description: \"Action ID to click\" },\n },\n required: [\"action\"],\n },\n );\n}\n\nfunction highlightTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\"highlight\", \"Temporarily highlight a field to draw the user's attention\", {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to highlight\" },\n duration: { type: \"integer\", default: 2000, description: \"Highlight duration in milliseconds\" },\n },\n required: [\"field\"],\n });\n}\n\nfunction focusTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\"focus\", \"Set keyboard focus on a field\", {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to focus\" },\n },\n required: [\"field\"],\n });\n}\n\nfunction openModalTool(modalIDs: string[]): ChatCompletionTool {\n return makeTool(\n \"open_modal\",\n `Open a modal/dialog. Available: ${modalIDs.join(\", \")}`,\n {\n type: \"object\",\n properties: {\n modal: { type: \"string\", description: \"Modal ID to open\" },\n query: { type: \"string\", description: \"Optional search query to pre-fill in the modal\" },\n },\n required: [\"modal\"],\n },\n );\n}\n\nfunction closeModalTool(): ChatCompletionTool {\n return makeTool(\"close_modal\", \"Close the currently open modal\", {\n type: \"object\",\n properties: {},\n });\n}\n\nfunction askConfirmTool(): ChatCompletionTool {\n return makeTool(\n \"ask_confirm\",\n \"Ask the user for confirmation before proceeding with a destructive or important action.\",\n {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Confirmation question to ask the user\" },\n },\n required: [\"message\"],\n },\n );\n}\n\nfunction showToastTool(): ChatCompletionTool {\n return makeTool(\"show_toast\", \"Show a temporary notification/toast message in the app\", {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Toast message text\" },\n level: { type: \"string\", enum: [\"info\", \"success\", \"warning\", \"error\"], default: \"info\" },\n duration: { type: \"integer\", default: 3000 },\n },\n required: [\"message\"],\n });\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction collectFields(m: ManifestMessage): FieldDescriptor[] {\n const seen = new Set<string>();\n const fields: FieldDescriptor[] = [];\n for (const s of Object.values(m.screens)) {\n for (const f of s.fields ?? []) {\n if (!seen.has(f.id)) {\n fields.push(f);\n seen.add(f.id);\n }\n }\n }\n return fields;\n}\n\nfunction collectActionIDs(m: ManifestMessage): string[] {\n const seen = new Set<string>();\n const ids: string[] = [];\n for (const s of Object.values(m.screens)) {\n for (const a of s.actions ?? []) {\n if (!seen.has(a.id)) {\n ids.push(a.id);\n seen.add(a.id);\n }\n }\n }\n return ids;\n}\n\nfunction collectModalIDs(m: ManifestMessage): string[] {\n const seen = new Set<string>();\n const ids: string[] = [];\n for (const s of Object.values(m.screens)) {\n for (const md of s.modals ?? []) {\n if (!seen.has(md.id)) {\n ids.push(md.id);\n seen.add(md.id);\n }\n }\n }\n return ids;\n}\n\nfunction str(v: unknown): string {\n return typeof v === \"string\" ? v : \"\";\n}\n\nfunction num(v: unknown): number {\n return typeof v === \"number\" ? v : 0;\n}\n","import type OpenAI from \"openai\";\nimport type { ChatCompletionChunk } from \"openai/resources/chat/completions\";\nimport type { Session } from \"./session.js\";\nimport type { UIAction, ResultMessage, ServerMessage } from \"./types.js\";\nimport { manifestToTools, toolCallToUIAction } from \"./tools.js\";\n\n/** Maximum number of LLM rounds before sending a fallback response. */\nconst MAX_ROUNDS = 5;\n\n/** Callback to send a server message to the client. */\nexport type SendFn = (msg: ServerMessage) => void;\n\n/**\n * Callback to execute UI actions on the client.\n *\n * Sends a `command` message with the given `seq` and `actions`, then waits\n * for the client's `result` (or `confirm` for ask_confirm) response.\n *\n * @param seq - Sequence number for command/result correlation.\n * @param actions - Array of UI actions to execute on the client.\n * @returns The client's result message.\n */\nexport type ExecuteFn = (seq: number, actions: UIAction[]) => Promise<ResultMessage>;\n\n/**\n * Runs the streaming agent loop: LLM call → stream tokens → execute tool calls → repeat.\n *\n * The loop processes up to {@link MAX_ROUNDS} (5) rounds of tool calls.\n * In each round:\n * 1. Streams the LLM response, forwarding `chat_token` messages in real-time\n * 2. Accumulates tool call deltas from the stream\n * 3. If no tool calls → sends a final `chat` message and returns\n * 4. Converts tool calls to UIActions via {@link toolCallToUIAction}\n * 5. Executes actions on the client and maps results back to the LLM\n * 6. Continues to the next round\n *\n * If the loop exhausts all rounds, a fallback chat message is sent.\n *\n * @param openai - OpenAI client instance.\n * @param model - Model name to use for completions.\n * @param session - The current session (history, manifest, screen).\n * @param text - The user's text message.\n * @param execute - Callback to execute UI actions on the client.\n * @param send - Callback to send server messages to the client.\n */\nexport async function runAgentLoop(\n openai: OpenAI,\n model: string,\n session: Session,\n text: string,\n execute: ExecuteFn,\n send: SendFn,\n): Promise<void> {\n // Add user message to history\n session.addMessage({ role: \"user\", content: text });\n\n // Build tools from manifest\n const tools = session.manifest ? manifestToTools(session.manifest) : undefined;\n\n let lastResponseText = \"\";\n\n for (let round = 0; round < MAX_ROUNDS; round++) {\n const history = session.getHistory();\n\n const stream = await openai.chat.completions.create({\n model,\n messages: history,\n tools: tools?.length ? tools : undefined,\n stream: true,\n });\n\n let contentBuf = \"\";\n const accToolCalls: Array<{\n id: string;\n type: string;\n function: { name: string; arguments: string };\n }> = [];\n\n for await (const chunk of stream as AsyncIterable<ChatCompletionChunk>) {\n if (!chunk.choices?.length) continue;\n const delta = chunk.choices[0].delta;\n\n // Stream content tokens\n if (delta.content) {\n contentBuf += delta.content;\n send({ type: \"chat_token\", token: delta.content });\n }\n\n // Accumulate tool call deltas\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n while (accToolCalls.length <= idx) {\n accToolCalls.push({ id: \"\", type: \"\", function: { name: \"\", arguments: \"\" } });\n }\n if (tc.id) accToolCalls[idx].id = tc.id;\n if (tc.type) accToolCalls[idx].type = tc.type;\n if (tc.function?.name) accToolCalls[idx].function.name = tc.function.name;\n if (tc.function?.arguments) accToolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n }\n\n // Build assistant message for history\n const assistantMsg: Record<string, unknown> = {\n role: \"assistant\" as const,\n content: contentBuf || null,\n };\n if (accToolCalls.length > 0) {\n assistantMsg.tool_calls = accToolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.function.name, arguments: tc.function.arguments },\n }));\n }\n session.addMessage(assistantMsg as any);\n\n // No tool calls → final text response\n if (accToolCalls.length === 0) {\n if (contentBuf) {\n send({ type: \"chat\", from: \"agent\", message: contentBuf, final: true });\n }\n return;\n }\n\n // Tool calls → convert to UIActions and execute\n if (contentBuf) {\n lastResponseText = contentBuf;\n }\n\n const roundActions: UIAction[] = [];\n const mappings: Array<{ action: UIAction; callId: string }> = [];\n\n for (const tc of accToolCalls) {\n try {\n const action = toolCallToUIAction(tc.function.name, tc.function.arguments);\n roundActions.push(action);\n mappings.push({ action, callId: tc.id });\n } catch (err) {\n // Report parse error back to LLM\n session.addMessage({\n role: \"tool\",\n content: JSON.stringify({ error: String(err) }),\n tool_call_id: tc.id,\n });\n }\n }\n\n if (roundActions.length === 0) continue;\n\n // Check if this round is ask_confirm only\n const isConfirmOnly =\n roundActions.length === 1 && roundActions[0].do === \"ask_confirm\";\n\n const seq = session.nextSeq();\n send({ type: \"status\", status: \"executing\" });\n\n let resultMsg: ResultMessage;\n try {\n // Send command and wait for result/confirm\n resultMsg = await execute(seq, roundActions);\n } catch (err) {\n // Execution failed — report to LLM\n for (const m of mappings) {\n session.addMessage({\n role: \"tool\",\n content: JSON.stringify({ success: false, error: String(err) }),\n tool_call_id: m.callId,\n });\n }\n send({ type: \"status\", status: \"thinking\" });\n continue;\n }\n\n send({ type: \"status\", status: \"thinking\" });\n\n // Map results back to tool messages\n const resultsByIndex = new Map<number, { success: boolean; error?: string }>();\n for (const r of resultMsg.results) {\n resultsByIndex.set(r.index, r);\n }\n\n for (let i = 0; i < mappings.length; i++) {\n const m = mappings[i];\n const result: Record<string, unknown> = { success: true, action: m.action.do };\n const r = resultsByIndex.get(i);\n if (r) {\n result.success = r.success;\n if (!r.success && r.error) result.error = r.error;\n }\n if (m.action.do === \"navigate\") {\n session.setScreen(m.action.screen!);\n result.screen = m.action.screen;\n }\n if (m.action.do === \"fill\") {\n result.field = m.action.field;\n result.value = m.action.value;\n }\n\n // For ask_confirm, inject the user's yes/no response\n if (isConfirmOnly && m.action.do === \"ask_confirm\") {\n const confirmed = resultMsg.results[0]?.success ?? false;\n result.user_response = confirmed ? \"Yes\" : \"No\";\n }\n\n session.addMessage({\n role: \"tool\",\n content: JSON.stringify(result),\n tool_call_id: m.callId,\n });\n }\n }\n\n // Ran out of rounds — send fallback\n if (lastResponseText) {\n send({ type: \"chat\", from: \"agent\", message: lastResponseText, final: true });\n } else {\n send({ type: \"chat\", from: \"agent\", message: \"Done.\", final: true });\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport type { IncomingMessage } from \"node:http\";\nimport type OpenAI from \"openai\";\nimport type {\n ClientMessage,\n ManifestMessage,\n ResultMessage,\n ConfirmMessage,\n ServerMessage,\n UIAction,\n} from \"./types.js\";\nimport { Session } from \"./session.js\";\nimport { runAgentLoop } from \"./agent.js\";\n\n/** Timeout in milliseconds for waiting on a client result/confirm. */\nconst EXECUTE_TIMEOUT_MS = 30_000;\n\n/**\n * Configuration options for creating an ACP server.\n */\nexport interface ServerOptions {\n /** OpenAI client instance (supports any OpenAI-compatible API via `baseURL`). */\n openai: OpenAI;\n /** Model name for LLM completions (e.g. `\"gpt-4o\"`, `\"claude-sonnet-4-5-20250929\"`). */\n model: string;\n /** WebSocket server port. */\n port: number;\n}\n\n/**\n * An ACP server instance with lifecycle methods.\n */\nexport interface ACPServer {\n /** Starts the WebSocket server and begins accepting connections. */\n start(): Promise<void>;\n /** Stops the server and closes all active connections. */\n stop(): Promise<void>;\n}\n\n/**\n * Creates an ACP reference server.\n *\n * The server listens for WebSocket connections on `/connect` and implements\n * the full ACP v1 text protocol: manifest, text, state, result, confirm,\n * llm_config, and response_lang_config messages.\n *\n * @param options - Server configuration.\n * @returns An {@link ACPServer} with `start()` and `stop()` methods.\n *\n * @example\n * ```ts\n * import { createServer } from \"@acprotocol/server\";\n * import OpenAI from \"openai\";\n *\n * const server = createServer({\n * openai: new OpenAI({ apiKey: \"sk-...\" }),\n * model: \"gpt-4o\",\n * port: 3000,\n * });\n * await server.start();\n * // server is now accepting connections at ws://localhost:3000/connect\n * ```\n */\nexport function createServer(options: ServerOptions): ACPServer {\n const { openai, model, port } = options;\n let wss: WebSocketServer | null = null;\n\n return {\n async start() {\n wss = new WebSocketServer({ port, path: \"/connect\" });\n wss.on(\"connection\", (ws: WebSocket, req: IncomingMessage) => {\n handleConnection(ws, openai, model);\n });\n },\n\n async stop() {\n if (!wss) return;\n for (const ws of wss.clients) {\n ws.close(1001, \"Server shutting down\");\n }\n await new Promise<void>((resolve) => wss!.close(() => resolve()));\n wss = null;\n },\n };\n}\n\n// ── Connection Handler ──────────────────────────────────────────────────────\n\nfunction handleConnection(ws: WebSocket, openai: OpenAI, model: string): void {\n const sessionId = randomUUID();\n const session = new Session(sessionId);\n\n // Pending result/confirm resolvers: seq → resolver\n const pendingResults = new Map<number, (msg: ResultMessage) => void>();\n const pendingConfirms = new Map<number, (confirmed: boolean) => void>();\n\n // Whether the agent is currently processing (prevent concurrent runs)\n let processing = false;\n\n const send = (msg: ServerMessage): void => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n };\n\n // Send config on connect\n send({\n type: \"config\",\n sessionId,\n features: { chat: true },\n providers: [{ id: \"default\", name: \"Default\", model }],\n current_provider: \"default\",\n });\n\n // Keepalive ping every 30s\n const pingInterval = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) ws.ping();\n }, 30_000);\n\n ws.on(\"close\", () => {\n clearInterval(pingInterval);\n // Clean up any pending resolvers\n for (const [, reject] of pendingResults) {\n // Will be caught by the timeout\n }\n pendingResults.clear();\n pendingConfirms.clear();\n });\n\n ws.on(\"message\", (data: Buffer) => {\n let msg: ClientMessage;\n try {\n msg = JSON.parse(data.toString());\n } catch {\n send({ type: \"error\", code: \"parse_error\", message: \"Invalid JSON\" });\n return;\n }\n\n switch (msg.type) {\n case \"manifest\":\n handleManifest(msg, session, send, openai, model);\n break;\n\n case \"text\":\n if (!msg.message) return;\n if (processing) {\n send({ type: \"error\", code: \"busy\", message: \"Already processing a request\" });\n return;\n }\n processing = true;\n handleText(msg.message, session, openai, model, send, makeExecuteFn(send, session, pendingResults, pendingConfirms))\n .finally(() => { processing = false; });\n break;\n\n case \"state\":\n session.setScreen(msg.screen);\n break;\n\n case \"result\":\n deliverResult(msg, pendingResults);\n break;\n\n case \"confirm\":\n deliverConfirm(msg, pendingConfirms, session, openai, model, send, makeExecuteFn(send, session, pendingResults, pendingConfirms));\n break;\n\n case \"llm_config\":\n // Acknowledge (single-provider server — no-op)\n break;\n\n case \"response_lang_config\":\n // Acknowledge\n break;\n }\n });\n}\n\n// ── Message Handlers ────────────────────────────────────────────────────────\n\nfunction handleManifest(\n msg: ManifestMessage,\n session: Session,\n send: (msg: ServerMessage) => void,\n openai: OpenAI,\n model: string,\n): void {\n session.setManifest(msg);\n send({ type: \"status\", status: \"idle\" });\n\n // Send greeting based on manifest context\n send({\n type: \"chat\",\n from: \"agent\",\n message: buildGreeting(msg),\n final: true,\n });\n}\n\nfunction buildGreeting(msg: ManifestMessage): string {\n const name = msg.persona?.name;\n const role = msg.persona?.role;\n const screens = Object.values(msg.screens);\n const screenLabel = screens.length === 1 ? screens[0].label : null;\n\n // Count capabilities\n const fieldCount = screens.reduce((sum, s) => sum + (s.fields?.length ?? 0), 0);\n const actionCount = screens.reduce((sum, s) => sum + (s.actions?.length ?? 0), 0);\n\n const intro = name ? `Hi, I'm ${name}!` : \"Hi!\";\n const roleDesc = role ? ` I'm your ${role}.` : \"\";\n\n let capability = \"\";\n if (screenLabel && fieldCount > 0) {\n capability = ` I can help you with the ${screenLabel} — just tell me what to fill in and I'll handle it.`;\n } else if (fieldCount > 0) {\n capability = ` I can fill forms, navigate screens, and click actions for you — just tell me what you need.`;\n }\n\n return `${intro}${roleDesc}${capability}`;\n}\n\nasync function handleText(\n text: string,\n session: Session,\n openai: OpenAI,\n model: string,\n send: (msg: ServerMessage) => void,\n execute: (seq: number, actions: UIAction[]) => Promise<ResultMessage>,\n): Promise<void> {\n send({ type: \"status\", status: \"thinking\" });\n\n try {\n await runAgentLoop(openai, model, session, text, execute, send);\n } catch (err) {\n console.error(\"[acp-server] Agent error:\", err);\n send({ type: \"error\", code: \"agent_error\", message: String(err) });\n }\n\n send({ type: \"status\", status: \"idle\" });\n}\n\nfunction deliverResult(\n msg: ResultMessage,\n pendingResults: Map<number, (msg: ResultMessage) => void>,\n): void {\n const resolver = pendingResults.get(msg.seq);\n if (resolver) {\n pendingResults.delete(msg.seq);\n resolver(msg);\n }\n}\n\nfunction deliverConfirm(\n msg: ConfirmMessage,\n pendingConfirms: Map<number, (confirmed: boolean) => void>,\n session: Session,\n openai: OpenAI,\n model: string,\n send: (m: ServerMessage) => void,\n execute: (seq: number, actions: UIAction[]) => Promise<ResultMessage>,\n): void {\n const resolver = pendingConfirms.get(msg.seq);\n if (resolver) {\n pendingConfirms.delete(msg.seq);\n resolver(msg.confirmed);\n }\n}\n\n// ── Execute Function ────────────────────────────────────────────────────────\n\nfunction makeExecuteFn(\n send: (msg: ServerMessage) => void,\n session: Session,\n pendingResults: Map<number, (msg: ResultMessage) => void>,\n pendingConfirms: Map<number, (confirmed: boolean) => void>,\n): (seq: number, actions: UIAction[]) => Promise<ResultMessage> {\n return (seq: number, actions: UIAction[]): Promise<ResultMessage> => {\n // Detect if this is a confirm-only command\n const isConfirmOnly =\n actions.length === 1 && actions[0].do === \"ask_confirm\";\n\n // Send command to client\n send({ type: \"command\", seq, actions });\n\n if (isConfirmOnly) {\n // Wait for confirm message instead of result\n return new Promise<ResultMessage>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingConfirms.delete(seq);\n reject(new Error(`Timeout waiting for confirm seq=${seq}`));\n }, EXECUTE_TIMEOUT_MS);\n\n pendingConfirms.set(seq, (confirmed: boolean) => {\n clearTimeout(timer);\n // Wrap confirmation as a ResultMessage\n resolve({\n type: \"result\",\n seq,\n results: [{ index: 0, success: confirmed }],\n });\n });\n });\n }\n\n // Wait for result message\n return new Promise<ResultMessage>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingResults.delete(seq);\n reject(new Error(`Timeout waiting for result seq=${seq}`));\n }, EXECUTE_TIMEOUT_MS);\n\n pendingResults.set(seq, (result: ResultMessage) => {\n clearTimeout(timer);\n resolve(result);\n });\n });\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acprotocol/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "ACP Reference Server — a minimal TypeScript server implementing the Agent Control Protocol",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Primoia Technologies (https://primoia.ai)",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/prompt.ts","../src/session.ts","../src/tools.ts","../src/agent.ts","../src/server.ts"],"sourcesContent":["import type { ManifestMessage } from \"./types.js\";\n\n/**\n * Builds the LLM system prompt from an ACP manifest.\n *\n * The prompt includes the following sections:\n * 1. **Identity** — from `persona.name` and `persona.role`, or a generic fallback\n * 2. **Instructions** — from `persona.instructions` (if present)\n * 3. **User context** — from `user.name`, `user.org`, `user.role`\n * 4. **Application context** — from `manifest.context` (JSON)\n * 5. **Screens & capabilities** — fields (with types, required flags, options), actions, modals\n * 6. **Rules** — 10 behavioral rules for UI control\n *\n * @param manifest - The ACP manifest message describing the application UI.\n * @returns A multi-section system prompt string for the LLM.\n *\n * @example\n * ```ts\n * import { buildSystemPrompt } from \"@acprotocol/server\";\n *\n * const prompt = buildSystemPrompt(manifest);\n * // => \"You are Aria, CRM assistant.\\n\\n...\"\n * ```\n */\nexport function buildSystemPrompt(manifest: ManifestMessage): string {\n const parts: string[] = [];\n\n // Identity\n if (manifest.persona?.name) {\n let identity = `You are ${manifest.persona.name}`;\n if (manifest.persona.role) identity += `, ${manifest.persona.role}`;\n identity += \".\";\n parts.push(identity);\n } else {\n parts.push(\n \"You are an AI assistant embedded in a software application.\",\n );\n }\n\n // Persona instructions\n if (manifest.persona?.instructions) {\n parts.push(manifest.persona.instructions);\n }\n\n // User context\n if (manifest.user) {\n const lines: string[] = [\"## User\"];\n if (manifest.user.name) lines.push(`- Name: ${manifest.user.name}`);\n if (manifest.user.org) lines.push(`- Organization: ${manifest.user.org}`);\n if (manifest.user.role) lines.push(`- Role: ${manifest.user.role}`);\n if (lines.length > 1) parts.push(lines.join(\"\\n\"));\n }\n\n // App context\n if (manifest.context && Object.keys(manifest.context).length > 0) {\n parts.push(\n \"## Application Context\\n\" + JSON.stringify(manifest.context, null, 2),\n );\n }\n\n // Screens & capabilities\n const screenLines: string[] = [\n \"## Application Screens\",\n \"You can control the application UI using the available tools. Here are the screens and their fields:\",\n \"\",\n ];\n\n for (const [id, screen] of Object.entries(manifest.screens)) {\n screenLines.push(`### Screen: ${id} (${screen.label})`);\n if (screen.route) screenLines.push(`Route: ${screen.route}`);\n\n if (screen.fields?.length) {\n screenLines.push(\"Fields:\");\n for (const f of screen.fields) {\n const req = f.required ? \" [REQUIRED]\" : \"\";\n screenLines.push(` - \\`${f.id}\\` (${f.type}): ${f.label}${req}`);\n if (f.options?.length) {\n const opts = f.options.map((o) => `${o.value}=${o.label}`).join(\", \");\n screenLines.push(` Options: ${opts}`);\n }\n }\n }\n\n if (screen.actions?.length) {\n screenLines.push(\"Actions:\");\n for (const act of screen.actions) {\n let flags = \"\";\n if (act.requiresConfirmation) flags += \" [REQUIRES_CONFIRMATION]\";\n if (act.destructive) flags += \" [DESTRUCTIVE]\";\n screenLines.push(` - \\`${act.id}\\`: ${act.label}${flags}`);\n }\n }\n\n if (screen.modals?.length) {\n screenLines.push(\"Modals:\");\n for (const md of screen.modals) {\n screenLines.push(` - \\`${md.id}\\`: ${md.label}`);\n }\n }\n\n screenLines.push(\"\");\n }\n parts.push(screenLines.join(\"\\n\"));\n\n // Rules\n parts.push(\n [\n \"## Rules\",\n '- Use `fill_field` with animate=\"typewriter\" so the user can see values being entered.',\n \"- ALWAYS call `ask_confirm` before clicking any action marked [REQUIRES_CONFIRMATION].\",\n \"- If the user's request is missing essential information, ask briefly and generically — do NOT list specific field names.\",\n \"- Do NOT narrate individual fields being filled. Just confirm the action briefly when done.\",\n \"- If a command fails (you'll see the error in the next message), explain and try to fix it.\",\n \"- Respond in the same language the user speaks.\",\n \"- Be concise. Keep responses short — prefer brief confirmations.\",\n \"- Navigate to the correct screen before filling fields.\",\n \"- When filling multiple fields on the same screen, combine ALL fill_field calls in a single response.\",\n \"- Do NOT fill one field at a time — batch them together.\",\n ].join(\"\\n\"),\n );\n\n return parts.join(\"\\n\\n\");\n}\n","import type { ChatCompletionMessageParam } from \"openai/resources/chat/completions\";\nimport type { ManifestMessage } from \"./types.js\";\nimport { buildSystemPrompt } from \"./prompt.js\";\n\n/** Maximum number of messages to keep in the sliding window. */\nconst MAX_HISTORY = 40;\n\n/**\n * Per-connection session state.\n *\n * Manages the manifest, current screen, message history (with a sliding window),\n * and a monotonically increasing sequence counter for command/result correlation.\n *\n * @example\n * ```ts\n * import { Session } from \"@acprotocol/server\";\n *\n * const session = new Session(\"session-id\");\n * session.setManifest(manifest);\n * session.addMessage({ role: \"user\", content: \"Hello\" });\n * const history = session.getHistory(); // returns a copy\n * const seq = session.nextSeq(); // 0, 1, 2, ...\n * ```\n */\nexport class Session {\n /** Unique session identifier. */\n readonly id: string;\n\n /** The current ACP manifest, or `null` if not yet received. */\n manifest: ManifestMessage | null = null;\n\n /** The current screen ID. */\n currentScreen = \"\";\n\n private history: ChatCompletionMessageParam[] = [];\n private _seq = 0;\n\n constructor(id: string) {\n this.id = id;\n }\n\n /**\n * Sets the manifest and rebuilds the system prompt.\n *\n * If the manifest includes a `currentScreen`, it is applied.\n * The system prompt is built from the manifest and added or replaced\n * in the message history.\n *\n * @param manifest - The ACP manifest message describing the application UI.\n */\n setManifest(manifest: ManifestMessage): void {\n this.manifest = manifest;\n if (manifest.currentScreen) {\n this.currentScreen = manifest.currentScreen;\n }\n this.updateSystemPrompt(buildSystemPrompt(manifest));\n }\n\n /**\n * Updates the current screen ID.\n * @param screen - The screen ID to switch to.\n */\n setScreen(screen: string): void {\n this.currentScreen = screen;\n }\n\n /**\n * Adds a message to the conversation history.\n *\n * If the history exceeds {@link MAX_HISTORY} (40), the oldest messages\n * after the system prompt are trimmed to maintain a sliding window.\n *\n * @param msg - An OpenAI-compatible chat message.\n */\n addMessage(msg: ChatCompletionMessageParam): void {\n this.history.push(msg);\n if (this.history.length > MAX_HISTORY) {\n // Keep system prompt at index 0, trim oldest after it\n const system = this.history[0];\n const start = this.history.length - MAX_HISTORY + 1;\n this.history = [system, ...this.history.slice(start)];\n }\n }\n\n /**\n * Returns a shallow copy of the message history.\n * Safe to iterate without affecting the session's internal state.\n */\n getHistory(): ChatCompletionMessageParam[] {\n return [...this.history];\n }\n\n /**\n * Replaces the first system message in history, or prepends one.\n * @param prompt - The new system prompt content.\n */\n updateSystemPrompt(prompt: string): void {\n const idx = this.history.findIndex((m) => m.role === \"system\");\n if (idx >= 0) {\n this.history[idx] = { role: \"system\", content: prompt };\n } else {\n this.history.unshift({ role: \"system\", content: prompt });\n }\n }\n\n /**\n * Returns and increments the sequence counter.\n *\n * Each command sent to the client carries a `seq` number which the client\n * echoes back in the `result` or `confirm` message. This counter is\n * monotonically increasing per session.\n */\n nextSeq(): number {\n return this._seq++;\n }\n}\n","import type { ChatCompletionTool } from \"openai/resources/chat/completions\";\nimport type { ManifestMessage, UIAction } from \"./types.js\";\n\n/**\n * Converts an ACP manifest into OpenAI-compatible tool definitions.\n *\n * Generates 8 base tools (navigate, fill_field, clear_field, click_action,\n * highlight, focus, ask_confirm, show_toast) plus 2 modal tools\n * (open_modal, close_modal) when the manifest contains modal descriptors.\n *\n * Field, action, and modal IDs are deduplicated across all screens.\n * Screen IDs and labels are included as enums in the navigate tool.\n *\n * @param manifest - The ACP manifest describing available screens and UI elements.\n * @returns An array of OpenAI-compatible tool definitions.\n *\n * @example\n * ```ts\n * const tools = manifestToTools(manifest);\n * // Pass to OpenAI: openai.chat.completions.create({ tools, ... })\n * ```\n */\nexport function manifestToTools(manifest: ManifestMessage): ChatCompletionTool[] {\n const screenIDs: string[] = [];\n const screenLabels: string[] = [];\n for (const [id, s] of Object.entries(manifest.screens)) {\n screenIDs.push(id);\n screenLabels.push(`${id} (${s.label})`);\n }\n\n const allFieldIDs = collectFieldIDs(manifest);\n const allActionIDs = collectActionIDs(manifest);\n const allModalIDs = collectModalIDs(manifest);\n\n const tools: ChatCompletionTool[] = [\n navigateTool(screenIDs, screenLabels),\n fillFieldTool(allFieldIDs),\n clearFieldTool(allFieldIDs),\n clickActionTool(allActionIDs),\n highlightTool(allFieldIDs),\n focusTool(allFieldIDs),\n askConfirmTool(),\n showToastTool(),\n ];\n\n if (allModalIDs.length > 0) {\n tools.push(openModalTool(allModalIDs), closeModalTool());\n }\n\n return tools;\n}\n\n/**\n * Converts an OpenAI tool call into an ACP UIAction.\n *\n * Supports all 10 tool names: navigate, fill_field, clear_field, click_action,\n * highlight, focus, open_modal, close_modal, ask_confirm, show_toast.\n *\n * @param name - The tool function name from the LLM response.\n * @param argsJSON - The JSON-encoded arguments string from the LLM response.\n * @returns A UIAction object ready to be sent in a command message.\n * @throws {Error} If the tool name is not recognized.\n *\n * @example\n * ```ts\n * const action = toolCallToUIAction(\"fill_field\", '{\"field\":\"name\",\"value\":\"Alice\"}');\n * // => { do: \"fill\", field: \"name\", value: \"Alice\", animate: \"typewriter\" }\n * ```\n */\nexport function toolCallToUIAction(name: string, argsJSON: string): UIAction {\n let args: Record<string, unknown>;\n try {\n args = JSON.parse(argsJSON);\n } catch {\n args = {};\n }\n\n switch (name) {\n case \"navigate\":\n return { do: \"navigate\", screen: str(args.screen) };\n\n case \"fill_field\":\n return {\n do: \"fill\",\n field: str(args.field),\n value: args.value,\n animate: (str(args.animate) as UIAction[\"animate\"]) || \"typewriter\",\n speed: num(args.speed) || undefined,\n };\n\n case \"clear_field\":\n return { do: \"clear\", field: str(args.field) };\n\n case \"click_action\":\n return { do: \"click\", action: str(args.action) };\n\n case \"highlight\":\n return {\n do: \"highlight\",\n field: str(args.field),\n duration: num(args.duration) || undefined,\n };\n\n case \"focus\":\n return { do: \"focus\", field: str(args.field) };\n\n case \"open_modal\":\n return {\n do: \"open_modal\",\n modal: str(args.modal),\n query: str(args.query) || undefined,\n };\n\n case \"close_modal\":\n return { do: \"close_modal\" };\n\n case \"ask_confirm\":\n return { do: \"ask_confirm\", message: str(args.message) };\n\n case \"show_toast\":\n return {\n do: \"show_toast\",\n message: str(args.message),\n level: (str(args.level) as UIAction[\"level\"]) || undefined,\n duration: num(args.duration) || undefined,\n };\n\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n}\n\n// ── Tool Builders ───────────────────────────────────────────────────────────\n\nfunction makeTool(name: string, description: string, parameters: object): ChatCompletionTool {\n return {\n type: \"function\",\n function: { name, description, parameters },\n } as ChatCompletionTool;\n}\n\nfunction navigateTool(screenIDs: string[], screenLabels: string[]): ChatCompletionTool {\n return makeTool(\n \"navigate\",\n `Navigate to a screen. Available: ${screenLabels.join(\", \")}`,\n {\n type: \"object\",\n properties: {\n screen: { type: \"string\", enum: screenIDs, description: \"Screen ID to navigate to\" },\n },\n required: [\"screen\"],\n },\n );\n}\n\nfunction fillFieldTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\n \"fill_field\",\n `Fill a form field with a value. The field animates as the value is typed. Available fields: ${fieldIDs.join(\", \")}`,\n {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to fill\" },\n value: { description: \"Value to set (string, number, or boolean depending on field type)\" },\n animate: {\n type: \"string\",\n enum: [\"typewriter\", \"count_up\", \"fade_in\", \"none\"],\n default: \"typewriter\",\n description: \"Animation style for filling\",\n },\n },\n required: [\"field\", \"value\"],\n },\n );\n}\n\nfunction clearFieldTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\"clear_field\", \"Clear a form field value\", {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to clear\" },\n },\n required: [\"field\"],\n });\n}\n\nfunction clickActionTool(actionIDs: string[]): ChatCompletionTool {\n return makeTool(\n \"click_action\",\n `Click a button or trigger an action. Available: ${actionIDs.join(\", \")}. IMPORTANT: if the action has requiresConfirmation=true, you MUST call ask_confirm first and wait for the user's response before clicking.`,\n {\n type: \"object\",\n properties: {\n action: { type: \"string\", description: \"Action ID to click\" },\n },\n required: [\"action\"],\n },\n );\n}\n\nfunction highlightTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\"highlight\", \"Temporarily highlight a field to draw the user's attention\", {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to highlight\" },\n duration: { type: \"integer\", default: 2000, description: \"Highlight duration in milliseconds\" },\n },\n required: [\"field\"],\n });\n}\n\nfunction focusTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\"focus\", \"Set keyboard focus on a field\", {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to focus\" },\n },\n required: [\"field\"],\n });\n}\n\nfunction openModalTool(modalIDs: string[]): ChatCompletionTool {\n return makeTool(\n \"open_modal\",\n `Open a modal/dialog. Available: ${modalIDs.join(\", \")}`,\n {\n type: \"object\",\n properties: {\n modal: { type: \"string\", description: \"Modal ID to open\" },\n query: { type: \"string\", description: \"Optional search query to pre-fill in the modal\" },\n },\n required: [\"modal\"],\n },\n );\n}\n\nfunction closeModalTool(): ChatCompletionTool {\n return makeTool(\"close_modal\", \"Close the currently open modal\", {\n type: \"object\",\n properties: {},\n });\n}\n\nfunction askConfirmTool(): ChatCompletionTool {\n return makeTool(\n \"ask_confirm\",\n \"Ask the user for confirmation before proceeding with a destructive or important action.\",\n {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Confirmation question to ask the user\" },\n },\n required: [\"message\"],\n },\n );\n}\n\nfunction showToastTool(): ChatCompletionTool {\n return makeTool(\"show_toast\", \"Show a temporary notification/toast message in the app\", {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Toast message text\" },\n level: { type: \"string\", enum: [\"info\", \"success\", \"warning\", \"error\"], default: \"info\" },\n duration: { type: \"integer\", default: 3000 },\n },\n required: [\"message\"],\n });\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction collectFieldIDs(m: ManifestMessage): string[] {\n const seen = new Set<string>();\n const ids: string[] = [];\n for (const s of Object.values(m.screens)) {\n for (const f of s.fields ?? []) {\n if (!seen.has(f.id)) {\n ids.push(f.id);\n seen.add(f.id);\n }\n }\n }\n return ids;\n}\n\nfunction collectActionIDs(m: ManifestMessage): string[] {\n const seen = new Set<string>();\n const ids: string[] = [];\n for (const s of Object.values(m.screens)) {\n for (const a of s.actions ?? []) {\n if (!seen.has(a.id)) {\n ids.push(a.id);\n seen.add(a.id);\n }\n }\n }\n return ids;\n}\n\nfunction collectModalIDs(m: ManifestMessage): string[] {\n const seen = new Set<string>();\n const ids: string[] = [];\n for (const s of Object.values(m.screens)) {\n for (const md of s.modals ?? []) {\n if (!seen.has(md.id)) {\n ids.push(md.id);\n seen.add(md.id);\n }\n }\n }\n return ids;\n}\n\nfunction str(v: unknown): string {\n return typeof v === \"string\" ? v : \"\";\n}\n\nfunction num(v: unknown): number {\n return typeof v === \"number\" ? v : 0;\n}\n","import type OpenAI from \"openai\";\nimport type { ChatCompletionChunk } from \"openai/resources/chat/completions\";\nimport type { Session } from \"./session.js\";\nimport type { UIAction, ResultMessage, ServerMessage } from \"./types.js\";\nimport { manifestToTools, toolCallToUIAction } from \"./tools.js\";\n\n/** Maximum number of LLM rounds before sending a fallback response. */\nconst MAX_ROUNDS = 5;\n\n/** Callback to send a server message to the client. */\nexport type SendFn = (msg: ServerMessage) => void;\n\n/**\n * Callback to execute UI actions on the client.\n *\n * Sends a `command` message with the given `seq` and `actions`, then waits\n * for the client's `result` (or `confirm` for ask_confirm) response.\n *\n * @param seq - Sequence number for command/result correlation.\n * @param actions - Array of UI actions to execute on the client.\n * @returns The client's result message.\n */\nexport type ExecuteFn = (seq: number, actions: UIAction[]) => Promise<ResultMessage>;\n\n/**\n * Runs the streaming agent loop: LLM call → stream tokens → execute tool calls → repeat.\n *\n * The loop processes up to {@link MAX_ROUNDS} (5) rounds of tool calls.\n * In each round:\n * 1. Streams the LLM response, forwarding `chat_token` messages in real-time\n * 2. Accumulates tool call deltas from the stream\n * 3. If no tool calls → sends a final `chat` message and returns\n * 4. Converts tool calls to UIActions via {@link toolCallToUIAction}\n * 5. Executes actions on the client and maps results back to the LLM\n * 6. Continues to the next round\n *\n * If the loop exhausts all rounds, a fallback chat message is sent.\n *\n * @param openai - OpenAI client instance.\n * @param model - Model name to use for completions.\n * @param session - The current session (history, manifest, screen).\n * @param text - The user's text message.\n * @param execute - Callback to execute UI actions on the client.\n * @param send - Callback to send server messages to the client.\n */\nexport async function runAgentLoop(\n openai: OpenAI,\n model: string,\n session: Session,\n text: string,\n execute: ExecuteFn,\n send: SendFn,\n): Promise<void> {\n // Add user message to history\n session.addMessage({ role: \"user\", content: text });\n\n // Build tools from manifest\n const tools = session.manifest ? manifestToTools(session.manifest) : undefined;\n\n let lastResponseText = \"\";\n\n for (let round = 0; round < MAX_ROUNDS; round++) {\n const history = session.getHistory();\n\n const stream = await openai.chat.completions.create({\n model,\n messages: history,\n tools: tools?.length ? tools : undefined,\n stream: true,\n });\n\n let contentBuf = \"\";\n const accToolCalls: Array<{\n id: string;\n type: string;\n function: { name: string; arguments: string };\n }> = [];\n\n for await (const chunk of stream as AsyncIterable<ChatCompletionChunk>) {\n if (!chunk.choices?.length) continue;\n const delta = chunk.choices[0].delta;\n\n // Stream content tokens\n if (delta.content) {\n contentBuf += delta.content;\n send({ type: \"chat_token\", token: delta.content });\n }\n\n // Accumulate tool call deltas\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n while (accToolCalls.length <= idx) {\n accToolCalls.push({ id: \"\", type: \"\", function: { name: \"\", arguments: \"\" } });\n }\n if (tc.id) accToolCalls[idx].id = tc.id;\n if (tc.type) accToolCalls[idx].type = tc.type;\n if (tc.function?.name) accToolCalls[idx].function.name = tc.function.name;\n if (tc.function?.arguments) accToolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n }\n\n // Build assistant message for history\n const assistantMsg: Record<string, unknown> = {\n role: \"assistant\" as const,\n content: contentBuf || null,\n };\n if (accToolCalls.length > 0) {\n assistantMsg.tool_calls = accToolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.function.name, arguments: tc.function.arguments },\n }));\n }\n session.addMessage(assistantMsg as any);\n\n // No tool calls → final text response\n if (accToolCalls.length === 0) {\n if (contentBuf) {\n send({ type: \"chat\", from: \"agent\", message: contentBuf, final: true });\n }\n return;\n }\n\n // Tool calls → convert to UIActions and execute\n if (contentBuf) {\n lastResponseText = contentBuf;\n }\n\n const roundActions: UIAction[] = [];\n const mappings: Array<{ action: UIAction; callId: string }> = [];\n\n for (const tc of accToolCalls) {\n try {\n const action = toolCallToUIAction(tc.function.name, tc.function.arguments);\n roundActions.push(action);\n mappings.push({ action, callId: tc.id });\n } catch (err) {\n // Report parse error back to LLM\n session.addMessage({\n role: \"tool\",\n content: JSON.stringify({ error: String(err) }),\n tool_call_id: tc.id,\n });\n }\n }\n\n if (roundActions.length === 0) continue;\n\n // Check if this round is ask_confirm only\n const isConfirmOnly =\n roundActions.length === 1 && roundActions[0].do === \"ask_confirm\";\n\n const seq = session.nextSeq();\n send({ type: \"status\", status: \"executing\" });\n\n let resultMsg: ResultMessage;\n try {\n // Send command and wait for result/confirm\n resultMsg = await execute(seq, roundActions);\n } catch (err) {\n // Execution failed — report to LLM\n for (const m of mappings) {\n session.addMessage({\n role: \"tool\",\n content: JSON.stringify({ success: false, error: String(err) }),\n tool_call_id: m.callId,\n });\n }\n send({ type: \"status\", status: \"thinking\" });\n continue;\n }\n\n send({ type: \"status\", status: \"thinking\" });\n\n // Map results back to tool messages\n const resultsByIndex = new Map<number, { success: boolean; error?: string }>();\n for (const r of resultMsg.results) {\n resultsByIndex.set(r.index, r);\n }\n\n for (let i = 0; i < mappings.length; i++) {\n const m = mappings[i];\n const result: Record<string, unknown> = { success: true, action: m.action.do };\n const r = resultsByIndex.get(i);\n if (r) {\n result.success = r.success;\n if (!r.success && r.error) result.error = r.error;\n }\n if (m.action.do === \"navigate\") {\n session.setScreen(m.action.screen!);\n result.screen = m.action.screen;\n }\n if (m.action.do === \"fill\") {\n result.field = m.action.field;\n result.value = m.action.value;\n }\n\n // For ask_confirm, inject the user's yes/no response\n if (isConfirmOnly && m.action.do === \"ask_confirm\") {\n const confirmed = resultMsg.results[0]?.success ?? false;\n result.user_response = confirmed ? \"Yes\" : \"No\";\n }\n\n session.addMessage({\n role: \"tool\",\n content: JSON.stringify(result),\n tool_call_id: m.callId,\n });\n }\n }\n\n // Ran out of rounds — send fallback\n if (lastResponseText) {\n send({ type: \"chat\", from: \"agent\", message: lastResponseText, final: true });\n } else {\n send({ type: \"chat\", from: \"agent\", message: \"Done.\", final: true });\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport type { IncomingMessage } from \"node:http\";\nimport type OpenAI from \"openai\";\nimport type {\n ClientMessage,\n ManifestMessage,\n ResultMessage,\n ConfirmMessage,\n ServerMessage,\n UIAction,\n} from \"./types.js\";\nimport { Session } from \"./session.js\";\nimport { runAgentLoop } from \"./agent.js\";\n\n/** Timeout in milliseconds for waiting on a client result/confirm. */\nconst EXECUTE_TIMEOUT_MS = 30_000;\n\n/**\n * Configuration options for creating an ACP server.\n */\nexport interface ServerOptions {\n /** OpenAI client instance (supports any OpenAI-compatible API via `baseURL`). */\n openai: OpenAI;\n /** Model name for LLM completions (e.g. `\"gpt-4o\"`, `\"claude-sonnet-4-5-20250929\"`). */\n model: string;\n /** WebSocket server port. */\n port: number;\n}\n\n/**\n * An ACP server instance with lifecycle methods.\n */\nexport interface ACPServer {\n /** Starts the WebSocket server and begins accepting connections. */\n start(): Promise<void>;\n /** Stops the server and closes all active connections. */\n stop(): Promise<void>;\n}\n\n/**\n * Creates an ACP reference server.\n *\n * The server listens for WebSocket connections on `/connect` and implements\n * the full ACP v1 text protocol: manifest, text, state, result, confirm,\n * llm_config, and response_lang_config messages.\n *\n * @param options - Server configuration.\n * @returns An {@link ACPServer} with `start()` and `stop()` methods.\n *\n * @example\n * ```ts\n * import { createServer } from \"@acprotocol/server\";\n * import OpenAI from \"openai\";\n *\n * const server = createServer({\n * openai: new OpenAI({ apiKey: \"sk-...\" }),\n * model: \"gpt-4o\",\n * port: 3000,\n * });\n * await server.start();\n * // server is now accepting connections at ws://localhost:3000/connect\n * ```\n */\nexport function createServer(options: ServerOptions): ACPServer {\n const { openai, model, port } = options;\n let wss: WebSocketServer | null = null;\n\n return {\n async start() {\n wss = new WebSocketServer({ port, path: \"/connect\" });\n wss.on(\"connection\", (ws: WebSocket, req: IncomingMessage) => {\n handleConnection(ws, openai, model);\n });\n },\n\n async stop() {\n if (!wss) return;\n for (const ws of wss.clients) {\n ws.close(1001, \"Server shutting down\");\n }\n await new Promise<void>((resolve) => wss!.close(() => resolve()));\n wss = null;\n },\n };\n}\n\n// ── Connection Handler ──────────────────────────────────────────────────────\n\nfunction handleConnection(ws: WebSocket, openai: OpenAI, model: string): void {\n const sessionId = randomUUID();\n const session = new Session(sessionId);\n\n // Pending result/confirm resolvers: seq → resolver\n const pendingResults = new Map<number, (msg: ResultMessage) => void>();\n const pendingConfirms = new Map<number, (confirmed: boolean) => void>();\n\n // Whether the agent is currently processing (prevent concurrent runs)\n let processing = false;\n\n const send = (msg: ServerMessage): void => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n };\n\n // Send config on connect\n send({\n type: \"config\",\n sessionId,\n features: { chat: true },\n providers: [{ id: \"default\", name: \"Default\", model }],\n current_provider: \"default\",\n });\n\n // Keepalive ping every 30s\n const pingInterval = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) ws.ping();\n }, 30_000);\n\n ws.on(\"close\", () => {\n clearInterval(pingInterval);\n // Clean up any pending resolvers\n for (const [, reject] of pendingResults) {\n // Will be caught by the timeout\n }\n pendingResults.clear();\n pendingConfirms.clear();\n });\n\n ws.on(\"message\", (data: Buffer) => {\n let msg: ClientMessage;\n try {\n msg = JSON.parse(data.toString());\n } catch {\n send({ type: \"error\", code: \"parse_error\", message: \"Invalid JSON\" });\n return;\n }\n\n switch (msg.type) {\n case \"manifest\":\n handleManifest(msg, session, send, openai, model);\n break;\n\n case \"text\":\n if (!msg.message) return;\n if (processing) {\n send({ type: \"error\", code: \"busy\", message: \"Already processing a request\" });\n return;\n }\n processing = true;\n handleText(msg.message, session, openai, model, send, makeExecuteFn(send, session, pendingResults, pendingConfirms))\n .finally(() => { processing = false; });\n break;\n\n case \"state\":\n session.setScreen(msg.screen);\n break;\n\n case \"result\":\n deliverResult(msg, pendingResults);\n break;\n\n case \"confirm\":\n deliverConfirm(msg, pendingConfirms, session, openai, model, send, makeExecuteFn(send, session, pendingResults, pendingConfirms));\n break;\n\n case \"llm_config\":\n // Acknowledge (single-provider server — no-op)\n break;\n\n case \"response_lang_config\":\n // Acknowledge\n break;\n }\n });\n}\n\n// ── Message Handlers ────────────────────────────────────────────────────────\n\nfunction handleManifest(\n msg: ManifestMessage,\n session: Session,\n send: (msg: ServerMessage) => void,\n openai: OpenAI,\n model: string,\n): void {\n session.setManifest(msg);\n send({ type: \"status\", status: \"idle\" });\n\n // Send greeting based on manifest context\n send({\n type: \"chat\",\n from: \"agent\",\n message: buildGreeting(msg),\n final: true,\n });\n}\n\nfunction buildGreeting(msg: ManifestMessage): string {\n const name = msg.persona?.name;\n const role = msg.persona?.role;\n const screens = Object.values(msg.screens);\n const screenLabel = screens.length === 1 ? screens[0].label : null;\n\n // Count capabilities\n const fieldCount = screens.reduce((sum, s) => sum + (s.fields?.length ?? 0), 0);\n const actionCount = screens.reduce((sum, s) => sum + (s.actions?.length ?? 0), 0);\n\n const intro = name ? `Hi, I'm ${name}!` : \"Hi!\";\n const roleDesc = role ? ` I'm your ${role}.` : \"\";\n\n let capability = \"\";\n if (screenLabel && fieldCount > 0) {\n capability = ` I can help you with the ${screenLabel} — just tell me what to fill in and I'll handle it.`;\n } else if (fieldCount > 0) {\n capability = ` I can fill forms, navigate screens, and click actions for you — just tell me what you need.`;\n }\n\n return `${intro}${roleDesc}${capability}`;\n}\n\nasync function handleText(\n text: string,\n session: Session,\n openai: OpenAI,\n model: string,\n send: (msg: ServerMessage) => void,\n execute: (seq: number, actions: UIAction[]) => Promise<ResultMessage>,\n): Promise<void> {\n send({ type: \"status\", status: \"thinking\" });\n\n try {\n await runAgentLoop(openai, model, session, text, execute, send);\n } catch (err) {\n console.error(\"[acp-server] Agent error:\", err);\n send({ type: \"error\", code: \"agent_error\", message: String(err) });\n }\n\n send({ type: \"status\", status: \"idle\" });\n}\n\nfunction deliverResult(\n msg: ResultMessage,\n pendingResults: Map<number, (msg: ResultMessage) => void>,\n): void {\n const resolver = pendingResults.get(msg.seq);\n if (resolver) {\n pendingResults.delete(msg.seq);\n resolver(msg);\n }\n}\n\nfunction deliverConfirm(\n msg: ConfirmMessage,\n pendingConfirms: Map<number, (confirmed: boolean) => void>,\n session: Session,\n openai: OpenAI,\n model: string,\n send: (m: ServerMessage) => void,\n execute: (seq: number, actions: UIAction[]) => Promise<ResultMessage>,\n): void {\n const resolver = pendingConfirms.get(msg.seq);\n if (resolver) {\n pendingConfirms.delete(msg.seq);\n resolver(msg.confirmed);\n }\n}\n\n// ── Execute Function ────────────────────────────────────────────────────────\n\nfunction makeExecuteFn(\n send: (msg: ServerMessage) => void,\n session: Session,\n pendingResults: Map<number, (msg: ResultMessage) => void>,\n pendingConfirms: Map<number, (confirmed: boolean) => void>,\n): (seq: number, actions: UIAction[]) => Promise<ResultMessage> {\n return (seq: number, actions: UIAction[]): Promise<ResultMessage> => {\n // Detect if this is a confirm-only command\n const isConfirmOnly =\n actions.length === 1 && actions[0].do === \"ask_confirm\";\n\n // Send command to client\n send({ type: \"command\", seq, actions });\n\n if (isConfirmOnly) {\n // Wait for confirm message instead of result\n return new Promise<ResultMessage>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingConfirms.delete(seq);\n reject(new Error(`Timeout waiting for confirm seq=${seq}`));\n }, EXECUTE_TIMEOUT_MS);\n\n pendingConfirms.set(seq, (confirmed: boolean) => {\n clearTimeout(timer);\n // Wrap confirmation as a ResultMessage\n resolve({\n type: \"result\",\n seq,\n results: [{ index: 0, success: confirmed }],\n });\n });\n });\n }\n\n // Wait for result message\n return new Promise<ResultMessage>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingResults.delete(seq);\n reject(new Error(`Timeout waiting for result seq=${seq}`));\n }, EXECUTE_TIMEOUT_MS);\n\n pendingResults.set(seq, (result: ResultMessage) => {\n clearTimeout(timer);\n resolve(result);\n });\n });\n };\n}\n"],"mappings":"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","names":[]}
|