pi-acp 0.0.30 → 0.0.32

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.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 "";
@@ -749,6 +795,8 @@ var PiAcpSession = class {
749
795
  // events may need to be implemented in pi in the future.
750
796
  fileSnapshots = /* @__PURE__ */ new Map();
751
797
  fileMutationToolCallIds = /* @__PURE__ */ new Set();
798
+ bashToolCallIds = /* @__PURE__ */ new Set();
799
+ bashOutputSnapshots = /* @__PURE__ */ new Map();
752
800
  // Ensure `session/update` notifications are sent in order and can be awaited
753
801
  // before completing a `session/prompt` request.
754
802
  lastEmit = Promise.resolve();
@@ -832,6 +880,41 @@ var PiAcpSession = class {
832
880
  async flushEmits() {
833
881
  await this.lastEmit;
834
882
  }
883
+ emitBashToolCall(params) {
884
+ this.bashToolCallIds.add(params.toolCallId);
885
+ this.emit({
886
+ sessionUpdate: params.sessionUpdate,
887
+ toolCallId: params.toolCallId,
888
+ title: bashCommand(params.args) ?? params.toolName,
889
+ kind: "execute",
890
+ status: params.status,
891
+ locations: params.locations,
892
+ ...params.includeTerminal ? { content: bashTerminalContent(params.toolCallId) } : {},
893
+ ...params.includeTerminal ? { _meta: bashTerminalInfoMeta(params.toolCallId, this.cwd) } : {}
894
+ });
895
+ }
896
+ emitBashOutputUpdate(params) {
897
+ const text = bashResultText(params.result);
898
+ const previous = this.bashOutputSnapshots.get(params.toolCallId) ?? "";
899
+ const delta = bashOutputDelta(previous, text);
900
+ this.bashOutputSnapshots.set(params.toolCallId, text);
901
+ this.emit({
902
+ sessionUpdate: "tool_call_update",
903
+ toolCallId: params.toolCallId,
904
+ status: params.status,
905
+ _meta: {
906
+ ...delta ? bashTerminalOutputMeta(params.toolCallId, delta) : {},
907
+ ...params.status === "completed" || params.status === "failed" ? bashTerminalExitMeta(params.toolCallId, bashExitCode(params.result, Boolean(params.isError))) : {}
908
+ }
909
+ });
910
+ }
911
+ cleanupToolCall(toolCallId) {
912
+ this.currentToolCalls.delete(toolCallId);
913
+ this.fileSnapshots.delete(toolCallId);
914
+ this.fileMutationToolCallIds.delete(toolCallId);
915
+ this.bashToolCallIds.delete(toolCallId);
916
+ this.bashOutputSnapshots.delete(toolCallId);
917
+ }
835
918
  startTurn(t) {
836
919
  this.cancelRequested = false;
837
920
  this.inAgentLoop = false;
@@ -899,7 +982,18 @@ var PiAcpSession = class {
899
982
  const locations = toToolCallLocations(rawInput, this.cwd);
900
983
  const existingStatus = this.currentToolCalls.get(toolCallId);
901
984
  const status = existingStatus ?? "pending";
902
- if (!existingStatus) {
985
+ if (isBashTool(toolName)) {
986
+ if (!existingStatus) this.currentToolCalls.set(toolCallId, "pending");
987
+ this.emitBashToolCall({
988
+ sessionUpdate: existingStatus ? "tool_call_update" : "tool_call",
989
+ toolCallId,
990
+ toolName,
991
+ args: rawInput,
992
+ status,
993
+ locations,
994
+ includeTerminal: !existingStatus
995
+ });
996
+ } else if (!existingStatus) {
903
997
  this.currentToolCalls.set(toolCallId, "pending");
904
998
  this.emit({
905
999
  sessionUpdate: "tool_call",
@@ -929,6 +1023,21 @@ var PiAcpSession = class {
929
1023
  const toolName = String(ev.toolName ?? "tool");
930
1024
  const args = ev.args;
931
1025
  let line;
1026
+ if (isBashTool(toolName)) {
1027
+ const locations2 = toToolCallLocations(args, this.cwd);
1028
+ const existingStatus = this.currentToolCalls.get(toolCallId);
1029
+ this.currentToolCalls.set(toolCallId, "in_progress");
1030
+ this.emitBashToolCall({
1031
+ sessionUpdate: existingStatus ? "tool_call_update" : "tool_call",
1032
+ toolCallId,
1033
+ toolName,
1034
+ args,
1035
+ status: "in_progress",
1036
+ locations: locations2,
1037
+ includeTerminal: !existingStatus
1038
+ });
1039
+ break;
1040
+ }
932
1041
  const isFileMutation = toolName === "edit" || toolName === "write";
933
1042
  let snapshotOldText;
934
1043
  if (isFileMutation) {
@@ -979,6 +1088,10 @@ var PiAcpSession = class {
979
1088
  const toolCallId = String(ev.toolCallId ?? "");
980
1089
  if (!toolCallId) break;
981
1090
  const partial = ev.partialResult;
1091
+ if (this.bashToolCallIds.has(toolCallId)) {
1092
+ this.emitBashOutputUpdate({ toolCallId, status: "in_progress", result: partial });
1093
+ break;
1094
+ }
982
1095
  const text = this.fileMutationToolCallIds.has(toolCallId) ? "" : toolResultToText(partial);
983
1096
  this.emit({
984
1097
  sessionUpdate: "tool_call_update",
@@ -994,6 +1107,16 @@ var PiAcpSession = class {
994
1107
  if (!toolCallId) break;
995
1108
  const result = ev.result;
996
1109
  const isError = Boolean(ev.isError);
1110
+ if (this.bashToolCallIds.has(toolCallId)) {
1111
+ this.emitBashOutputUpdate({
1112
+ toolCallId,
1113
+ status: isError ? "failed" : "completed",
1114
+ result,
1115
+ isError
1116
+ });
1117
+ this.cleanupToolCall(toolCallId);
1118
+ break;
1119
+ }
997
1120
  const text = toolResultToText(result);
998
1121
  const snapshot = this.fileSnapshots.get(toolCallId);
999
1122
  let content;
@@ -1026,9 +1149,7 @@ var PiAcpSession = class {
1026
1149
  content,
1027
1150
  ...hasStructuredDiff ? {} : { rawOutput: result }
1028
1151
  });
1029
- this.currentToolCalls.delete(toolCallId);
1030
- this.fileSnapshots.delete(toolCallId);
1031
- this.fileMutationToolCallIds.delete(toolCallId);
1152
+ this.cleanupToolCall(toolCallId);
1032
1153
  break;
1033
1154
  }
1034
1155
  case "extension_ui_request": {
@@ -1137,7 +1258,8 @@ var PiAcpSession = class {
1137
1258
  if (method === "notify") {
1138
1259
  this.emit({
1139
1260
  sessionUpdate: "agent_message_chunk",
1140
- content: { type: "text", text: stringProp(ev, "message") ?? "Pi notification" }
1261
+ content: { type: "text", text: stringProp(ev, "message") ?? "Pi notification" },
1262
+ _meta: { piAcp: { notify: { level: stringProp(ev, "notifyType") ?? "info" } } }
1141
1263
  });
1142
1264
  await this.proc.sendExtensionUiResponse({ id, cancelled: true });
1143
1265
  return;
@@ -1238,7 +1360,7 @@ function toToolKind(toolName) {
1238
1360
  case "edit":
1239
1361
  return "edit";
1240
1362
  case "bash":
1241
- return "other";
1363
+ return "execute";
1242
1364
  default:
1243
1365
  return "other";
1244
1366
  }
@@ -1815,7 +1937,8 @@ var PiAcpAgent = class {
1815
1937
  sessionCapabilities: {
1816
1938
  // **UNSTABLE** ACP capability used by Zed's codex-acp adapter.
1817
1939
  // Enables a native session picker in clients that support it.
1818
- list: {}
1940
+ list: {},
1941
+ delete: {}
1819
1942
  }
1820
1943
  }
1821
1944
  };
@@ -2312,7 +2435,7 @@ ${JSON.stringify(stats, null, 2)}`;
2312
2435
  if (!session) return;
2313
2436
  await session.cancel();
2314
2437
  }
2315
- async unstable_listSessions(params) {
2438
+ async listSessions(params) {
2316
2439
  const all = listPiSessions();
2317
2440
  const effectiveCwd = params.cwd ?? this.lastSessionCwd;
2318
2441
  const filtered = effectiveCwd ? all.filter((s) => s.cwd === effectiveCwd) : all;
@@ -2384,6 +2507,35 @@ ${JSON.stringify(stats, null, 2)}`;
2384
2507
  const toolName = String(m?.toolName ?? "tool");
2385
2508
  const toolCallId = String(m?.toolCallId ?? crypto.randomUUID());
2386
2509
  const isError = Boolean(m?.isError);
2510
+ const isBash = isBashTool(toolName);
2511
+ if (isBash) {
2512
+ const text2 = bashResultText(m);
2513
+ await this.conn.sessionUpdate({
2514
+ sessionId: session.sessionId,
2515
+ update: {
2516
+ sessionUpdate: "tool_call",
2517
+ toolCallId,
2518
+ title: bashCommand(m) ?? toolName,
2519
+ kind: "execute",
2520
+ status: "completed",
2521
+ content: bashTerminalContent(toolCallId),
2522
+ _meta: bashTerminalInfoMeta(toolCallId, params.cwd)
2523
+ }
2524
+ });
2525
+ await this.conn.sessionUpdate({
2526
+ sessionId: session.sessionId,
2527
+ update: {
2528
+ sessionUpdate: "tool_call_update",
2529
+ toolCallId,
2530
+ status: isError ? "failed" : "completed",
2531
+ _meta: {
2532
+ ...text2 ? bashTerminalOutputMeta(toolCallId, text2) : {},
2533
+ ...bashTerminalExitMeta(toolCallId, bashExitCode(m, isError))
2534
+ }
2535
+ }
2536
+ });
2537
+ continue;
2538
+ }
2387
2539
  await this.conn.sessionUpdate({
2388
2540
  sessionId: session.sessionId,
2389
2541
  update: {
@@ -2449,6 +2601,22 @@ ${JSON.stringify(stats, null, 2)}`;
2449
2601
  }, 0);
2450
2602
  return response;
2451
2603
  }
2604
+ async deleteSession(params) {
2605
+ const stored = this.store.get(params.sessionId);
2606
+ const piSession = findPiSession(params.sessionId);
2607
+ if (!stored && !piSession) {
2608
+ return {};
2609
+ }
2610
+ const sessionFile = stored?.sessionFile ?? piSession?.sessionFile;
2611
+ if (sessionFile) {
2612
+ try {
2613
+ if (existsSync4(sessionFile)) unlinkSync(sessionFile);
2614
+ } catch {
2615
+ }
2616
+ }
2617
+ this.store.delete(params.sessionId);
2618
+ return {};
2619
+ }
2452
2620
  async unstable_setSessionModel(params) {
2453
2621
  const session = await this.restoreSession(params.sessionId);
2454
2622
  await setSessionModel(session.proc, params.modelId);
@@ -2764,20 +2932,22 @@ function buildStartupInfo(opts) {
2764
2932
  for (const f of exts) extItems.push(join5(extDir, f));
2765
2933
  } catch {
2766
2934
  }
2767
- try {
2768
- const settingsPath = join5(process.env.HOME ?? "", ".pi", "agent", "settings.json");
2769
- const settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
2770
- const pkgs = Array.isArray(settings?.packages) ? settings.packages : [];
2771
- for (const pkg2 of pkgs) {
2772
- const s = String(pkg2);
2773
- if (s.startsWith("npm:")) {
2774
- extItems.push(`${s}
2935
+ const settingsPaths = [join5(getAgentDir(), "settings.json"), join5(opts.cwd, ".pi", "settings.json")];
2936
+ for (const settingsPath of settingsPaths) {
2937
+ try {
2938
+ const settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
2939
+ const pkgs = Array.isArray(settings?.packages) ? settings.packages : [];
2940
+ for (const pkg2 of pkgs) {
2941
+ const s = String(pkg2);
2942
+ if (s.startsWith("npm:")) {
2943
+ extItems.push(`${s}
2775
2944
  - index.ts`);
2776
- } else {
2777
- extItems.push(s);
2945
+ } else {
2946
+ extItems.push(s);
2947
+ }
2778
2948
  }
2949
+ } catch {
2779
2950
  }
2780
- } catch {
2781
2951
  }
2782
2952
  addSection("Extensions", extItems);
2783
2953
  if (opts.updateNotice) {