pi-acp 0.0.31 → 0.0.33
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 +1 -1
- package/dist/index.js +195 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ npm install -g @earendil-works/pi-coding-agent
|
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
- Node.js 22+
|
|
41
|
-
- `pi` installed and available on your `PATH` (the adapter runs the `pi` executable)
|
|
41
|
+
- `pi` v0.80.4+ installed and available on your `PATH` (the adapter runs the `pi` executable)
|
|
42
42
|
- Configure `pi` separately for your model providers/API keys
|
|
43
43
|
|
|
44
44
|
## Install
|
package/dist/index.js
CHANGED
|
@@ -526,6 +526,52 @@ function expandSlashCommand(text, fileCommands) {
|
|
|
526
526
|
return substituteArgs(cmd.content, args);
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
+
// src/acp/translate/bash.ts
|
|
530
|
+
function isBashTool(toolName) {
|
|
531
|
+
return toolName.toLowerCase() === "bash";
|
|
532
|
+
}
|
|
533
|
+
function bashCommand(value) {
|
|
534
|
+
const record = value;
|
|
535
|
+
const command = record?.command ?? record?.cmd ?? record?.args?.command ?? record?.args?.cmd ?? record?.input?.command ?? record?.input?.cmd ?? record?.rawInput?.command ?? record?.rawInput?.cmd ?? record?.toolInput?.command ?? record?.toolInput?.cmd ?? record?.details?.command ?? record?.details?.cmd;
|
|
536
|
+
return typeof command === "string" && command.trim() ? command : void 0;
|
|
537
|
+
}
|
|
538
|
+
function bashResultText(result) {
|
|
539
|
+
const record = result;
|
|
540
|
+
const content = record?.content;
|
|
541
|
+
if (Array.isArray(content)) {
|
|
542
|
+
const texts = content.map((c) => {
|
|
543
|
+
const block = c;
|
|
544
|
+
return block.type === "text" && typeof block.text === "string" ? block.text : "";
|
|
545
|
+
}).filter(Boolean);
|
|
546
|
+
if (texts.length) return texts.join("");
|
|
547
|
+
}
|
|
548
|
+
const details = record?.details;
|
|
549
|
+
const stdout = (typeof details?.stdout === "string" ? details.stdout : void 0) ?? (typeof record?.stdout === "string" ? record.stdout : void 0) ?? (typeof details?.output === "string" ? details.output : void 0) ?? (typeof record?.output === "string" ? record.output : void 0);
|
|
550
|
+
const stderr = (typeof details?.stderr === "string" ? details.stderr : void 0) ?? (typeof record?.stderr === "string" ? record.stderr : void 0);
|
|
551
|
+
return [stdout, stderr].filter((part) => typeof part === "string" && part.length > 0).join("\n");
|
|
552
|
+
}
|
|
553
|
+
function bashExitCode(result, isError) {
|
|
554
|
+
const record = result;
|
|
555
|
+
const details = record?.details;
|
|
556
|
+
const exitCode = details?.exitCode ?? record?.exitCode ?? details?.code ?? record?.code;
|
|
557
|
+
return typeof exitCode === "number" ? exitCode : isError ? 1 : 0;
|
|
558
|
+
}
|
|
559
|
+
function bashOutputDelta(previous, next) {
|
|
560
|
+
return next.startsWith(previous) ? next.slice(previous.length) : next;
|
|
561
|
+
}
|
|
562
|
+
function bashTerminalContent(toolCallId) {
|
|
563
|
+
return [{ type: "terminal", terminalId: toolCallId }];
|
|
564
|
+
}
|
|
565
|
+
function bashTerminalInfoMeta(toolCallId, cwd) {
|
|
566
|
+
return { terminal_info: { terminal_id: toolCallId, cwd } };
|
|
567
|
+
}
|
|
568
|
+
function bashTerminalOutputMeta(toolCallId, data) {
|
|
569
|
+
return { terminal_output: { terminal_id: toolCallId, data } };
|
|
570
|
+
}
|
|
571
|
+
function bashTerminalExitMeta(toolCallId, exitCode) {
|
|
572
|
+
return { terminal_exit: { terminal_id: toolCallId, exit_code: exitCode, signal: null } };
|
|
573
|
+
}
|
|
574
|
+
|
|
529
575
|
// src/acp/translate/pi-tools.ts
|
|
530
576
|
function toolResultToText(result) {
|
|
531
577
|
if (!result) return "";
|
|
@@ -741,14 +787,17 @@ var PiAcpSession = class {
|
|
|
741
787
|
// Some pi events can arrive out of order (e.g. late toolcall_* deltas after execution starts),
|
|
742
788
|
// and clients may hide progress if we ever downgrade back to `pending`.
|
|
743
789
|
currentToolCalls = /* @__PURE__ */ new Map();
|
|
744
|
-
// pi can emit multiple `turn_end` events for a single user prompt
|
|
745
|
-
//
|
|
790
|
+
// pi can emit multiple `turn_end` and `agent_end` events for a single user prompt
|
|
791
|
+
// when retry, compaction, or queued continuations run. The session-level prompt
|
|
792
|
+
// completes only when `agent_settled` is emitted.
|
|
746
793
|
inAgentLoop = false;
|
|
747
794
|
// For ACP diff support: capture file contents before edit/write mutations,
|
|
748
795
|
// then emit ToolCallContent {type:"diff"}. Compatible structured edit/write
|
|
749
796
|
// events may need to be implemented in pi in the future.
|
|
750
797
|
fileSnapshots = /* @__PURE__ */ new Map();
|
|
751
798
|
fileMutationToolCallIds = /* @__PURE__ */ new Set();
|
|
799
|
+
bashToolCallIds = /* @__PURE__ */ new Set();
|
|
800
|
+
bashOutputSnapshots = /* @__PURE__ */ new Map();
|
|
752
801
|
// Ensure `session/update` notifications are sent in order and can be awaited
|
|
753
802
|
// before completing a `session/prompt` request.
|
|
754
803
|
lastEmit = Promise.resolve();
|
|
@@ -832,6 +881,41 @@ var PiAcpSession = class {
|
|
|
832
881
|
async flushEmits() {
|
|
833
882
|
await this.lastEmit;
|
|
834
883
|
}
|
|
884
|
+
emitBashToolCall(params) {
|
|
885
|
+
this.bashToolCallIds.add(params.toolCallId);
|
|
886
|
+
this.emit({
|
|
887
|
+
sessionUpdate: params.sessionUpdate,
|
|
888
|
+
toolCallId: params.toolCallId,
|
|
889
|
+
title: bashCommand(params.args) ?? params.toolName,
|
|
890
|
+
kind: "execute",
|
|
891
|
+
status: params.status,
|
|
892
|
+
locations: params.locations,
|
|
893
|
+
...params.includeTerminal ? { content: bashTerminalContent(params.toolCallId) } : {},
|
|
894
|
+
...params.includeTerminal ? { _meta: bashTerminalInfoMeta(params.toolCallId, this.cwd) } : {}
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
emitBashOutputUpdate(params) {
|
|
898
|
+
const text = bashResultText(params.result);
|
|
899
|
+
const previous = this.bashOutputSnapshots.get(params.toolCallId) ?? "";
|
|
900
|
+
const delta = bashOutputDelta(previous, text);
|
|
901
|
+
this.bashOutputSnapshots.set(params.toolCallId, text);
|
|
902
|
+
this.emit({
|
|
903
|
+
sessionUpdate: "tool_call_update",
|
|
904
|
+
toolCallId: params.toolCallId,
|
|
905
|
+
status: params.status,
|
|
906
|
+
_meta: {
|
|
907
|
+
...delta ? bashTerminalOutputMeta(params.toolCallId, delta) : {},
|
|
908
|
+
...params.status === "completed" || params.status === "failed" ? bashTerminalExitMeta(params.toolCallId, bashExitCode(params.result, Boolean(params.isError))) : {}
|
|
909
|
+
}
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
cleanupToolCall(toolCallId) {
|
|
913
|
+
this.currentToolCalls.delete(toolCallId);
|
|
914
|
+
this.fileSnapshots.delete(toolCallId);
|
|
915
|
+
this.fileMutationToolCallIds.delete(toolCallId);
|
|
916
|
+
this.bashToolCallIds.delete(toolCallId);
|
|
917
|
+
this.bashOutputSnapshots.delete(toolCallId);
|
|
918
|
+
}
|
|
835
919
|
startTurn(t) {
|
|
836
920
|
this.cancelRequested = false;
|
|
837
921
|
this.inAgentLoop = false;
|
|
@@ -899,7 +983,18 @@ var PiAcpSession = class {
|
|
|
899
983
|
const locations = toToolCallLocations(rawInput, this.cwd);
|
|
900
984
|
const existingStatus = this.currentToolCalls.get(toolCallId);
|
|
901
985
|
const status = existingStatus ?? "pending";
|
|
902
|
-
if (
|
|
986
|
+
if (isBashTool(toolName)) {
|
|
987
|
+
if (!existingStatus) this.currentToolCalls.set(toolCallId, "pending");
|
|
988
|
+
this.emitBashToolCall({
|
|
989
|
+
sessionUpdate: existingStatus ? "tool_call_update" : "tool_call",
|
|
990
|
+
toolCallId,
|
|
991
|
+
toolName,
|
|
992
|
+
args: rawInput,
|
|
993
|
+
status,
|
|
994
|
+
locations,
|
|
995
|
+
includeTerminal: !existingStatus
|
|
996
|
+
});
|
|
997
|
+
} else if (!existingStatus) {
|
|
903
998
|
this.currentToolCalls.set(toolCallId, "pending");
|
|
904
999
|
this.emit({
|
|
905
1000
|
sessionUpdate: "tool_call",
|
|
@@ -929,6 +1024,21 @@ var PiAcpSession = class {
|
|
|
929
1024
|
const toolName = String(ev.toolName ?? "tool");
|
|
930
1025
|
const args = ev.args;
|
|
931
1026
|
let line;
|
|
1027
|
+
if (isBashTool(toolName)) {
|
|
1028
|
+
const locations2 = toToolCallLocations(args, this.cwd);
|
|
1029
|
+
const existingStatus = this.currentToolCalls.get(toolCallId);
|
|
1030
|
+
this.currentToolCalls.set(toolCallId, "in_progress");
|
|
1031
|
+
this.emitBashToolCall({
|
|
1032
|
+
sessionUpdate: existingStatus ? "tool_call_update" : "tool_call",
|
|
1033
|
+
toolCallId,
|
|
1034
|
+
toolName,
|
|
1035
|
+
args,
|
|
1036
|
+
status: "in_progress",
|
|
1037
|
+
locations: locations2,
|
|
1038
|
+
includeTerminal: !existingStatus
|
|
1039
|
+
});
|
|
1040
|
+
break;
|
|
1041
|
+
}
|
|
932
1042
|
const isFileMutation = toolName === "edit" || toolName === "write";
|
|
933
1043
|
let snapshotOldText;
|
|
934
1044
|
if (isFileMutation) {
|
|
@@ -979,6 +1089,10 @@ var PiAcpSession = class {
|
|
|
979
1089
|
const toolCallId = String(ev.toolCallId ?? "");
|
|
980
1090
|
if (!toolCallId) break;
|
|
981
1091
|
const partial = ev.partialResult;
|
|
1092
|
+
if (this.bashToolCallIds.has(toolCallId)) {
|
|
1093
|
+
this.emitBashOutputUpdate({ toolCallId, status: "in_progress", result: partial });
|
|
1094
|
+
break;
|
|
1095
|
+
}
|
|
982
1096
|
const text = this.fileMutationToolCallIds.has(toolCallId) ? "" : toolResultToText(partial);
|
|
983
1097
|
this.emit({
|
|
984
1098
|
sessionUpdate: "tool_call_update",
|
|
@@ -994,6 +1108,16 @@ var PiAcpSession = class {
|
|
|
994
1108
|
if (!toolCallId) break;
|
|
995
1109
|
const result = ev.result;
|
|
996
1110
|
const isError = Boolean(ev.isError);
|
|
1111
|
+
if (this.bashToolCallIds.has(toolCallId)) {
|
|
1112
|
+
this.emitBashOutputUpdate({
|
|
1113
|
+
toolCallId,
|
|
1114
|
+
status: isError ? "failed" : "completed",
|
|
1115
|
+
result,
|
|
1116
|
+
isError
|
|
1117
|
+
});
|
|
1118
|
+
this.cleanupToolCall(toolCallId);
|
|
1119
|
+
break;
|
|
1120
|
+
}
|
|
997
1121
|
const text = toolResultToText(result);
|
|
998
1122
|
const snapshot = this.fileSnapshots.get(toolCallId);
|
|
999
1123
|
let content;
|
|
@@ -1026,9 +1150,7 @@ var PiAcpSession = class {
|
|
|
1026
1150
|
content,
|
|
1027
1151
|
...hasStructuredDiff ? {} : { rawOutput: result }
|
|
1028
1152
|
});
|
|
1029
|
-
this.
|
|
1030
|
-
this.fileSnapshots.delete(toolCallId);
|
|
1031
|
-
this.fileMutationToolCallIds.delete(toolCallId);
|
|
1153
|
+
this.cleanupToolCall(toolCallId);
|
|
1032
1154
|
break;
|
|
1033
1155
|
}
|
|
1034
1156
|
case "extension_ui_request": {
|
|
@@ -1084,6 +1206,10 @@ var PiAcpSession = class {
|
|
|
1084
1206
|
break;
|
|
1085
1207
|
}
|
|
1086
1208
|
case "agent_end": {
|
|
1209
|
+
this.inAgentLoop = false;
|
|
1210
|
+
break;
|
|
1211
|
+
}
|
|
1212
|
+
case "agent_settled": {
|
|
1087
1213
|
void this.flushEmits().finally(() => {
|
|
1088
1214
|
const reason = this.cancelRequested ? "cancelled" : "end_turn";
|
|
1089
1215
|
this.pendingTurn?.resolve(reason);
|
|
@@ -1137,7 +1263,8 @@ var PiAcpSession = class {
|
|
|
1137
1263
|
if (method === "notify") {
|
|
1138
1264
|
this.emit({
|
|
1139
1265
|
sessionUpdate: "agent_message_chunk",
|
|
1140
|
-
content: { type: "text", text: stringProp(ev, "message") ?? "Pi notification" }
|
|
1266
|
+
content: { type: "text", text: stringProp(ev, "message") ?? "Pi notification" },
|
|
1267
|
+
_meta: { piAcp: { notify: { level: stringProp(ev, "notifyType") ?? "info" } } }
|
|
1141
1268
|
});
|
|
1142
1269
|
await this.proc.sendExtensionUiResponse({ id, cancelled: true });
|
|
1143
1270
|
return;
|
|
@@ -1238,7 +1365,7 @@ function toToolKind(toolName) {
|
|
|
1238
1365
|
case "edit":
|
|
1239
1366
|
return "edit";
|
|
1240
1367
|
case "bash":
|
|
1241
|
-
return "
|
|
1368
|
+
return "execute";
|
|
1242
1369
|
default:
|
|
1243
1370
|
return "other";
|
|
1244
1371
|
}
|
|
@@ -1815,7 +1942,8 @@ var PiAcpAgent = class {
|
|
|
1815
1942
|
sessionCapabilities: {
|
|
1816
1943
|
// **UNSTABLE** ACP capability used by Zed's codex-acp adapter.
|
|
1817
1944
|
// Enables a native session picker in clients that support it.
|
|
1818
|
-
list: {}
|
|
1945
|
+
list: {},
|
|
1946
|
+
delete: {}
|
|
1819
1947
|
}
|
|
1820
1948
|
}
|
|
1821
1949
|
};
|
|
@@ -2384,6 +2512,35 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2384
2512
|
const toolName = String(m?.toolName ?? "tool");
|
|
2385
2513
|
const toolCallId = String(m?.toolCallId ?? crypto.randomUUID());
|
|
2386
2514
|
const isError = Boolean(m?.isError);
|
|
2515
|
+
const isBash = isBashTool(toolName);
|
|
2516
|
+
if (isBash) {
|
|
2517
|
+
const text2 = bashResultText(m);
|
|
2518
|
+
await this.conn.sessionUpdate({
|
|
2519
|
+
sessionId: session.sessionId,
|
|
2520
|
+
update: {
|
|
2521
|
+
sessionUpdate: "tool_call",
|
|
2522
|
+
toolCallId,
|
|
2523
|
+
title: bashCommand(m) ?? toolName,
|
|
2524
|
+
kind: "execute",
|
|
2525
|
+
status: "completed",
|
|
2526
|
+
content: bashTerminalContent(toolCallId),
|
|
2527
|
+
_meta: bashTerminalInfoMeta(toolCallId, params.cwd)
|
|
2528
|
+
}
|
|
2529
|
+
});
|
|
2530
|
+
await this.conn.sessionUpdate({
|
|
2531
|
+
sessionId: session.sessionId,
|
|
2532
|
+
update: {
|
|
2533
|
+
sessionUpdate: "tool_call_update",
|
|
2534
|
+
toolCallId,
|
|
2535
|
+
status: isError ? "failed" : "completed",
|
|
2536
|
+
_meta: {
|
|
2537
|
+
...text2 ? bashTerminalOutputMeta(toolCallId, text2) : {},
|
|
2538
|
+
...bashTerminalExitMeta(toolCallId, bashExitCode(m, isError))
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
});
|
|
2542
|
+
continue;
|
|
2543
|
+
}
|
|
2387
2544
|
await this.conn.sessionUpdate({
|
|
2388
2545
|
sessionId: session.sessionId,
|
|
2389
2546
|
update: {
|
|
@@ -2449,6 +2606,22 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2449
2606
|
}, 0);
|
|
2450
2607
|
return response;
|
|
2451
2608
|
}
|
|
2609
|
+
async deleteSession(params) {
|
|
2610
|
+
const stored = this.store.get(params.sessionId);
|
|
2611
|
+
const piSession = findPiSession(params.sessionId);
|
|
2612
|
+
if (!stored && !piSession) {
|
|
2613
|
+
return {};
|
|
2614
|
+
}
|
|
2615
|
+
const sessionFile = stored?.sessionFile ?? piSession?.sessionFile;
|
|
2616
|
+
if (sessionFile) {
|
|
2617
|
+
try {
|
|
2618
|
+
if (existsSync4(sessionFile)) unlinkSync(sessionFile);
|
|
2619
|
+
} catch {
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
this.store.delete(params.sessionId);
|
|
2623
|
+
return {};
|
|
2624
|
+
}
|
|
2452
2625
|
async unstable_setSessionModel(params) {
|
|
2453
2626
|
const session = await this.restoreSession(params.sessionId);
|
|
2454
2627
|
await setSessionModel(session.proc, params.modelId);
|
|
@@ -2764,20 +2937,22 @@ function buildStartupInfo(opts) {
|
|
|
2764
2937
|
for (const f of exts) extItems.push(join5(extDir, f));
|
|
2765
2938
|
} catch {
|
|
2766
2939
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
const
|
|
2773
|
-
|
|
2774
|
-
|
|
2940
|
+
const settingsPaths = [join5(getAgentDir(), "settings.json"), join5(opts.cwd, ".pi", "settings.json")];
|
|
2941
|
+
for (const settingsPath of settingsPaths) {
|
|
2942
|
+
try {
|
|
2943
|
+
const settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
2944
|
+
const pkgs = Array.isArray(settings?.packages) ? settings.packages : [];
|
|
2945
|
+
for (const pkg2 of pkgs) {
|
|
2946
|
+
const s = String(pkg2);
|
|
2947
|
+
if (s.startsWith("npm:")) {
|
|
2948
|
+
extItems.push(`${s}
|
|
2775
2949
|
- index.ts`);
|
|
2776
|
-
|
|
2777
|
-
|
|
2950
|
+
} else {
|
|
2951
|
+
extItems.push(s);
|
|
2952
|
+
}
|
|
2778
2953
|
}
|
|
2954
|
+
} catch {
|
|
2779
2955
|
}
|
|
2780
|
-
} catch {
|
|
2781
2956
|
}
|
|
2782
2957
|
addSection("Extensions", extItems);
|
|
2783
2958
|
if (opts.updateNotice) {
|