@acprotocol/server 0.1.2 → 2.0.0
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 +14 -9
- package/dist/{chunk-NWDR4KUJ.js → chunk-OKTNQRAG.js} +66 -88
- package/dist/chunk-OKTNQRAG.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +28 -34
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-NWDR4KUJ.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@acprotocol/server)
|
|
4
4
|
[](./LICENSE)
|
|
5
|
-
[]()
|
|
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
|
|
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
|
-
│◄────
|
|
51
|
-
│◄────
|
|
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
|
|
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`, `
|
|
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
|
|
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
|
|
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://
|
|
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
|
|
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
|
|
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
|
|
81
|
-
"- When
|
|
82
|
-
"- Do NOT
|
|
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");
|
|
@@ -177,16 +172,15 @@ function manifestToTools(manifest) {
|
|
|
177
172
|
screenIDs.push(id);
|
|
178
173
|
screenLabels.push(`${id} (${s.label})`);
|
|
179
174
|
}
|
|
180
|
-
const
|
|
175
|
+
const allFields = collectFields(manifest);
|
|
176
|
+
const allFieldIDs = allFields.map((f) => f.id);
|
|
181
177
|
const allActionIDs = collectActionIDs(manifest);
|
|
182
178
|
const allModalIDs = collectModalIDs(manifest);
|
|
183
179
|
const tools = [
|
|
184
180
|
navigateTool(screenIDs, screenLabels),
|
|
185
|
-
|
|
181
|
+
setFieldTool(allFields),
|
|
186
182
|
clearFieldTool(allFieldIDs),
|
|
187
183
|
clickActionTool(allActionIDs),
|
|
188
|
-
highlightTool(allFieldIDs),
|
|
189
|
-
focusTool(allFieldIDs),
|
|
190
184
|
askConfirmTool(),
|
|
191
185
|
showToastTool()
|
|
192
186
|
];
|
|
@@ -205,26 +199,16 @@ function toolCallToUIAction(name, argsJSON) {
|
|
|
205
199
|
switch (name) {
|
|
206
200
|
case "navigate":
|
|
207
201
|
return { do: "navigate", screen: str(args.screen) };
|
|
208
|
-
case "
|
|
202
|
+
case "set_field":
|
|
209
203
|
return {
|
|
210
|
-
do: "
|
|
204
|
+
do: "set_field",
|
|
211
205
|
field: str(args.field),
|
|
212
|
-
value: args.value
|
|
213
|
-
animate: str(args.animate) || "typewriter",
|
|
214
|
-
speed: num(args.speed) || void 0
|
|
206
|
+
value: args.value
|
|
215
207
|
};
|
|
216
208
|
case "clear_field":
|
|
217
209
|
return { do: "clear", field: str(args.field) };
|
|
218
210
|
case "click_action":
|
|
219
211
|
return { do: "click", action: str(args.action) };
|
|
220
|
-
case "highlight":
|
|
221
|
-
return {
|
|
222
|
-
do: "highlight",
|
|
223
|
-
field: str(args.field),
|
|
224
|
-
duration: num(args.duration) || void 0
|
|
225
|
-
};
|
|
226
|
-
case "focus":
|
|
227
|
-
return { do: "focus", field: str(args.field) };
|
|
228
212
|
case "open_modal":
|
|
229
213
|
return {
|
|
230
214
|
do: "open_modal",
|
|
@@ -253,32 +237,34 @@ function makeTool(name, description, parameters) {
|
|
|
253
237
|
};
|
|
254
238
|
}
|
|
255
239
|
function navigateTool(screenIDs, screenLabels) {
|
|
256
|
-
return makeTool(
|
|
257
|
-
"
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
},
|
|
264
|
-
required: ["screen"]
|
|
265
|
-
}
|
|
266
|
-
);
|
|
240
|
+
return makeTool("navigate", `Navigate to a screen. Available: ${screenLabels.join(", ")}`, {
|
|
241
|
+
type: "object",
|
|
242
|
+
properties: {
|
|
243
|
+
screen: { type: "string", enum: screenIDs, description: "Screen ID to navigate to" }
|
|
244
|
+
},
|
|
245
|
+
required: ["screen"]
|
|
246
|
+
});
|
|
267
247
|
}
|
|
268
|
-
function
|
|
248
|
+
function setFieldTool(fields) {
|
|
249
|
+
const fieldDescriptions = fields.map((f) => {
|
|
250
|
+
let desc = `${f.id} (${f.type})`;
|
|
251
|
+
if (f.options?.length) {
|
|
252
|
+
const opts = f.options.map((o) => o.value).join(", ");
|
|
253
|
+
desc += ` \u2014 valid values: [${opts}]`;
|
|
254
|
+
}
|
|
255
|
+
if (f.required) desc += " REQUIRED";
|
|
256
|
+
return desc;
|
|
257
|
+
});
|
|
269
258
|
return makeTool(
|
|
270
|
-
"
|
|
271
|
-
`
|
|
259
|
+
"set_field",
|
|
260
|
+
`Set a form field value. Available fields:
|
|
261
|
+
${fieldDescriptions.join("\n")}`,
|
|
272
262
|
{
|
|
273
263
|
type: "object",
|
|
274
264
|
properties: {
|
|
275
|
-
field: { type: "string", description: "Field ID to
|
|
276
|
-
value: {
|
|
277
|
-
|
|
278
|
-
type: "string",
|
|
279
|
-
enum: ["typewriter", "count_up", "fade_in", "none"],
|
|
280
|
-
default: "typewriter",
|
|
281
|
-
description: "Animation style for filling"
|
|
265
|
+
field: { type: "string", description: "Field ID to set" },
|
|
266
|
+
value: {
|
|
267
|
+
description: "Value to set. For select fields, use one of the valid option values listed above."
|
|
282
268
|
}
|
|
283
269
|
},
|
|
284
270
|
required: ["field", "value"]
|
|
@@ -307,39 +293,16 @@ function clickActionTool(actionIDs) {
|
|
|
307
293
|
}
|
|
308
294
|
);
|
|
309
295
|
}
|
|
310
|
-
function
|
|
311
|
-
return makeTool("
|
|
312
|
-
type: "object",
|
|
313
|
-
properties: {
|
|
314
|
-
field: { type: "string", description: "Field ID to highlight" },
|
|
315
|
-
duration: { type: "integer", default: 2e3, description: "Highlight duration in milliseconds" }
|
|
316
|
-
},
|
|
317
|
-
required: ["field"]
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
function focusTool(fieldIDs) {
|
|
321
|
-
return makeTool("focus", "Set keyboard focus on a field", {
|
|
296
|
+
function openModalTool(modalIDs) {
|
|
297
|
+
return makeTool("open_modal", `Open a modal/dialog. Available: ${modalIDs.join(", ")}`, {
|
|
322
298
|
type: "object",
|
|
323
299
|
properties: {
|
|
324
|
-
|
|
300
|
+
modal: { type: "string", description: "Modal ID to open" },
|
|
301
|
+
query: { type: "string", description: "Optional search query to pre-fill in the modal" }
|
|
325
302
|
},
|
|
326
|
-
required: ["
|
|
303
|
+
required: ["modal"]
|
|
327
304
|
});
|
|
328
305
|
}
|
|
329
|
-
function openModalTool(modalIDs) {
|
|
330
|
-
return makeTool(
|
|
331
|
-
"open_modal",
|
|
332
|
-
`Open a modal/dialog. Available: ${modalIDs.join(", ")}`,
|
|
333
|
-
{
|
|
334
|
-
type: "object",
|
|
335
|
-
properties: {
|
|
336
|
-
modal: { type: "string", description: "Modal ID to open" },
|
|
337
|
-
query: { type: "string", description: "Optional search query to pre-fill in the modal" }
|
|
338
|
-
},
|
|
339
|
-
required: ["modal"]
|
|
340
|
-
}
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
306
|
function closeModalTool() {
|
|
344
307
|
return makeTool("close_modal", "Close the currently open modal", {
|
|
345
308
|
type: "object",
|
|
@@ -370,18 +333,18 @@ function showToastTool() {
|
|
|
370
333
|
required: ["message"]
|
|
371
334
|
});
|
|
372
335
|
}
|
|
373
|
-
function
|
|
336
|
+
function collectFields(m) {
|
|
374
337
|
const seen = /* @__PURE__ */ new Set();
|
|
375
|
-
const
|
|
338
|
+
const fields = [];
|
|
376
339
|
for (const s of Object.values(m.screens)) {
|
|
377
340
|
for (const f of s.fields ?? []) {
|
|
378
341
|
if (!seen.has(f.id)) {
|
|
379
|
-
|
|
342
|
+
fields.push(f);
|
|
380
343
|
seen.add(f.id);
|
|
381
344
|
}
|
|
382
345
|
}
|
|
383
346
|
}
|
|
384
|
-
return
|
|
347
|
+
return fields;
|
|
385
348
|
}
|
|
386
349
|
function collectActionIDs(m) {
|
|
387
350
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -417,7 +380,7 @@ function num(v) {
|
|
|
417
380
|
}
|
|
418
381
|
|
|
419
382
|
// src/agent.ts
|
|
420
|
-
var MAX_ROUNDS =
|
|
383
|
+
var MAX_ROUNDS = 15;
|
|
421
384
|
async function runAgentLoop(openai, model, session, text, execute, send) {
|
|
422
385
|
session.addMessage({ role: "user", content: text });
|
|
423
386
|
const tools = session.manifest ? manifestToTools(session.manifest) : void 0;
|
|
@@ -437,7 +400,7 @@ async function runAgentLoop(openai, model, session, text, execute, send) {
|
|
|
437
400
|
const delta = chunk.choices[0].delta;
|
|
438
401
|
if (delta.content) {
|
|
439
402
|
contentBuf += delta.content;
|
|
440
|
-
send({ type: "
|
|
403
|
+
send({ type: "chat", from: "agent", message: delta.content, delta: true });
|
|
441
404
|
}
|
|
442
405
|
if (delta.tool_calls) {
|
|
443
406
|
for (const tc of delta.tool_calls) {
|
|
@@ -523,7 +486,7 @@ async function runAgentLoop(openai, model, session, text, execute, send) {
|
|
|
523
486
|
session.setScreen(m.action.screen);
|
|
524
487
|
result.screen = m.action.screen;
|
|
525
488
|
}
|
|
526
|
-
if (m.action.do === "
|
|
489
|
+
if (m.action.do === "set_field") {
|
|
527
490
|
result.field = m.action.field;
|
|
528
491
|
result.value = m.action.value;
|
|
529
492
|
}
|
|
@@ -616,7 +579,14 @@ function handleConnection(ws, openai, model) {
|
|
|
616
579
|
return;
|
|
617
580
|
}
|
|
618
581
|
processing = true;
|
|
619
|
-
handleText(
|
|
582
|
+
handleText(
|
|
583
|
+
msg.message,
|
|
584
|
+
session,
|
|
585
|
+
openai,
|
|
586
|
+
model,
|
|
587
|
+
send,
|
|
588
|
+
makeExecuteFn(send, session, pendingResults, pendingConfirms)
|
|
589
|
+
).finally(() => {
|
|
620
590
|
processing = false;
|
|
621
591
|
});
|
|
622
592
|
break;
|
|
@@ -627,7 +597,15 @@ function handleConnection(ws, openai, model) {
|
|
|
627
597
|
deliverResult(msg, pendingResults);
|
|
628
598
|
break;
|
|
629
599
|
case "confirm":
|
|
630
|
-
deliverConfirm(
|
|
600
|
+
deliverConfirm(
|
|
601
|
+
msg,
|
|
602
|
+
pendingConfirms,
|
|
603
|
+
session,
|
|
604
|
+
openai,
|
|
605
|
+
model,
|
|
606
|
+
send,
|
|
607
|
+
makeExecuteFn(send, session, pendingResults, pendingConfirms)
|
|
608
|
+
);
|
|
631
609
|
break;
|
|
632
610
|
case "llm_config":
|
|
633
611
|
break;
|
|
@@ -728,4 +706,4 @@ export {
|
|
|
728
706
|
runAgentLoop,
|
|
729
707
|
createServer
|
|
730
708
|
};
|
|
731
|
-
//# sourceMappingURL=chunk-
|
|
709
|
+
//# sourceMappingURL=chunk-OKTNQRAG.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 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 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
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport OpenAI from
|
|
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 =
|
|
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 =
|
|
124
|
-
type
|
|
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:
|
|
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:
|
|
148
|
+
type: 'text';
|
|
152
149
|
message: string;
|
|
153
150
|
}
|
|
154
151
|
interface StateMessage {
|
|
155
|
-
type:
|
|
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:
|
|
158
|
+
type: 'result';
|
|
162
159
|
seq: number;
|
|
163
160
|
results: ActionResult[];
|
|
164
161
|
state?: InlineState;
|
|
165
162
|
}
|
|
166
163
|
interface ConfirmMessage {
|
|
167
|
-
type:
|
|
164
|
+
type: 'confirm';
|
|
168
165
|
seq: number;
|
|
169
166
|
confirmed: boolean;
|
|
170
167
|
}
|
|
171
168
|
interface LlmConfigMessage {
|
|
172
|
-
type:
|
|
169
|
+
type: 'llm_config';
|
|
173
170
|
provider: string;
|
|
174
171
|
}
|
|
175
172
|
interface ResponseLangConfigMessage {
|
|
176
|
-
type:
|
|
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:
|
|
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:
|
|
188
|
+
type: 'command';
|
|
192
189
|
seq: number;
|
|
193
190
|
actions: UIAction[];
|
|
194
191
|
}
|
|
195
192
|
interface ChatMessage {
|
|
196
|
-
type:
|
|
197
|
-
from:
|
|
193
|
+
type: 'chat';
|
|
194
|
+
from: 'agent' | 'user' | 'system';
|
|
198
195
|
message: string;
|
|
196
|
+
delta?: boolean;
|
|
199
197
|
final?: boolean;
|
|
200
198
|
}
|
|
201
|
-
|
|
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:
|
|
201
|
+
type: 'status';
|
|
208
202
|
status: AgentStatus;
|
|
209
203
|
}
|
|
210
204
|
interface ErrorMessage {
|
|
211
|
-
type:
|
|
205
|
+
type: 'error';
|
|
212
206
|
code?: string;
|
|
213
207
|
message: string;
|
|
214
208
|
}
|
|
215
|
-
type ServerMessage = ConfigResponse | CommandMessage | ChatMessage |
|
|
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
|
|
316
|
-
*
|
|
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
|
|
336
|
-
*
|
|
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("
|
|
346
|
-
* // => { do: "
|
|
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} (
|
|
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 `
|
|
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
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acprotocol/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
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": "^
|
|
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 } from \"./types.js\";\n\n/**\n * Converts an ACP manifest into OpenAI-compatible tool definitions.\n *\n * Generates 8 base tools (navigate, fill_field, clear_field, click_action,\n * highlight, focus, ask_confirm, show_toast) plus 2 modal tools\n * (open_modal, close_modal) when the manifest contains modal descriptors.\n *\n * Field, action, and modal IDs are deduplicated across all screens.\n * Screen IDs and labels are included as enums in the navigate tool.\n *\n * @param manifest - The ACP manifest describing available screens and UI elements.\n * @returns An array of OpenAI-compatible tool definitions.\n *\n * @example\n * ```ts\n * const tools = manifestToTools(manifest);\n * // Pass to OpenAI: openai.chat.completions.create({ tools, ... })\n * ```\n */\nexport function manifestToTools(manifest: ManifestMessage): ChatCompletionTool[] {\n const screenIDs: string[] = [];\n const screenLabels: string[] = [];\n for (const [id, s] of Object.entries(manifest.screens)) {\n screenIDs.push(id);\n screenLabels.push(`${id} (${s.label})`);\n }\n\n const allFieldIDs = collectFieldIDs(manifest);\n const allActionIDs = collectActionIDs(manifest);\n const allModalIDs = collectModalIDs(manifest);\n\n const tools: ChatCompletionTool[] = [\n navigateTool(screenIDs, screenLabels),\n fillFieldTool(allFieldIDs),\n clearFieldTool(allFieldIDs),\n clickActionTool(allActionIDs),\n highlightTool(allFieldIDs),\n focusTool(allFieldIDs),\n askConfirmTool(),\n showToastTool(),\n ];\n\n if (allModalIDs.length > 0) {\n tools.push(openModalTool(allModalIDs), closeModalTool());\n }\n\n return tools;\n}\n\n/**\n * Converts an OpenAI tool call into an ACP UIAction.\n *\n * Supports all 10 tool names: navigate, fill_field, clear_field, click_action,\n * highlight, focus, open_modal, close_modal, ask_confirm, show_toast.\n *\n * @param name - The tool function name from the LLM response.\n * @param argsJSON - The JSON-encoded arguments string from the LLM response.\n * @returns A UIAction object ready to be sent in a command message.\n * @throws {Error} If the tool name is not recognized.\n *\n * @example\n * ```ts\n * const action = toolCallToUIAction(\"fill_field\", '{\"field\":\"name\",\"value\":\"Alice\"}');\n * // => { do: \"fill\", field: \"name\", value: \"Alice\", animate: \"typewriter\" }\n * ```\n */\nexport function toolCallToUIAction(name: string, argsJSON: string): UIAction {\n let args: Record<string, unknown>;\n try {\n args = JSON.parse(argsJSON);\n } catch {\n args = {};\n }\n\n switch (name) {\n case \"navigate\":\n return { do: \"navigate\", screen: str(args.screen) };\n\n case \"fill_field\":\n return {\n do: \"fill\",\n field: str(args.field),\n value: args.value,\n animate: (str(args.animate) as UIAction[\"animate\"]) || \"typewriter\",\n speed: num(args.speed) || undefined,\n };\n\n case \"clear_field\":\n return { do: \"clear\", field: str(args.field) };\n\n case \"click_action\":\n return { do: \"click\", action: str(args.action) };\n\n case \"highlight\":\n return {\n do: \"highlight\",\n field: str(args.field),\n duration: num(args.duration) || undefined,\n };\n\n case \"focus\":\n return { do: \"focus\", field: str(args.field) };\n\n case \"open_modal\":\n return {\n do: \"open_modal\",\n modal: str(args.modal),\n query: str(args.query) || undefined,\n };\n\n case \"close_modal\":\n return { do: \"close_modal\" };\n\n case \"ask_confirm\":\n return { do: \"ask_confirm\", message: str(args.message) };\n\n case \"show_toast\":\n return {\n do: \"show_toast\",\n message: str(args.message),\n level: (str(args.level) as UIAction[\"level\"]) || undefined,\n duration: num(args.duration) || undefined,\n };\n\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n}\n\n// ── Tool Builders ───────────────────────────────────────────────────────────\n\nfunction makeTool(name: string, description: string, parameters: object): ChatCompletionTool {\n return {\n type: \"function\",\n function: { name, description, parameters },\n } as ChatCompletionTool;\n}\n\nfunction navigateTool(screenIDs: string[], screenLabels: string[]): ChatCompletionTool {\n return makeTool(\n \"navigate\",\n `Navigate to a screen. Available: ${screenLabels.join(\", \")}`,\n {\n type: \"object\",\n properties: {\n screen: { type: \"string\", enum: screenIDs, description: \"Screen ID to navigate to\" },\n },\n required: [\"screen\"],\n },\n );\n}\n\nfunction fillFieldTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\n \"fill_field\",\n `Fill a form field with a value. The field animates as the value is typed. Available fields: ${fieldIDs.join(\", \")}`,\n {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to fill\" },\n value: { description: \"Value to set (string, number, or boolean depending on field type)\" },\n animate: {\n type: \"string\",\n enum: [\"typewriter\", \"count_up\", \"fade_in\", \"none\"],\n default: \"typewriter\",\n description: \"Animation style for filling\",\n },\n },\n required: [\"field\", \"value\"],\n },\n );\n}\n\nfunction clearFieldTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\"clear_field\", \"Clear a form field value\", {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to clear\" },\n },\n required: [\"field\"],\n });\n}\n\nfunction clickActionTool(actionIDs: string[]): ChatCompletionTool {\n return makeTool(\n \"click_action\",\n `Click a button or trigger an action. Available: ${actionIDs.join(\", \")}. IMPORTANT: if the action has requiresConfirmation=true, you MUST call ask_confirm first and wait for the user's response before clicking.`,\n {\n type: \"object\",\n properties: {\n action: { type: \"string\", description: \"Action ID to click\" },\n },\n required: [\"action\"],\n },\n );\n}\n\nfunction highlightTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\"highlight\", \"Temporarily highlight a field to draw the user's attention\", {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to highlight\" },\n duration: { type: \"integer\", default: 2000, description: \"Highlight duration in milliseconds\" },\n },\n required: [\"field\"],\n });\n}\n\nfunction focusTool(fieldIDs: string[]): ChatCompletionTool {\n return makeTool(\"focus\", \"Set keyboard focus on a field\", {\n type: \"object\",\n properties: {\n field: { type: \"string\", description: \"Field ID to focus\" },\n },\n required: [\"field\"],\n });\n}\n\nfunction openModalTool(modalIDs: string[]): ChatCompletionTool {\n return makeTool(\n \"open_modal\",\n `Open a modal/dialog. Available: ${modalIDs.join(\", \")}`,\n {\n type: \"object\",\n properties: {\n modal: { type: \"string\", description: \"Modal ID to open\" },\n query: { type: \"string\", description: \"Optional search query to pre-fill in the modal\" },\n },\n required: [\"modal\"],\n },\n );\n}\n\nfunction closeModalTool(): ChatCompletionTool {\n return makeTool(\"close_modal\", \"Close the currently open modal\", {\n type: \"object\",\n properties: {},\n });\n}\n\nfunction askConfirmTool(): ChatCompletionTool {\n return makeTool(\n \"ask_confirm\",\n \"Ask the user for confirmation before proceeding with a destructive or important action.\",\n {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Confirmation question to ask the user\" },\n },\n required: [\"message\"],\n },\n );\n}\n\nfunction showToastTool(): ChatCompletionTool {\n return makeTool(\"show_toast\", \"Show a temporary notification/toast message in the app\", {\n type: \"object\",\n properties: {\n message: { type: \"string\", description: \"Toast message text\" },\n level: { type: \"string\", enum: [\"info\", \"success\", \"warning\", \"error\"], default: \"info\" },\n duration: { type: \"integer\", default: 3000 },\n },\n required: [\"message\"],\n });\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction collectFieldIDs(m: ManifestMessage): string[] {\n const seen = new Set<string>();\n const ids: string[] = [];\n for (const s of Object.values(m.screens)) {\n for (const f of s.fields ?? []) {\n if (!seen.has(f.id)) {\n ids.push(f.id);\n seen.add(f.id);\n }\n }\n }\n return ids;\n}\n\nfunction collectActionIDs(m: ManifestMessage): string[] {\n const seen = new Set<string>();\n const ids: string[] = [];\n for (const s of Object.values(m.screens)) {\n for (const a of s.actions ?? []) {\n if (!seen.has(a.id)) {\n ids.push(a.id);\n seen.add(a.id);\n }\n }\n }\n return ids;\n}\n\nfunction collectModalIDs(m: ManifestMessage): string[] {\n const seen = new Set<string>();\n const ids: string[] = [];\n for (const s of Object.values(m.screens)) {\n for (const md of s.modals ?? []) {\n if (!seen.has(md.id)) {\n ids.push(md.id);\n seen.add(md.id);\n }\n }\n }\n return ids;\n}\n\nfunction str(v: unknown): string {\n return typeof v === \"string\" ? v : \"\";\n}\n\nfunction num(v: unknown): number {\n return typeof v === \"number\" ? v : 0;\n}\n","import type OpenAI from \"openai\";\nimport type { ChatCompletionChunk } from \"openai/resources/chat/completions\";\nimport type { Session } from \"./session.js\";\nimport type { UIAction, ResultMessage, ServerMessage } from \"./types.js\";\nimport { manifestToTools, toolCallToUIAction } from \"./tools.js\";\n\n/** Maximum number of LLM rounds before sending a fallback response. */\nconst MAX_ROUNDS = 5;\n\n/** Callback to send a server message to the client. */\nexport type SendFn = (msg: ServerMessage) => void;\n\n/**\n * Callback to execute UI actions on the client.\n *\n * Sends a `command` message with the given `seq` and `actions`, then waits\n * for the client's `result` (or `confirm` for ask_confirm) response.\n *\n * @param seq - Sequence number for command/result correlation.\n * @param actions - Array of UI actions to execute on the client.\n * @returns The client's result message.\n */\nexport type ExecuteFn = (seq: number, actions: UIAction[]) => Promise<ResultMessage>;\n\n/**\n * Runs the streaming agent loop: LLM call → stream tokens → execute tool calls → repeat.\n *\n * The loop processes up to {@link MAX_ROUNDS} (5) rounds of tool calls.\n * In each round:\n * 1. Streams the LLM response, forwarding `chat_token` messages in real-time\n * 2. Accumulates tool call deltas from the stream\n * 3. If no tool calls → sends a final `chat` message and returns\n * 4. Converts tool calls to UIActions via {@link toolCallToUIAction}\n * 5. Executes actions on the client and maps results back to the LLM\n * 6. Continues to the next round\n *\n * If the loop exhausts all rounds, a fallback chat message is sent.\n *\n * @param openai - OpenAI client instance.\n * @param model - Model name to use for completions.\n * @param session - The current session (history, manifest, screen).\n * @param text - The user's text message.\n * @param execute - Callback to execute UI actions on the client.\n * @param send - Callback to send server messages to the client.\n */\nexport async function runAgentLoop(\n openai: OpenAI,\n model: string,\n session: Session,\n text: string,\n execute: ExecuteFn,\n send: SendFn,\n): Promise<void> {\n // Add user message to history\n session.addMessage({ role: \"user\", content: text });\n\n // Build tools from manifest\n const tools = session.manifest ? manifestToTools(session.manifest) : undefined;\n\n let lastResponseText = \"\";\n\n for (let round = 0; round < MAX_ROUNDS; round++) {\n const history = session.getHistory();\n\n const stream = await openai.chat.completions.create({\n model,\n messages: history,\n tools: tools?.length ? tools : undefined,\n stream: true,\n });\n\n let contentBuf = \"\";\n const accToolCalls: Array<{\n id: string;\n type: string;\n function: { name: string; arguments: string };\n }> = [];\n\n for await (const chunk of stream as AsyncIterable<ChatCompletionChunk>) {\n if (!chunk.choices?.length) continue;\n const delta = chunk.choices[0].delta;\n\n // Stream content tokens\n if (delta.content) {\n contentBuf += delta.content;\n send({ type: \"chat_token\", token: delta.content });\n }\n\n // Accumulate tool call deltas\n if (delta.tool_calls) {\n for (const tc of delta.tool_calls) {\n const idx = tc.index ?? 0;\n while (accToolCalls.length <= idx) {\n accToolCalls.push({ id: \"\", type: \"\", function: { name: \"\", arguments: \"\" } });\n }\n if (tc.id) accToolCalls[idx].id = tc.id;\n if (tc.type) accToolCalls[idx].type = tc.type;\n if (tc.function?.name) accToolCalls[idx].function.name = tc.function.name;\n if (tc.function?.arguments) accToolCalls[idx].function.arguments += tc.function.arguments;\n }\n }\n }\n\n // Build assistant message for history\n const assistantMsg: Record<string, unknown> = {\n role: \"assistant\" as const,\n content: contentBuf || null,\n };\n if (accToolCalls.length > 0) {\n assistantMsg.tool_calls = accToolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.function.name, arguments: tc.function.arguments },\n }));\n }\n session.addMessage(assistantMsg as any);\n\n // No tool calls → final text response\n if (accToolCalls.length === 0) {\n if (contentBuf) {\n send({ type: \"chat\", from: \"agent\", message: contentBuf, final: true });\n }\n return;\n }\n\n // Tool calls → convert to UIActions and execute\n if (contentBuf) {\n lastResponseText = contentBuf;\n }\n\n const roundActions: UIAction[] = [];\n const mappings: Array<{ action: UIAction; callId: string }> = [];\n\n for (const tc of accToolCalls) {\n try {\n const action = toolCallToUIAction(tc.function.name, tc.function.arguments);\n roundActions.push(action);\n mappings.push({ action, callId: tc.id });\n } catch (err) {\n // Report parse error back to LLM\n session.addMessage({\n role: \"tool\",\n content: JSON.stringify({ error: String(err) }),\n tool_call_id: tc.id,\n });\n }\n }\n\n if (roundActions.length === 0) continue;\n\n // Check if this round is ask_confirm only\n const isConfirmOnly =\n roundActions.length === 1 && roundActions[0].do === \"ask_confirm\";\n\n const seq = session.nextSeq();\n send({ type: \"status\", status: \"executing\" });\n\n let resultMsg: ResultMessage;\n try {\n // Send command and wait for result/confirm\n resultMsg = await execute(seq, roundActions);\n } catch (err) {\n // Execution failed — report to LLM\n for (const m of mappings) {\n session.addMessage({\n role: \"tool\",\n content: JSON.stringify({ success: false, error: String(err) }),\n tool_call_id: m.callId,\n });\n }\n send({ type: \"status\", status: \"thinking\" });\n continue;\n }\n\n send({ type: \"status\", status: \"thinking\" });\n\n // Map results back to tool messages\n const resultsByIndex = new Map<number, { success: boolean; error?: string }>();\n for (const r of resultMsg.results) {\n resultsByIndex.set(r.index, r);\n }\n\n for (let i = 0; i < mappings.length; i++) {\n const m = mappings[i];\n const result: Record<string, unknown> = { success: true, action: m.action.do };\n const r = resultsByIndex.get(i);\n if (r) {\n result.success = r.success;\n if (!r.success && r.error) result.error = r.error;\n }\n if (m.action.do === \"navigate\") {\n session.setScreen(m.action.screen!);\n result.screen = m.action.screen;\n }\n if (m.action.do === \"fill\") {\n result.field = m.action.field;\n result.value = m.action.value;\n }\n\n // For ask_confirm, inject the user's yes/no response\n if (isConfirmOnly && m.action.do === \"ask_confirm\") {\n const confirmed = resultMsg.results[0]?.success ?? false;\n result.user_response = confirmed ? \"Yes\" : \"No\";\n }\n\n session.addMessage({\n role: \"tool\",\n content: JSON.stringify(result),\n tool_call_id: m.callId,\n });\n }\n }\n\n // Ran out of rounds — send fallback\n if (lastResponseText) {\n send({ type: \"chat\", from: \"agent\", message: lastResponseText, final: true });\n } else {\n send({ type: \"chat\", from: \"agent\", message: \"Done.\", final: true });\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport type { IncomingMessage } from \"node:http\";\nimport type OpenAI from \"openai\";\nimport type {\n ClientMessage,\n ManifestMessage,\n ResultMessage,\n ConfirmMessage,\n ServerMessage,\n UIAction,\n} from \"./types.js\";\nimport { Session } from \"./session.js\";\nimport { runAgentLoop } from \"./agent.js\";\n\n/** Timeout in milliseconds for waiting on a client result/confirm. */\nconst EXECUTE_TIMEOUT_MS = 30_000;\n\n/**\n * Configuration options for creating an ACP server.\n */\nexport interface ServerOptions {\n /** OpenAI client instance (supports any OpenAI-compatible API via `baseURL`). */\n openai: OpenAI;\n /** Model name for LLM completions (e.g. `\"gpt-4o\"`, `\"claude-sonnet-4-5-20250929\"`). */\n model: string;\n /** WebSocket server port. */\n port: number;\n}\n\n/**\n * An ACP server instance with lifecycle methods.\n */\nexport interface ACPServer {\n /** Starts the WebSocket server and begins accepting connections. */\n start(): Promise<void>;\n /** Stops the server and closes all active connections. */\n stop(): Promise<void>;\n}\n\n/**\n * Creates an ACP reference server.\n *\n * The server listens for WebSocket connections on `/connect` and implements\n * the full ACP v1 text protocol: manifest, text, state, result, confirm,\n * llm_config, and response_lang_config messages.\n *\n * @param options - Server configuration.\n * @returns An {@link ACPServer} with `start()` and `stop()` methods.\n *\n * @example\n * ```ts\n * import { createServer } from \"@acprotocol/server\";\n * import OpenAI from \"openai\";\n *\n * const server = createServer({\n * openai: new OpenAI({ apiKey: \"sk-...\" }),\n * model: \"gpt-4o\",\n * port: 3000,\n * });\n * await server.start();\n * // server is now accepting connections at ws://localhost:3000/connect\n * ```\n */\nexport function createServer(options: ServerOptions): ACPServer {\n const { openai, model, port } = options;\n let wss: WebSocketServer | null = null;\n\n return {\n async start() {\n wss = new WebSocketServer({ port, path: \"/connect\" });\n wss.on(\"connection\", (ws: WebSocket, req: IncomingMessage) => {\n handleConnection(ws, openai, model);\n });\n },\n\n async stop() {\n if (!wss) return;\n for (const ws of wss.clients) {\n ws.close(1001, \"Server shutting down\");\n }\n await new Promise<void>((resolve) => wss!.close(() => resolve()));\n wss = null;\n },\n };\n}\n\n// ── Connection Handler ──────────────────────────────────────────────────────\n\nfunction handleConnection(ws: WebSocket, openai: OpenAI, model: string): void {\n const sessionId = randomUUID();\n const session = new Session(sessionId);\n\n // Pending result/confirm resolvers: seq → resolver\n const pendingResults = new Map<number, (msg: ResultMessage) => void>();\n const pendingConfirms = new Map<number, (confirmed: boolean) => void>();\n\n // Whether the agent is currently processing (prevent concurrent runs)\n let processing = false;\n\n const send = (msg: ServerMessage): void => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n };\n\n // Send config on connect\n send({\n type: \"config\",\n sessionId,\n features: { chat: true },\n providers: [{ id: \"default\", name: \"Default\", model }],\n current_provider: \"default\",\n });\n\n // Keepalive ping every 30s\n const pingInterval = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) ws.ping();\n }, 30_000);\n\n ws.on(\"close\", () => {\n clearInterval(pingInterval);\n // Clean up any pending resolvers\n for (const [, reject] of pendingResults) {\n // Will be caught by the timeout\n }\n pendingResults.clear();\n pendingConfirms.clear();\n });\n\n ws.on(\"message\", (data: Buffer) => {\n let msg: ClientMessage;\n try {\n msg = JSON.parse(data.toString());\n } catch {\n send({ type: \"error\", code: \"parse_error\", message: \"Invalid JSON\" });\n return;\n }\n\n switch (msg.type) {\n case \"manifest\":\n handleManifest(msg, session, send, openai, model);\n break;\n\n case \"text\":\n if (!msg.message) return;\n if (processing) {\n send({ type: \"error\", code: \"busy\", message: \"Already processing a request\" });\n return;\n }\n processing = true;\n handleText(msg.message, session, openai, model, send, makeExecuteFn(send, session, pendingResults, pendingConfirms))\n .finally(() => { processing = false; });\n break;\n\n case \"state\":\n session.setScreen(msg.screen);\n break;\n\n case \"result\":\n deliverResult(msg, pendingResults);\n break;\n\n case \"confirm\":\n deliverConfirm(msg, pendingConfirms, session, openai, model, send, makeExecuteFn(send, session, pendingResults, pendingConfirms));\n break;\n\n case \"llm_config\":\n // Acknowledge (single-provider server — no-op)\n break;\n\n case \"response_lang_config\":\n // Acknowledge\n break;\n }\n });\n}\n\n// ── Message Handlers ────────────────────────────────────────────────────────\n\nfunction handleManifest(\n msg: ManifestMessage,\n session: Session,\n send: (msg: ServerMessage) => void,\n openai: OpenAI,\n model: string,\n): void {\n session.setManifest(msg);\n send({ type: \"status\", status: \"idle\" });\n\n // Send greeting based on manifest context\n send({\n type: \"chat\",\n from: \"agent\",\n message: buildGreeting(msg),\n final: true,\n });\n}\n\nfunction buildGreeting(msg: ManifestMessage): string {\n const name = msg.persona?.name;\n const role = msg.persona?.role;\n const screens = Object.values(msg.screens);\n const screenLabel = screens.length === 1 ? screens[0].label : null;\n\n // Count capabilities\n const fieldCount = screens.reduce((sum, s) => sum + (s.fields?.length ?? 0), 0);\n const actionCount = screens.reduce((sum, s) => sum + (s.actions?.length ?? 0), 0);\n\n const intro = name ? `Hi, I'm ${name}!` : \"Hi!\";\n const roleDesc = role ? ` I'm your ${role}.` : \"\";\n\n let capability = \"\";\n if (screenLabel && fieldCount > 0) {\n capability = ` I can help you with the ${screenLabel} — just tell me what to fill in and I'll handle it.`;\n } else if (fieldCount > 0) {\n capability = ` I can fill forms, navigate screens, and click actions for you — just tell me what you need.`;\n }\n\n return `${intro}${roleDesc}${capability}`;\n}\n\nasync function handleText(\n text: string,\n session: Session,\n openai: OpenAI,\n model: string,\n send: (msg: ServerMessage) => void,\n execute: (seq: number, actions: UIAction[]) => Promise<ResultMessage>,\n): Promise<void> {\n send({ type: \"status\", status: \"thinking\" });\n\n try {\n await runAgentLoop(openai, model, session, text, execute, send);\n } catch (err) {\n console.error(\"[acp-server] Agent error:\", err);\n send({ type: \"error\", code: \"agent_error\", message: String(err) });\n }\n\n send({ type: \"status\", status: \"idle\" });\n}\n\nfunction deliverResult(\n msg: ResultMessage,\n pendingResults: Map<number, (msg: ResultMessage) => void>,\n): void {\n const resolver = pendingResults.get(msg.seq);\n if (resolver) {\n pendingResults.delete(msg.seq);\n resolver(msg);\n }\n}\n\nfunction deliverConfirm(\n msg: ConfirmMessage,\n pendingConfirms: Map<number, (confirmed: boolean) => void>,\n session: Session,\n openai: OpenAI,\n model: string,\n send: (m: ServerMessage) => void,\n execute: (seq: number, actions: UIAction[]) => Promise<ResultMessage>,\n): void {\n const resolver = pendingConfirms.get(msg.seq);\n if (resolver) {\n pendingConfirms.delete(msg.seq);\n resolver(msg.confirmed);\n }\n}\n\n// ── Execute Function ────────────────────────────────────────────────────────\n\nfunction makeExecuteFn(\n send: (msg: ServerMessage) => void,\n session: Session,\n pendingResults: Map<number, (msg: ResultMessage) => void>,\n pendingConfirms: Map<number, (confirmed: boolean) => void>,\n): (seq: number, actions: UIAction[]) => Promise<ResultMessage> {\n return (seq: number, actions: UIAction[]): Promise<ResultMessage> => {\n // Detect if this is a confirm-only command\n const isConfirmOnly =\n actions.length === 1 && actions[0].do === \"ask_confirm\";\n\n // Send command to client\n send({ type: \"command\", seq, actions });\n\n if (isConfirmOnly) {\n // Wait for confirm message instead of result\n return new Promise<ResultMessage>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingConfirms.delete(seq);\n reject(new Error(`Timeout waiting for confirm seq=${seq}`));\n }, EXECUTE_TIMEOUT_MS);\n\n pendingConfirms.set(seq, (confirmed: boolean) => {\n clearTimeout(timer);\n // Wrap confirmation as a ResultMessage\n resolve({\n type: \"result\",\n seq,\n results: [{ index: 0, success: confirmed }],\n });\n });\n });\n }\n\n // Wait for result message\n return new Promise<ResultMessage>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingResults.delete(seq);\n reject(new Error(`Timeout waiting for result seq=${seq}`));\n }, EXECUTE_TIMEOUT_MS);\n\n pendingResults.set(seq, (result: ResultMessage) => {\n clearTimeout(timer);\n resolve(result);\n });\n });\n };\n}\n"],"mappings":"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","names":[]}
|