@acprotocol/server 0.1.3 → 2.0.1

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/README.md CHANGED
@@ -2,10 +2,13 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@acprotocol/server)](https://www.npmjs.com/package/@acprotocol/server)
4
4
  [![license](https://img.shields.io/npm/l/@acprotocol/server)](./LICENSE)
5
- [![tests](https://img.shields.io/badge/tests-211%20passed-brightgreen)]()
5
+ [![tests](https://img.shields.io/badge/tests-195%20passed-brightgreen)]()
6
6
 
7
7
  > ACP Reference Server — a minimal TypeScript server implementing the [Agent Control Protocol](https://acp-protocol.org).
8
8
 
9
+ > [!NOTE]
10
+ > This is a reference implementation for development, testing, and learning. For production workloads, implement the ACP protocol directly in your language/framework of choice, or see [Vocall Engine](https://vocall.emitta.com.br) for a production-grade implementation.
11
+
9
12
  **MCP reads. ACP acts.** While MCP connects models to data, ACP connects AI agents to existing application user interfaces — letting them navigate screens, fill forms, click buttons, open modals, and confirm destructive actions.
10
13
 
11
14
  ## What is ACP?
@@ -30,7 +33,7 @@ The SDK sends a **manifest** describing screens, fields, actions, and modals. Th
30
33
  ├── session.ts Per-connection state: manifest, history, seq counter
31
34
  ├── prompt.ts System prompt builder from manifest
32
35
  ├── tools.ts Manifest ↔ OpenAI tool conversion
33
- ├── types.ts Full ACP v1 type definitions
36
+ ├── types.ts Full ACP v2 type definitions
34
37
  ├── index.ts Public API exports
35
38
  └── cli.ts CLI entry point
36
39
  ```
@@ -47,8 +50,8 @@ SDK Engine LLM
47
50
  │ │ │
48
51
  │───── text ─────────────────►│ │
49
52
  │◄──── status: thinking ──────│───── stream completion ─────►│
50
- │◄──── chat_token ────────────│◄──── delta.content ──────────│
51
- │◄──── chat_token ────────────│◄──── delta.content ──────────│
53
+ │◄──── chat (delta) ──────────│◄──── delta.content ──────────│
54
+ │◄──── chat (delta) ──────────│◄──── delta.content ──────────│
52
55
  │◄──── status: executing ─────│◄──── delta.tool_calls ───────│
53
56
  │◄──── command {seq, actions}─│ │
54
57
  │───── result {seq, results}─►│───── tool results ──────────►│
@@ -137,9 +140,9 @@ Builds a multi-section LLM system prompt from an ACP manifest. Includes identity
137
140
 
138
141
  #### `manifestToTools(manifest): ChatCompletionTool[]`
139
142
 
140
- Converts an ACP manifest into OpenAI-compatible tool definitions. Generates 8 base tools plus 2 modal tools when modals are present.
143
+ Converts an ACP manifest into OpenAI-compatible tool definitions. Generates 6 base tools plus 2 modal tools when modals are present.
141
144
 
142
- **Tools:** `navigate`, `fill_field`, `clear_field`, `click_action`, `highlight`, `focus`, `ask_confirm`, `show_toast`, `open_modal`, `close_modal`
145
+ **Tools:** `navigate`, `set_field`, `clear_field`, `click_action`, `ask_confirm`, `show_toast`, `open_modal`, `close_modal`
143
146
 
144
147
  #### `toolCallToUIAction(name, argsJSON): UIAction`
145
148
 
@@ -147,7 +150,7 @@ Converts an OpenAI tool call into an ACP UIAction for inclusion in a `command` m
147
150
 
148
151
  #### `runAgentLoop(openai, model, session, text, execute, send): Promise<void>`
149
152
 
150
- The core agent loop. Streams LLM completions, accumulates tool calls, executes UI actions on the client, and maps results back to the LLM. Runs up to 5 rounds before sending a fallback response.
153
+ The core agent loop. Streams LLM completions, accumulates tool calls, executes UI actions on the client, and maps results back to the LLM. Runs up to 15 rounds before sending a fallback response.
151
154
 
152
155
  ## Testing
153
156
 
@@ -161,7 +164,7 @@ The test suite includes:
161
164
 
162
165
  - **Unit tests** — prompt builder, tool conversion, session state management
163
166
  - **Integration tests** — agent loop with mock OpenAI, WebSocket server lifecycle
164
- - **Conformance tests** — AJV validation of all messages against the ACP v1 JSON Schema
167
+ - **Conformance tests** — AJV validation of all messages against the ACP v2 JSON Schema
165
168
 
166
169
  ## Development
167
170
 
@@ -174,7 +177,9 @@ npm start # Run built version
174
177
 
175
178
  ## Related
176
179
 
177
- - [ACP Specification](https://acp-protocol.org) — the protocol spec and JSON Schema
180
+ - [ACP Specification](https://github.com/agent-control-protocol/acp) — the protocol spec and JSON Schema
181
+ - [ACP Demo](https://github.com/agent-control-protocol/acp-demo) — interactive demo (Pet Registration form)
182
+ - [Live Sandbox](https://primoia.ai/sandbox) — try ACP without installing anything
178
183
  - [ACP Conformance Suite](https://github.com/agent-control-protocol/acp/tree/main/conformance) — test fixtures and validators
179
184
 
180
185
  > **MCP reads. ACP acts.** MCP connects models to data. ACP connects agents to interfaces.
@@ -9,9 +9,7 @@ function buildSystemPrompt(manifest) {
9
9
  identity += ".";
10
10
  parts.push(identity);
11
11
  } else {
12
- parts.push(
13
- "You are an AI assistant embedded in a software application."
14
- );
12
+ parts.push("You are an AI assistant embedded in a software application.");
15
13
  }
16
14
  if (manifest.persona?.instructions) {
17
15
  parts.push(manifest.persona.instructions);
@@ -24,9 +22,7 @@ function buildSystemPrompt(manifest) {
24
22
  if (lines.length > 1) parts.push(lines.join("\n"));
25
23
  }
26
24
  if (manifest.context && Object.keys(manifest.context).length > 0) {
27
- parts.push(
28
- "## Application Context\n" + JSON.stringify(manifest.context, null, 2)
29
- );
25
+ parts.push("## Application Context\n" + JSON.stringify(manifest.context, null, 2));
30
26
  }
31
27
  const screenLines = [
32
28
  "## Application Screens",
@@ -68,18 +64,17 @@ function buildSystemPrompt(manifest) {
68
64
  parts.push(
69
65
  [
70
66
  "## 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.",
67
+ "- When the user provides information that matches available fields, IMMEDIATELY set those fields using tools \u2014 do not wait for an explicit request to fill the form.",
72
68
  "- Your primary job is to operate the UI. Whenever you can act, act \u2014 don't just acknowledge.",
73
- '- Use `fill_field` with animate="typewriter" so the user can see values being entered.',
74
69
  "- ALWAYS call `ask_confirm` before clicking any action marked [REQUIRES_CONFIRMATION].",
75
70
  "- If the user's request is missing essential information, ask briefly and generically \u2014 do NOT list specific field names.",
76
- "- Do NOT narrate individual fields being filled. Just confirm the action briefly when done.",
71
+ "- Do NOT narrate individual fields being set. Just confirm the action briefly when done.",
77
72
  "- If a command fails (you'll see the error in the next message), explain and try to fix it.",
78
73
  "- Respond in the same language the user speaks.",
79
74
  "- Be concise. Keep responses short \u2014 prefer brief confirmations.",
80
- "- Navigate to the correct screen before filling fields.",
81
- "- When filling multiple fields on the same screen, combine ALL fill_field calls in a single response.",
82
- "- Do NOT fill one field at a time \u2014 batch them together."
75
+ "- Navigate to the correct screen before setting fields.",
76
+ "- When setting multiple fields on the same screen, combine ALL set_field calls in a single response.",
77
+ "- Do NOT set one field at a time \u2014 batch them together."
83
78
  ].join("\n")
84
79
  );
85
80
  return parts.join("\n\n");
@@ -134,7 +129,10 @@ var Session = class {
134
129
  this.history.push(msg);
135
130
  if (this.history.length > MAX_HISTORY) {
136
131
  const system = this.history[0];
137
- const start = this.history.length - MAX_HISTORY + 1;
132
+ let start = this.history.length - MAX_HISTORY + 1;
133
+ while (start < this.history.length && this.history[start].role === "tool") {
134
+ start++;
135
+ }
138
136
  this.history = [system, ...this.history.slice(start)];
139
137
  }
140
138
  }
@@ -183,11 +181,9 @@ function manifestToTools(manifest) {
183
181
  const allModalIDs = collectModalIDs(manifest);
184
182
  const tools = [
185
183
  navigateTool(screenIDs, screenLabels),
186
- fillFieldTool(allFields),
184
+ setFieldTool(allFields),
187
185
  clearFieldTool(allFieldIDs),
188
186
  clickActionTool(allActionIDs),
189
- highlightTool(allFieldIDs),
190
- focusTool(allFieldIDs),
191
187
  askConfirmTool(),
192
188
  showToastTool()
193
189
  ];
@@ -206,26 +202,16 @@ function toolCallToUIAction(name, argsJSON) {
206
202
  switch (name) {
207
203
  case "navigate":
208
204
  return { do: "navigate", screen: str(args.screen) };
209
- case "fill_field":
205
+ case "set_field":
210
206
  return {
211
- do: "fill",
207
+ do: "set_field",
212
208
  field: str(args.field),
213
- value: args.value,
214
- animate: str(args.animate) || "typewriter",
215
- speed: num(args.speed) || void 0
209
+ value: args.value
216
210
  };
217
211
  case "clear_field":
218
212
  return { do: "clear", field: str(args.field) };
219
213
  case "click_action":
220
214
  return { do: "click", action: str(args.action) };
221
- case "highlight":
222
- return {
223
- do: "highlight",
224
- field: str(args.field),
225
- duration: num(args.duration) || void 0
226
- };
227
- case "focus":
228
- return { do: "focus", field: str(args.field) };
229
215
  case "open_modal":
230
216
  return {
231
217
  do: "open_modal",
@@ -254,19 +240,15 @@ function makeTool(name, description, parameters) {
254
240
  };
255
241
  }
256
242
  function navigateTool(screenIDs, screenLabels) {
257
- return makeTool(
258
- "navigate",
259
- `Navigate to a screen. Available: ${screenLabels.join(", ")}`,
260
- {
261
- type: "object",
262
- properties: {
263
- screen: { type: "string", enum: screenIDs, description: "Screen ID to navigate to" }
264
- },
265
- required: ["screen"]
266
- }
267
- );
243
+ return makeTool("navigate", `Navigate to a screen. Available: ${screenLabels.join(", ")}`, {
244
+ type: "object",
245
+ properties: {
246
+ screen: { type: "string", enum: screenIDs, description: "Screen ID to navigate to" }
247
+ },
248
+ required: ["screen"]
249
+ });
268
250
  }
269
- function fillFieldTool(fields) {
251
+ function setFieldTool(fields) {
270
252
  const fieldDescriptions = fields.map((f) => {
271
253
  let desc = `${f.id} (${f.type})`;
272
254
  if (f.options?.length) {
@@ -277,19 +259,15 @@ function fillFieldTool(fields) {
277
259
  return desc;
278
260
  });
279
261
  return makeTool(
280
- "fill_field",
281
- `Fill a form field with a value. Available fields:
262
+ "set_field",
263
+ `Set a form field value. Available fields:
282
264
  ${fieldDescriptions.join("\n")}`,
283
265
  {
284
266
  type: "object",
285
267
  properties: {
286
- field: { type: "string", description: "Field ID to fill" },
287
- value: { description: "Value to set. For select fields, use one of the valid option values listed above." },
288
- animate: {
289
- type: "string",
290
- enum: ["typewriter", "count_up", "fade_in", "none"],
291
- default: "typewriter",
292
- description: "Animation style for filling"
268
+ field: { type: "string", description: "Field ID to set" },
269
+ value: {
270
+ description: "Value to set. For select fields, use one of the valid option values listed above."
293
271
  }
294
272
  },
295
273
  required: ["field", "value"]
@@ -318,39 +296,16 @@ function clickActionTool(actionIDs) {
318
296
  }
319
297
  );
320
298
  }
321
- function highlightTool(fieldIDs) {
322
- return makeTool("highlight", "Temporarily highlight a field to draw the user's attention", {
323
- type: "object",
324
- properties: {
325
- field: { type: "string", description: "Field ID to highlight" },
326
- duration: { type: "integer", default: 2e3, description: "Highlight duration in milliseconds" }
327
- },
328
- required: ["field"]
329
- });
330
- }
331
- function focusTool(fieldIDs) {
332
- return makeTool("focus", "Set keyboard focus on a field", {
299
+ function openModalTool(modalIDs) {
300
+ return makeTool("open_modal", `Open a modal/dialog. Available: ${modalIDs.join(", ")}`, {
333
301
  type: "object",
334
302
  properties: {
335
- field: { type: "string", description: "Field ID to focus" }
303
+ modal: { type: "string", description: "Modal ID to open" },
304
+ query: { type: "string", description: "Optional search query to pre-fill in the modal" }
336
305
  },
337
- required: ["field"]
306
+ required: ["modal"]
338
307
  });
339
308
  }
340
- function openModalTool(modalIDs) {
341
- return makeTool(
342
- "open_modal",
343
- `Open a modal/dialog. Available: ${modalIDs.join(", ")}`,
344
- {
345
- type: "object",
346
- properties: {
347
- modal: { type: "string", description: "Modal ID to open" },
348
- query: { type: "string", description: "Optional search query to pre-fill in the modal" }
349
- },
350
- required: ["modal"]
351
- }
352
- );
353
- }
354
309
  function closeModalTool() {
355
310
  return makeTool("close_modal", "Close the currently open modal", {
356
311
  type: "object",
@@ -428,7 +383,7 @@ function num(v) {
428
383
  }
429
384
 
430
385
  // src/agent.ts
431
- var MAX_ROUNDS = 5;
386
+ var MAX_ROUNDS = 15;
432
387
  async function runAgentLoop(openai, model, session, text, execute, send) {
433
388
  session.addMessage({ role: "user", content: text });
434
389
  const tools = session.manifest ? manifestToTools(session.manifest) : void 0;
@@ -448,7 +403,7 @@ async function runAgentLoop(openai, model, session, text, execute, send) {
448
403
  const delta = chunk.choices[0].delta;
449
404
  if (delta.content) {
450
405
  contentBuf += delta.content;
451
- send({ type: "chat_token", token: delta.content });
406
+ send({ type: "chat", from: "agent", message: delta.content, delta: true });
452
407
  }
453
408
  if (delta.tool_calls) {
454
409
  for (const tc of delta.tool_calls) {
@@ -534,7 +489,7 @@ async function runAgentLoop(openai, model, session, text, execute, send) {
534
489
  session.setScreen(m.action.screen);
535
490
  result.screen = m.action.screen;
536
491
  }
537
- if (m.action.do === "fill") {
492
+ if (m.action.do === "set_field") {
538
493
  result.field = m.action.field;
539
494
  result.value = m.action.value;
540
495
  }
@@ -627,7 +582,14 @@ function handleConnection(ws, openai, model) {
627
582
  return;
628
583
  }
629
584
  processing = true;
630
- handleText(msg.message, session, openai, model, send, makeExecuteFn(send, session, pendingResults, pendingConfirms)).finally(() => {
585
+ handleText(
586
+ msg.message,
587
+ session,
588
+ openai,
589
+ model,
590
+ send,
591
+ makeExecuteFn(send, session, pendingResults, pendingConfirms)
592
+ ).finally(() => {
631
593
  processing = false;
632
594
  });
633
595
  break;
@@ -638,7 +600,15 @@ function handleConnection(ws, openai, model) {
638
600
  deliverResult(msg, pendingResults);
639
601
  break;
640
602
  case "confirm":
641
- deliverConfirm(msg, pendingConfirms, session, openai, model, send, makeExecuteFn(send, session, pendingResults, pendingConfirms));
603
+ deliverConfirm(
604
+ msg,
605
+ pendingConfirms,
606
+ session,
607
+ openai,
608
+ model,
609
+ send,
610
+ makeExecuteFn(send, session, pendingResults, pendingConfirms)
611
+ );
642
612
  break;
643
613
  case "llm_config":
644
614
  break;
@@ -739,4 +709,4 @@ export {
739
709
  runAgentLoop,
740
710
  createServer
741
711
  };
742
- //# sourceMappingURL=chunk-4TQWR52X.js.map
712
+ //# sourceMappingURL=chunk-IS4WO3WH.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('You are an AI assistant embedded in a software application.');\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('## Application Context\\n' + JSON.stringify(manifest.context, null, 2));\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 set 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 '- 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 set. 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 setting fields.',\n '- When setting multiple fields on the same screen, combine ALL set_field calls in a single response.',\n '- Do NOT set 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 let start = this.history.length - MAX_HISTORY + 1;\n // Advance past orphaned tool messages so we never break a\n // tool_calls / tool pair (OpenAI rejects orphaned tool messages)\n while (start < this.history.length && this.history[start].role === 'tool') {\n start++;\n }\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 6 base tools (navigate, set_field, clear_field, click_action,\n * 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 setFieldTool(allFields),\n clearFieldTool(allFieldIDs),\n clickActionTool(allActionIDs),\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 8 tool names: navigate, set_field, clear_field, click_action,\n * 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(\"set_field\", '{\"field\":\"name\",\"value\":\"Alice\"}');\n * // => { do: \"set_field\", field: \"name\", value: \"Alice\" }\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 'set_field':\n return {\n do: 'set_field',\n field: str(args.field),\n value: args.value,\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 '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('navigate', `Navigate to a screen. Available: ${screenLabels.join(', ')}`, {\n type: 'object',\n properties: {\n screen: { type: 'string', enum: screenIDs, description: 'Screen ID to navigate to' },\n },\n required: ['screen'],\n });\n}\n\nfunction setFieldTool(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 'set_field',\n `Set a form field value. Available fields:\\n${fieldDescriptions.join('\\n')}`,\n {\n type: 'object',\n properties: {\n field: { type: 'string', description: 'Field ID to set' },\n value: {\n description:\n 'Value to set. For select fields, use one of the valid option values listed above.',\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 openModalTool(modalIDs: string[]): ChatCompletionTool {\n return makeTool('open_modal', `Open a modal/dialog. Available: ${modalIDs.join(', ')}`, {\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\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 = 15;\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} (15) rounds of tool calls.\n * In each round:\n * 1. Streams the LLM response, forwarding `chat` delta 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', from: 'agent', message: delta.content, delta: true });\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 = 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 === 'set_field') {\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(\n msg.message,\n session,\n openai,\n model,\n send,\n makeExecuteFn(send, session, pendingResults, pendingConfirms),\n ).finally(() => {\n processing = false;\n });\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(\n msg,\n pendingConfirms,\n session,\n openai,\n model,\n send,\n makeExecuteFn(send, session, pendingResults, pendingConfirms),\n );\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 = 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
@@ -2,7 +2,7 @@
2
2
  // @acprotocol/server — Apache 2.0
3
3
  import {
4
4
  createServer
5
- } from "./chunk-4TQWR52X.js";
5
+ } from "./chunk-IS4WO3WH.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import OpenAI from "openai";
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport OpenAI from \"openai\";\nimport { createServer } from \"./server.js\";\n\nconst apiKey = process.env.OPENAI_API_KEY;\nconst baseURL = process.env.OPENAI_BASE_URL;\nconst model = process.env.ACP_MODEL ?? \"gpt-4o\";\nconst port = parseInt(process.env.ACP_PORT ?? \"3000\", 10);\n\nif (!apiKey) {\n console.error(\"Error: OPENAI_API_KEY environment variable is required.\");\n console.error(\"\");\n console.error(\"Usage:\");\n console.error(\" OPENAI_API_KEY=sk-... npx @acprotocol/server\");\n console.error(\"\");\n console.error(\"Options:\");\n console.error(\" OPENAI_BASE_URL LLM base URL (default: OpenAI)\");\n console.error(\" ACP_MODEL Model name (default: gpt-4o)\");\n console.error(\" ACP_PORT WebSocket port (default: 3000)\");\n process.exit(1);\n}\n\nconst openai = new OpenAI({\n apiKey,\n ...(baseURL ? { baseURL } : {}),\n});\n\nconst server = createServer({ openai, model, port });\n\nserver.start().then(() => {\n console.log(\"\");\n console.log(\" ACP Reference Server\");\n console.log(\" --------------------\");\n console.log(` WebSocket: ws://localhost:${port}/connect`);\n console.log(` Model: ${model}`);\n if (baseURL) console.log(` Base URL: ${baseURL}`);\n console.log(\"\");\n});\n\nconst shutdown = () => {\n console.log(\"\\nShutting down...\");\n server.stop().then(() => process.exit(0));\n};\n\nprocess.on(\"SIGINT\", shutdown);\nprocess.on(\"SIGTERM\", shutdown);\n"],"mappings":";;;;;;;AACA,OAAO,YAAY;AAGnB,IAAM,SAAS,QAAQ,IAAI;AAC3B,IAAM,UAAU,QAAQ,IAAI;AAC5B,IAAM,QAAQ,QAAQ,IAAI,aAAa;AACvC,IAAM,OAAO,SAAS,QAAQ,IAAI,YAAY,QAAQ,EAAE;AAExD,IAAI,CAAC,QAAQ;AACX,UAAQ,MAAM,yDAAyD;AACvE,UAAQ,MAAM,EAAE;AAChB,UAAQ,MAAM,QAAQ;AACtB,UAAQ,MAAM,gDAAgD;AAC9D,UAAQ,MAAM,EAAE;AAChB,UAAQ,MAAM,UAAU;AACxB,UAAQ,MAAM,oDAAoD;AAClE,UAAQ,MAAM,kDAAkD;AAChE,UAAQ,MAAM,oDAAoD;AAClE,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,IAAI,OAAO;AAAA,EACxB;AAAA,EACA,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAC/B,CAAC;AAED,IAAM,SAAS,aAAa,EAAE,QAAQ,OAAO,KAAK,CAAC;AAEnD,OAAO,MAAM,EAAE,KAAK,MAAM;AACxB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,wBAAwB;AACpC,UAAQ,IAAI,wBAAwB;AACpC,UAAQ,IAAI,gCAAgC,IAAI,UAAU;AAC1D,UAAQ,IAAI,iBAAiB,KAAK,EAAE;AACpC,MAAI,QAAS,SAAQ,IAAI,iBAAiB,OAAO,EAAE;AACnD,UAAQ,IAAI,EAAE;AAChB,CAAC;AAED,IAAM,WAAW,MAAM;AACrB,UAAQ,IAAI,oBAAoB;AAChC,SAAO,KAAK,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAC1C;AAEA,QAAQ,GAAG,UAAU,QAAQ;AAC7B,QAAQ,GAAG,WAAW,QAAQ;","names":[]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport OpenAI from 'openai';\nimport { createServer } from './server.js';\n\nconst apiKey = process.env.OPENAI_API_KEY;\nconst baseURL = process.env.OPENAI_BASE_URL;\nconst model = process.env.ACP_MODEL ?? 'gpt-4o';\nconst port = parseInt(process.env.ACP_PORT ?? '3000', 10);\n\nif (!apiKey) {\n console.error('Error: OPENAI_API_KEY environment variable is required.');\n console.error('');\n console.error('Usage:');\n console.error(' OPENAI_API_KEY=sk-... npx @acprotocol/server');\n console.error('');\n console.error('Options:');\n console.error(' OPENAI_BASE_URL LLM base URL (default: OpenAI)');\n console.error(' ACP_MODEL Model name (default: gpt-4o)');\n console.error(' ACP_PORT WebSocket port (default: 3000)');\n process.exit(1);\n}\n\nconst openai = new OpenAI({\n apiKey,\n ...(baseURL ? { baseURL } : {}),\n});\n\nconst server = createServer({ openai, model, port });\n\nserver.start().then(() => {\n console.log('');\n console.log(' ACP Reference Server');\n console.log(' --------------------');\n console.log(` WebSocket: ws://localhost:${port}/connect`);\n console.log(` Model: ${model}`);\n if (baseURL) console.log(` Base URL: ${baseURL}`);\n console.log('');\n});\n\nconst shutdown = () => {\n console.log('\\nShutting down...');\n server.stop().then(() => process.exit(0));\n};\n\nprocess.on('SIGINT', shutdown);\nprocess.on('SIGTERM', shutdown);\n"],"mappings":";;;;;;;AACA,OAAO,YAAY;AAGnB,IAAM,SAAS,QAAQ,IAAI;AAC3B,IAAM,UAAU,QAAQ,IAAI;AAC5B,IAAM,QAAQ,QAAQ,IAAI,aAAa;AACvC,IAAM,OAAO,SAAS,QAAQ,IAAI,YAAY,QAAQ,EAAE;AAExD,IAAI,CAAC,QAAQ;AACX,UAAQ,MAAM,yDAAyD;AACvE,UAAQ,MAAM,EAAE;AAChB,UAAQ,MAAM,QAAQ;AACtB,UAAQ,MAAM,gDAAgD;AAC9D,UAAQ,MAAM,EAAE;AAChB,UAAQ,MAAM,UAAU;AACxB,UAAQ,MAAM,oDAAoD;AAClE,UAAQ,MAAM,kDAAkD;AAChE,UAAQ,MAAM,oDAAoD;AAClE,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,IAAI,OAAO;AAAA,EACxB;AAAA,EACA,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAC/B,CAAC;AAED,IAAM,SAAS,aAAa,EAAE,QAAQ,OAAO,KAAK,CAAC;AAEnD,OAAO,MAAM,EAAE,KAAK,MAAM;AACxB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,wBAAwB;AACpC,UAAQ,IAAI,wBAAwB;AACpC,UAAQ,IAAI,gCAAgC,IAAI,UAAU;AAC1D,UAAQ,IAAI,iBAAiB,KAAK,EAAE;AACpC,MAAI,QAAS,SAAQ,IAAI,iBAAiB,OAAO,EAAE;AACnD,UAAQ,IAAI,EAAE;AAChB,CAAC;AAED,IAAM,WAAW,MAAM;AACrB,UAAQ,IAAI,oBAAoB;AAChC,SAAO,KAAK,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC;AAC1C;AAEA,QAAQ,GAAG,UAAU,QAAQ;AAC7B,QAAQ,GAAG,WAAW,QAAQ;","names":[]}
package/dist/index.d.ts CHANGED
@@ -47,7 +47,7 @@ interface ACPServer {
47
47
  */
48
48
  declare function createServer(options: ServerOptions): ACPServer;
49
49
 
50
- type FieldType = "text" | "number" | "currency" | "date" | "datetime" | "email" | "phone" | "masked" | "select" | "autocomplete" | "checkbox" | "radio" | "textarea" | "file" | "hidden";
50
+ type FieldType = 'text' | 'number' | 'currency' | 'date' | 'datetime' | 'email' | 'phone' | 'masked' | 'select' | 'autocomplete' | 'checkbox' | 'radio' | 'textarea' | 'file' | 'hidden';
51
51
  interface SelectOption {
52
52
  value: string;
53
53
  label: string;
@@ -120,9 +120,8 @@ interface ActionResult {
120
120
  success: boolean;
121
121
  error?: string;
122
122
  }
123
- type UIActionDo = "navigate" | "fill" | "clear" | "select" | "click" | "highlight" | "focus" | "scroll_to" | "show_toast" | "ask_confirm" | "open_modal" | "close_modal" | "enable" | "disable";
124
- type AnimationType = "typewriter" | "count_up" | "fade_in" | "none";
125
- type ToastLevel = "info" | "success" | "warning" | "error";
123
+ type UIActionDo = 'navigate' | 'set_field' | 'clear' | 'click' | 'show_toast' | 'ask_confirm' | 'open_modal' | 'close_modal';
124
+ type ToastLevel = 'info' | 'success' | 'warning' | 'error';
126
125
  interface UIAction {
127
126
  do: UIActionDo;
128
127
  screen?: string;
@@ -132,13 +131,11 @@ interface UIAction {
132
131
  value?: unknown;
133
132
  query?: string;
134
133
  message?: string;
135
- animate?: AnimationType;
136
- speed?: number;
137
134
  duration?: number;
138
135
  level?: ToastLevel;
139
136
  }
140
137
  interface ManifestMessage {
141
- type: "manifest";
138
+ type: 'manifest';
142
139
  app: string;
143
140
  version?: string;
144
141
  currentScreen?: string;
@@ -148,37 +145,37 @@ interface ManifestMessage {
148
145
  persona?: Persona;
149
146
  }
150
147
  interface TextMessage {
151
- type: "text";
148
+ type: 'text';
152
149
  message: string;
153
150
  }
154
151
  interface StateMessage {
155
- type: "state";
152
+ type: 'state';
156
153
  screen: string;
157
154
  fields?: Record<string, FieldState>;
158
155
  canSubmit?: boolean;
159
156
  }
160
157
  interface ResultMessage {
161
- type: "result";
158
+ type: 'result';
162
159
  seq: number;
163
160
  results: ActionResult[];
164
161
  state?: InlineState;
165
162
  }
166
163
  interface ConfirmMessage {
167
- type: "confirm";
164
+ type: 'confirm';
168
165
  seq: number;
169
166
  confirmed: boolean;
170
167
  }
171
168
  interface LlmConfigMessage {
172
- type: "llm_config";
169
+ type: 'llm_config';
173
170
  provider: string;
174
171
  }
175
172
  interface ResponseLangConfigMessage {
176
- type: "response_lang_config";
173
+ type: 'response_lang_config';
177
174
  language: string;
178
175
  }
179
176
  type ClientMessage = ManifestMessage | TextMessage | StateMessage | ResultMessage | ConfirmMessage | LlmConfigMessage | ResponseLangConfigMessage;
180
177
  interface ConfigResponse {
181
- type: "config";
178
+ type: 'config';
182
179
  sessionId: string;
183
180
  features?: {
184
181
  chat?: boolean;
@@ -188,31 +185,28 @@ interface ConfigResponse {
188
185
  current_provider?: string;
189
186
  }
190
187
  interface CommandMessage {
191
- type: "command";
188
+ type: 'command';
192
189
  seq: number;
193
190
  actions: UIAction[];
194
191
  }
195
192
  interface ChatMessage {
196
- type: "chat";
197
- from: "agent" | "user" | "system";
193
+ type: 'chat';
194
+ from: 'agent' | 'user' | 'system';
198
195
  message: string;
196
+ delta?: boolean;
199
197
  final?: boolean;
200
198
  }
201
- interface ChatTokenMessage {
202
- type: "chat_token";
203
- token: string;
204
- }
205
- type AgentStatus = "idle" | "thinking" | "executing";
199
+ type AgentStatus = 'idle' | 'thinking' | 'executing';
206
200
  interface StatusMessage {
207
- type: "status";
201
+ type: 'status';
208
202
  status: AgentStatus;
209
203
  }
210
204
  interface ErrorMessage {
211
- type: "error";
205
+ type: 'error';
212
206
  code?: string;
213
207
  message: string;
214
208
  }
215
- type ServerMessage = ConfigResponse | CommandMessage | ChatMessage | ChatTokenMessage | StatusMessage | ErrorMessage;
209
+ type ServerMessage = ConfigResponse | CommandMessage | ChatMessage | StatusMessage | ErrorMessage;
216
210
 
217
211
  /**
218
212
  * Per-connection session state.
@@ -312,8 +306,8 @@ declare function buildSystemPrompt(manifest: ManifestMessage): string;
312
306
  /**
313
307
  * Converts an ACP manifest into OpenAI-compatible tool definitions.
314
308
  *
315
- * Generates 8 base tools (navigate, fill_field, clear_field, click_action,
316
- * highlight, focus, ask_confirm, show_toast) plus 2 modal tools
309
+ * Generates 6 base tools (navigate, set_field, clear_field, click_action,
310
+ * ask_confirm, show_toast) plus 2 modal tools
317
311
  * (open_modal, close_modal) when the manifest contains modal descriptors.
318
312
  *
319
313
  * Field, action, and modal IDs are deduplicated across all screens.
@@ -332,8 +326,8 @@ declare function manifestToTools(manifest: ManifestMessage): ChatCompletionTool[
332
326
  /**
333
327
  * Converts an OpenAI tool call into an ACP UIAction.
334
328
  *
335
- * Supports all 10 tool names: navigate, fill_field, clear_field, click_action,
336
- * highlight, focus, open_modal, close_modal, ask_confirm, show_toast.
329
+ * Supports all 8 tool names: navigate, set_field, clear_field, click_action,
330
+ * open_modal, close_modal, ask_confirm, show_toast.
337
331
  *
338
332
  * @param name - The tool function name from the LLM response.
339
333
  * @param argsJSON - The JSON-encoded arguments string from the LLM response.
@@ -342,8 +336,8 @@ declare function manifestToTools(manifest: ManifestMessage): ChatCompletionTool[
342
336
  *
343
337
  * @example
344
338
  * ```ts
345
- * const action = toolCallToUIAction("fill_field", '{"field":"name","value":"Alice"}');
346
- * // => { do: "fill", field: "name", value: "Alice", animate: "typewriter" }
339
+ * const action = toolCallToUIAction("set_field", '{"field":"name","value":"Alice"}');
340
+ * // => { do: "set_field", field: "name", value: "Alice" }
347
341
  * ```
348
342
  */
349
343
  declare function toolCallToUIAction(name: string, argsJSON: string): UIAction;
@@ -364,9 +358,9 @@ type ExecuteFn = (seq: number, actions: UIAction[]) => Promise<ResultMessage>;
364
358
  /**
365
359
  * Runs the streaming agent loop: LLM call → stream tokens → execute tool calls → repeat.
366
360
  *
367
- * The loop processes up to {@link MAX_ROUNDS} (5) rounds of tool calls.
361
+ * The loop processes up to {@link MAX_ROUNDS} (15) rounds of tool calls.
368
362
  * In each round:
369
- * 1. Streams the LLM response, forwarding `chat_token` messages in real-time
363
+ * 1. Streams the LLM response, forwarding `chat` delta messages in real-time
370
364
  * 2. Accumulates tool call deltas from the stream
371
365
  * 3. If no tool calls → sends a final `chat` message and returns
372
366
  * 4. Converts tool calls to UIActions via {@link toolCallToUIAction}
@@ -384,4 +378,4 @@ type ExecuteFn = (seq: number, actions: UIAction[]) => Promise<ResultMessage>;
384
378
  */
385
379
  declare function runAgentLoop(openai: OpenAI, model: string, session: Session, text: string, execute: ExecuteFn, send: SendFn): Promise<void>;
386
380
 
387
- export { type ACPServer, type ActionDescriptor, type ActionResult, type AgentStatus, type AnimationType, type ChatMessage, type ChatTokenMessage, type ClientMessage, type CommandMessage, type ConfigResponse, type ConfirmMessage, type ErrorMessage, type ExecuteFn, type FieldDescriptor, type FieldState, type FieldType, type InlineState, type LlmConfigMessage, type ManifestMessage, type ModalDescriptor, type Persona, type ProviderInfo, type ResponseLangConfigMessage, type ResultMessage, type ScreenDescriptor, type SelectOption, type SendFn, type ServerMessage, type ServerOptions, Session, type StateMessage, type StatusMessage, type TextMessage, type ToastLevel, type UIAction, type UIActionDo, type UserInfo, buildSystemPrompt, createServer, manifestToTools, runAgentLoop, toolCallToUIAction };
381
+ export { type ACPServer, type ActionDescriptor, type ActionResult, type AgentStatus, type ChatMessage, type ClientMessage, type CommandMessage, type ConfigResponse, type ConfirmMessage, type ErrorMessage, type ExecuteFn, type FieldDescriptor, type FieldState, type FieldType, type InlineState, type LlmConfigMessage, type ManifestMessage, type ModalDescriptor, type Persona, type ProviderInfo, type ResponseLangConfigMessage, type ResultMessage, type ScreenDescriptor, type SelectOption, type SendFn, type ServerMessage, type ServerOptions, Session, type StateMessage, type StatusMessage, type TextMessage, type ToastLevel, type UIAction, type UIActionDo, type UserInfo, buildSystemPrompt, createServer, manifestToTools, runAgentLoop, toolCallToUIAction };
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  manifestToTools,
7
7
  runAgentLoop,
8
8
  toolCallToUIAction
9
- } from "./chunk-4TQWR52X.js";
9
+ } from "./chunk-IS4WO3WH.js";
10
10
  export {
11
11
  Session,
12
12
  buildSystemPrompt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acprotocol/server",
3
- "version": "0.1.3",
3
+ "version": "2.0.1",
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)",
@@ -42,7 +42,7 @@
42
42
  "format:check": "prettier --check ."
43
43
  },
44
44
  "dependencies": {
45
- "openai": "^4.73.0",
45
+ "openai": "^6.33.0",
46
46
  "ws": "^8.18.0"
47
47
  },
48
48
  "devDependencies": {
@@ -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 \"- 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":[]}