@victor-software-house/pi-acp 0.2.0 → 0.3.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/dist/index.mjs +417 -87
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -64,6 +64,39 @@ function detectAuthError(err) {
|
|
|
64
64
|
return RequestError.authRequired({ authMethods: buildAuthMethods() }, "Configure an API key or log in with an OAuth provider.");
|
|
65
65
|
}
|
|
66
66
|
//#endregion
|
|
67
|
+
//#region src/acp/client-capabilities.ts
|
|
68
|
+
/**
|
|
69
|
+
* Extract well-known capability flags from ACP `ClientCapabilities`.
|
|
70
|
+
*
|
|
71
|
+
* Reads from:
|
|
72
|
+
* - `_meta.terminal_output` (terminal output rendering)
|
|
73
|
+
* - `_meta.terminal-auth` (terminal auth with command metadata)
|
|
74
|
+
* - `auth._meta.gateway` (gateway auth, future use)
|
|
75
|
+
*/
|
|
76
|
+
function parseClientCapabilities(caps) {
|
|
77
|
+
if (caps === void 0 || caps === null) return {
|
|
78
|
+
terminalOutput: false,
|
|
79
|
+
terminalAuth: false,
|
|
80
|
+
gatewayAuth: false
|
|
81
|
+
};
|
|
82
|
+
const meta = caps._meta;
|
|
83
|
+
const terminalOutput = typeof meta === "object" && meta !== null && meta["terminal_output"] === true;
|
|
84
|
+
const terminalAuth = typeof meta === "object" && meta !== null && meta["terminal-auth"] === true;
|
|
85
|
+
let gatewayAuth = false;
|
|
86
|
+
if ("auth" in caps) {
|
|
87
|
+
const auth = caps.auth;
|
|
88
|
+
if (typeof auth === "object" && auth !== null && "_meta" in auth) {
|
|
89
|
+
const authMeta = auth._meta;
|
|
90
|
+
if (typeof authMeta === "object" && authMeta !== null && "gateway" in authMeta) gatewayAuth = authMeta["gateway"] === true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
terminalOutput,
|
|
95
|
+
terminalAuth,
|
|
96
|
+
gatewayAuth
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
67
100
|
//#region src/acp/pi-settings.ts
|
|
68
101
|
/**
|
|
69
102
|
* Read pi settings from global and project config files.
|
|
@@ -123,62 +156,77 @@ function quietStartupEnabled(cwd) {
|
|
|
123
156
|
return false;
|
|
124
157
|
}
|
|
125
158
|
//#endregion
|
|
126
|
-
//#region src/acp/translate/
|
|
127
|
-
/**
|
|
128
|
-
* Extract displayable text from a pi tool result.
|
|
129
|
-
*
|
|
130
|
-
* Pi tool results have varying shapes depending on the tool. This function
|
|
131
|
-
* tries content blocks first, then falls back to details fields (diff, stdout/stderr),
|
|
132
|
-
* and finally JSON serialization as a last resort.
|
|
133
|
-
*/
|
|
159
|
+
//#region src/acp/translate/tool-content.ts
|
|
134
160
|
const textBlockSchema = z.object({
|
|
135
161
|
type: z.literal("text"),
|
|
136
162
|
text: z.string()
|
|
137
163
|
});
|
|
138
|
-
const
|
|
139
|
-
|
|
164
|
+
const imageBlockSchema = z.object({ type: z.literal("image") });
|
|
165
|
+
const contentBlockSchema = z.union([textBlockSchema, imageBlockSchema]);
|
|
166
|
+
const bashDetailsSchema = z.object({
|
|
140
167
|
stdout: z.string().optional(),
|
|
141
168
|
stderr: z.string().optional(),
|
|
142
169
|
output: z.string().optional(),
|
|
143
170
|
exitCode: z.number().optional(),
|
|
144
171
|
code: z.number().optional()
|
|
145
172
|
});
|
|
146
|
-
const
|
|
173
|
+
const bashResultSchema = z.object({
|
|
147
174
|
content: z.array(z.unknown()).optional(),
|
|
148
|
-
details:
|
|
175
|
+
details: bashDetailsSchema.optional(),
|
|
149
176
|
stdout: z.string().optional(),
|
|
150
177
|
stderr: z.string().optional(),
|
|
151
178
|
output: z.string().optional(),
|
|
152
179
|
exitCode: z.number().optional(),
|
|
153
180
|
code: z.number().optional()
|
|
154
181
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
182
|
+
/**
|
|
183
|
+
* Extract stdout/stderr and exit code from a pi bash/tmux result.
|
|
184
|
+
*/
|
|
185
|
+
function extractBashOutput(result) {
|
|
186
|
+
if (result === null || result === void 0 || typeof result !== "object") return {
|
|
187
|
+
output: "",
|
|
188
|
+
exitCode: void 0
|
|
189
|
+
};
|
|
190
|
+
const parsed = bashResultSchema.safeParse(result);
|
|
191
|
+
if (!parsed.success) return {
|
|
192
|
+
output: "",
|
|
193
|
+
exitCode: void 0
|
|
194
|
+
};
|
|
163
195
|
const r = parsed.data;
|
|
196
|
+
const d = r.details;
|
|
164
197
|
if (r.content !== void 0) {
|
|
165
198
|
const texts = r.content.map((block) => textBlockSchema.safeParse(block)).filter((res) => res.success).map((res) => res.data.text);
|
|
166
|
-
if (texts.length > 0)
|
|
199
|
+
if (texts.length > 0) {
|
|
200
|
+
const exitCode = d?.exitCode ?? r.exitCode ?? d?.code ?? r.code;
|
|
201
|
+
return {
|
|
202
|
+
output: texts.join(""),
|
|
203
|
+
exitCode
|
|
204
|
+
};
|
|
205
|
+
}
|
|
167
206
|
}
|
|
168
|
-
const d = r.details;
|
|
169
|
-
const diff = d?.diff;
|
|
170
|
-
if (diff !== void 0 && diff.trim() !== "") return diff;
|
|
171
207
|
const stdout = d?.stdout ?? r.stdout ?? d?.output ?? r.output;
|
|
172
208
|
const stderr = d?.stderr ?? r.stderr;
|
|
173
209
|
const exitCode = d?.exitCode ?? r.exitCode ?? d?.code ?? r.code;
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
210
|
+
const parts = [];
|
|
211
|
+
if (stdout !== void 0 && stdout.trim() !== "") parts.push(stdout);
|
|
212
|
+
if (stderr !== void 0 && stderr.trim() !== "") parts.push(stderr);
|
|
213
|
+
return {
|
|
214
|
+
output: parts.join("\n"),
|
|
215
|
+
exitCode
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Extract text content from a pi tool result (generic).
|
|
220
|
+
*/
|
|
221
|
+
function extractTextContent(result) {
|
|
222
|
+
if (result === null || result === void 0 || typeof result !== "object") return "";
|
|
223
|
+
if ("content" in result && Array.isArray(result.content)) {
|
|
224
|
+
const texts = [];
|
|
225
|
+
for (const block of result.content) {
|
|
226
|
+
const parsed = textBlockSchema.safeParse(block);
|
|
227
|
+
if (parsed.success) texts.push(parsed.data.text);
|
|
228
|
+
}
|
|
229
|
+
if (texts.length > 0) return texts.join("");
|
|
182
230
|
}
|
|
183
231
|
try {
|
|
184
232
|
return JSON.stringify(result, null, 2);
|
|
@@ -186,6 +234,134 @@ function toolResultToText(result) {
|
|
|
186
234
|
return String(result);
|
|
187
235
|
}
|
|
188
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Extract content blocks from a pi result, preserving type information.
|
|
239
|
+
* Used for read results where images need to be preserved.
|
|
240
|
+
*/
|
|
241
|
+
function extractContentBlocks(result) {
|
|
242
|
+
if (result === null || result === void 0 || typeof result !== "object") return [];
|
|
243
|
+
if (!("content" in result) || !Array.isArray(result.content)) return [];
|
|
244
|
+
const blocks = [];
|
|
245
|
+
for (const raw of result.content) {
|
|
246
|
+
const parsed = contentBlockSchema.safeParse(raw);
|
|
247
|
+
if (parsed.success) blocks.push(parsed.data);
|
|
248
|
+
}
|
|
249
|
+
return blocks;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Escape text that would be interpreted as markdown formatting.
|
|
253
|
+
*
|
|
254
|
+
* Prevents file content from being rendered as headings, links, code
|
|
255
|
+
* blocks, or horizontal rules when displayed in an ACP client.
|
|
256
|
+
*/
|
|
257
|
+
function markdownEscape(text) {
|
|
258
|
+
return text.replace(/^(#{1,6})\s/gm, "\\$1 ").replace(/\[/g, "\\[").replace(/\]/g, "\\]").replace(/^([-*_])\1{2,}$/gm, "\\$1$1$1").replace(/</g, "\\<");
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Format tool output into `ToolCallContent[]` by tool name.
|
|
262
|
+
*
|
|
263
|
+
* Returns the appropriate content shape for each tool type:
|
|
264
|
+
* - bash/tmux: console code fences
|
|
265
|
+
* - read: markdown-escaped text (images preserved)
|
|
266
|
+
* - edit/write: empty (diff handled separately)
|
|
267
|
+
* - lsp: code fences
|
|
268
|
+
* - errors: code fences with failed status
|
|
269
|
+
* - everything else: plain text
|
|
270
|
+
*/
|
|
271
|
+
function formatToolContent(toolName, result, isError) {
|
|
272
|
+
if (isError) {
|
|
273
|
+
const text = extractTextContent(result);
|
|
274
|
+
if (text === "") return [];
|
|
275
|
+
return [{
|
|
276
|
+
type: "content",
|
|
277
|
+
content: {
|
|
278
|
+
type: "text",
|
|
279
|
+
text: `\`\`\`\n${text}\n\`\`\``
|
|
280
|
+
}
|
|
281
|
+
}];
|
|
282
|
+
}
|
|
283
|
+
switch (toolName) {
|
|
284
|
+
case "bash":
|
|
285
|
+
case "tmux": return formatBashContent(result);
|
|
286
|
+
case "read": return formatReadContent(result);
|
|
287
|
+
case "edit":
|
|
288
|
+
case "write": return [];
|
|
289
|
+
case "lsp": return formatLspContent(result);
|
|
290
|
+
default: return formatFallbackContent(result);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function formatBashContent(result) {
|
|
294
|
+
const { output, exitCode } = extractBashOutput(result);
|
|
295
|
+
if (output === "" && exitCode === void 0) return [];
|
|
296
|
+
const parts = [];
|
|
297
|
+
if (output !== "") parts.push(`\`\`\`console\n${output}\n\`\`\``);
|
|
298
|
+
if (exitCode !== void 0 && exitCode !== 0) parts.push(`exit code: ${exitCode}`);
|
|
299
|
+
const text = parts.join("\n\n");
|
|
300
|
+
if (text === "") return [];
|
|
301
|
+
return [{
|
|
302
|
+
type: "content",
|
|
303
|
+
content: {
|
|
304
|
+
type: "text",
|
|
305
|
+
text
|
|
306
|
+
}
|
|
307
|
+
}];
|
|
308
|
+
}
|
|
309
|
+
function formatReadContent(result) {
|
|
310
|
+
const blocks = extractContentBlocks(result);
|
|
311
|
+
if (blocks.length === 0) {
|
|
312
|
+
if (typeof result === "object" && result !== null && "content" in result && Array.isArray(result.content) && result.content.length === 0) return [];
|
|
313
|
+
const text = extractTextContent(result);
|
|
314
|
+
if (text === "") return [];
|
|
315
|
+
return [{
|
|
316
|
+
type: "content",
|
|
317
|
+
content: {
|
|
318
|
+
type: "text",
|
|
319
|
+
text: markdownEscape(text)
|
|
320
|
+
}
|
|
321
|
+
}];
|
|
322
|
+
}
|
|
323
|
+
const content = [];
|
|
324
|
+
for (const block of blocks) if (block.type === "text") content.push({
|
|
325
|
+
type: "content",
|
|
326
|
+
content: {
|
|
327
|
+
type: "text",
|
|
328
|
+
text: markdownEscape(block.text)
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
return content;
|
|
332
|
+
}
|
|
333
|
+
function formatLspContent(result) {
|
|
334
|
+
const text = extractTextContent(result);
|
|
335
|
+
if (text === "") return [];
|
|
336
|
+
return [{
|
|
337
|
+
type: "content",
|
|
338
|
+
content: {
|
|
339
|
+
type: "text",
|
|
340
|
+
text: `\`\`\`\n${text}\n\`\`\``
|
|
341
|
+
}
|
|
342
|
+
}];
|
|
343
|
+
}
|
|
344
|
+
function formatFallbackContent(result) {
|
|
345
|
+
const text = extractTextContent(result);
|
|
346
|
+
if (text === "") return [];
|
|
347
|
+
return [{
|
|
348
|
+
type: "content",
|
|
349
|
+
content: {
|
|
350
|
+
type: "text",
|
|
351
|
+
text
|
|
352
|
+
}
|
|
353
|
+
}];
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Wrap streaming output text in a console code fence for bash/tmux.
|
|
357
|
+
*
|
|
358
|
+
* Each streaming update is self-contained (full accumulated buffer),
|
|
359
|
+
* following the codex-acp pattern.
|
|
360
|
+
*/
|
|
361
|
+
function wrapStreamingBashOutput(text) {
|
|
362
|
+
if (text === "") return "";
|
|
363
|
+
return `\`\`\`console\n${text}\n\`\`\``;
|
|
364
|
+
}
|
|
189
365
|
//#endregion
|
|
190
366
|
//#region src/acp/session.ts
|
|
191
367
|
function findUniqueLineNumber(text, needle) {
|
|
@@ -210,7 +386,9 @@ function toToolKind(toolName) {
|
|
|
210
386
|
case "read": return "read";
|
|
211
387
|
case "write":
|
|
212
388
|
case "edit": return "edit";
|
|
213
|
-
case "bash":
|
|
389
|
+
case "bash":
|
|
390
|
+
case "tmux": return "execute";
|
|
391
|
+
case "lsp": return "search";
|
|
214
392
|
default: return "other";
|
|
215
393
|
}
|
|
216
394
|
}
|
|
@@ -220,6 +398,10 @@ function truncateTitle(text) {
|
|
|
220
398
|
if (oneLine.length <= MAX_TITLE_LEN) return oneLine;
|
|
221
399
|
return `${oneLine.slice(0, MAX_TITLE_LEN - 1)}…`;
|
|
222
400
|
}
|
|
401
|
+
function capitalize(s) {
|
|
402
|
+
if (s.length === 0) return s;
|
|
403
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
404
|
+
}
|
|
223
405
|
/**
|
|
224
406
|
* Build a descriptive tool title from tool name and args.
|
|
225
407
|
*
|
|
@@ -235,6 +417,36 @@ function buildToolTitle(toolName, args) {
|
|
|
235
417
|
const command = typeof args["command"] === "string" ? args["command"] : typeof args["cmd"] === "string" ? args["cmd"] : void 0;
|
|
236
418
|
return command !== void 0 ? truncateTitle(`Run ${command}`) : "bash";
|
|
237
419
|
}
|
|
420
|
+
case "lsp": {
|
|
421
|
+
const action = typeof args["action"] === "string" ? args["action"] : void 0;
|
|
422
|
+
const file = typeof args["file"] === "string" ? args["file"] : void 0;
|
|
423
|
+
const query = typeof args["query"] === "string" ? args["query"] : void 0;
|
|
424
|
+
const line = typeof args["line"] === "number" ? args["line"] : void 0;
|
|
425
|
+
if (action !== void 0) {
|
|
426
|
+
const target = file !== void 0 ? line !== void 0 ? `${file}:${line}` : file : query;
|
|
427
|
+
return target !== void 0 ? truncateTitle(`${capitalize(action)} ${target}`) : capitalize(action);
|
|
428
|
+
}
|
|
429
|
+
return "LSP";
|
|
430
|
+
}
|
|
431
|
+
case "tmux": {
|
|
432
|
+
const action = typeof args["action"] === "string" ? args["action"] : void 0;
|
|
433
|
+
const command = typeof args["command"] === "string" ? args["command"] : void 0;
|
|
434
|
+
const name = typeof args["name"] === "string" ? args["name"] : void 0;
|
|
435
|
+
if (action === "run" && command !== void 0) return truncateTitle(`Tmux: ${command}`);
|
|
436
|
+
if (action !== void 0 && name !== void 0) return truncateTitle(`Tmux ${action} ${name}`);
|
|
437
|
+
if (action !== void 0) return `Tmux ${action}`;
|
|
438
|
+
return "Tmux";
|
|
439
|
+
}
|
|
440
|
+
case "context_tag": {
|
|
441
|
+
const name = typeof args["name"] === "string" ? args["name"] : void 0;
|
|
442
|
+
return name !== void 0 ? `Tag ${name}` : "Tag";
|
|
443
|
+
}
|
|
444
|
+
case "context_log": return "Context log";
|
|
445
|
+
case "context_checkout": {
|
|
446
|
+
const target = typeof args["target"] === "string" ? args["target"] : void 0;
|
|
447
|
+
return target !== void 0 ? truncateTitle(`Checkout ${target}`) : "Checkout";
|
|
448
|
+
}
|
|
449
|
+
case "claudemon": return "Check quota";
|
|
238
450
|
default: return toolName;
|
|
239
451
|
}
|
|
240
452
|
}
|
|
@@ -269,6 +481,19 @@ function toToolArgs(raw) {
|
|
|
269
481
|
const result = toolArgsSchema.safeParse(raw);
|
|
270
482
|
return result.success ? result.data : {};
|
|
271
483
|
}
|
|
484
|
+
/** Build the `_meta.piAcp` tool name metadata. */
|
|
485
|
+
function buildToolMeta(toolName, extra) {
|
|
486
|
+
const base = { piAcp: { toolName } };
|
|
487
|
+
if (extra !== void 0) return {
|
|
488
|
+
...base,
|
|
489
|
+
...extra
|
|
490
|
+
};
|
|
491
|
+
return base;
|
|
492
|
+
}
|
|
493
|
+
/** Tools that produce terminal-style output. */
|
|
494
|
+
function isTerminalTool(toolName) {
|
|
495
|
+
return toolName === "bash" || toolName === "tmux";
|
|
496
|
+
}
|
|
272
497
|
var SessionManager$1 = class {
|
|
273
498
|
sessions = /* @__PURE__ */ new Map();
|
|
274
499
|
disposeAll() {
|
|
@@ -302,12 +527,15 @@ var PiAcpSession = class {
|
|
|
302
527
|
cwd;
|
|
303
528
|
mcpServers;
|
|
304
529
|
piSession;
|
|
530
|
+
supportsTerminalOutput;
|
|
305
531
|
startupInfo = null;
|
|
306
532
|
startupInfoSent = false;
|
|
307
533
|
conn;
|
|
308
534
|
cancelRequested = false;
|
|
309
535
|
pendingTurn = null;
|
|
310
536
|
currentToolCalls = /* @__PURE__ */ new Map();
|
|
537
|
+
/** Map of toolCallId -> toolName for streaming updates (Phase 5). */
|
|
538
|
+
toolCallNames = /* @__PURE__ */ new Map();
|
|
311
539
|
editSnapshots = /* @__PURE__ */ new Map();
|
|
312
540
|
lastAssistantStopReason = null;
|
|
313
541
|
lastEmit = Promise.resolve();
|
|
@@ -318,6 +546,7 @@ var PiAcpSession = class {
|
|
|
318
546
|
this.mcpServers = opts.mcpServers;
|
|
319
547
|
this.piSession = opts.piSession;
|
|
320
548
|
this.conn = opts.conn;
|
|
549
|
+
this.supportsTerminalOutput = opts.supportsTerminalOutput ?? false;
|
|
321
550
|
this.unsubscribe = this.piSession.subscribe((ev) => this.handlePiEvent(ev));
|
|
322
551
|
}
|
|
323
552
|
dispose() {
|
|
@@ -385,10 +614,10 @@ var PiAcpSession = class {
|
|
|
385
614
|
this.handleToolStart(ev.toolCallId, ev.toolName, toToolArgs(ev.args));
|
|
386
615
|
break;
|
|
387
616
|
case "tool_execution_update":
|
|
388
|
-
this.handleToolUpdate(ev.toolCallId, ev.partialResult);
|
|
617
|
+
this.handleToolUpdate(ev.toolCallId, ev.toolName, ev.partialResult);
|
|
389
618
|
break;
|
|
390
619
|
case "tool_execution_end":
|
|
391
|
-
this.handleToolEnd(ev.toolCallId, ev.result, ev.isError);
|
|
620
|
+
this.handleToolEnd(ev.toolCallId, ev.toolName, ev.result, ev.isError);
|
|
392
621
|
break;
|
|
393
622
|
case "agent_end":
|
|
394
623
|
this.handleAgentEnd();
|
|
@@ -433,14 +662,16 @@ var PiAcpSession = class {
|
|
|
433
662
|
kind: toToolKind(toolCall.name),
|
|
434
663
|
status,
|
|
435
664
|
...locations ? { locations } : {},
|
|
436
|
-
rawInput
|
|
665
|
+
rawInput,
|
|
666
|
+
_meta: buildToolMeta(toolCall.name)
|
|
437
667
|
});
|
|
438
668
|
} else this.emit({
|
|
439
669
|
sessionUpdate: "tool_call_update",
|
|
440
670
|
toolCallId: toolCall.id,
|
|
441
671
|
status,
|
|
442
672
|
...locations ? { locations } : {},
|
|
443
|
-
rawInput
|
|
673
|
+
rawInput,
|
|
674
|
+
_meta: buildToolMeta(toolCall.name)
|
|
444
675
|
});
|
|
445
676
|
}
|
|
446
677
|
}
|
|
@@ -448,6 +679,7 @@ var PiAcpSession = class {
|
|
|
448
679
|
if ("role" in msg && msg.role === "assistant") this.lastAssistantStopReason = msg.stopReason;
|
|
449
680
|
}
|
|
450
681
|
handleToolStart(toolCallId, toolName, args) {
|
|
682
|
+
this.toolCallNames.set(toolCallId, toolName);
|
|
451
683
|
let line;
|
|
452
684
|
if ((toolName === "edit" || toolName === "write") && args.path !== void 0) try {
|
|
453
685
|
const abs = isAbsolute(args.path) ? args.path : resolve(this.cwd, args.path);
|
|
@@ -462,6 +694,14 @@ var PiAcpSession = class {
|
|
|
462
694
|
if (toolName === "edit") line = findUniqueLineNumber(oldText, args.oldText ?? "");
|
|
463
695
|
} catch {}
|
|
464
696
|
const locations = resolveToolPath(args, this.cwd, line);
|
|
697
|
+
const meta = buildToolMeta(toolName, this.supportsTerminalOutput && isTerminalTool(toolName) ? { terminal_info: {
|
|
698
|
+
terminal_id: toolCallId,
|
|
699
|
+
cwd: this.cwd
|
|
700
|
+
} } : void 0);
|
|
701
|
+
const terminalContent = this.supportsTerminalOutput && isTerminalTool(toolName) ? [{
|
|
702
|
+
type: "terminal",
|
|
703
|
+
terminalId: toolCallId
|
|
704
|
+
}] : void 0;
|
|
465
705
|
if (!this.currentToolCalls.has(toolCallId)) {
|
|
466
706
|
this.currentToolCalls.set(toolCallId, "in_progress");
|
|
467
707
|
this.emit({
|
|
@@ -471,7 +711,9 @@ var PiAcpSession = class {
|
|
|
471
711
|
kind: toToolKind(toolName),
|
|
472
712
|
status: "in_progress",
|
|
473
713
|
...locations ? { locations } : {},
|
|
474
|
-
|
|
714
|
+
...terminalContent !== void 0 ? { content: terminalContent } : {},
|
|
715
|
+
rawInput: args,
|
|
716
|
+
_meta: meta
|
|
475
717
|
});
|
|
476
718
|
} else {
|
|
477
719
|
this.currentToolCalls.set(toolCallId, "in_progress");
|
|
@@ -481,61 +723,105 @@ var PiAcpSession = class {
|
|
|
481
723
|
title: buildToolTitle(toolName, args),
|
|
482
724
|
status: "in_progress",
|
|
483
725
|
...locations ? { locations } : {},
|
|
484
|
-
|
|
726
|
+
...terminalContent !== void 0 ? { content: terminalContent } : {},
|
|
727
|
+
rawInput: args,
|
|
728
|
+
_meta: meta
|
|
485
729
|
});
|
|
486
730
|
}
|
|
487
731
|
}
|
|
488
|
-
handleToolUpdate(toolCallId, partialResult) {
|
|
489
|
-
const
|
|
490
|
-
this.
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
text
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
})
|
|
732
|
+
handleToolUpdate(toolCallId, toolName, partialResult) {
|
|
733
|
+
const name = this.toolCallNames.get(toolCallId) ?? toolName;
|
|
734
|
+
if (this.supportsTerminalOutput && isTerminalTool(name)) {
|
|
735
|
+
const text = extractStreamingText(partialResult);
|
|
736
|
+
this.emit({
|
|
737
|
+
sessionUpdate: "tool_call_update",
|
|
738
|
+
toolCallId,
|
|
739
|
+
status: "in_progress",
|
|
740
|
+
_meta: buildToolMeta(name, { terminal_output: {
|
|
741
|
+
terminal_id: toolCallId,
|
|
742
|
+
data: text
|
|
743
|
+
} }),
|
|
744
|
+
rawOutput: partialResult
|
|
745
|
+
});
|
|
746
|
+
} else if (isTerminalTool(name)) {
|
|
747
|
+
const wrapped = wrapStreamingBashOutput(extractStreamingText(partialResult));
|
|
748
|
+
this.emit({
|
|
749
|
+
sessionUpdate: "tool_call_update",
|
|
750
|
+
toolCallId,
|
|
751
|
+
status: "in_progress",
|
|
752
|
+
content: wrapped ? [{
|
|
753
|
+
type: "content",
|
|
754
|
+
content: {
|
|
755
|
+
type: "text",
|
|
756
|
+
text: wrapped
|
|
757
|
+
}
|
|
758
|
+
}] : null,
|
|
759
|
+
_meta: buildToolMeta(name),
|
|
760
|
+
rawOutput: partialResult
|
|
761
|
+
});
|
|
762
|
+
} else {
|
|
763
|
+
const text = extractStreamingText(partialResult);
|
|
764
|
+
this.emit({
|
|
765
|
+
sessionUpdate: "tool_call_update",
|
|
766
|
+
toolCallId,
|
|
767
|
+
status: "in_progress",
|
|
768
|
+
content: text ? [{
|
|
769
|
+
type: "content",
|
|
770
|
+
content: {
|
|
771
|
+
type: "text",
|
|
772
|
+
text
|
|
773
|
+
}
|
|
774
|
+
}] : null,
|
|
775
|
+
_meta: buildToolMeta(name),
|
|
776
|
+
rawOutput: partialResult
|
|
777
|
+
});
|
|
778
|
+
}
|
|
503
779
|
}
|
|
504
|
-
handleToolEnd(toolCallId, result, isError) {
|
|
505
|
-
const text = toolResultToText(result);
|
|
780
|
+
handleToolEnd(toolCallId, toolName, result, isError) {
|
|
506
781
|
const snapshot = this.editSnapshots.get(toolCallId);
|
|
507
782
|
let content = null;
|
|
508
783
|
if (!isError && snapshot) try {
|
|
509
784
|
const newText = readFileSync(snapshot.path, "utf8");
|
|
510
|
-
if (newText !== snapshot.oldText)
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
785
|
+
if (newText !== snapshot.oldText) {
|
|
786
|
+
const formatted = formatToolContent(toolName, result, isError);
|
|
787
|
+
content = [{
|
|
788
|
+
type: "diff",
|
|
789
|
+
path: snapshot.path,
|
|
790
|
+
oldText: snapshot.oldText,
|
|
791
|
+
newText
|
|
792
|
+
}, ...formatted];
|
|
793
|
+
}
|
|
794
|
+
} catch {}
|
|
795
|
+
const meta = buildToolMeta(toolName, this.supportsTerminalOutput && isTerminalTool(toolName) ? { terminal_exit: {
|
|
796
|
+
terminal_id: toolCallId,
|
|
797
|
+
exit_code: extractExitCode(result),
|
|
798
|
+
signal: null
|
|
799
|
+
} } : void 0);
|
|
800
|
+
if (content === null) {
|
|
801
|
+
const formatted = formatToolContent(toolName, result, isError);
|
|
802
|
+
content = formatted.length > 0 ? formatted : null;
|
|
803
|
+
}
|
|
804
|
+
if (content === null && !isError && toolName !== "edit" && toolName !== "write") {
|
|
805
|
+
const text = extractStreamingText(result);
|
|
806
|
+
if (text) content = [{
|
|
516
807
|
type: "content",
|
|
517
808
|
content: {
|
|
518
809
|
type: "text",
|
|
519
810
|
text
|
|
520
811
|
}
|
|
521
|
-
}]
|
|
522
|
-
}
|
|
523
|
-
if (!content && text) content = [{
|
|
524
|
-
type: "content",
|
|
525
|
-
content: {
|
|
526
|
-
type: "text",
|
|
527
|
-
text
|
|
528
|
-
}
|
|
529
|
-
}];
|
|
812
|
+
}];
|
|
813
|
+
}
|
|
530
814
|
this.emit({
|
|
531
815
|
sessionUpdate: "tool_call_update",
|
|
532
816
|
toolCallId,
|
|
533
817
|
status: isError ? "failed" : "completed",
|
|
534
818
|
content,
|
|
819
|
+
_meta: meta,
|
|
535
820
|
rawOutput: result
|
|
536
821
|
});
|
|
537
822
|
this.currentToolCalls.delete(toolCallId);
|
|
538
823
|
this.editSnapshots.delete(toolCallId);
|
|
824
|
+
this.toolCallNames.delete(toolCallId);
|
|
539
825
|
}
|
|
540
826
|
handleAgentEnd() {
|
|
541
827
|
this.emitUsageUpdate();
|
|
@@ -583,6 +869,42 @@ var PiAcpSession = class {
|
|
|
583
869
|
return this.piSession.getSessionStats().cost;
|
|
584
870
|
}
|
|
585
871
|
};
|
|
872
|
+
function isTextBlock$1(v) {
|
|
873
|
+
return typeof v === "object" && v !== null && "type" in v && v.type === "text" && "text" in v && typeof v.text === "string";
|
|
874
|
+
}
|
|
875
|
+
function extractStreamingText(result) {
|
|
876
|
+
if (result === null || result === void 0) return "";
|
|
877
|
+
if (typeof result === "string") return result;
|
|
878
|
+
if (typeof result !== "object") return String(result);
|
|
879
|
+
if ("content" in result && Array.isArray(result.content)) {
|
|
880
|
+
const texts = [];
|
|
881
|
+
for (const raw of result.content) if (isTextBlock$1(raw)) texts.push(raw.text);
|
|
882
|
+
if (texts.length > 0) return texts.join("");
|
|
883
|
+
}
|
|
884
|
+
if ("details" in result) {
|
|
885
|
+
const details = result.details;
|
|
886
|
+
if (typeof details === "object" && details !== null) {
|
|
887
|
+
if ("stdout" in details && typeof details.stdout === "string" && details.stdout.trim() !== "") return details.stdout;
|
|
888
|
+
if ("output" in details && typeof details.output === "string" && details.output.trim() !== "") return details.output;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if ("output" in result && typeof result.output === "string" && result.output.trim() !== "") return result.output;
|
|
892
|
+
if ("stdout" in result && typeof result.stdout === "string" && result.stdout.trim() !== "") return result.stdout;
|
|
893
|
+
return "";
|
|
894
|
+
}
|
|
895
|
+
function extractExitCode(result) {
|
|
896
|
+
if (result === null || result === void 0 || typeof result !== "object") return null;
|
|
897
|
+
if ("details" in result) {
|
|
898
|
+
const details = result.details;
|
|
899
|
+
if (typeof details === "object" && details !== null) {
|
|
900
|
+
if ("exitCode" in details && typeof details.exitCode === "number") return details.exitCode;
|
|
901
|
+
if ("code" in details && typeof details.code === "number") return details.code;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if ("exitCode" in result && typeof result.exitCode === "number") return result.exitCode;
|
|
905
|
+
if ("code" in result && typeof result.code === "number") return result.code;
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
586
908
|
/**
|
|
587
909
|
* Type guard to narrow AgentSessionEvent to the AgentEvent subset
|
|
588
910
|
* (the variants we handle). Session-specific events like auto_compaction
|
|
@@ -797,6 +1119,12 @@ var PiAcpAgent = class {
|
|
|
797
1119
|
sessions = new SessionManager$1();
|
|
798
1120
|
/** Cache of sessionId → file path, populated by listSessions and newSession. */
|
|
799
1121
|
sessionPaths = /* @__PURE__ */ new Map();
|
|
1122
|
+
/** Parsed client capability flags from initialize(). */
|
|
1123
|
+
clientCapabilities = {
|
|
1124
|
+
terminalOutput: false,
|
|
1125
|
+
terminalAuth: false,
|
|
1126
|
+
gatewayAuth: false
|
|
1127
|
+
};
|
|
800
1128
|
dispose() {
|
|
801
1129
|
this.sessions.disposeAll();
|
|
802
1130
|
}
|
|
@@ -806,6 +1134,7 @@ var PiAcpAgent = class {
|
|
|
806
1134
|
async initialize(params) {
|
|
807
1135
|
const supportedVersion = 1;
|
|
808
1136
|
const requested = params.protocolVersion;
|
|
1137
|
+
this.clientCapabilities = parseClientCapabilities(params.clientCapabilities);
|
|
809
1138
|
return {
|
|
810
1139
|
protocolVersion: requested === supportedVersion ? requested : supportedVersion,
|
|
811
1140
|
agentInfo: {
|
|
@@ -813,7 +1142,7 @@ var PiAcpAgent = class {
|
|
|
813
1142
|
title: "pi ACP adapter",
|
|
814
1143
|
version: pkg.version
|
|
815
1144
|
},
|
|
816
|
-
authMethods: buildAuthMethods({ supportsTerminalAuthMeta:
|
|
1145
|
+
authMethods: buildAuthMethods({ supportsTerminalAuthMeta: this.clientCapabilities.terminalAuth }),
|
|
817
1146
|
agentCapabilities: {
|
|
818
1147
|
loadSession: true,
|
|
819
1148
|
mcpCapabilities: {
|
|
@@ -859,7 +1188,8 @@ var PiAcpAgent = class {
|
|
|
859
1188
|
cwd: params.cwd,
|
|
860
1189
|
mcpServers: params.mcpServers,
|
|
861
1190
|
piSession,
|
|
862
|
-
conn: this.conn
|
|
1191
|
+
conn: this.conn,
|
|
1192
|
+
supportsTerminalOutput: this.clientCapabilities.terminalOutput
|
|
863
1193
|
});
|
|
864
1194
|
this.sessions.register(session);
|
|
865
1195
|
const quietStartup = quietStartupEnabled(params.cwd);
|
|
@@ -1009,7 +1339,8 @@ var PiAcpAgent = class {
|
|
|
1009
1339
|
kind: toToolKind(block.name),
|
|
1010
1340
|
status: "completed",
|
|
1011
1341
|
rawInput: args,
|
|
1012
|
-
...locations ? { locations } : {}
|
|
1342
|
+
...locations ? { locations } : {},
|
|
1343
|
+
_meta: { piAcp: { toolName: block.name } }
|
|
1013
1344
|
}
|
|
1014
1345
|
});
|
|
1015
1346
|
}
|
|
@@ -1032,25 +1363,21 @@ var PiAcpAgent = class {
|
|
|
1032
1363
|
kind: toToolKind(toolName),
|
|
1033
1364
|
status: "completed",
|
|
1034
1365
|
rawInput: null,
|
|
1035
|
-
rawOutput: m
|
|
1366
|
+
rawOutput: m,
|
|
1367
|
+
_meta: { piAcp: { toolName } }
|
|
1036
1368
|
}
|
|
1037
1369
|
});
|
|
1038
|
-
const
|
|
1370
|
+
const content = formatToolContent(toolName, m, isError);
|
|
1039
1371
|
await this.conn.sessionUpdate({
|
|
1040
1372
|
sessionId: session.sessionId,
|
|
1041
1373
|
update: {
|
|
1042
1374
|
sessionUpdate: "tool_call_update",
|
|
1043
1375
|
toolCallId,
|
|
1044
1376
|
status: isError ? "failed" : "completed",
|
|
1045
|
-
content:
|
|
1046
|
-
type: "content",
|
|
1047
|
-
content: {
|
|
1048
|
-
type: "text",
|
|
1049
|
-
text
|
|
1050
|
-
}
|
|
1051
|
-
}] : null,
|
|
1377
|
+
content: content.length > 0 ? content : null,
|
|
1052
1378
|
rawOutput: m,
|
|
1053
|
-
...locations ? { locations } : {}
|
|
1379
|
+
...locations ? { locations } : {},
|
|
1380
|
+
_meta: { piAcp: { toolName } }
|
|
1054
1381
|
}
|
|
1055
1382
|
});
|
|
1056
1383
|
}
|
|
@@ -1109,7 +1436,8 @@ var PiAcpAgent = class {
|
|
|
1109
1436
|
cwd: params.cwd,
|
|
1110
1437
|
mcpServers: params.mcpServers,
|
|
1111
1438
|
piSession,
|
|
1112
|
-
conn: this.conn
|
|
1439
|
+
conn: this.conn,
|
|
1440
|
+
supportsTerminalOutput: this.clientCapabilities.terminalOutput
|
|
1113
1441
|
});
|
|
1114
1442
|
this.sessions.register(session);
|
|
1115
1443
|
await this.replaySessionHistory(session, piSession.messages);
|
|
@@ -1176,7 +1504,8 @@ var PiAcpAgent = class {
|
|
|
1176
1504
|
cwd: params.cwd,
|
|
1177
1505
|
mcpServers: params.mcpServers ?? [],
|
|
1178
1506
|
piSession,
|
|
1179
|
-
conn: this.conn
|
|
1507
|
+
conn: this.conn,
|
|
1508
|
+
supportsTerminalOutput: this.clientCapabilities.terminalOutput
|
|
1180
1509
|
});
|
|
1181
1510
|
this.sessions.register(session);
|
|
1182
1511
|
this.sessionPaths.set(params.sessionId, sessionFile);
|
|
@@ -1229,7 +1558,8 @@ var PiAcpAgent = class {
|
|
|
1229
1558
|
cwd: params.cwd,
|
|
1230
1559
|
mcpServers: params.mcpServers ?? [],
|
|
1231
1560
|
piSession,
|
|
1232
|
-
conn: this.conn
|
|
1561
|
+
conn: this.conn,
|
|
1562
|
+
supportsTerminalOutput: this.clientCapabilities.terminalOutput
|
|
1233
1563
|
});
|
|
1234
1564
|
this.sessions.register(session);
|
|
1235
1565
|
const enableSkillCommands = skillCommandsEnabled(params.cwd);
|