pi-acp 0.0.28 → 0.0.30
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 +283 -99
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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 } : {} }];
|
|
@@ -677,8 +727,7 @@ var PiAcpSession = class {
|
|
|
677
727
|
cwd;
|
|
678
728
|
mcpServers;
|
|
679
729
|
startupInfo = null;
|
|
680
|
-
|
|
681
|
-
startupInfoSentInPrompt = false;
|
|
730
|
+
startupInfoSent = false;
|
|
682
731
|
proc;
|
|
683
732
|
conn;
|
|
684
733
|
fileCommands;
|
|
@@ -695,10 +744,11 @@ var PiAcpSession = class {
|
|
|
695
744
|
// pi can emit multiple `turn_end` events for a single user prompt (e.g. after tool_use).
|
|
696
745
|
// The overall agent loop completes when `agent_end` is emitted.
|
|
697
746
|
inAgentLoop = false;
|
|
698
|
-
// For ACP diff support: capture file contents before
|
|
699
|
-
//
|
|
700
|
-
//
|
|
701
|
-
|
|
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();
|
|
702
752
|
// Ensure `session/update` notifications are sent in order and can be awaited
|
|
703
753
|
// before completing a `session/prompt` request.
|
|
704
754
|
lastEmit = Promise.resolve();
|
|
@@ -713,8 +763,7 @@ var PiAcpSession = class {
|
|
|
713
763
|
}
|
|
714
764
|
setStartupInfo(text) {
|
|
715
765
|
this.startupInfo = text;
|
|
716
|
-
this.
|
|
717
|
-
this.startupInfoSentInPrompt = false;
|
|
766
|
+
this.startupInfoSent = false;
|
|
718
767
|
}
|
|
719
768
|
/**
|
|
720
769
|
* Best-effort attempt to send startup info outside of a prompt turn.
|
|
@@ -722,23 +771,14 @@ var PiAcpSession = class {
|
|
|
722
771
|
* callers can invoke this shortly after session/new returns.
|
|
723
772
|
*/
|
|
724
773
|
sendStartupInfoIfPending() {
|
|
725
|
-
if (this.
|
|
726
|
-
this.
|
|
727
|
-
this.emit({
|
|
728
|
-
sessionUpdate: "agent_message_chunk",
|
|
729
|
-
content: { type: "text", text: this.startupInfo }
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
sendStartupInfoOnFirstPromptIfPending() {
|
|
733
|
-
if (this.startupInfoSentInPrompt || !this.startupInfo) return;
|
|
734
|
-
this.startupInfoSentInPrompt = true;
|
|
774
|
+
if (this.startupInfoSent || !this.startupInfo) return;
|
|
775
|
+
this.startupInfoSent = true;
|
|
735
776
|
this.emit({
|
|
736
777
|
sessionUpdate: "agent_message_chunk",
|
|
737
778
|
content: { type: "text", text: this.startupInfo }
|
|
738
779
|
});
|
|
739
780
|
}
|
|
740
781
|
async prompt(message, images = []) {
|
|
741
|
-
this.sendStartupInfoOnFirstPromptIfPending();
|
|
742
782
|
const expandedMessage = expandSlashCommand(message, this.fileCommands);
|
|
743
783
|
const turnPromise = new Promise((resolve4, reject) => {
|
|
744
784
|
const queued = { message: expandedMessage, images, resolve: resolve4, reject };
|
|
@@ -889,16 +929,25 @@ var PiAcpSession = class {
|
|
|
889
929
|
const toolName = String(ev.toolName ?? "tool");
|
|
890
930
|
const args = ev.args;
|
|
891
931
|
let line;
|
|
892
|
-
|
|
893
|
-
|
|
932
|
+
const isFileMutation = toolName === "edit" || toolName === "write";
|
|
933
|
+
let snapshotOldText;
|
|
934
|
+
if (isFileMutation) {
|
|
935
|
+
this.fileMutationToolCallIds.add(toolCallId);
|
|
936
|
+
const p = getToolPath(args);
|
|
894
937
|
if (p) {
|
|
895
938
|
try {
|
|
896
939
|
const abs = isAbsolute(p) ? p : resolvePath(this.cwd, p);
|
|
897
|
-
|
|
898
|
-
this.
|
|
899
|
-
|
|
900
|
-
|
|
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
|
+
}
|
|
901
948
|
} catch {
|
|
949
|
+
snapshotOldText = null;
|
|
950
|
+
this.fileSnapshots.set(toolCallId, { path: p, oldText: null });
|
|
902
951
|
}
|
|
903
952
|
}
|
|
904
953
|
}
|
|
@@ -930,13 +979,13 @@ var PiAcpSession = class {
|
|
|
930
979
|
const toolCallId = String(ev.toolCallId ?? "");
|
|
931
980
|
if (!toolCallId) break;
|
|
932
981
|
const partial = ev.partialResult;
|
|
933
|
-
const text = toolResultToText(partial);
|
|
982
|
+
const text = this.fileMutationToolCallIds.has(toolCallId) ? "" : toolResultToText(partial);
|
|
934
983
|
this.emit({
|
|
935
984
|
sessionUpdate: "tool_call_update",
|
|
936
985
|
toolCallId,
|
|
937
986
|
status: "in_progress",
|
|
938
987
|
content: text ? [{ type: "content", content: { type: "text", text } }] : void 0,
|
|
939
|
-
rawOutput: partial
|
|
988
|
+
...this.fileMutationToolCallIds.has(toolCallId) ? {} : { rawOutput: partial }
|
|
940
989
|
});
|
|
941
990
|
break;
|
|
942
991
|
}
|
|
@@ -946,27 +995,28 @@ var PiAcpSession = class {
|
|
|
946
995
|
const result = ev.result;
|
|
947
996
|
const isError = Boolean(ev.isError);
|
|
948
997
|
const text = toolResultToText(result);
|
|
949
|
-
const snapshot = this.
|
|
998
|
+
const snapshot = this.fileSnapshots.get(toolCallId);
|
|
950
999
|
let content;
|
|
1000
|
+
let hasStructuredDiff = false;
|
|
951
1001
|
if (!isError && snapshot) {
|
|
952
1002
|
try {
|
|
953
1003
|
const abs = isAbsolute(snapshot.path) ? snapshot.path : resolvePath(this.cwd, snapshot.path);
|
|
954
1004
|
const newText = readFileSync3(abs, "utf8");
|
|
955
|
-
if (newText !== snapshot.oldText) {
|
|
1005
|
+
if (snapshot.oldText === null || newText !== snapshot.oldText) {
|
|
1006
|
+
hasStructuredDiff = true;
|
|
956
1007
|
content = [
|
|
957
1008
|
{
|
|
958
1009
|
type: "diff",
|
|
959
1010
|
path: snapshot.path,
|
|
960
1011
|
oldText: snapshot.oldText,
|
|
961
1012
|
newText
|
|
962
|
-
}
|
|
963
|
-
...text ? [{ type: "content", content: { type: "text", text } }] : []
|
|
1013
|
+
}
|
|
964
1014
|
];
|
|
965
1015
|
}
|
|
966
1016
|
} catch {
|
|
967
1017
|
}
|
|
968
1018
|
}
|
|
969
|
-
if (!content && text) {
|
|
1019
|
+
if (!content && !hasStructuredDiff && text) {
|
|
970
1020
|
content = [{ type: "content", content: { type: "text", text } }];
|
|
971
1021
|
}
|
|
972
1022
|
this.emit({
|
|
@@ -974,10 +1024,11 @@ var PiAcpSession = class {
|
|
|
974
1024
|
toolCallId,
|
|
975
1025
|
status: isError ? "failed" : "completed",
|
|
976
1026
|
content,
|
|
977
|
-
rawOutput: result
|
|
1027
|
+
...hasStructuredDiff ? {} : { rawOutput: result }
|
|
978
1028
|
});
|
|
979
1029
|
this.currentToolCalls.delete(toolCallId);
|
|
980
|
-
this.
|
|
1030
|
+
this.fileSnapshots.delete(toolCallId);
|
|
1031
|
+
this.fileMutationToolCallIds.delete(toolCallId);
|
|
981
1032
|
break;
|
|
982
1033
|
}
|
|
983
1034
|
case "extension_ui_request": {
|
|
@@ -1441,10 +1492,9 @@ function listPiSessions() {
|
|
|
1441
1492
|
});
|
|
1442
1493
|
return items;
|
|
1443
1494
|
}
|
|
1444
|
-
function
|
|
1495
|
+
function findPiSession(sessionId) {
|
|
1445
1496
|
const all = listPiSessions();
|
|
1446
|
-
|
|
1447
|
-
return found?.sessionFile ?? null;
|
|
1497
|
+
return all.find((s) => s.sessionId === sessionId) ?? null;
|
|
1448
1498
|
}
|
|
1449
1499
|
|
|
1450
1500
|
// src/acp/translate/pi-messages.ts
|
|
@@ -1600,6 +1650,8 @@ import { existsSync as existsSync4, readFileSync as readFileSync6, realpathSync,
|
|
|
1600
1650
|
import { join as join5, dirname as dirname2, basename } from "path";
|
|
1601
1651
|
import { spawnSync } from "child_process";
|
|
1602
1652
|
import { fileURLToPath } from "url";
|
|
1653
|
+
var MODEL_CONFIG_ID = "model";
|
|
1654
|
+
var THOUGHT_LEVEL_CONFIG_ID = "thought_level";
|
|
1603
1655
|
function builtinAvailableCommands() {
|
|
1604
1656
|
return [
|
|
1605
1657
|
{
|
|
@@ -1656,6 +1708,7 @@ var PiAcpAgent = class {
|
|
|
1656
1708
|
conn;
|
|
1657
1709
|
sessions = new SessionManager();
|
|
1658
1710
|
store = new SessionStore();
|
|
1711
|
+
restoringSessions = /* @__PURE__ */ new Map();
|
|
1659
1712
|
dispose() {
|
|
1660
1713
|
this.sessions.disposeAll();
|
|
1661
1714
|
}
|
|
@@ -1676,6 +1729,66 @@ var PiAcpAgent = class {
|
|
|
1676
1729
|
}
|
|
1677
1730
|
this.store.delete(sessionId);
|
|
1678
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
|
+
}
|
|
1679
1792
|
async initialize(params) {
|
|
1680
1793
|
const supportedVersion = 1;
|
|
1681
1794
|
const requested = params.protocolVersion;
|
|
@@ -1763,8 +1876,10 @@ var PiAcpAgent = class {
|
|
|
1763
1876
|
"Configure an API key or log in with an OAuth provider."
|
|
1764
1877
|
);
|
|
1765
1878
|
}
|
|
1766
|
-
const models = await
|
|
1767
|
-
|
|
1879
|
+
const { configOptions, models, modes } = await getSessionConfiguration(session.proc, {
|
|
1880
|
+
state,
|
|
1881
|
+
availableModels
|
|
1882
|
+
});
|
|
1768
1883
|
const quietStartup = getQuietStartup(params.cwd);
|
|
1769
1884
|
const updateNotice = buildUpdateNotice();
|
|
1770
1885
|
const preludeText = quietStartup ? updateNotice ? updateNotice + "\n" : "" : buildStartupInfo({
|
|
@@ -1777,8 +1892,9 @@ var PiAcpAgent = class {
|
|
|
1777
1892
|
this.sessions.closeAllExcept?.(session.sessionId);
|
|
1778
1893
|
const response = {
|
|
1779
1894
|
sessionId: session.sessionId,
|
|
1895
|
+
configOptions,
|
|
1780
1896
|
models,
|
|
1781
|
-
modes
|
|
1897
|
+
modes,
|
|
1782
1898
|
_meta: {
|
|
1783
1899
|
piAcp: {
|
|
1784
1900
|
startupInfo: preludeText || null
|
|
@@ -1819,7 +1935,7 @@ var PiAcpAgent = class {
|
|
|
1819
1935
|
return;
|
|
1820
1936
|
}
|
|
1821
1937
|
async prompt(params) {
|
|
1822
|
-
const session = this.
|
|
1938
|
+
const session = await this.restoreSession(params.sessionId);
|
|
1823
1939
|
const { message, images } = promptToPiMessage(params.prompt);
|
|
1824
1940
|
if (images.length === 0 && message.trimStart().startsWith("/")) {
|
|
1825
1941
|
const trimmed = message.trim();
|
|
@@ -2192,7 +2308,8 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2192
2308
|
return { stopReason };
|
|
2193
2309
|
}
|
|
2194
2310
|
async cancel(params) {
|
|
2195
|
-
const session = this.sessions.
|
|
2311
|
+
const session = this.sessions.maybeGet(params.sessionId);
|
|
2312
|
+
if (!session) return;
|
|
2196
2313
|
await session.cancel();
|
|
2197
2314
|
}
|
|
2198
2315
|
async unstable_listSessions(params) {
|
|
@@ -2218,38 +2335,22 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2218
2335
|
}
|
|
2219
2336
|
this.sessions.close(params.sessionId);
|
|
2220
2337
|
this.lastSessionCwd = params.cwd;
|
|
2221
|
-
const stored = this.
|
|
2222
|
-
|
|
2223
|
-
if (!sessionFile) {
|
|
2338
|
+
const stored = this.findStoredSession(params.sessionId);
|
|
2339
|
+
if (!stored) {
|
|
2224
2340
|
throw RequestError3.invalidParams(`Unknown sessionId: ${params.sessionId}`);
|
|
2225
2341
|
}
|
|
2226
|
-
let proc;
|
|
2227
|
-
try {
|
|
2228
|
-
proc = await PiRpcProcess.spawn({
|
|
2229
|
-
cwd: params.cwd,
|
|
2230
|
-
sessionPath: sessionFile,
|
|
2231
|
-
piCommand: process.env.PI_ACP_PI_COMMAND
|
|
2232
|
-
});
|
|
2233
|
-
} catch (e) {
|
|
2234
|
-
if (e?.name === "PiRpcSpawnError") {
|
|
2235
|
-
throw RequestError3.internalError({ code: e?.code }, String(e?.message ?? e));
|
|
2236
|
-
}
|
|
2237
|
-
throw e;
|
|
2238
|
-
}
|
|
2239
|
-
const fileCommands = loadSlashCommands(params.cwd);
|
|
2240
2342
|
const enableSkillCommands = getEnableSkillCommands(params.cwd);
|
|
2241
|
-
const session = this.
|
|
2343
|
+
const session = await this.restoreSession(params.sessionId, {
|
|
2242
2344
|
cwd: params.cwd,
|
|
2243
|
-
mcpServers: params.mcpServers
|
|
2244
|
-
conn: this.conn,
|
|
2245
|
-
proc,
|
|
2246
|
-
fileCommands
|
|
2345
|
+
mcpServers: params.mcpServers
|
|
2247
2346
|
});
|
|
2347
|
+
const proc = session.proc;
|
|
2348
|
+
const fileCommands = loadSlashCommands(params.cwd);
|
|
2248
2349
|
this.sessions.closeAllExcept?.(session.sessionId);
|
|
2249
2350
|
this.store.upsert({
|
|
2250
2351
|
sessionId: params.sessionId,
|
|
2251
2352
|
cwd: params.cwd,
|
|
2252
|
-
sessionFile
|
|
2353
|
+
sessionFile: stored.sessionFile
|
|
2253
2354
|
});
|
|
2254
2355
|
const data = await proc.getMessages();
|
|
2255
2356
|
const messages = Array.isArray(data?.messages) ? data.messages : [];
|
|
@@ -2308,11 +2409,11 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2308
2409
|
});
|
|
2309
2410
|
}
|
|
2310
2411
|
}
|
|
2311
|
-
const models = await
|
|
2312
|
-
const thinking = await getThinkingState(proc);
|
|
2412
|
+
const { configOptions, models, modes } = await getSessionConfiguration(proc);
|
|
2313
2413
|
const response = {
|
|
2414
|
+
configOptions,
|
|
2314
2415
|
models,
|
|
2315
|
-
modes
|
|
2416
|
+
modes,
|
|
2316
2417
|
_meta: {
|
|
2317
2418
|
piAcp: {
|
|
2318
2419
|
startupInfo: null
|
|
@@ -2349,32 +2450,12 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2349
2450
|
return response;
|
|
2350
2451
|
}
|
|
2351
2452
|
async unstable_setSessionModel(params) {
|
|
2352
|
-
const session = this.
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
if (params.modelId.includes("/")) {
|
|
2356
|
-
const [p, ...rest] = params.modelId.split("/");
|
|
2357
|
-
provider = p;
|
|
2358
|
-
modelId = rest.join("/");
|
|
2359
|
-
} else {
|
|
2360
|
-
modelId = params.modelId;
|
|
2361
|
-
}
|
|
2362
|
-
if (!provider) {
|
|
2363
|
-
const data = await session.proc.getAvailableModels();
|
|
2364
|
-
const models = Array.isArray(data?.models) ? data.models : [];
|
|
2365
|
-
const found = models.find((m) => String(m?.id) === modelId);
|
|
2366
|
-
if (found) {
|
|
2367
|
-
provider = String(found.provider);
|
|
2368
|
-
modelId = String(found.id);
|
|
2369
|
-
}
|
|
2370
|
-
}
|
|
2371
|
-
if (!provider || !modelId) {
|
|
2372
|
-
throw RequestError3.invalidParams(`Unknown modelId: ${params.modelId}`);
|
|
2373
|
-
}
|
|
2374
|
-
await session.proc.setModel(provider, modelId);
|
|
2453
|
+
const session = await this.restoreSession(params.sessionId);
|
|
2454
|
+
await setSessionModel(session.proc, params.modelId);
|
|
2455
|
+
await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
|
|
2375
2456
|
}
|
|
2376
2457
|
async setSessionMode(params) {
|
|
2377
|
-
const session = this.
|
|
2458
|
+
const session = await this.restoreSession(params.sessionId);
|
|
2378
2459
|
const mode = String(params.modeId);
|
|
2379
2460
|
if (!isThinkingLevel(mode)) {
|
|
2380
2461
|
throw RequestError3.invalidParams(`Unknown modeId: ${mode}`);
|
|
@@ -2387,8 +2468,35 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2387
2468
|
currentModeId: mode
|
|
2388
2469
|
}
|
|
2389
2470
|
});
|
|
2471
|
+
await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
|
|
2390
2472
|
return {};
|
|
2391
2473
|
}
|
|
2474
|
+
async setSessionConfigOption(params) {
|
|
2475
|
+
const session = await this.restoreSession(params.sessionId);
|
|
2476
|
+
const configId = String(params.configId);
|
|
2477
|
+
if (typeof params.value !== "string") {
|
|
2478
|
+
throw RequestError3.invalidParams(`Expected string value for config option: ${configId}`);
|
|
2479
|
+
}
|
|
2480
|
+
if (configId === MODEL_CONFIG_ID) {
|
|
2481
|
+
await setSessionModel(session.proc, params.value);
|
|
2482
|
+
} else if (configId === THOUGHT_LEVEL_CONFIG_ID) {
|
|
2483
|
+
if (!isThinkingLevel(params.value)) {
|
|
2484
|
+
throw RequestError3.invalidParams(`Unknown thinking level: ${params.value}`);
|
|
2485
|
+
}
|
|
2486
|
+
await session.proc.setThinkingLevel(params.value);
|
|
2487
|
+
void this.conn.sessionUpdate({
|
|
2488
|
+
sessionId: session.sessionId,
|
|
2489
|
+
update: {
|
|
2490
|
+
sessionUpdate: "current_mode_update",
|
|
2491
|
+
currentModeId: params.value
|
|
2492
|
+
}
|
|
2493
|
+
});
|
|
2494
|
+
} else {
|
|
2495
|
+
throw RequestError3.invalidParams(`Unknown config option: ${configId}`);
|
|
2496
|
+
}
|
|
2497
|
+
const configOptions = await emitConfigOptionsUpdate(this.conn, session.sessionId, session.proc);
|
|
2498
|
+
return { configOptions };
|
|
2499
|
+
}
|
|
2392
2500
|
};
|
|
2393
2501
|
function isThinkingLevel(x) {
|
|
2394
2502
|
return x === "off" || x === "minimal" || x === "low" || x === "medium" || x === "high" || x === "xhigh";
|
|
@@ -2414,6 +2522,47 @@ async function getThinkingState(proc, pre) {
|
|
|
2414
2522
|
}))
|
|
2415
2523
|
};
|
|
2416
2524
|
}
|
|
2525
|
+
async function getSessionConfiguration(proc, pre) {
|
|
2526
|
+
const [models, modes] = await Promise.all([getModelState(proc, pre), getThinkingState(proc, { state: pre?.state })]);
|
|
2527
|
+
return {
|
|
2528
|
+
configOptions: buildConfigOptions({ models, modes }),
|
|
2529
|
+
models,
|
|
2530
|
+
modes
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
function buildConfigOptions(state) {
|
|
2534
|
+
const configOptions = [
|
|
2535
|
+
{
|
|
2536
|
+
type: "select",
|
|
2537
|
+
id: THOUGHT_LEVEL_CONFIG_ID,
|
|
2538
|
+
category: "thought_level",
|
|
2539
|
+
name: "Thinking",
|
|
2540
|
+
description: "Set the reasoning effort for this session",
|
|
2541
|
+
currentValue: state.modes.currentModeId,
|
|
2542
|
+
options: state.modes.availableModes.map((mode) => ({
|
|
2543
|
+
value: mode.id,
|
|
2544
|
+
name: mode.name,
|
|
2545
|
+
description: mode.description ?? null
|
|
2546
|
+
}))
|
|
2547
|
+
}
|
|
2548
|
+
];
|
|
2549
|
+
if (state.models?.availableModels.length) {
|
|
2550
|
+
configOptions.unshift({
|
|
2551
|
+
type: "select",
|
|
2552
|
+
id: MODEL_CONFIG_ID,
|
|
2553
|
+
category: "model",
|
|
2554
|
+
name: "Model",
|
|
2555
|
+
description: "Select the model for this session",
|
|
2556
|
+
currentValue: state.models.currentModelId,
|
|
2557
|
+
options: state.models.availableModels.map((model) => ({
|
|
2558
|
+
value: model.modelId,
|
|
2559
|
+
name: model.name,
|
|
2560
|
+
description: model.description ?? null
|
|
2561
|
+
}))
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
return configOptions;
|
|
2565
|
+
}
|
|
2417
2566
|
async function getModelState(proc, pre) {
|
|
2418
2567
|
let availableModels = [];
|
|
2419
2568
|
const data = pre?.availableModels ?? await (async () => {
|
|
@@ -2453,9 +2602,44 @@ async function getModelState(proc, pre) {
|
|
|
2453
2602
|
if (!currentModelId) currentModelId = availableModels[0]?.modelId ?? "default";
|
|
2454
2603
|
return {
|
|
2455
2604
|
availableModels,
|
|
2456
|
-
currentModelId
|
|
2605
|
+
currentModelId: currentModelId ?? availableModels[0]?.modelId ?? "default"
|
|
2457
2606
|
};
|
|
2458
2607
|
}
|
|
2608
|
+
async function emitConfigOptionsUpdate(conn, sessionId, proc) {
|
|
2609
|
+
const { configOptions } = await getSessionConfiguration(proc);
|
|
2610
|
+
await conn.sessionUpdate({
|
|
2611
|
+
sessionId,
|
|
2612
|
+
update: {
|
|
2613
|
+
sessionUpdate: "config_option_update",
|
|
2614
|
+
configOptions
|
|
2615
|
+
}
|
|
2616
|
+
});
|
|
2617
|
+
return configOptions;
|
|
2618
|
+
}
|
|
2619
|
+
async function setSessionModel(proc, requestedModelId) {
|
|
2620
|
+
let provider = null;
|
|
2621
|
+
let modelId = null;
|
|
2622
|
+
if (requestedModelId.includes("/")) {
|
|
2623
|
+
const [candidateProvider, ...rest] = requestedModelId.split("/");
|
|
2624
|
+
provider = candidateProvider;
|
|
2625
|
+
modelId = rest.join("/");
|
|
2626
|
+
} else {
|
|
2627
|
+
modelId = requestedModelId;
|
|
2628
|
+
}
|
|
2629
|
+
if (!provider) {
|
|
2630
|
+
const data = await proc.getAvailableModels();
|
|
2631
|
+
const models = Array.isArray(data?.models) ? data.models : [];
|
|
2632
|
+
const found = models.find((m) => String(m?.id) === modelId);
|
|
2633
|
+
if (found) {
|
|
2634
|
+
provider = String(found.provider);
|
|
2635
|
+
modelId = String(found.id);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
if (!provider || !modelId) {
|
|
2639
|
+
throw RequestError3.invalidParams(`Unknown modelId: ${requestedModelId}`);
|
|
2640
|
+
}
|
|
2641
|
+
await proc.setModel(provider, modelId);
|
|
2642
|
+
}
|
|
2459
2643
|
function isSemver(v) {
|
|
2460
2644
|
return /^\d+\.\d+\.\d+(?:[-+].+)?$/.test(v);
|
|
2461
2645
|
}
|