pi-acp 0.0.29 → 0.0.31
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 +164 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -529,16 +529,16 @@ function expandSlashCommand(text, fileCommands) {
|
|
|
529
529
|
// src/acp/translate/pi-tools.ts
|
|
530
530
|
function toolResultToText(result) {
|
|
531
531
|
if (!result) return "";
|
|
532
|
-
const content = result.content;
|
|
533
|
-
if (Array.isArray(content)) {
|
|
534
|
-
const texts = content.map((c) => c?.type === "text" && typeof c.text === "string" ? c.text : "").filter(Boolean);
|
|
535
|
-
if (texts.length) return texts.join("");
|
|
536
|
-
}
|
|
537
532
|
const details = result?.details;
|
|
538
533
|
const diff = details?.diff;
|
|
539
534
|
if (typeof diff === "string" && diff.trim()) {
|
|
540
535
|
return diff;
|
|
541
536
|
}
|
|
537
|
+
const content = result.content;
|
|
538
|
+
if (Array.isArray(content)) {
|
|
539
|
+
const texts = content.map((c) => c?.type === "text" && typeof c.text === "string" ? c.text : "").filter(Boolean);
|
|
540
|
+
if (texts.length) return texts.join("");
|
|
541
|
+
}
|
|
542
542
|
const stdout = (typeof details?.stdout === "string" ? details.stdout : void 0) ?? (typeof result?.stdout === "string" ? result.stdout : void 0) ?? (typeof details?.output === "string" ? details.output : void 0) ?? (typeof result?.output === "string" ? result.output : void 0);
|
|
543
543
|
const stderr = (typeof details?.stderr === "string" ? details.stderr : void 0) ?? (typeof result?.stderr === "string" ? result.stderr : void 0);
|
|
544
544
|
const exitCode = (typeof details?.exitCode === "number" ? details.exitCode : void 0) ?? (typeof result?.exitCode === "number" ? result.exitCode : void 0) ?? (typeof details?.code === "number" ? details.code : void 0) ?? (typeof result?.code === "number" ? result.code : void 0);
|
|
@@ -576,8 +576,58 @@ function findUniqueLineNumber(text, needle) {
|
|
|
576
576
|
}
|
|
577
577
|
return line;
|
|
578
578
|
}
|
|
579
|
+
function getToolPath(args) {
|
|
580
|
+
const record = args;
|
|
581
|
+
if (typeof record?.path === "string") return record.path;
|
|
582
|
+
if (typeof record?.file_path === "string") return record.file_path;
|
|
583
|
+
return void 0;
|
|
584
|
+
}
|
|
585
|
+
function getParsedEdits(args) {
|
|
586
|
+
const record = args;
|
|
587
|
+
const parsed = [];
|
|
588
|
+
if (typeof record?.oldText === "string" && typeof record?.newText === "string") {
|
|
589
|
+
parsed.push({ oldText: record.oldText, newText: record.newText });
|
|
590
|
+
}
|
|
591
|
+
let edits = record?.edits;
|
|
592
|
+
if (typeof edits === "string") {
|
|
593
|
+
try {
|
|
594
|
+
edits = JSON.parse(edits);
|
|
595
|
+
} catch {
|
|
596
|
+
edits = void 0;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (Array.isArray(edits)) {
|
|
600
|
+
for (const edit of edits) {
|
|
601
|
+
const item = edit;
|
|
602
|
+
if (typeof item?.oldText === "string" && typeof item?.newText === "string") {
|
|
603
|
+
parsed.push({ oldText: item.oldText, newText: item.newText });
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return parsed;
|
|
608
|
+
}
|
|
609
|
+
function getEditOldTexts(args) {
|
|
610
|
+
const record = args;
|
|
611
|
+
const oldTexts = getParsedEdits(args).map((edit) => edit.oldText);
|
|
612
|
+
if (typeof record?.oldText === "string" && !oldTexts.includes(record.oldText)) oldTexts.push(record.oldText);
|
|
613
|
+
let edits = record?.edits;
|
|
614
|
+
if (typeof edits === "string") {
|
|
615
|
+
try {
|
|
616
|
+
edits = JSON.parse(edits);
|
|
617
|
+
} catch {
|
|
618
|
+
edits = void 0;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (Array.isArray(edits)) {
|
|
622
|
+
for (const edit of edits) {
|
|
623
|
+
const oldText = edit?.oldText;
|
|
624
|
+
if (typeof oldText === "string" && !oldTexts.includes(oldText)) oldTexts.push(oldText);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return oldTexts;
|
|
628
|
+
}
|
|
579
629
|
function toToolCallLocations(args, cwd, line) {
|
|
580
|
-
const path =
|
|
630
|
+
const path = getToolPath(args);
|
|
581
631
|
if (!path) return void 0;
|
|
582
632
|
const resolvedPath = isAbsolute(path) ? path : resolvePath(cwd, path);
|
|
583
633
|
return [{ path: resolvedPath, ...typeof line === "number" ? { line } : {} }];
|
|
@@ -694,10 +744,11 @@ var PiAcpSession = class {
|
|
|
694
744
|
// pi can emit multiple `turn_end` events for a single user prompt (e.g. after tool_use).
|
|
695
745
|
// The overall agent loop completes when `agent_end` is emitted.
|
|
696
746
|
inAgentLoop = false;
|
|
697
|
-
// For ACP diff support: capture file contents before
|
|
698
|
-
//
|
|
699
|
-
//
|
|
700
|
-
|
|
747
|
+
// For ACP diff support: capture file contents before edit/write mutations,
|
|
748
|
+
// then emit ToolCallContent {type:"diff"}. Compatible structured edit/write
|
|
749
|
+
// events may need to be implemented in pi in the future.
|
|
750
|
+
fileSnapshots = /* @__PURE__ */ new Map();
|
|
751
|
+
fileMutationToolCallIds = /* @__PURE__ */ new Set();
|
|
701
752
|
// Ensure `session/update` notifications are sent in order and can be awaited
|
|
702
753
|
// before completing a `session/prompt` request.
|
|
703
754
|
lastEmit = Promise.resolve();
|
|
@@ -878,16 +929,25 @@ var PiAcpSession = class {
|
|
|
878
929
|
const toolName = String(ev.toolName ?? "tool");
|
|
879
930
|
const args = ev.args;
|
|
880
931
|
let line;
|
|
881
|
-
|
|
882
|
-
|
|
932
|
+
const isFileMutation = toolName === "edit" || toolName === "write";
|
|
933
|
+
let snapshotOldText;
|
|
934
|
+
if (isFileMutation) {
|
|
935
|
+
this.fileMutationToolCallIds.add(toolCallId);
|
|
936
|
+
const p = getToolPath(args);
|
|
883
937
|
if (p) {
|
|
884
938
|
try {
|
|
885
939
|
const abs = isAbsolute(p) ? p : resolvePath(this.cwd, p);
|
|
886
|
-
|
|
887
|
-
this.
|
|
888
|
-
|
|
889
|
-
|
|
940
|
+
snapshotOldText = readFileSync3(abs, "utf8");
|
|
941
|
+
this.fileSnapshots.set(toolCallId, { path: p, oldText: snapshotOldText });
|
|
942
|
+
if (toolName === "edit") {
|
|
943
|
+
for (const needle of getEditOldTexts(args)) {
|
|
944
|
+
line = findUniqueLineNumber(snapshotOldText, needle);
|
|
945
|
+
if (typeof line === "number") break;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
890
948
|
} catch {
|
|
949
|
+
snapshotOldText = null;
|
|
950
|
+
this.fileSnapshots.set(toolCallId, { path: p, oldText: null });
|
|
891
951
|
}
|
|
892
952
|
}
|
|
893
953
|
}
|
|
@@ -919,13 +979,13 @@ var PiAcpSession = class {
|
|
|
919
979
|
const toolCallId = String(ev.toolCallId ?? "");
|
|
920
980
|
if (!toolCallId) break;
|
|
921
981
|
const partial = ev.partialResult;
|
|
922
|
-
const text = toolResultToText(partial);
|
|
982
|
+
const text = this.fileMutationToolCallIds.has(toolCallId) ? "" : toolResultToText(partial);
|
|
923
983
|
this.emit({
|
|
924
984
|
sessionUpdate: "tool_call_update",
|
|
925
985
|
toolCallId,
|
|
926
986
|
status: "in_progress",
|
|
927
987
|
content: text ? [{ type: "content", content: { type: "text", text } }] : void 0,
|
|
928
|
-
rawOutput: partial
|
|
988
|
+
...this.fileMutationToolCallIds.has(toolCallId) ? {} : { rawOutput: partial }
|
|
929
989
|
});
|
|
930
990
|
break;
|
|
931
991
|
}
|
|
@@ -935,27 +995,28 @@ var PiAcpSession = class {
|
|
|
935
995
|
const result = ev.result;
|
|
936
996
|
const isError = Boolean(ev.isError);
|
|
937
997
|
const text = toolResultToText(result);
|
|
938
|
-
const snapshot = this.
|
|
998
|
+
const snapshot = this.fileSnapshots.get(toolCallId);
|
|
939
999
|
let content;
|
|
1000
|
+
let hasStructuredDiff = false;
|
|
940
1001
|
if (!isError && snapshot) {
|
|
941
1002
|
try {
|
|
942
1003
|
const abs = isAbsolute(snapshot.path) ? snapshot.path : resolvePath(this.cwd, snapshot.path);
|
|
943
1004
|
const newText = readFileSync3(abs, "utf8");
|
|
944
|
-
if (newText !== snapshot.oldText) {
|
|
1005
|
+
if (snapshot.oldText === null || newText !== snapshot.oldText) {
|
|
1006
|
+
hasStructuredDiff = true;
|
|
945
1007
|
content = [
|
|
946
1008
|
{
|
|
947
1009
|
type: "diff",
|
|
948
1010
|
path: snapshot.path,
|
|
949
1011
|
oldText: snapshot.oldText,
|
|
950
1012
|
newText
|
|
951
|
-
}
|
|
952
|
-
...text ? [{ type: "content", content: { type: "text", text } }] : []
|
|
1013
|
+
}
|
|
953
1014
|
];
|
|
954
1015
|
}
|
|
955
1016
|
} catch {
|
|
956
1017
|
}
|
|
957
1018
|
}
|
|
958
|
-
if (!content && text) {
|
|
1019
|
+
if (!content && !hasStructuredDiff && text) {
|
|
959
1020
|
content = [{ type: "content", content: { type: "text", text } }];
|
|
960
1021
|
}
|
|
961
1022
|
this.emit({
|
|
@@ -963,10 +1024,11 @@ var PiAcpSession = class {
|
|
|
963
1024
|
toolCallId,
|
|
964
1025
|
status: isError ? "failed" : "completed",
|
|
965
1026
|
content,
|
|
966
|
-
rawOutput: result
|
|
1027
|
+
...hasStructuredDiff ? {} : { rawOutput: result }
|
|
967
1028
|
});
|
|
968
1029
|
this.currentToolCalls.delete(toolCallId);
|
|
969
|
-
this.
|
|
1030
|
+
this.fileSnapshots.delete(toolCallId);
|
|
1031
|
+
this.fileMutationToolCallIds.delete(toolCallId);
|
|
970
1032
|
break;
|
|
971
1033
|
}
|
|
972
1034
|
case "extension_ui_request": {
|
|
@@ -1430,10 +1492,9 @@ function listPiSessions() {
|
|
|
1430
1492
|
});
|
|
1431
1493
|
return items;
|
|
1432
1494
|
}
|
|
1433
|
-
function
|
|
1495
|
+
function findPiSession(sessionId) {
|
|
1434
1496
|
const all = listPiSessions();
|
|
1435
|
-
|
|
1436
|
-
return found?.sessionFile ?? null;
|
|
1497
|
+
return all.find((s) => s.sessionId === sessionId) ?? null;
|
|
1437
1498
|
}
|
|
1438
1499
|
|
|
1439
1500
|
// src/acp/translate/pi-messages.ts
|
|
@@ -1647,6 +1708,7 @@ var PiAcpAgent = class {
|
|
|
1647
1708
|
conn;
|
|
1648
1709
|
sessions = new SessionManager();
|
|
1649
1710
|
store = new SessionStore();
|
|
1711
|
+
restoringSessions = /* @__PURE__ */ new Map();
|
|
1650
1712
|
dispose() {
|
|
1651
1713
|
this.sessions.disposeAll();
|
|
1652
1714
|
}
|
|
@@ -1667,6 +1729,66 @@ var PiAcpAgent = class {
|
|
|
1667
1729
|
}
|
|
1668
1730
|
this.store.delete(sessionId);
|
|
1669
1731
|
}
|
|
1732
|
+
findStoredSession(sessionId) {
|
|
1733
|
+
const stored = this.store.get(sessionId);
|
|
1734
|
+
if (stored?.cwd && stored?.sessionFile) {
|
|
1735
|
+
return { cwd: stored.cwd, sessionFile: stored.sessionFile };
|
|
1736
|
+
}
|
|
1737
|
+
const piSession = findPiSession(sessionId);
|
|
1738
|
+
if (!piSession) return null;
|
|
1739
|
+
this.store.upsert({
|
|
1740
|
+
sessionId,
|
|
1741
|
+
cwd: piSession.cwd,
|
|
1742
|
+
sessionFile: piSession.sessionFile
|
|
1743
|
+
});
|
|
1744
|
+
return {
|
|
1745
|
+
cwd: piSession.cwd,
|
|
1746
|
+
sessionFile: piSession.sessionFile
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
async restoreSession(sessionId, opts) {
|
|
1750
|
+
const existing = this.sessions.maybeGet(sessionId);
|
|
1751
|
+
if (existing) return existing;
|
|
1752
|
+
const inFlight = this.restoringSessions.get(sessionId);
|
|
1753
|
+
if (inFlight) return inFlight;
|
|
1754
|
+
const restorePromise = (async () => {
|
|
1755
|
+
const stored = this.findStoredSession(sessionId);
|
|
1756
|
+
if (!stored) {
|
|
1757
|
+
throw RequestError3.invalidParams(`Unknown sessionId: ${sessionId}`);
|
|
1758
|
+
}
|
|
1759
|
+
const cwd = opts?.cwd ?? stored.cwd;
|
|
1760
|
+
let proc;
|
|
1761
|
+
try {
|
|
1762
|
+
proc = await PiRpcProcess.spawn({
|
|
1763
|
+
cwd,
|
|
1764
|
+
sessionPath: stored.sessionFile,
|
|
1765
|
+
piCommand: process.env.PI_ACP_PI_COMMAND
|
|
1766
|
+
});
|
|
1767
|
+
} catch (e) {
|
|
1768
|
+
if (e?.name === "PiRpcSpawnError") {
|
|
1769
|
+
throw RequestError3.internalError({ code: e?.code }, String(e?.message ?? e));
|
|
1770
|
+
}
|
|
1771
|
+
throw e;
|
|
1772
|
+
}
|
|
1773
|
+
const fileCommands = loadSlashCommands(cwd);
|
|
1774
|
+
const session = this.sessions.getOrCreate(sessionId, {
|
|
1775
|
+
cwd,
|
|
1776
|
+
mcpServers: opts?.mcpServers ?? [],
|
|
1777
|
+
conn: this.conn,
|
|
1778
|
+
proc,
|
|
1779
|
+
fileCommands
|
|
1780
|
+
});
|
|
1781
|
+
this.lastSessionCwd = cwd;
|
|
1782
|
+
this.store.upsert({ sessionId, cwd, sessionFile: stored.sessionFile });
|
|
1783
|
+
return session;
|
|
1784
|
+
})();
|
|
1785
|
+
this.restoringSessions.set(sessionId, restorePromise);
|
|
1786
|
+
try {
|
|
1787
|
+
return await restorePromise;
|
|
1788
|
+
} finally {
|
|
1789
|
+
this.restoringSessions.delete(sessionId);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1670
1792
|
async initialize(params) {
|
|
1671
1793
|
const supportedVersion = 1;
|
|
1672
1794
|
const requested = params.protocolVersion;
|
|
@@ -1813,7 +1935,7 @@ var PiAcpAgent = class {
|
|
|
1813
1935
|
return;
|
|
1814
1936
|
}
|
|
1815
1937
|
async prompt(params) {
|
|
1816
|
-
const session = this.
|
|
1938
|
+
const session = await this.restoreSession(params.sessionId);
|
|
1817
1939
|
const { message, images } = promptToPiMessage(params.prompt);
|
|
1818
1940
|
if (images.length === 0 && message.trimStart().startsWith("/")) {
|
|
1819
1941
|
const trimmed = message.trim();
|
|
@@ -2186,10 +2308,11 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2186
2308
|
return { stopReason };
|
|
2187
2309
|
}
|
|
2188
2310
|
async cancel(params) {
|
|
2189
|
-
const session = this.sessions.
|
|
2311
|
+
const session = this.sessions.maybeGet(params.sessionId);
|
|
2312
|
+
if (!session) return;
|
|
2190
2313
|
await session.cancel();
|
|
2191
2314
|
}
|
|
2192
|
-
async
|
|
2315
|
+
async listSessions(params) {
|
|
2193
2316
|
const all = listPiSessions();
|
|
2194
2317
|
const effectiveCwd = params.cwd ?? this.lastSessionCwd;
|
|
2195
2318
|
const filtered = effectiveCwd ? all.filter((s) => s.cwd === effectiveCwd) : all;
|
|
@@ -2212,38 +2335,22 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2212
2335
|
}
|
|
2213
2336
|
this.sessions.close(params.sessionId);
|
|
2214
2337
|
this.lastSessionCwd = params.cwd;
|
|
2215
|
-
const stored = this.
|
|
2216
|
-
|
|
2217
|
-
if (!sessionFile) {
|
|
2338
|
+
const stored = this.findStoredSession(params.sessionId);
|
|
2339
|
+
if (!stored) {
|
|
2218
2340
|
throw RequestError3.invalidParams(`Unknown sessionId: ${params.sessionId}`);
|
|
2219
2341
|
}
|
|
2220
|
-
let proc;
|
|
2221
|
-
try {
|
|
2222
|
-
proc = await PiRpcProcess.spawn({
|
|
2223
|
-
cwd: params.cwd,
|
|
2224
|
-
sessionPath: sessionFile,
|
|
2225
|
-
piCommand: process.env.PI_ACP_PI_COMMAND
|
|
2226
|
-
});
|
|
2227
|
-
} catch (e) {
|
|
2228
|
-
if (e?.name === "PiRpcSpawnError") {
|
|
2229
|
-
throw RequestError3.internalError({ code: e?.code }, String(e?.message ?? e));
|
|
2230
|
-
}
|
|
2231
|
-
throw e;
|
|
2232
|
-
}
|
|
2233
|
-
const fileCommands = loadSlashCommands(params.cwd);
|
|
2234
2342
|
const enableSkillCommands = getEnableSkillCommands(params.cwd);
|
|
2235
|
-
const session = this.
|
|
2343
|
+
const session = await this.restoreSession(params.sessionId, {
|
|
2236
2344
|
cwd: params.cwd,
|
|
2237
|
-
mcpServers: params.mcpServers
|
|
2238
|
-
conn: this.conn,
|
|
2239
|
-
proc,
|
|
2240
|
-
fileCommands
|
|
2345
|
+
mcpServers: params.mcpServers
|
|
2241
2346
|
});
|
|
2347
|
+
const proc = session.proc;
|
|
2348
|
+
const fileCommands = loadSlashCommands(params.cwd);
|
|
2242
2349
|
this.sessions.closeAllExcept?.(session.sessionId);
|
|
2243
2350
|
this.store.upsert({
|
|
2244
2351
|
sessionId: params.sessionId,
|
|
2245
2352
|
cwd: params.cwd,
|
|
2246
|
-
sessionFile
|
|
2353
|
+
sessionFile: stored.sessionFile
|
|
2247
2354
|
});
|
|
2248
2355
|
const data = await proc.getMessages();
|
|
2249
2356
|
const messages = Array.isArray(data?.messages) ? data.messages : [];
|
|
@@ -2343,12 +2450,12 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2343
2450
|
return response;
|
|
2344
2451
|
}
|
|
2345
2452
|
async unstable_setSessionModel(params) {
|
|
2346
|
-
const session = this.
|
|
2453
|
+
const session = await this.restoreSession(params.sessionId);
|
|
2347
2454
|
await setSessionModel(session.proc, params.modelId);
|
|
2348
2455
|
await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
|
|
2349
2456
|
}
|
|
2350
2457
|
async setSessionMode(params) {
|
|
2351
|
-
const session = this.
|
|
2458
|
+
const session = await this.restoreSession(params.sessionId);
|
|
2352
2459
|
const mode = String(params.modeId);
|
|
2353
2460
|
if (!isThinkingLevel(mode)) {
|
|
2354
2461
|
throw RequestError3.invalidParams(`Unknown modeId: ${mode}`);
|
|
@@ -2365,7 +2472,7 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2365
2472
|
return {};
|
|
2366
2473
|
}
|
|
2367
2474
|
async setSessionConfigOption(params) {
|
|
2368
|
-
const session = this.
|
|
2475
|
+
const session = await this.restoreSession(params.sessionId);
|
|
2369
2476
|
const configId = String(params.configId);
|
|
2370
2477
|
if (typeof params.value !== "string") {
|
|
2371
2478
|
throw RequestError3.invalidParams(`Expected string value for config option: ${configId}`);
|