@pi-chamber/web 0.2.2 → 0.3.1
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/bin/cli.test.js +6 -8
- package/bin/lib/cli-tunnel-capabilities.js +0 -2
- package/dist/assets/{CommitSection-DPjTv8fA.js → CommitSection-C336377Z.js} +1 -1
- package/dist/assets/{DiagramEditor-ChOor9l9.js → DiagramEditor-gHLCXHtr.js} +1 -1
- package/dist/assets/{DiagramView-3hAT86v_.js → DiagramView-DlHzRRBc.js} +1 -1
- package/dist/assets/{DiffView-D1dyOiw2.js → DiffView-5wHwK-lg.js} +1 -1
- package/dist/assets/{FilesView-BCzghrc7.js → FilesView-5bswI3JZ.js} +6 -6
- package/dist/assets/{GitView-CYF1E1p4.js → GitView-BfykGpGh.js} +4 -4
- package/dist/assets/{JsonTreeView-CrJO6LNu.js → JsonTreeView-HVwQS7qG.js} +1 -1
- package/dist/assets/{MarkdownRendererImpl-BmKjrkGD.js → MarkdownRendererImpl-kjd4e0N8.js} +1 -1
- package/dist/assets/{PatchDiff-DgvPLx6x.js → PatchDiff-Dlm4RU8n.js} +1 -1
- package/dist/assets/{PierreDiffViewer-InnT0JVA.js → PierreDiffViewer-St32rXlD.js} +1 -1
- package/dist/assets/{SessionSwitcherDropdown-C0OdvirK.js → SessionSwitcherDropdown-D5bWfZNA.js} +100 -101
- package/dist/assets/SettingsView-FGe5TEQq.js +4 -0
- package/dist/assets/SettingsWindow-Ck_uhTnx.js +1 -0
- package/dist/assets/{ToolOutputDialog-gzwcb8nI.js → ToolOutputDialog-BCONUv3r.js} +5 -5
- package/dist/assets/{ToolPartDiffPreview-C45V5bhy.js → ToolPartDiffPreview-BdVuk2kc.js} +1 -1
- package/dist/assets/{VirtualizedFileDiff-uKfnkQWb.js → VirtualizedFileDiff-BsAZM0jo.js} +1 -1
- package/dist/assets/{collapsible-Bq8sDiKS.js → collapsible-Wnmq-13H.js} +1 -1
- package/dist/assets/{computeVirtualFileMetrics-BPgZ_RSe.js → computeVirtualFileMetrics-skbyL2Az.js} +2 -2
- package/dist/assets/{contextFileOpenGuard-CydO7PkI.js → contextFileOpenGuard-BsxQITMZ.js} +1 -1
- package/dist/assets/index-8TL2HlaR.js +8 -0
- package/dist/assets/{index-BSuNNtBh.js → index-Bfp6eYap.js} +1 -1
- package/dist/assets/index-riABQmQ_.css +1 -0
- package/dist/assets/{isDiffPlainText-f9b4cKNo.js → isDiffPlainText-dZkfk3yG.js} +1 -1
- package/dist/assets/{isFilePlainText-BjESb-gU.js → isFilePlainText-UL_tMQZx.js} +1 -1
- package/dist/assets/{main-B508FBXr.js → main-CHQfoyRP.js} +2 -2
- package/dist/assets/main-DbnYXZeK.js +133 -0
- package/dist/assets/{markdownCore-BFmf8a-O.js → markdownCore-qX3mJFkL.js} +2 -2
- package/dist/assets/{miniChat-BTlz8IPL.js → miniChat-3QtCNwHl.js} +2 -2
- package/dist/assets/{mobile-DBkrRt80.js → mobile-B_CyfRAX.js} +2 -2
- package/dist/assets/renderElectronMiniChatApp-OToslrv-.js +1 -0
- package/dist/assets/renderMobileApp-BD_wep_I.js +14 -0
- package/dist/assets/{runtimeSurface-Bec4O8FD.js → runtimeSurface-CkdOpqo7.js} +1 -1
- package/dist/assets/{useInlineCommentController-CHmIm0Rr.js → useInlineCommentController-DVif5Fr8.js} +2 -2
- package/dist/assets/usePanelSlide-C_PJmPXW.js +56 -0
- package/dist/assets/{useWindowTitle-CE5N_NeU.js → useWindowTitle-BZVlLMCy.js} +3 -3
- package/dist/assets/{vendor-base-ui-DwfTg5wD.js → vendor-base-ui-DL8r2fgQ.js} +1 -1
- package/dist/assets/{vendor-codemirror-language-data-q1yHU1R9.js → vendor-codemirror-language-data-C-924Z4o.js} +2 -2
- package/dist/assets/{vendor-sonner-dWA7qvjr.js → vendor-sonner-CW8w438f.js} +1 -1
- package/dist/assets/{web-components-CLirSfwt.js → web-components-BG-6PEN4.js} +1 -1
- package/dist/index.html +4 -4
- package/dist/mini-chat.html +3 -3
- package/dist/mobile.html +3 -3
- package/package.json +1 -1
- package/server/lib/package-manager.js +0 -34
- package/server/lib/pi/routes.js +52 -4
- package/server/lib/pi/routes.test.js +14 -6
- package/server/lib/pi/session-daemon/DOCUMENTATION.md +4 -3
- package/server/lib/pi/session-daemon/message-entry-aliases.js +100 -0
- package/server/lib/pi/session-daemon/runtime-registry.js +3 -0
- package/server/lib/pi/session-daemon/session-daemon.js +184 -16
- package/server/lib/pi/session-daemon/session-daemon.test.js +310 -5
- package/server/lib/pi/session-daemon/supervisor.js +20 -1
- package/server/lib/pi/session-daemon/supervisor.test.js +43 -0
- package/server/lib/tunnels/DOCUMENTATION.md +6 -7
- package/server/lib/tunnels/executable-search.test.js +4 -4
- package/server/lib/tunnels/install-help.js +0 -10
- package/server/lib/tunnels/install-help.test.js +0 -9
- package/server/lib/tunnels/types.js +0 -1
- package/dist/assets/SettingsView-JAfC_cBL.js +0 -3
- package/dist/assets/SettingsWindow-BBEsjcJE.js +0 -1
- package/dist/assets/index-CH7o4-_q.js +0 -8
- package/dist/assets/index-o4Eea60G.css +0 -1
- package/dist/assets/main-nG4uFyAy.js +0 -133
- package/dist/assets/renderElectronMiniChatApp-DW3fSR5w.js +0 -1
- package/dist/assets/renderMobileApp-9qkroKty.js +0 -14
- package/dist/assets/usePanelSlide-DCX8qQ4K.js +0 -56
- package/server/lib/ngrok-tunnel.js +0 -343
- package/server/lib/ngrok-tunnel.test.js +0 -50
- package/server/lib/tunnels/providers/ngrok.js +0 -32
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
|
|
19
19
|
import { createPiModelConfigStore } from '../model-config-store.js';
|
|
20
20
|
import { clampThinkingLevel, getSupportedThinkingLevels, isPiThinkingLevel } from '../thinking-levels.js';
|
|
21
|
+
import { createMessageEntryAliases } from './message-entry-aliases.js';
|
|
21
22
|
import { createSessionRuntimeRegistry } from './runtime-registry.js';
|
|
22
23
|
import {
|
|
23
24
|
findPiSessionJsonlById,
|
|
@@ -136,9 +137,11 @@ export function createSessionDaemon({
|
|
|
136
137
|
const messageStartedAt = new Map();
|
|
137
138
|
const toolStartedAt = new Map();
|
|
138
139
|
const latestUserMessageIds = new Map();
|
|
140
|
+
const retryStateBySession = new Map();
|
|
139
141
|
const sendGenerationBySession = new Map();
|
|
140
142
|
const streamingRedactionBuffers = new Map();
|
|
141
143
|
const loginAttempts = new Map();
|
|
144
|
+
const messageEntryAliases = createMessageEntryAliases();
|
|
142
145
|
const MAX_REPLAY_EVENTS = 1_024;
|
|
143
146
|
|
|
144
147
|
const validateDirectoryPath = async (dir) => {
|
|
@@ -238,6 +241,7 @@ export function createSessionDaemon({
|
|
|
238
241
|
: getSessionState();
|
|
239
242
|
if (requestedSessionId && requestedSessionId !== session.sessionId) return;
|
|
240
243
|
const activeSession = targetRuntime?.session || runtime?.session;
|
|
244
|
+
const retry = session.sessionId ? retryStateBySession.get(session.sessionId) : undefined;
|
|
241
245
|
const targetDirectory = targetRuntime?.cwd || activeDirectory || cwd;
|
|
242
246
|
const messages = activeSession ? projectMessageEntries(activeSession, targetDirectory) : [];
|
|
243
247
|
const lastAssistant = [...messages].reverse().find((entry) => entry.message.role === 'assistant')?.message;
|
|
@@ -252,7 +256,8 @@ export function createSessionDaemon({
|
|
|
252
256
|
...(session.sessionId ? { sessionId: session.sessionId } : {}),
|
|
253
257
|
directory: targetDirectory,
|
|
254
258
|
isStreaming: session.isStreaming ?? false,
|
|
255
|
-
lifecycle: session.isStreaming ? 'busy' : 'idle',
|
|
259
|
+
lifecycle: retry ? 'retry' : session.isStreaming ? 'busy' : 'idle',
|
|
260
|
+
...(retry ? { retry } : {}),
|
|
256
261
|
queue: activeSession ? {
|
|
257
262
|
steering: activeSession.getSteeringMessages?.().length ?? 0,
|
|
258
263
|
followUp: activeSession.getFollowUpMessages?.().length ?? 0,
|
|
@@ -631,7 +636,11 @@ export function createSessionDaemon({
|
|
|
631
636
|
};
|
|
632
637
|
|
|
633
638
|
const projectMessageEntries = (session, targetDir = activeDirectory || cwd) => {
|
|
634
|
-
|
|
639
|
+
// Use the active branch, not the full file. `getEntries()` returns every
|
|
640
|
+
// entry ever written, so a bare `branch()`/`resetLeaf()` would appear to
|
|
641
|
+
// do nothing. `getBranch()` follows the current leaf and is what
|
|
642
|
+
// `navigateTree` and `buildSessionContext` use for the model context.
|
|
643
|
+
const persisted = session?.sessionManager?.getBranch?.() ?? session?.sessionManager?.getEntries?.();
|
|
635
644
|
const entries = liveProjectionEntries(session, Array.isArray(persisted) ? persisted : []);
|
|
636
645
|
if (entries.length === 0) return [];
|
|
637
646
|
const streaming = session?.isStreaming === true;
|
|
@@ -723,6 +732,7 @@ export function createSessionDaemon({
|
|
|
723
732
|
?? (model?.provider && model?.id ? { providerId: model.provider, modelId: model.id } : undefined);
|
|
724
733
|
const sessionThinking = lastAssistant?.thinkingLevel || session.thinkingLevel;
|
|
725
734
|
const isStreaming = session.isStreaming === true;
|
|
735
|
+
const retry = retryStateBySession.get(session.sessionId);
|
|
726
736
|
return {
|
|
727
737
|
session: {
|
|
728
738
|
id: session.sessionId, directory: targetDir, createdAt, updatedAt: createdAt,
|
|
@@ -734,7 +744,8 @@ export function createSessionDaemon({
|
|
|
734
744
|
messages,
|
|
735
745
|
lastSequence: sequence,
|
|
736
746
|
isStreaming,
|
|
737
|
-
lifecycle: isStreaming ? 'busy' : 'idle',
|
|
747
|
+
lifecycle: retry ? 'retry' : isStreaming ? 'busy' : 'idle',
|
|
748
|
+
...(retry ? { retry } : {}),
|
|
738
749
|
};
|
|
739
750
|
};
|
|
740
751
|
|
|
@@ -820,6 +831,84 @@ export function createSessionDaemon({
|
|
|
820
831
|
return { providers: [...providers.values()] };
|
|
821
832
|
};
|
|
822
833
|
|
|
834
|
+
let refreshProvidersInflight = null;
|
|
835
|
+
const refreshProviders = async (requestedDirectory) => {
|
|
836
|
+
if (refreshProvidersInflight) return refreshProvidersInflight;
|
|
837
|
+
const task = (async () => {
|
|
838
|
+
const signal = typeof AbortSignal.timeout === 'function' ? AbortSignal.timeout(15_000) : undefined;
|
|
839
|
+
const runtimes = new Set();
|
|
840
|
+
for (const services of servicesCache.values()) {
|
|
841
|
+
const mr = services?.modelRuntime;
|
|
842
|
+
if (mr) runtimes.add(mr);
|
|
843
|
+
}
|
|
844
|
+
if (runtime?.session?.modelRuntime) runtimes.add(runtime.session.modelRuntime);
|
|
845
|
+
if (runtime?.services?.modelRuntime) runtimes.add(runtime.services.modelRuntime);
|
|
846
|
+
if (runtimeRegistry?.listAll) {
|
|
847
|
+
try {
|
|
848
|
+
for (const tracked of runtimeRegistry.listAll()) {
|
|
849
|
+
const mr = tracked?.session?.modelRuntime ?? tracked?.services?.modelRuntime;
|
|
850
|
+
if (mr) runtimes.add(mr);
|
|
851
|
+
}
|
|
852
|
+
} catch {}
|
|
853
|
+
}
|
|
854
|
+
if (runtimes.size === 0) {
|
|
855
|
+
const active = await ensureRuntime(requestedDirectory ? await resolveDirectory(requestedDirectory) : undefined);
|
|
856
|
+
const mr = active?.session?.modelRuntime ?? active?.services?.modelRuntime;
|
|
857
|
+
if (mr) runtimes.add(mr);
|
|
858
|
+
}
|
|
859
|
+
const dummyMap = new Map();
|
|
860
|
+
for (const mr of runtimes) {
|
|
861
|
+
try {
|
|
862
|
+
const providers = typeof mr.getProviders === 'function' ? mr.getProviders() : [];
|
|
863
|
+
for (const provider of providers) {
|
|
864
|
+
const auth = mr.getProviderAuthStatus?.(provider.id);
|
|
865
|
+
if (auth?.configured === true) continue;
|
|
866
|
+
try {
|
|
867
|
+
await mr.setRuntimeApiKey(provider.id, 'pichamber-catalog-refresh');
|
|
868
|
+
let list = dummyMap.get(mr);
|
|
869
|
+
if (!list) { list = []; dummyMap.set(mr, list); }
|
|
870
|
+
list.push(provider.id);
|
|
871
|
+
} catch {}
|
|
872
|
+
}
|
|
873
|
+
} catch {}
|
|
874
|
+
}
|
|
875
|
+
const errors = new Map();
|
|
876
|
+
let aborted = false;
|
|
877
|
+
try {
|
|
878
|
+
await Promise.all([...runtimes].map(async (mr) => {
|
|
879
|
+
try {
|
|
880
|
+
const result = await mr.refresh({ allowNetwork: true, force: true, ...(signal ? { signal } : {}) });
|
|
881
|
+
if (result?.aborted) aborted = true;
|
|
882
|
+
if (result?.errors) {
|
|
883
|
+
for (const [providerId, err] of result.errors) errors.set(providerId, err);
|
|
884
|
+
}
|
|
885
|
+
} catch (error) {
|
|
886
|
+
if (error?.code === 'PI_MODEL_CONFIG_INVALID') throw error;
|
|
887
|
+
errors.set('_global', error);
|
|
888
|
+
}
|
|
889
|
+
}));
|
|
890
|
+
} finally {
|
|
891
|
+
for (const [mr, ids] of dummyMap.entries()) {
|
|
892
|
+
for (const id of ids) {
|
|
893
|
+
try { await mr.removeRuntimeApiKey(id); } catch {}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (errors.has('_global') && runtimes.size > 0) {
|
|
898
|
+
const globalError = errors.get('_global');
|
|
899
|
+
if (globalError?.code === 'PI_MODEL_CONFIG_INVALID') {
|
|
900
|
+
throw new SessionDaemonProtocolError('PI_MODEL_CONFIG_INVALID', 'Pi models configuration is invalid.');
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
const catalog = await listProviders(requestedDirectory);
|
|
904
|
+
return catalog;
|
|
905
|
+
})().finally(() => {
|
|
906
|
+
refreshProvidersInflight = null;
|
|
907
|
+
});
|
|
908
|
+
refreshProvidersInflight = task;
|
|
909
|
+
return task;
|
|
910
|
+
};
|
|
911
|
+
|
|
823
912
|
const getProviderConfiguration = async (providerId) => {
|
|
824
913
|
if (typeof providerId !== 'string' || providerId.length === 0 || providerId.length > 256) {
|
|
825
914
|
throw new SessionDaemonProtocolError('INVALID_ARGUMENT', 'The requested provider is invalid.');
|
|
@@ -1101,9 +1190,21 @@ export function createSessionDaemon({
|
|
|
1101
1190
|
if (!loader || typeof loader.getSkills !== 'function' || typeof loader.getPrompts !== 'function' || typeof loader.getAgentsFiles !== 'function') {
|
|
1102
1191
|
throw new SessionDaemonProtocolError('DAEMON_REQUEST_FAILED', 'Pi resource discovery is unavailable.');
|
|
1103
1192
|
}
|
|
1104
|
-
const skills = loader.getSkills().skills.map((skill) =>
|
|
1105
|
-
|
|
1106
|
-
|
|
1193
|
+
const skills = await Promise.all(loader.getSkills().skills.map(async (skill) => {
|
|
1194
|
+
let content = '';
|
|
1195
|
+
try {
|
|
1196
|
+
content = await readFile(skill.filePath, 'utf8');
|
|
1197
|
+
} catch {}
|
|
1198
|
+
return {
|
|
1199
|
+
id: resourceId('skill', skill.filePath),
|
|
1200
|
+
kind: 'skill',
|
|
1201
|
+
name: skill.name,
|
|
1202
|
+
...(skill.description ? { description: skill.description } : {}),
|
|
1203
|
+
location: resourceLocation(skill.sourceInfo),
|
|
1204
|
+
editable: false,
|
|
1205
|
+
...(content ? { content } : {}),
|
|
1206
|
+
filePath: skill.filePath,
|
|
1207
|
+
};
|
|
1107
1208
|
}));
|
|
1108
1209
|
const prompts = loader.getPrompts().prompts.map((prompt) => ({
|
|
1109
1210
|
id: resourceId('prompt', prompt.filePath),
|
|
@@ -1508,6 +1609,10 @@ export function createSessionDaemon({
|
|
|
1508
1609
|
targetDir = directory;
|
|
1509
1610
|
await rm(target.path, { force: false });
|
|
1510
1611
|
}
|
|
1612
|
+
messageEntryAliases.clearSession({ cwd: active?.cwd || targetDir, sessionId });
|
|
1613
|
+
retryStateBySession.delete(sessionId);
|
|
1614
|
+
latestUserMessageIds.delete(sessionId);
|
|
1615
|
+
latestAssistantMessageIds.delete(sessionId);
|
|
1511
1616
|
publish('session.lifecycle', { state: 'idle', deleted: true }, sessionId, targetDir);
|
|
1512
1617
|
};
|
|
1513
1618
|
|
|
@@ -1522,6 +1627,7 @@ export function createSessionDaemon({
|
|
|
1522
1627
|
? content.filter((part) => part?.type === 'text').map((part) => part.text).join('')
|
|
1523
1628
|
: '');
|
|
1524
1629
|
const messageId = `user-${sessionId}-${sequence + 1}`;
|
|
1630
|
+
messageEntryAliases.retain({ cwd: directory, sessionId, syntheticMessageId: messageId, message: event.message });
|
|
1525
1631
|
latestUserMessageIds.set(sessionId, messageId);
|
|
1526
1632
|
publish('assistant.message.start', {
|
|
1527
1633
|
messageId,
|
|
@@ -1531,6 +1637,7 @@ export function createSessionDaemon({
|
|
|
1531
1637
|
}, sessionId, directory);
|
|
1532
1638
|
} else if (event.message?.role === 'assistant') {
|
|
1533
1639
|
const messageId = `assistant-${sessionId}-${sequence + 1}`;
|
|
1640
|
+
messageEntryAliases.retain({ cwd: directory, sessionId, syntheticMessageId: messageId, message: event.message });
|
|
1534
1641
|
clearStreamingRedactionBuffers(sessionId);
|
|
1535
1642
|
streamingMessageIds.set(sessionId, messageId);
|
|
1536
1643
|
latestAssistantMessageIds.set(sessionId, messageId);
|
|
@@ -1559,6 +1666,19 @@ export function createSessionDaemon({
|
|
|
1559
1666
|
break;
|
|
1560
1667
|
}
|
|
1561
1668
|
case 'message_end': {
|
|
1669
|
+
const eventRuntime = runtimeRegistry?.get({ cwd: directory, sessionId });
|
|
1670
|
+
const syntheticMessageId = event.message?.role === 'assistant'
|
|
1671
|
+
? streamingMessageIds.get(sessionId) ?? latestAssistantMessageIds.get(sessionId)
|
|
1672
|
+
: event.message?.role === 'user'
|
|
1673
|
+
? latestUserMessageIds.get(sessionId)
|
|
1674
|
+
: undefined;
|
|
1675
|
+
messageEntryAliases.observeMessageEnd({
|
|
1676
|
+
cwd: directory,
|
|
1677
|
+
sessionId,
|
|
1678
|
+
syntheticMessageId,
|
|
1679
|
+
message: event.message,
|
|
1680
|
+
sessionManager: eventRuntime?.session?.sessionManager,
|
|
1681
|
+
});
|
|
1562
1682
|
if (event.message?.role === 'assistant') {
|
|
1563
1683
|
const content = Array.isArray(event.message.content) ? event.message.content : [];
|
|
1564
1684
|
const messageId = streamingMessageIds.get(sessionId) ?? latestAssistantMessageIds.get(sessionId) ?? `assistant-${sessionId}`;
|
|
@@ -1571,6 +1691,7 @@ export function createSessionDaemon({
|
|
|
1571
1691
|
text: redactAttachmentPaths(content.filter((part) => part?.type === 'text').map((part) => part.text).join('')),
|
|
1572
1692
|
thinking: redactAttachmentPaths(content.filter((part) => part?.type === 'thinking').map((part) => part.thinking).join('')),
|
|
1573
1693
|
durationMs,
|
|
1694
|
+
...(content.some((part) => part?.type === 'toolCall') ? { continuing: true } : {}),
|
|
1574
1695
|
...(event.message.errorMessage ? { error: { code: 'ASSISTANT_ERROR', message: redactAttachmentPaths(event.message.errorMessage) } } : {}),
|
|
1575
1696
|
...(usage ? { usage } : {}),
|
|
1576
1697
|
}, sessionId, directory);
|
|
@@ -1634,20 +1755,35 @@ export function createSessionDaemon({
|
|
|
1634
1755
|
break;
|
|
1635
1756
|
case 'agent_start':
|
|
1636
1757
|
clearIdleDisposal(sessionId);
|
|
1758
|
+
retryStateBySession.delete(sessionId);
|
|
1637
1759
|
publish('session.lifecycle', { state: 'busy' }, sessionId, directory);
|
|
1638
1760
|
break;
|
|
1639
1761
|
case 'agent_end': {
|
|
1640
|
-
latestUserMessageIds.delete(sessionId);
|
|
1641
|
-
latestAssistantMessageIds.delete(sessionId);
|
|
1642
1762
|
const finalMessage = event.messages?.at?.(-1);
|
|
1643
1763
|
if (finalMessage?.role === 'assistant' && finalMessage.stopReason === 'aborted') {
|
|
1644
1764
|
publish('session.interrupted', { reason: 'user-abort', streaming: false }, sessionId, directory);
|
|
1645
|
-
} else if (finalMessage?.role === 'assistant' && typeof finalMessage.errorMessage === 'string') {
|
|
1765
|
+
} else if (finalMessage?.role === 'assistant' && typeof finalMessage.errorMessage === 'string' && event.willRetry !== true) {
|
|
1646
1766
|
publish('session.error', { code: 'ASSISTANT_ERROR', message: redactAttachmentPaths(finalMessage.errorMessage) }, sessionId, directory);
|
|
1647
1767
|
}
|
|
1648
1768
|
break;
|
|
1649
1769
|
}
|
|
1770
|
+
case 'auto_retry_start': {
|
|
1771
|
+
const retry = {
|
|
1772
|
+
attempt: event.attempt,
|
|
1773
|
+
next: Date.now() + event.delayMs,
|
|
1774
|
+
message: redactAttachmentPaths(event.errorMessage),
|
|
1775
|
+
};
|
|
1776
|
+
retryStateBySession.set(sessionId, retry);
|
|
1777
|
+
publish('session.lifecycle', { state: 'retry', ...retry }, sessionId, directory);
|
|
1778
|
+
break;
|
|
1779
|
+
}
|
|
1780
|
+
case 'auto_retry_end':
|
|
1781
|
+
retryStateBySession.delete(sessionId);
|
|
1782
|
+
break;
|
|
1650
1783
|
case 'agent_settled':
|
|
1784
|
+
retryStateBySession.delete(sessionId);
|
|
1785
|
+
latestUserMessageIds.delete(sessionId);
|
|
1786
|
+
latestAssistantMessageIds.delete(sessionId);
|
|
1651
1787
|
publish('session.lifecycle', { state: 'idle' }, sessionId, directory);
|
|
1652
1788
|
scheduleIdleDisposal(sessionId);
|
|
1653
1789
|
break;
|
|
@@ -1684,7 +1820,7 @@ export function createSessionDaemon({
|
|
|
1684
1820
|
'projects.list', 'projects.select', 'sessions.list', 'sessions.create', 'sessions.open', 'sessions.rename', 'sessions.delete',
|
|
1685
1821
|
'sessions.tree', 'sessions.navigate', 'sessions.fork', 'sessions.clone', 'sessions.prompt',
|
|
1686
1822
|
'sessions.steer', 'sessions.followUp', 'sessions.abort', 'sessions.setModel',
|
|
1687
|
-
'sessions.setThinking', 'sessions.compact', 'providers.list', 'providers.config.get', 'providers.models.set', 'providers.status', 'providers.login',
|
|
1823
|
+
'sessions.setThinking', 'sessions.compact', 'providers.list', 'providers.refresh', 'providers.config.get', 'providers.models.set', 'providers.status', 'providers.login',
|
|
1688
1824
|
'providers.login.respond', 'providers.login.status', 'providers.logout', 'settings.get', 'settings.set',
|
|
1689
1825
|
'resources.list', 'resources.update', 'resources.prompts.create', 'resources.prompts.delete',
|
|
1690
1826
|
],
|
|
@@ -1716,6 +1852,11 @@ export function createSessionDaemon({
|
|
|
1716
1852
|
writeFrame(socket, { protocolVersion: PROTOCOL_VERSION, kind: 'response', requestId: message.requestId, result });
|
|
1717
1853
|
return;
|
|
1718
1854
|
}
|
|
1855
|
+
case 'providers.refresh': {
|
|
1856
|
+
const result = await refreshProviders(message.payload?.directory);
|
|
1857
|
+
writeFrame(socket, { protocolVersion: PROTOCOL_VERSION, kind: 'response', requestId: message.requestId, result });
|
|
1858
|
+
return;
|
|
1859
|
+
}
|
|
1719
1860
|
case 'providers.config.get': {
|
|
1720
1861
|
const result = await getProviderConfiguration(message.payload?.providerId);
|
|
1721
1862
|
writeFrame(socket, { protocolVersion: PROTOCOL_VERSION, kind: 'response', requestId: message.requestId, result });
|
|
@@ -1817,18 +1958,40 @@ export function createSessionDaemon({
|
|
|
1817
1958
|
}
|
|
1818
1959
|
case 'sessions.navigate': {
|
|
1819
1960
|
const activeRuntime = await activateSession(message.payload?.sessionId, message.payload?.directory || message.payload?.cwd);
|
|
1820
|
-
const
|
|
1821
|
-
if (typeof
|
|
1961
|
+
const requestedId = message.payload?.messageId;
|
|
1962
|
+
if (typeof requestedId !== 'string' || requestedId.length === 0) throw new SessionDaemonProtocolError('INVALID_ARGUMENT', 'The requested tree entry is invalid.');
|
|
1963
|
+
const messageId = messageEntryAliases.resolve({
|
|
1964
|
+
cwd: activeRuntime.cwd,
|
|
1965
|
+
sessionId: message.payload.sessionId,
|
|
1966
|
+
requestedId,
|
|
1967
|
+
sessionManager: activeRuntime.session.sessionManager,
|
|
1968
|
+
});
|
|
1969
|
+
const previousLeafId = activeRuntime.session.sessionManager?.getLeafId?.() ?? null;
|
|
1822
1970
|
const result = await activeRuntime.session.navigateTree(messageId);
|
|
1823
|
-
if (result
|
|
1824
|
-
|
|
1971
|
+
if (result?.cancelled) throw new SessionDaemonProtocolError('SESSION_TREE_NOT_FOUND', 'Pi cancelled tree navigation.');
|
|
1972
|
+
const newLeafId = activeRuntime.session.sessionManager?.getLeafId?.() ?? null;
|
|
1973
|
+
const navigation = {
|
|
1974
|
+
targetEntryId: messageId,
|
|
1975
|
+
previousLeafId: typeof previousLeafId === 'string' ? previousLeafId : null,
|
|
1976
|
+
newLeafId: typeof newLeafId === 'string' ? newLeafId : null,
|
|
1977
|
+
...(typeof result?.editorText === 'string' && result.editorText.length > 0 ? { editorText: result.editorText } : {}),
|
|
1978
|
+
};
|
|
1979
|
+
writeFrame(socket, { protocolVersion: PROTOCOL_VERSION, kind: 'response', requestId: message.requestId, result: { ...projectActiveSession(activeRuntime, activeRuntime.cwd), navigation } });
|
|
1825
1980
|
return;
|
|
1826
1981
|
}
|
|
1827
1982
|
case 'sessions.fork':
|
|
1828
1983
|
case 'sessions.clone': {
|
|
1829
1984
|
const activeRuntime = await activateSession(message.payload?.sessionId, message.payload?.directory || message.payload?.cwd);
|
|
1830
|
-
const
|
|
1831
|
-
if (typeof
|
|
1985
|
+
const requestedId = message.command === 'sessions.fork' ? message.payload?.messageId : activeRuntime.session.sessionManager?.getLeafId?.();
|
|
1986
|
+
if (typeof requestedId !== 'string' || requestedId.length === 0) throw new SessionDaemonProtocolError('SESSION_TREE_NOT_FOUND', 'The Pi session has no fork point.');
|
|
1987
|
+
const entryId = message.command === 'sessions.fork'
|
|
1988
|
+
? messageEntryAliases.resolve({
|
|
1989
|
+
cwd: activeRuntime.cwd,
|
|
1990
|
+
sessionId: message.payload.sessionId,
|
|
1991
|
+
requestedId,
|
|
1992
|
+
sessionManager: activeRuntime.session.sessionManager,
|
|
1993
|
+
})
|
|
1994
|
+
: requestedId;
|
|
1832
1995
|
const result = await activeRuntime.fork(entryId, { position: 'at' });
|
|
1833
1996
|
if (result.cancelled) throw new SessionDaemonProtocolError('SESSION_CREATE_CANCELLED', 'Pi cancelled session creation.');
|
|
1834
1997
|
rememberRuntimeSession();
|
|
@@ -1996,6 +2159,7 @@ export function createSessionDaemon({
|
|
|
1996
2159
|
if (platform !== 'win32') await chmod(endpoint, 0o600);
|
|
1997
2160
|
started = true;
|
|
1998
2161
|
} catch (error) {
|
|
2162
|
+
messageEntryAliases.clear();
|
|
1999
2163
|
await disposeRuntime();
|
|
2000
2164
|
server = undefined;
|
|
2001
2165
|
throw error;
|
|
@@ -2009,8 +2173,12 @@ export function createSessionDaemon({
|
|
|
2009
2173
|
clearTimeout(attempt.expiry);
|
|
2010
2174
|
}
|
|
2011
2175
|
loginAttempts.clear();
|
|
2176
|
+
retryStateBySession.clear();
|
|
2177
|
+
latestUserMessageIds.clear();
|
|
2178
|
+
latestAssistantMessageIds.clear();
|
|
2012
2179
|
for (const client of clients) client.destroy();
|
|
2013
2180
|
clients.clear();
|
|
2181
|
+
messageEntryAliases.clear();
|
|
2014
2182
|
await new Promise((resolve, reject) => {
|
|
2015
2183
|
server.close((error) => error ? reject(error) : resolve());
|
|
2016
2184
|
});
|