@sumicom/quicksave 0.8.8 → 0.8.10
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/ai/cardBuilder.d.ts +65 -4
- package/dist/ai/cardBuilder.d.ts.map +1 -1
- package/dist/ai/cardBuilder.js +338 -28
- package/dist/ai/cardBuilder.js.map +1 -1
- package/dist/ai/claudeCliProvider.d.ts +15 -0
- package/dist/ai/claudeCliProvider.d.ts.map +1 -1
- package/dist/ai/claudeCliProvider.js +87 -3
- package/dist/ai/claudeCliProvider.js.map +1 -1
- package/dist/ai/claudeCodeProvider.d.ts +4 -0
- package/dist/ai/claudeCodeProvider.d.ts.map +1 -1
- package/dist/ai/claudeCodeProvider.js +41 -0
- package/dist/ai/claudeCodeProvider.js.map +1 -1
- package/dist/ai/claudeSdkProvider.d.ts +1 -0
- package/dist/ai/claudeSdkProvider.d.ts.map +1 -1
- package/dist/ai/claudeSdkProvider.js +14 -2
- package/dist/ai/claudeSdkProvider.js.map +1 -1
- package/dist/ai/claudeTerminal/cardSynth.d.ts +63 -0
- package/dist/ai/claudeTerminal/cardSynth.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/cardSynth.js +92 -0
- package/dist/ai/claudeTerminal/cardSynth.js.map +1 -0
- package/dist/ai/claudeTerminal/hookBridge.d.ts +56 -0
- package/dist/ai/claudeTerminal/hookBridge.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/hookBridge.js +131 -0
- package/dist/ai/claudeTerminal/hookBridge.js.map +1 -0
- package/dist/ai/claudeTerminal/hookHandler.d.ts +18 -0
- package/dist/ai/claudeTerminal/hookHandler.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/hookHandler.js +124 -0
- package/dist/ai/claudeTerminal/hookHandler.js.map +1 -0
- package/dist/ai/claudeTerminal/index.d.ts +5 -0
- package/dist/ai/claudeTerminal/index.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/index.js +7 -0
- package/dist/ai/claudeTerminal/index.js.map +1 -0
- package/dist/ai/claudeTerminal/jsonlTail.d.ts +40 -0
- package/dist/ai/claudeTerminal/jsonlTail.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/jsonlTail.js +129 -0
- package/dist/ai/claudeTerminal/jsonlTail.js.map +1 -0
- package/dist/ai/claudeTerminal/provider.d.ts +106 -0
- package/dist/ai/claudeTerminal/provider.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/provider.js +579 -0
- package/dist/ai/claudeTerminal/provider.js.map +1 -0
- package/dist/ai/claudeTerminal/settingsBuilder.d.ts +44 -0
- package/dist/ai/claudeTerminal/settingsBuilder.d.ts.map +1 -0
- package/dist/ai/claudeTerminal/settingsBuilder.js +40 -0
- package/dist/ai/claudeTerminal/settingsBuilder.js.map +1 -0
- package/dist/ai/codexAppServer/cardAdapter.d.ts.map +1 -1
- package/dist/ai/codexAppServer/cardAdapter.js +13 -5
- package/dist/ai/codexAppServer/cardAdapter.js.map +1 -1
- package/dist/ai/codexAppServer/provider.d.ts +10 -2
- package/dist/ai/codexAppServer/provider.d.ts.map +1 -1
- package/dist/ai/codexAppServer/provider.js +158 -22
- package/dist/ai/codexAppServer/provider.js.map +1 -1
- package/dist/ai/openCodeProvider.d.ts +106 -0
- package/dist/ai/openCodeProvider.d.ts.map +1 -0
- package/dist/ai/openCodeProvider.js +617 -0
- package/dist/ai/openCodeProvider.js.map +1 -0
- package/dist/ai/openCodeServer.d.ts +88 -0
- package/dist/ai/openCodeServer.d.ts.map +1 -0
- package/dist/ai/openCodeServer.js +350 -0
- package/dist/ai/openCodeServer.js.map +1 -0
- package/dist/ai/profiles/project-sandbox.sb +26 -0
- package/dist/ai/provider.d.ts +54 -1
- package/dist/ai/provider.d.ts.map +1 -1
- package/dist/ai/provider.js +10 -0
- package/dist/ai/provider.js.map +1 -1
- package/dist/ai/queuedUserPrompts.d.ts +8 -0
- package/dist/ai/queuedUserPrompts.d.ts.map +1 -0
- package/dist/ai/queuedUserPrompts.js +19 -0
- package/dist/ai/queuedUserPrompts.js.map +1 -0
- package/dist/ai/realtimeTranscription.d.ts +39 -0
- package/dist/ai/realtimeTranscription.d.ts.map +1 -0
- package/dist/ai/realtimeTranscription.js +154 -0
- package/dist/ai/realtimeTranscription.js.map +1 -0
- package/dist/ai/sandboxMcp.d.ts +7 -0
- package/dist/ai/sandboxMcp.d.ts.map +1 -1
- package/dist/ai/sandboxMcp.js +4 -0
- package/dist/ai/sandboxMcp.js.map +1 -1
- package/dist/ai/sandboxMcpStdio.d.ts +1 -1
- package/dist/ai/sandboxMcpStdio.js +120 -47
- package/dist/ai/sandboxMcpStdio.js.map +1 -1
- package/dist/ai/sessionManager.d.ts +20 -1
- package/dist/ai/sessionManager.d.ts.map +1 -1
- package/dist/ai/sessionManager.js +220 -10
- package/dist/ai/sessionManager.js.map +1 -1
- package/dist/ai/sessionRegistry.d.ts +6 -0
- package/dist/ai/sessionRegistry.d.ts.map +1 -1
- package/dist/ai/sessionRegistry.js +13 -0
- package/dist/ai/sessionRegistry.js.map +1 -1
- package/dist/ai/sessionRegistryLocator.d.ts +14 -0
- package/dist/ai/sessionRegistryLocator.d.ts.map +1 -0
- package/dist/ai/sessionRegistryLocator.js +44 -0
- package/dist/ai/sessionRegistryLocator.js.map +1 -0
- package/dist/ai/systemPrompt.d.ts.map +1 -1
- package/dist/ai/systemPrompt.js +2 -1
- package/dist/ai/systemPrompt.js.map +1 -1
- package/dist/ai/voiceStream.d.ts +74 -0
- package/dist/ai/voiceStream.d.ts.map +1 -0
- package/dist/ai/voiceStream.js +179 -0
- package/dist/ai/voiceStream.js.map +1 -0
- package/dist/ai/voiceTranscription.d.ts +33 -0
- package/dist/ai/voiceTranscription.d.ts.map +1 -0
- package/dist/ai/voiceTranscription.js +122 -0
- package/dist/ai/voiceTranscription.js.map +1 -0
- package/dist/files/fileBrowser.d.ts.map +1 -1
- package/dist/files/fileBrowser.js +15 -2
- package/dist/files/fileBrowser.js.map +1 -1
- package/dist/handlers/legacyBusAdapter.d.ts +1 -1
- package/dist/handlers/legacyBusAdapter.d.ts.map +1 -1
- package/dist/handlers/legacyBusAdapter.js +7 -1
- package/dist/handlers/legacyBusAdapter.js.map +1 -1
- package/dist/handlers/messageHandler.d.ts +6 -0
- package/dist/handlers/messageHandler.d.ts.map +1 -1
- package/dist/handlers/messageHandler.js +119 -8
- package/dist/handlers/messageHandler.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/service/ipcClient.js +1 -1
- package/dist/service/ipcClient.js.map +1 -1
- package/dist/service/run.d.ts.map +1 -1
- package/dist/service/run.js +81 -3
- package/dist/service/run.js.map +1 -1
- package/dist/service/types.js +1 -1
- package/package.json +6 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import type { AgentId, Attachment, ClaudeSessionSummary, ClaudeUserInputRequestPayload, ClaudeUserInputResponsePayload, ClaudePreferences, ConfigValue, CardHistoryResponse, SessionUpdatePayload } from '@sumicom/quicksave-shared';
|
|
2
|
+
import type { AgentId, AgentProviderInfo, Attachment, ClaudeSessionSummary, ClaudeUserInputRequestPayload, ClaudeUserInputResponsePayload, ClaudePreferences, ConfigValue, CardHistoryResponse, SessionUpdatePayload } from '@sumicom/quicksave-shared';
|
|
3
3
|
import { StreamCardBuilder } from './cardBuilder.js';
|
|
4
4
|
import type { PermissionLevel, ProviderSession, ProviderCallbacks, CodingAgentProvider } from './provider.js';
|
|
5
5
|
export interface ManagedSession {
|
|
@@ -24,6 +24,11 @@ export interface ManagedSession {
|
|
|
24
24
|
/** Per-session UUID baked into the PermissionRequest hook command. The daemon
|
|
25
25
|
* toggles bypass by creating/removing the sentinel file at `bypassFlagPath(bypassToken)`. */
|
|
26
26
|
bypassToken: string;
|
|
27
|
+
/** Per-session correlation id minted at spawn and baked into the sandbox MCP
|
|
28
|
+
* server's `--corr`. Persisted onto the registry entry (`mcpCorrId`) so that
|
|
29
|
+
* stdio server can locate this session's entry on a fresh start, before the
|
|
30
|
+
* real sessionId exists. */
|
|
31
|
+
mcpCorrId: string;
|
|
27
32
|
/** Epoch ms of the most recent SDK message whose `usage` reported a cache
|
|
28
33
|
* hit or write. Provider fires `onCacheTouch` per inner API call; this is
|
|
29
34
|
* the most reliable anchor for the PWA's prompt-cache countdown because
|
|
@@ -87,8 +92,13 @@ export declare class SessionManager extends EventEmitter {
|
|
|
87
92
|
agent?: AgentId;
|
|
88
93
|
/** Attachments resolved from staging by the messageHandler. */
|
|
89
94
|
attachments?: readonly Attachment[];
|
|
95
|
+
interruptCurrentTurn?: boolean;
|
|
90
96
|
}): Promise<string>;
|
|
91
97
|
cancelSession(sessionId: string): Promise<boolean>;
|
|
98
|
+
interruptSession(sessionId: string): Promise<boolean>;
|
|
99
|
+
steerQueuedMessage(sessionId: string, opts?: {
|
|
100
|
+
interruptCurrentTurn?: boolean;
|
|
101
|
+
}): Promise<boolean>;
|
|
92
102
|
/** Send a raw control_request to the active provider session (CLI only). */
|
|
93
103
|
sendControlRequest(sessionId: string, subtype: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
94
104
|
closeSession(sessionId: string): boolean;
|
|
@@ -124,6 +134,10 @@ export declare class SessionManager extends EventEmitter {
|
|
|
124
134
|
* `get_context_usage` (only the claude-code CLI does). */
|
|
125
135
|
getSessionContextUsage(sessionId: string): Promise<import('@sumicom/quicksave-shared').ContextUsageBreakdown | null>;
|
|
126
136
|
getSessionAgent(sessionId: string, cwd?: string): AgentId;
|
|
137
|
+
/** Correlation id minted for this session's sandbox MCP server, persisted
|
|
138
|
+
* onto the registry entry so the stdio server can locate it on a fresh
|
|
139
|
+
* start. Undefined for sessions not tracked in memory. */
|
|
140
|
+
getSessionMcpCorrId(sessionId: string): string | undefined;
|
|
127
141
|
getActiveSessionCount(): number;
|
|
128
142
|
getCardBuilder(sessionId: string): StreamCardBuilder | null;
|
|
129
143
|
listAvailableSessions(cwd: string): Promise<ClaudeSessionSummary[]>;
|
|
@@ -193,6 +207,11 @@ export declare class SessionManager extends EventEmitter {
|
|
|
193
207
|
snapshotActiveSessions(): SessionUpdatePayload[];
|
|
194
208
|
private persistRegistryField;
|
|
195
209
|
private persistAllowPattern;
|
|
210
|
+
/** Probe every registered provider and return discoverable metadata.
|
|
211
|
+
|
|
212
|
+
* Used as the response to `agent:probe` commands and to populate the
|
|
213
|
+
* `availableProviders` field of the {@link HandshakeAckPayload}. */
|
|
214
|
+
probeProviders(): Promise<AgentProviderInfo[]>;
|
|
196
215
|
private waitForUserInput;
|
|
197
216
|
}
|
|
198
217
|
//# sourceMappingURL=sessionManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionManager.d.ts","sourceRoot":"","sources":["../../src/ai/sessionManager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAMtC,OAAO,KAAK,EACV,OAAO,EACP,UAAU,EACV,oBAAoB,EACpB,6BAA6B,EAC7B,8BAA8B,EAC9B,iBAAiB,EACjB,WAAW,
|
|
1
|
+
{"version":3,"file":"sessionManager.d.ts","sourceRoot":"","sources":["../../src/ai/sessionManager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAMtC,OAAO,KAAK,EACV,OAAO,EACP,iBAAiB,EAEjB,UAAU,EACV,oBAAoB,EACpB,6BAA6B,EAC7B,8BAA8B,EAC9B,iBAAiB,EACjB,WAAW,EAGX,mBAAmB,EAMnB,oBAAoB,EACrB,MAAM,2BAA2B,CAAC;AAOnC,OAAO,EAAE,iBAAiB,EAA6C,MAAM,kBAAkB,CAAC;AAMhG,OAAO,KAAK,EAGV,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,eAAe,CAAC;AAyJvB,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACtC,yGAAyG;IACzG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;6EAMyE;IACzE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;kGAC8F;IAC9F,WAAW,EAAE,MAAM,CAAC;IACpB;;;iCAG6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;+EAK2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;0CAEsC;IACtC,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC;AAgBD;gEACgE;AAChE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAuBD,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,QAAQ,CAA0C;IAC1D,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,kBAAkB,CAA2C;IACrE,OAAO,CAAC,gBAAgB,CAAmC;IAC3D,OAAO,CAAC,cAAc,CAAuD;IAC7E,OAAO,CAAC,oBAAoB,CAA4C;IACxE,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,WAAW,CAA+C;IAClE,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,cAAc,CAAU;IAEhC,kGAAkG;IAClG,OAAO,CAAC,kBAAkB,CAA6G;gBAE3H,SAAS,EAAE,mBAAmB,EAAE,EAAE,cAAc,GAAE,OAAuB;IAQrF,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,cAAc;IAoCtB,OAAO,CAAC,0BAA0B;IAmB5B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,cAAc,IAAI,iBAAiB;IAInC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,iBAAiB;IA6BpE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC;IAIhE;oDACgD;IAChD,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAQ7D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IA0H1G,YAAY,CAAC,IAAI,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,8EAA8E;QAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,+DAA+D;QAC/D,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;KACrC,GAAG,OAAO,CAAC,MAAM,CAAC;IA4Fb,aAAa,CAAC,IAAI,EAAE;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,+DAA+D;QAC/D,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;QACpC,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,GAAG,OAAO,CAAC,MAAM,CAAC;IAoOb,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAalD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASrD,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,oBAAoB,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAWxG,4EAA4E;IACtE,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAYhH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAiBxC;;6DAEyD;IACzD,OAAO,CAAC,6BAA6B;IAU/B,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuErF,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe;IAOtD;;;;;;;OAOG;IACH,OAAO,CAAC,gCAAgC;IAgBxC,gBAAgB,CAAC,QAAQ,EAAE,8BAA8B,GAAG,OAAO;IAkCnE,iBAAiB,IAAI;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,eAAe,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE;IAetJ,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIvC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIlC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpD;;8DAE0D;IACpD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,2BAA2B,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAO1H,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO;IAIzD;;+DAE2D;IAC3D,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI1D,qBAAqB,IAAI,MAAM;IAI/B,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAIrD,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAoDnE,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,SAAI,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA4EpG,uBAAuB,IAAI,6BAA6B,EAAE;IAI1D;;iEAE6D;IAC7D,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAOrD,aAAa;;;;;;;;;uBAtMqB,MAAM;iBAAO,MAAM;yBAAe,OAAO;6BAAmB,OAAO;4BAAkB,MAAM;wBAAc,OAAO;;;IAiNlJ,OAAO,IAAI,IAAI;IAiBf,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB;YA2EpC,uBAAuB;IAuFrC,OAAO,CAAC,iBAAiB;IAgDzB;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IA6F3B;;sEAEkE;IAClE,OAAO,CAAC,qBAAqB;IA4B7B;;;;;OAKG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI1C;;;;OAIG;IACH,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,oBAAoB;IA0DlE;;;;;;;;6EAQyE;IACzE,sBAAsB,IAAI,oBAAoB,EAAE;IAIhD,OAAO,CAAC,oBAAoB;YAiBd,mBAAmB;IAsBjC;;;wEAGoE;IAC9D,cAAc,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAsBpD,OAAO,CAAC,gBAAgB;CAiBzB"}
|
|
@@ -13,7 +13,18 @@ import { SANDBOX_BASH_TOOL, UPDATE_SESSION_STATUS_TOOL } from './sandboxMcp.js';
|
|
|
13
13
|
import { getSessionRegistry } from './sessionRegistry.js';
|
|
14
14
|
import { getEventStore } from '../storage/eventStore.js';
|
|
15
15
|
import { buildSystemPrompt } from './systemPrompt.js';
|
|
16
|
-
import { defaultPermissionLevelForAgent, isFullAccessPermission, isPermissionLevelAcceptedForAgent, normalizePermissionLevelForAgent, } from './provider.js';
|
|
16
|
+
import { defaultPermissionLevelForAgent, getAgentLabel, isFullAccessPermission, isPermissionLevelAcceptedForAgent, normalizePermissionLevelForAgent, } from './provider.js';
|
|
17
|
+
/** Capabilities sentinel for providers that haven't implemented `probeProvider`
|
|
18
|
+
* yet. Lets the handshake still report the provider's existence without
|
|
19
|
+
* fabricating unsupported features. */
|
|
20
|
+
const EMPTY_AGENT_CAPABILITIES = {
|
|
21
|
+
hasApiKey: false,
|
|
22
|
+
hasCli: false,
|
|
23
|
+
hasPlugin: false,
|
|
24
|
+
supportsResume: false,
|
|
25
|
+
supportsSandbox: false,
|
|
26
|
+
supportsStreaming: false,
|
|
27
|
+
};
|
|
17
28
|
/**
|
|
18
29
|
* Events emitted:
|
|
19
30
|
* 'card-event' (event: CardEvent)
|
|
@@ -27,12 +38,15 @@ import { defaultPermissionLevelForAgent, isFullAccessPermission, isPermissionLev
|
|
|
27
38
|
/** Tools auto-approved at each permission level (no user prompt). */
|
|
28
39
|
const CLAUDE_AUTO_APPROVE = {
|
|
29
40
|
bypassPermissions: new Set([
|
|
30
|
-
'Edit', 'Write', 'NotebookEdit', 'TodoWrite', 'Agent', 'EnterWorktree',
|
|
41
|
+
'Edit', 'Write', 'NotebookEdit', 'TodoWrite', 'Agent', 'EnterWorktree',
|
|
31
42
|
'WebFetch', 'WebSearch', 'Bash',
|
|
32
43
|
'Skill', 'ToolSearch', 'Config',
|
|
33
44
|
'CronCreate', 'CronDelete', 'CronList', 'RemoteTrigger',
|
|
34
|
-
'EnterPlanMode',
|
|
45
|
+
'EnterPlanMode',
|
|
35
46
|
'TaskOutput', 'TaskStop',
|
|
47
|
+
// ExitWorktree intentionally absent: exiting a worktree must require user confirmation.
|
|
48
|
+
// ExitPlanMode intentionally absent: plan review must always reach the user.
|
|
49
|
+
// AskUserQuestion intentionally absent: interactive prompts must always reach the user.
|
|
36
50
|
]),
|
|
37
51
|
acceptEdits: new Set(['Edit', 'Write', 'NotebookEdit', 'TodoWrite', 'Agent', 'EnterWorktree', 'ExitWorktree', 'EnterPlanMode']),
|
|
38
52
|
default: new Set(['TodoWrite', 'EnterWorktree', 'ExitWorktree', 'Agent', 'EnterPlanMode']),
|
|
@@ -61,6 +75,29 @@ const SESSION_STAGES = [
|
|
|
61
75
|
function isSessionStage(value) {
|
|
62
76
|
return SESSION_STAGES.includes(value);
|
|
63
77
|
}
|
|
78
|
+
function parsePendingMissionUntil(value) {
|
|
79
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0)
|
|
80
|
+
return value;
|
|
81
|
+
if (typeof value !== 'string' || value.trim().length === 0)
|
|
82
|
+
return null;
|
|
83
|
+
const parsed = Date.parse(value);
|
|
84
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
85
|
+
}
|
|
86
|
+
function buildPendingMission(input, existing) {
|
|
87
|
+
const label = typeof input.pendingMissionLabel === 'string' && input.pendingMissionLabel.trim().length > 0
|
|
88
|
+
? input.pendingMissionLabel.trim()
|
|
89
|
+
: existing?.label;
|
|
90
|
+
const until = parsePendingMissionUntil(input.pendingMissionUntil) ?? existing?.until;
|
|
91
|
+
if (!label || typeof until !== 'number')
|
|
92
|
+
return null;
|
|
93
|
+
const isNewSchedule = existing?.label !== label || existing?.until !== until;
|
|
94
|
+
return {
|
|
95
|
+
label,
|
|
96
|
+
until,
|
|
97
|
+
startedAt: existing?.startedAt ?? Date.now(),
|
|
98
|
+
...(isNewSchedule || existing?.dismissedAt === undefined ? {} : { dismissedAt: existing.dismissedAt }),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
64
101
|
function autoApproveToolsFor(agentId, level) {
|
|
65
102
|
const normalized = normalizePermissionLevelForAgent(agentId, level);
|
|
66
103
|
if (agentId === 'codex') {
|
|
@@ -99,9 +136,21 @@ function normalizeAgentId(value) {
|
|
|
99
136
|
if (value === 'claude-code' || value === 'claude-cli' || value === 'claude-sdk') {
|
|
100
137
|
return 'claude-code';
|
|
101
138
|
}
|
|
139
|
+
// claude-terminal is a distinct provider (TUI + PTY), NOT an alias of
|
|
140
|
+
// claude-code. Omitting it here made resolveAgentId() return undefined for
|
|
141
|
+
// claude-terminal sessions on every config/registry/request path, so a cold
|
|
142
|
+
// resume (when the in-memory agentId is gone) silently fell back to the
|
|
143
|
+
// default agent — downgrading the session to claude-code and dropping its
|
|
144
|
+
// terminalId. See sessionManager resolveAgentId() fallback chain.
|
|
145
|
+
if (value === 'claude-terminal')
|
|
146
|
+
return 'claude-terminal';
|
|
102
147
|
if (value === 'codex' || value === 'codex-mcp') {
|
|
103
148
|
return 'codex';
|
|
104
149
|
}
|
|
150
|
+
if (value === 'opencode')
|
|
151
|
+
return 'opencode';
|
|
152
|
+
if (value === 'pi')
|
|
153
|
+
return 'pi';
|
|
105
154
|
return undefined;
|
|
106
155
|
}
|
|
107
156
|
/** How often we record a `cache_touched` event to the event store. Anthropic's
|
|
@@ -263,10 +312,33 @@ export class SessionManager extends EventEmitter {
|
|
|
263
312
|
// agent prefs bucket — causing a Codex GPT pick to corrupt the user's
|
|
264
313
|
// saved Claude default and leak across sessions on resume.
|
|
265
314
|
// Codex app-server sessions pick the change up on the next turn/start
|
|
266
|
-
// via the runtime override pipeline.
|
|
267
|
-
// session; the value here is consumed by the next cold resume.
|
|
315
|
+
// via the runtime override pipeline.
|
|
268
316
|
this.enqueueCodexOverrideIfApplicable(sessionId, { model: value });
|
|
269
317
|
this.persistRegistryField(sessionId, 'model', value);
|
|
318
|
+
// For Claude CLI sessions, also drive a `set_model` control_request so
|
|
319
|
+
// the running process switches immediately rather than waiting for the
|
|
320
|
+
// next prompt's cold-respawn. The CLI keeps the same session_id when
|
|
321
|
+
// it accepts `set_model`, so we also bump `spawnedModel` to keep
|
|
322
|
+
// resumeSession's modelChanged check from kicking off an unnecessary
|
|
323
|
+
// respawn on the next turn.
|
|
324
|
+
const ps = this.sessions.get(sessionId);
|
|
325
|
+
const cliSession = ps?.providerSession;
|
|
326
|
+
if (ps?.providerSession?.alive && cliSession && typeof cliSession.sendControlRequest === 'function') {
|
|
327
|
+
const { decorateModelWithContextWindow } = await import('./claudeCliProvider.js');
|
|
328
|
+
const ctxWindow = next.contextWindow ?? ps.spawnedContextWindow;
|
|
329
|
+
const decorated = decorateModelWithContextWindow(value, ctxWindow) ?? value;
|
|
330
|
+
try {
|
|
331
|
+
await cliSession.sendControlRequest('set_model', { model: decorated }, 5_000);
|
|
332
|
+
ps.spawnedModel = value;
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
// CLI rejected — fall back to cold-respawn on next prompt via the
|
|
336
|
+
// modelChanged check. Leave spawnedModel untouched so that check
|
|
337
|
+
// fires. Don't roll back sessionConfigs: the user-intended value is
|
|
338
|
+
// what should apply on respawn.
|
|
339
|
+
console.warn(`[session-manager] set_model failed for ${sessionId.slice(0, 8)}, will cold-respawn on next prompt:`, err);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
270
342
|
}
|
|
271
343
|
else if (key === 'reasoningEffort' && (typeof value === 'string' || value === null)) {
|
|
272
344
|
const effort = value === null ? null : value;
|
|
@@ -362,6 +434,10 @@ export class SessionManager extends EventEmitter {
|
|
|
362
434
|
// the sentinel file without a respawn.
|
|
363
435
|
const bypassToken = randomUUID();
|
|
364
436
|
applyBypassFlag(bypassToken, isFullAccessPermission(provider.id, level));
|
|
437
|
+
// Mint a correlation id up front so the sandbox MCP server (spawned by the
|
|
438
|
+
// provider before the real sessionId exists) can find this session's
|
|
439
|
+
// registry entry by matching `mcpCorrId`. See `sandboxMcpStdio.ts`.
|
|
440
|
+
const mcpCorrId = randomUUID();
|
|
365
441
|
// Create cardBuilder with 'pending' sessionId — will be updated after provider returns real one
|
|
366
442
|
const cardBuilder = new StreamCardBuilder('pending', opts.cwd);
|
|
367
443
|
const callbacks = this.makeCallbacks(provider.id);
|
|
@@ -375,6 +451,7 @@ export class SessionManager extends EventEmitter {
|
|
|
375
451
|
reasoningEffort: opts.reasoningEffort,
|
|
376
452
|
contextWindow: opts.contextWindow,
|
|
377
453
|
bypassFlagPath: bypassFlagPath(bypassToken),
|
|
454
|
+
mcpCorrId,
|
|
378
455
|
attachments: opts.attachments,
|
|
379
456
|
}, cardBuilder, callbacks);
|
|
380
457
|
// Update cardBuilder with real sessionId — new session has no prior JSONL
|
|
@@ -402,6 +479,7 @@ export class SessionManager extends EventEmitter {
|
|
|
402
479
|
...(recordedModel !== undefined ? { model: recordedModel } : {}),
|
|
403
480
|
...(opts.reasoningEffort !== undefined ? { reasoningEffort: opts.reasoningEffort } : {}),
|
|
404
481
|
...(opts.contextWindow !== undefined ? { contextWindow: opts.contextWindow } : {}),
|
|
482
|
+
...(providerSession?.terminalId !== undefined ? { terminalId: providerSession.terminalId } : {}),
|
|
405
483
|
permissionMode: level,
|
|
406
484
|
sandboxed,
|
|
407
485
|
});
|
|
@@ -417,6 +495,7 @@ export class SessionManager extends EventEmitter {
|
|
|
417
495
|
spawnedModel: opts.model,
|
|
418
496
|
spawnedContextWindow: opts.contextWindow,
|
|
419
497
|
bypassToken,
|
|
498
|
+
mcpCorrId,
|
|
420
499
|
};
|
|
421
500
|
this.sessions.set(sessionId, managed);
|
|
422
501
|
this.emitSessionUpdate(sessionId);
|
|
@@ -463,7 +542,17 @@ export class SessionManager extends EventEmitter {
|
|
|
463
542
|
if (opts.attachments && opts.attachments.length > 0) {
|
|
464
543
|
await persistAttachments(opts.sessionId, opts.attachments);
|
|
465
544
|
}
|
|
466
|
-
|
|
545
|
+
if (opts.interruptCurrentTurn && existing.providerSession.interruptThenSendUserMessage) {
|
|
546
|
+
existing.providerSession.interruptThenSendUserMessage(opts.prompt, opts.attachments);
|
|
547
|
+
}
|
|
548
|
+
else if (opts.interruptCurrentTurn) {
|
|
549
|
+
existing.providerSession.interrupt();
|
|
550
|
+
existing.providerSession.sendUserMessage(opts.prompt, opts.attachments);
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
existing.providerSession.sendUserMessage(opts.prompt, opts.attachments);
|
|
554
|
+
}
|
|
555
|
+
this.emitSessionUpdate(opts.sessionId);
|
|
467
556
|
return opts.sessionId;
|
|
468
557
|
}
|
|
469
558
|
// Hot resume (idle): CLI alive but between turns — reuse the same process
|
|
@@ -524,6 +613,10 @@ export class SessionManager extends EventEmitter {
|
|
|
524
613
|
// e.g. after a daemon restart).
|
|
525
614
|
const bypassToken = existing?.bypassToken ?? randomUUID();
|
|
526
615
|
applyBypassFlag(bypassToken, isFullAccessPermission(provider.id, level));
|
|
616
|
+
// Reuse the session's existing correlation id (in-memory or persisted on
|
|
617
|
+
// the registry entry) so a cold re-spawn's MCP server still matches the
|
|
618
|
+
// same entry; mint one only when resuming a session we've never tracked.
|
|
619
|
+
const mcpCorrId = existing?.mcpCorrId ?? registryEntry?.mcpCorrId ?? randomUUID();
|
|
527
620
|
const { sessionId, session: providerSession } = await provider.resumeSession({
|
|
528
621
|
sessionId: opts.sessionId,
|
|
529
622
|
prompt: opts.prompt,
|
|
@@ -535,6 +628,7 @@ export class SessionManager extends EventEmitter {
|
|
|
535
628
|
reasoningEffort: resumeReasoningEffort,
|
|
536
629
|
contextWindow: resumeContextWindow,
|
|
537
630
|
bypassFlagPath: bypassFlagPath(bypassToken),
|
|
631
|
+
mcpCorrId,
|
|
538
632
|
attachments: opts.attachments,
|
|
539
633
|
}, cardBuilder, callbacks);
|
|
540
634
|
// Seed sessionConfigs so /sessions/config snapshot reflects the
|
|
@@ -567,6 +661,7 @@ export class SessionManager extends EventEmitter {
|
|
|
567
661
|
existing.streaming = true;
|
|
568
662
|
existing.spawnedModel = resumeModel;
|
|
569
663
|
existing.spawnedContextWindow = resumeContextWindow;
|
|
664
|
+
existing.mcpCorrId = mcpCorrId;
|
|
570
665
|
// Cold resume can fork a new CLI session_id. Rekey the map + side
|
|
571
666
|
// maps to the new ID so emitSessionUpdate finds the entry and the
|
|
572
667
|
// PWA sees isActive=true (the ghost-inactive bug otherwise).
|
|
@@ -592,6 +687,7 @@ export class SessionManager extends EventEmitter {
|
|
|
592
687
|
spawnedModel: resumeModel,
|
|
593
688
|
spawnedContextWindow: resumeContextWindow,
|
|
594
689
|
bypassToken,
|
|
690
|
+
mcpCorrId,
|
|
595
691
|
};
|
|
596
692
|
this.sessions.set(sessionId, managed);
|
|
597
693
|
}
|
|
@@ -625,6 +721,27 @@ export class SessionManager extends EventEmitter {
|
|
|
625
721
|
this.cancelPendingInputsForSession(sessionId);
|
|
626
722
|
return true;
|
|
627
723
|
}
|
|
724
|
+
async interruptSession(sessionId) {
|
|
725
|
+
const ps = this.sessions.get(sessionId);
|
|
726
|
+
if (!ps?.providerSession)
|
|
727
|
+
return false;
|
|
728
|
+
console.log(`[session-manager] interrupt current turn session=${sessionId.slice(0, 8)}`);
|
|
729
|
+
ps.providerSession.interrupt();
|
|
730
|
+
this.emitSessionUpdate(sessionId);
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
async steerQueuedMessage(sessionId, opts) {
|
|
734
|
+
const ps = this.sessions.get(sessionId);
|
|
735
|
+
if (!ps?.providerSession?.alive)
|
|
736
|
+
return false;
|
|
737
|
+
const steerQueuedMessage = ps.providerSession.steerQueuedMessage;
|
|
738
|
+
if (!steerQueuedMessage)
|
|
739
|
+
return false;
|
|
740
|
+
console.log(`[session-manager] steer queued message session=${sessionId.slice(0, 8)} interrupt=${opts?.interruptCurrentTurn === true}`);
|
|
741
|
+
const ok = await steerQueuedMessage.call(ps.providerSession, opts);
|
|
742
|
+
this.emitSessionUpdate(sessionId);
|
|
743
|
+
return ok;
|
|
744
|
+
}
|
|
628
745
|
/** Send a raw control_request to the active provider session (CLI only). */
|
|
629
746
|
async sendControlRequest(sessionId, subtype, params) {
|
|
630
747
|
const ps = this.sessions.get(sessionId);
|
|
@@ -767,10 +884,13 @@ export class SessionManager extends EventEmitter {
|
|
|
767
884
|
return false;
|
|
768
885
|
this.pendingInputRequests.delete(response.requestId);
|
|
769
886
|
pending.resolve(response);
|
|
770
|
-
// Clear pending input on the card
|
|
887
|
+
// Clear pending input on the card; attach rejection reason if present
|
|
771
888
|
const ps = this.sessions.get(pending.request.sessionId);
|
|
772
889
|
if (ps?.cardBuilder) {
|
|
773
|
-
const
|
|
890
|
+
const rejectionAnswers = (response.action === 'deny' && response.response)
|
|
891
|
+
? { _rejection: response.response }
|
|
892
|
+
: undefined;
|
|
893
|
+
const cardEvt = ps.cardBuilder.clearPendingInput(response.requestId, rejectionAnswers);
|
|
774
894
|
if (cardEvt)
|
|
775
895
|
this.emit('card-event', cardEvt);
|
|
776
896
|
}
|
|
@@ -825,6 +945,12 @@ export class SessionManager extends EventEmitter {
|
|
|
825
945
|
getSessionAgent(sessionId, cwd) {
|
|
826
946
|
return this.resolveAgentId(sessionId, cwd);
|
|
827
947
|
}
|
|
948
|
+
/** Correlation id minted for this session's sandbox MCP server, persisted
|
|
949
|
+
* onto the registry entry so the stdio server can locate it on a fresh
|
|
950
|
+
* start. Undefined for sessions not tracked in memory. */
|
|
951
|
+
getSessionMcpCorrId(sessionId) {
|
|
952
|
+
return this.sessions.get(sessionId)?.mcpCorrId;
|
|
953
|
+
}
|
|
828
954
|
getActiveSessionCount() {
|
|
829
955
|
return this.sessions.size;
|
|
830
956
|
}
|
|
@@ -871,6 +997,7 @@ export class SessionManager extends EventEmitter {
|
|
|
871
997
|
lastTurnCacheReadTokens: lastTurn?.cacheReadTokens,
|
|
872
998
|
lastTurnContextUsage: lastTurn?.contextUsage,
|
|
873
999
|
lastReadAt: entry.lastReadAt,
|
|
1000
|
+
pendingMission: entry.pendingMission,
|
|
874
1001
|
};
|
|
875
1002
|
});
|
|
876
1003
|
}
|
|
@@ -887,7 +1014,12 @@ export class SessionManager extends EventEmitter {
|
|
|
887
1014
|
// Memory-mode: use in-memory cards from active turn, falling back to persisted history
|
|
888
1015
|
const streamCards = ps?.cardBuilder?.getCards() ?? [];
|
|
889
1016
|
const persisted = await loadPersistedCards(sessionId);
|
|
890
|
-
const
|
|
1017
|
+
const cardsById = new Map();
|
|
1018
|
+
for (const card of persisted)
|
|
1019
|
+
cardsById.set(card.id, card);
|
|
1020
|
+
for (const card of streamCards)
|
|
1021
|
+
cardsById.set(card.id, card);
|
|
1022
|
+
const cards = Array.from(cardsById.values());
|
|
891
1023
|
const total = cards.length;
|
|
892
1024
|
const start = Math.max(0, total - offset - limit);
|
|
893
1025
|
const end = Math.max(0, total - offset);
|
|
@@ -1025,6 +1157,13 @@ export class SessionManager extends EventEmitter {
|
|
|
1025
1157
|
}
|
|
1026
1158
|
this.emitSessionUpdate(sessionId);
|
|
1027
1159
|
},
|
|
1160
|
+
onQueueStateChange: (sessionId) => {
|
|
1161
|
+
const ps = this.sessions.get(sessionId);
|
|
1162
|
+
if (ps?.providerSession?.alive) {
|
|
1163
|
+
ps.streaming = true;
|
|
1164
|
+
}
|
|
1165
|
+
this.emitSessionUpdate(sessionId);
|
|
1166
|
+
},
|
|
1028
1167
|
onModelDetected: (model) => {
|
|
1029
1168
|
console.log(`[session-manager] model detected: ${model} (agent=${agentId})`);
|
|
1030
1169
|
if (agentId === 'claude-code') {
|
|
@@ -1136,6 +1275,16 @@ export class SessionManager extends EventEmitter {
|
|
|
1136
1275
|
const level = this.sessions.get(sessionId)?.permissionLevel
|
|
1137
1276
|
?? this.sessionPermissions.get(sessionId)
|
|
1138
1277
|
?? defaultPermissionLevelForAgent(agentId);
|
|
1278
|
+
// In full-access (bypass) mode the PermissionRequest hook handles built-in
|
|
1279
|
+
// tools via the sentinel file. MCP tools (full names like
|
|
1280
|
+
// "mcp__quicksave-sandbox__TaskCreate") bypass the hook entirely in default
|
|
1281
|
+
// CLI mode and ALWAYS send can_use_tool to the daemon. Rather than keeping
|
|
1282
|
+
// a fragile explicit list, approve everything here except the three tools
|
|
1283
|
+
// that must always reach the user (matching the hook's own exclusion list).
|
|
1284
|
+
if (isFullAccessPermission(agentId, level)) {
|
|
1285
|
+
const BYPASS_NEVER_SKIP = new Set(['AskUserQuestion', 'ExitPlanMode', 'ExitWorktree']);
|
|
1286
|
+
return !BYPASS_NEVER_SKIP.has(toolName);
|
|
1287
|
+
}
|
|
1139
1288
|
if (!autoApproveToolsFor(agentId, level).has(toolName))
|
|
1140
1289
|
return false;
|
|
1141
1290
|
// For file-writing tools, restrict to project cwd
|
|
@@ -1165,6 +1314,9 @@ export class SessionManager extends EventEmitter {
|
|
|
1165
1314
|
const entryUpdates = {};
|
|
1166
1315
|
let noteToAppend = null;
|
|
1167
1316
|
let changed = false;
|
|
1317
|
+
const registryEntry = this.sessions.get(sessionId)?.cwd
|
|
1318
|
+
? getSessionRegistry().getEntry(this.sessions.get(sessionId).cwd, sessionId)
|
|
1319
|
+
: undefined;
|
|
1168
1320
|
if (typeof input.subject === 'string' && input.subject.length > 0) {
|
|
1169
1321
|
configUpdates.title = input.subject;
|
|
1170
1322
|
entryUpdates.title = input.subject;
|
|
@@ -1174,6 +1326,12 @@ export class SessionManager extends EventEmitter {
|
|
|
1174
1326
|
configUpdates.stage = input.stage;
|
|
1175
1327
|
entryUpdates.stage = input.stage;
|
|
1176
1328
|
changed = true;
|
|
1329
|
+
if (input.stage === 'done') {
|
|
1330
|
+
configUpdates.pendingMissionLabel = null;
|
|
1331
|
+
configUpdates.pendingMissionUntil = null;
|
|
1332
|
+
configUpdates.pendingMissionDismissedAt = null;
|
|
1333
|
+
entryUpdates.pendingMission = undefined;
|
|
1334
|
+
}
|
|
1177
1335
|
}
|
|
1178
1336
|
if (typeof input.blocked === 'boolean') {
|
|
1179
1337
|
configUpdates.blocked = input.blocked;
|
|
@@ -1186,6 +1344,23 @@ export class SessionManager extends EventEmitter {
|
|
|
1186
1344
|
noteToAppend = input.note;
|
|
1187
1345
|
changed = true;
|
|
1188
1346
|
}
|
|
1347
|
+
if (input.clearPendingMission === true) {
|
|
1348
|
+
configUpdates.pendingMissionLabel = null;
|
|
1349
|
+
configUpdates.pendingMissionUntil = null;
|
|
1350
|
+
configUpdates.pendingMissionDismissedAt = null;
|
|
1351
|
+
entryUpdates.pendingMission = undefined;
|
|
1352
|
+
changed = true;
|
|
1353
|
+
}
|
|
1354
|
+
else if (input.pendingMissionLabel !== undefined || input.pendingMissionUntil !== undefined) {
|
|
1355
|
+
const pendingMission = buildPendingMission(input, registryEntry?.pendingMission);
|
|
1356
|
+
if (pendingMission) {
|
|
1357
|
+
configUpdates.pendingMissionLabel = pendingMission.label;
|
|
1358
|
+
configUpdates.pendingMissionUntil = pendingMission.until;
|
|
1359
|
+
configUpdates.pendingMissionDismissedAt = pendingMission.dismissedAt ?? null;
|
|
1360
|
+
entryUpdates.pendingMission = pendingMission;
|
|
1361
|
+
changed = true;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1189
1364
|
if (!changed)
|
|
1190
1365
|
return;
|
|
1191
1366
|
const prev = this.sessionConfigs.get(sessionId) ?? {};
|
|
@@ -1195,7 +1370,7 @@ export class SessionManager extends EventEmitter {
|
|
|
1195
1370
|
const ps = this.sessions.get(sessionId);
|
|
1196
1371
|
if (ps?.cwd) {
|
|
1197
1372
|
const registry = getSessionRegistry();
|
|
1198
|
-
const entry = registry.getEntry(ps.cwd, sessionId);
|
|
1373
|
+
const entry = registryEntry ?? registry.getEntry(ps.cwd, sessionId);
|
|
1199
1374
|
if (entry) {
|
|
1200
1375
|
let nextHistory = entry.noteHistory;
|
|
1201
1376
|
if (noteToAppend !== null) {
|
|
@@ -1211,6 +1386,9 @@ export class SessionManager extends EventEmitter {
|
|
|
1211
1386
|
registry.upsertEntry({
|
|
1212
1387
|
...entry,
|
|
1213
1388
|
...entryUpdates,
|
|
1389
|
+
...(Object.prototype.hasOwnProperty.call(entryUpdates, 'pendingMission')
|
|
1390
|
+
? { pendingMission: entryUpdates.pendingMission }
|
|
1391
|
+
: {}),
|
|
1214
1392
|
...(nextHistory !== entry.noteHistory ? { noteHistory: nextHistory } : {}),
|
|
1215
1393
|
lastAccessedAt: Date.now(),
|
|
1216
1394
|
});
|
|
@@ -1292,6 +1470,7 @@ export class SessionManager extends EventEmitter {
|
|
|
1292
1470
|
agent: ps?.agentId ?? this.sessionAgents.get(sessionId),
|
|
1293
1471
|
isStreaming: ps?.streaming ?? false,
|
|
1294
1472
|
hasPendingInput,
|
|
1473
|
+
queueState: ps?.providerSession?.getQueueState?.() ?? null,
|
|
1295
1474
|
permissionMode: ps?.permissionLevel ?? this.sessionPermissions.get(sessionId),
|
|
1296
1475
|
sandboxed: ps?.sandboxed ?? this.sessionSandboxed.get(sessionId) ?? false,
|
|
1297
1476
|
lastPromptAt: stats.lastPromptAt ?? undefined,
|
|
@@ -1309,6 +1488,12 @@ export class SessionManager extends EventEmitter {
|
|
|
1309
1488
|
// update so each PWA client converges on the latest read state without
|
|
1310
1489
|
// having to wait for the slower /sessions/history snapshot to roll over.
|
|
1311
1490
|
lastReadAt: registryEntry?.lastReadAt,
|
|
1491
|
+
pendingMission: registryEntry?.pendingMission,
|
|
1492
|
+
// Surface the PTY terminal id when this provider owns one. Only the
|
|
1493
|
+
// claude-terminal provider populates it today. The PWA mirrors it onto
|
|
1494
|
+
// ClaudeSessionSummary so the session view can render a TerminalView
|
|
1495
|
+
// alongside the structured card stream.
|
|
1496
|
+
terminalId: ps?.providerSession?.terminalId ?? undefined,
|
|
1312
1497
|
};
|
|
1313
1498
|
}
|
|
1314
1499
|
/** Snapshot of every in-memory session's state (both streaming and idle —
|
|
@@ -1359,6 +1544,31 @@ export class SessionManager extends EventEmitter {
|
|
|
1359
1544
|
console.log(`[persistAllowPattern] saved ${pattern} to ${settingsPath}`);
|
|
1360
1545
|
}
|
|
1361
1546
|
}
|
|
1547
|
+
/** Probe every registered provider and return discoverable metadata.
|
|
1548
|
+
|
|
1549
|
+
* Used as the response to `agent:probe` commands and to populate the
|
|
1550
|
+
* `availableProviders` field of the {@link HandshakeAckPayload}. */
|
|
1551
|
+
async probeProviders() {
|
|
1552
|
+
const results = [];
|
|
1553
|
+
for (const [id, provider] of this.providers) {
|
|
1554
|
+
const label = provider.label ?? getAgentLabel(id);
|
|
1555
|
+
if (typeof provider.probeProvider !== 'function') {
|
|
1556
|
+
// Provider hasn't been migrated yet — advertise it with zeroed
|
|
1557
|
+
// capabilities so the PWA still knows it exists.
|
|
1558
|
+
results.push({ id, label, capabilities: EMPTY_AGENT_CAPABILITIES });
|
|
1559
|
+
continue;
|
|
1560
|
+
}
|
|
1561
|
+
try {
|
|
1562
|
+
const probe = await provider.probeProvider();
|
|
1563
|
+
results.push({ id, label, ...probe });
|
|
1564
|
+
}
|
|
1565
|
+
catch (error) {
|
|
1566
|
+
console.warn(`[SessionManager] probe failed for ${id}:`, error);
|
|
1567
|
+
results.push({ id, label, capabilities: EMPTY_AGENT_CAPABILITIES });
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
return results;
|
|
1571
|
+
}
|
|
1362
1572
|
waitForUserInput(requestId, request) {
|
|
1363
1573
|
return new Promise((resolve) => {
|
|
1364
1574
|
this.pendingInputRequests.set(requestId, { resolve, request });
|