@code-yeongyu/senpi 2026.8.28 → 2026.8.29
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/CHANGELOG.md +74 -0
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +2 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +5 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +1 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -0
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/credential-accounts.d.ts.map +1 -1
- package/dist/core/credential-accounts.js +22 -12
- package/dist/core/credential-accounts.js.map +1 -1
- package/dist/core/credential-pool/classify.d.ts +6 -1
- package/dist/core/credential-pool/classify.d.ts.map +1 -1
- package/dist/core/credential-pool/classify.js +22 -5
- package/dist/core/credential-pool/classify.js.map +1 -1
- package/dist/core/credential-pool/failover.d.ts +8 -0
- package/dist/core/credential-pool/failover.d.ts.map +1 -1
- package/dist/core/credential-pool/failover.js +24 -6
- package/dist/core/credential-pool/failover.js.map +1 -1
- package/dist/core/credential-pool/rotation-stream.d.ts +13 -2
- package/dist/core/credential-pool/rotation-stream.d.ts.map +1 -1
- package/dist/core/credential-pool/rotation-stream.js +97 -10
- package/dist/core/credential-pool/rotation-stream.js.map +1 -1
- package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/index.js +7 -0
- package/dist/core/extensions/builtin/goal/index.js.map +1 -1
- package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
- package/dist/core/extensions/builtin/service-tier.js +13 -0
- package/dist/core/extensions/builtin/service-tier.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +4 -2
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/model-config-schema.d.ts +4 -4
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +74 -37
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/retry-fallback/chains.d.ts +23 -1
- package/dist/core/retry-fallback/chains.d.ts.map +1 -1
- package/dist/core/retry-fallback/chains.js +59 -3
- package/dist/core/retry-fallback/chains.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +9 -2
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/retry-fallback/settings.d.ts.map +1 -1
- package/dist/core/retry-fallback/settings.js +9 -0
- package/dist/core/retry-fallback/settings.js.map +1 -1
- package/dist/core/retry-fallback/validate.js +1 -1
- package/dist/core/retry-fallback/validate.js.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +9 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +31 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/main.d.ts +7 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +10 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.js +2 -2
- package/dist/modes/interactive/components/favorite-models-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +323 -33
- package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +276 -237
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +133 -24
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +14 -4
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +82 -13
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +85 -28
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +3 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +162 -2
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts +22 -0
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +147 -16
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +2 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +4 -0
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +7 -0
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +9 -0
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/docs/rpc.md +37 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +6 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive-host-runtime.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-host-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAgD,MAAM,6BAA6B,CAAC;AAC9G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"interactive-host-runtime.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/interactive-host-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAgD,MAAM,6BAA6B,CAAC;AAC9G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAO/E,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtE,eAAO,MAAM,iCAAiC,qEAAqE,CAAC;AAEpH,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,IAAI,EAAE,2BAA2B,GAAG,gCAAgC,CAAC;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACpC,YAAY,EACZ,oBAAoB,GAAG,4BAA4B,GAAG,iBAAiB,GAAG,2BAA2B,CACrG,GAAG;IACH,kBAAkB,IAAI,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IACrF,0BAA0B,IAAI,aAAa,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACzE,eAAe,IACZ,UAAU,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC,GAC3C,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IACxD,yBAAyB,IACtB,UAAU,CAAC,YAAY,CAAC,2BAA2B,CAAC,CAAC,GACrD,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,2BAA2B,CAAC,CAAC,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,WAAW,6BAA6B;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;IAC3G,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACtD;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CACjD,YAAY,EAAE,mBAAmB,EACjC,OAAO,EAAE,6BAA6B,GACpC,OAAO,CAAC,mBAAmB,CAAC,CAgC9B;AAqiBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,GAAG,KAAK,IAAI,OAAO,CAAC,cAAc,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAEhH"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { executeBashWithOperations } from "../../core/bash-executor.js";
|
|
2
|
+
import { SessionManager } from "../../core/session-manager.js";
|
|
3
|
+
import { SettingsManager } from "../../core/settings-manager.js";
|
|
1
4
|
import { ensureHost } from "../rpc/host-ensure.js";
|
|
2
5
|
import { RpcClient } from "../rpc/rpc-client.js";
|
|
3
6
|
export const INTERACTIVE_HOST_FALLBACK_WARNING = "Warning: shared interactive host unavailable; continuing locally";
|
|
@@ -23,7 +26,7 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
|
|
|
23
26
|
modelId: localRuntime.session.model?.id,
|
|
24
27
|
thinkingLevel: localRuntime.session.thinkingLevel,
|
|
25
28
|
});
|
|
26
|
-
const remoteSession = createRemoteSessionProxy(localRuntime.session, client, opened.state, options.onWarning);
|
|
29
|
+
const remoteSession = createRemoteSessionProxy(localRuntime.session, localRuntime.services.agentDir, client, opened.state, options.onWarning);
|
|
27
30
|
return new RemoteInteractiveRuntime(localRuntime, remoteSession, client);
|
|
28
31
|
}
|
|
29
32
|
catch (cause) {
|
|
@@ -38,23 +41,23 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
|
|
|
38
41
|
}
|
|
39
42
|
class RemoteInteractiveRuntime {
|
|
40
43
|
#local;
|
|
41
|
-
#
|
|
44
|
+
#remoteSession;
|
|
42
45
|
#client;
|
|
43
46
|
#rebindSession;
|
|
44
47
|
#beforeSessionInvalidate;
|
|
45
|
-
constructor(local,
|
|
48
|
+
constructor(local, remoteSession, client) {
|
|
46
49
|
this.#local = local;
|
|
47
|
-
this.#
|
|
50
|
+
this.#remoteSession = remoteSession;
|
|
48
51
|
this.#client = client;
|
|
49
52
|
}
|
|
50
53
|
get session() {
|
|
51
|
-
return this.#session;
|
|
54
|
+
return this.#remoteSession.session;
|
|
52
55
|
}
|
|
53
56
|
get services() {
|
|
54
57
|
return this.#local.services;
|
|
55
58
|
}
|
|
56
59
|
get cwd() {
|
|
57
|
-
return this.#
|
|
60
|
+
return this.#remoteSession.session.sessionManager.getCwd();
|
|
58
61
|
}
|
|
59
62
|
get diagnostics() {
|
|
60
63
|
return this.#local.diagnostics;
|
|
@@ -76,32 +79,66 @@ class RemoteInteractiveRuntime {
|
|
|
76
79
|
await this.#client.stop();
|
|
77
80
|
await this.#local.dispose();
|
|
78
81
|
}
|
|
79
|
-
async newSession() {
|
|
80
|
-
this.#
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
async newSession(options) {
|
|
83
|
+
const result = await this.#client.newSession(options?.parentSession);
|
|
84
|
+
if (!result.cancelled) {
|
|
85
|
+
this.#beforeSessionInvalidate?.();
|
|
86
|
+
this.#remoteSession.abortLocalBash();
|
|
87
|
+
await this.#remoteSession.refresh();
|
|
88
|
+
if (options?.setup) {
|
|
89
|
+
const capture = SessionManager.inMemory(this.#remoteSession.session.sessionManager.getCwd());
|
|
90
|
+
await options.setup(capture);
|
|
91
|
+
for (const entry of capture.getEntries())
|
|
92
|
+
await this.#client.appendSessionEntry(entry);
|
|
93
|
+
await this.#remoteSession.refresh();
|
|
94
|
+
}
|
|
83
95
|
await this.#rebindSession?.();
|
|
96
|
+
if (options?.withSession)
|
|
97
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
98
|
+
}
|
|
84
99
|
return result;
|
|
85
100
|
}
|
|
86
|
-
async switchSession(sessionPath) {
|
|
87
|
-
this.#
|
|
88
|
-
|
|
89
|
-
|
|
101
|
+
async switchSession(sessionPath, options) {
|
|
102
|
+
const result = await this.#client.switchSession(sessionPath, options);
|
|
103
|
+
if (!result.cancelled) {
|
|
104
|
+
this.#beforeSessionInvalidate?.();
|
|
105
|
+
this.#remoteSession.abortLocalBash();
|
|
106
|
+
await this.#remoteSession.refresh();
|
|
90
107
|
await this.#rebindSession?.();
|
|
108
|
+
// The shared host already resolved trust; this callback is retained for
|
|
109
|
+
// compatibility, but its result cannot override host-authoritative state.
|
|
110
|
+
options?.projectTrustContextFactory?.(this.#remoteSession.session.sessionManager.getCwd());
|
|
111
|
+
if (options?.withSession)
|
|
112
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
113
|
+
}
|
|
91
114
|
return result;
|
|
92
115
|
}
|
|
93
|
-
async fork(entryId) {
|
|
94
|
-
this.#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
116
|
+
async fork(entryId, options) {
|
|
117
|
+
const result = await this.#client.fork(entryId, options);
|
|
118
|
+
if (!result.cancelled) {
|
|
119
|
+
this.#beforeSessionInvalidate?.();
|
|
120
|
+
this.#remoteSession.abortLocalBash();
|
|
121
|
+
await this.#refreshAndRebind();
|
|
122
|
+
if (options?.withSession)
|
|
123
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
124
|
+
}
|
|
98
125
|
return { cancelled: result.cancelled, selectedText: result.text };
|
|
99
126
|
}
|
|
100
|
-
async importFromJsonl() {
|
|
101
|
-
|
|
127
|
+
async importFromJsonl(inputPath, cwdOverride) {
|
|
128
|
+
const result = await this.#client.importJsonl(inputPath, cwdOverride);
|
|
129
|
+
if (!result.cancelled) {
|
|
130
|
+
this.#beforeSessionInvalidate?.();
|
|
131
|
+
this.#remoteSession.abortLocalBash();
|
|
132
|
+
await this.#refreshAndRebind();
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
async #refreshAndRebind() {
|
|
137
|
+
await this.#remoteSession.refresh();
|
|
138
|
+
await this.#rebindSession?.();
|
|
102
139
|
}
|
|
103
140
|
}
|
|
104
|
-
function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
141
|
+
function createRemoteSessionProxy(local, agentDir, client, initialState, onWarning) {
|
|
105
142
|
// Fire-and-forget setters keep the sync AgentSession signature, but their RPC
|
|
106
143
|
// failures must not vanish: the matching *_changed wire event confirms success,
|
|
107
144
|
// and a rejection here is the only signal of failure.
|
|
@@ -112,36 +149,145 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
112
149
|
cause: error,
|
|
113
150
|
});
|
|
114
151
|
};
|
|
115
|
-
let state = initialState;
|
|
152
|
+
let state = { ...initialState };
|
|
153
|
+
let bashChunk;
|
|
154
|
+
let localBashAbortController;
|
|
155
|
+
let localBashRunning = false;
|
|
156
|
+
let hostBashRunning = initialState.isBashRunning;
|
|
157
|
+
let sessionManager = local.sessionManager;
|
|
158
|
+
let settingsManager = SettingsManager.create(initialState.cwd, agentDir, {
|
|
159
|
+
projectTrusted: initialState.projectTrusted,
|
|
160
|
+
});
|
|
161
|
+
const updateBashState = () => {
|
|
162
|
+
state = { ...state, isBashRunning: localBashRunning || hostBashRunning };
|
|
163
|
+
};
|
|
164
|
+
const remoteSessionManager = new Proxy({}, {
|
|
165
|
+
get(_target, property, _receiver) {
|
|
166
|
+
if (property === "appendLabelChange") {
|
|
167
|
+
return (entryId, label) => void client.setLabel(entryId, label);
|
|
168
|
+
}
|
|
169
|
+
if (property === "getSessionName")
|
|
170
|
+
return () => state.sessionName;
|
|
171
|
+
if (property === "getUsageTotals")
|
|
172
|
+
return () => state.usageTotals;
|
|
173
|
+
const value = Reflect.get(sessionManager, property, sessionManager);
|
|
174
|
+
return typeof value === "function" ? value.bind(sessionManager) : value;
|
|
175
|
+
},
|
|
176
|
+
});
|
|
116
177
|
let streamingAssistant;
|
|
178
|
+
let mirroredCurrentAssistantUsage = false;
|
|
117
179
|
const listeners = new Set();
|
|
118
180
|
client.onEvent((wireEvent) => {
|
|
119
181
|
if (wireEvent.type === "agent_settled")
|
|
120
|
-
state = { ...state, isStreaming: false };
|
|
182
|
+
state = { ...state, isStreaming: false, retryAttempt: 0 };
|
|
183
|
+
if (wireEvent.type === "bash_start") {
|
|
184
|
+
hostBashRunning = true;
|
|
185
|
+
updateBashState();
|
|
186
|
+
}
|
|
187
|
+
if (wireEvent.type === "bash_end") {
|
|
188
|
+
hostBashRunning = false;
|
|
189
|
+
updateBashState();
|
|
190
|
+
}
|
|
191
|
+
if (wireEvent.type === "bash_execution_update")
|
|
192
|
+
bashChunk?.(wireEvent.delta);
|
|
121
193
|
if (wireEvent.type === "agent_start")
|
|
122
194
|
state = { ...state, isStreaming: true };
|
|
195
|
+
if (wireEvent.type === "compaction_start")
|
|
196
|
+
state = { ...state, isCompacting: true };
|
|
197
|
+
if (wireEvent.type === "compaction_end")
|
|
198
|
+
state = { ...state, isCompacting: false };
|
|
199
|
+
if (wireEvent.type === "auto_retry_start")
|
|
200
|
+
state = { ...state, retryAttempt: wireEvent.attempt };
|
|
201
|
+
if (wireEvent.type === "auto_retry_end")
|
|
202
|
+
state = { ...state, retryAttempt: 0 };
|
|
203
|
+
if (wireEvent.type === "queue_update") {
|
|
204
|
+
state = {
|
|
205
|
+
...state,
|
|
206
|
+
steering: [...wireEvent.steering],
|
|
207
|
+
followUp: [...wireEvent.followUp],
|
|
208
|
+
ordered: [...wireEvent.ordered],
|
|
209
|
+
pendingMessageCount: wireEvent.steering.length + wireEvent.followUp.length,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
123
212
|
if (wireEvent.type === "model_changed") {
|
|
124
213
|
state = { ...state, model: wireEvent.model, thinkingLevel: wireEvent.thinkingLevel };
|
|
125
214
|
}
|
|
126
215
|
if (wireEvent.type === "thinking_level_changed")
|
|
127
216
|
state = { ...state, thinkingLevel: wireEvent.level };
|
|
217
|
+
if (wireEvent.type === "service_tier_changed") {
|
|
218
|
+
state = { ...state, serviceTier: wireEvent.tier, fastMode: wireEvent.fastMode };
|
|
219
|
+
}
|
|
220
|
+
if (wireEvent.type === "session_info_changed")
|
|
221
|
+
state = { ...state, sessionName: wireEvent.name };
|
|
128
222
|
if (wireEvent.type === "message_start") {
|
|
129
|
-
if (wireEvent.message.role === "assistant")
|
|
223
|
+
if (wireEvent.message.role === "assistant") {
|
|
130
224
|
streamingAssistant = structuredClone(wireEvent.message);
|
|
225
|
+
mirroredCurrentAssistantUsage = false;
|
|
226
|
+
}
|
|
131
227
|
local.agent.state.messages.push(structuredClone(wireEvent.message));
|
|
132
228
|
}
|
|
133
229
|
if (wireEvent.type === "message_end") {
|
|
134
|
-
if (wireEvent.message.role === "assistant")
|
|
230
|
+
if (wireEvent.message.role === "assistant") {
|
|
135
231
|
streamingAssistant = structuredClone(wireEvent.message);
|
|
232
|
+
const usage = wireEvent.message.usage;
|
|
233
|
+
if (usage && !mirroredCurrentAssistantUsage) {
|
|
234
|
+
mirroredCurrentAssistantUsage = true;
|
|
235
|
+
const latestPromptTokens = usage.input + usage.cacheRead + usage.cacheWrite;
|
|
236
|
+
state = {
|
|
237
|
+
...state,
|
|
238
|
+
usageTotals: {
|
|
239
|
+
...state.usageTotals,
|
|
240
|
+
input: state.usageTotals.input + usage.input,
|
|
241
|
+
output: state.usageTotals.output + usage.output,
|
|
242
|
+
cacheRead: state.usageTotals.cacheRead + usage.cacheRead,
|
|
243
|
+
cacheWrite: state.usageTotals.cacheWrite + usage.cacheWrite,
|
|
244
|
+
cost: state.usageTotals.cost + (usage.cost?.total ?? 0),
|
|
245
|
+
latestCacheHitRate: latestPromptTokens > 0 ? (usage.cacheRead / latestPromptTokens) * 100 : undefined,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
136
250
|
const messages = local.agent.state.messages;
|
|
137
251
|
const previous = messages.at(-1);
|
|
138
252
|
if (previous?.role === wireEvent.message.role)
|
|
139
253
|
messages[messages.length - 1] = structuredClone(wireEvent.message);
|
|
140
254
|
}
|
|
255
|
+
if (wireEvent.type === "compaction_end" && wireEvent.accepted && !wireEvent.aborted) {
|
|
256
|
+
try {
|
|
257
|
+
sessionManager.reloadFromDisk?.();
|
|
258
|
+
local.agent.state.messages = sessionManager.buildSessionContext().messages;
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
// Non-fatal if session file is transiently locked or unavailable
|
|
262
|
+
}
|
|
263
|
+
}
|
|
141
264
|
const event = hydrateMessageUpdate(wireEvent, streamingAssistant);
|
|
142
265
|
for (const listener of listeners)
|
|
143
266
|
listener(event);
|
|
144
267
|
});
|
|
268
|
+
const refresh = async () => {
|
|
269
|
+
const nextState = await client.getState();
|
|
270
|
+
state = { ...stateFromRpc(nextState) };
|
|
271
|
+
let messages;
|
|
272
|
+
settingsManager = SettingsManager.create(nextState.cwd, agentDir, {
|
|
273
|
+
projectTrusted: nextState.projectTrusted,
|
|
274
|
+
});
|
|
275
|
+
if (nextState.sessionFile) {
|
|
276
|
+
// SessionManager.open retains the explicit path even when the host has
|
|
277
|
+
// deferred creating the file for a setup-only session.
|
|
278
|
+
sessionManager = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
|
|
279
|
+
if (nextState.entries?.length && !sessionManager.getEntries().length) {
|
|
280
|
+
for (const entry of nextState.entries)
|
|
281
|
+
sessionManager.appendEntry(entry);
|
|
282
|
+
}
|
|
283
|
+
messages = sessionManager.buildSessionContext().messages;
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
messages = await client.getMessages();
|
|
287
|
+
}
|
|
288
|
+
local.agent.state.messages.splice(0, local.agent.state.messages.length, ...structuredClone(messages));
|
|
289
|
+
streamingAssistant = undefined;
|
|
290
|
+
};
|
|
145
291
|
const session = new Proxy(local, {
|
|
146
292
|
get(target, property, receiver) {
|
|
147
293
|
if (property === "prompt")
|
|
@@ -154,17 +300,38 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
154
300
|
});
|
|
155
301
|
if (property === "abort")
|
|
156
302
|
return () => client.abort();
|
|
303
|
+
if (property === "abortCompaction")
|
|
304
|
+
return () => void client.abortCompaction();
|
|
157
305
|
if (property === "steer")
|
|
158
|
-
return (message, images) =>
|
|
306
|
+
return (message, images, recovery) => {
|
|
307
|
+
const enqueueOrder = recovery?.enqueueOrder ?? Math.max(0, ...state.ordered.map((item) => item.enqueueOrder)) + 1;
|
|
308
|
+
state = {
|
|
309
|
+
...state,
|
|
310
|
+
steering: [...state.steering, message],
|
|
311
|
+
ordered: [...state.ordered, { text: message, mode: "steer", enqueueOrder }],
|
|
312
|
+
pendingMessageCount: state.pendingMessageCount + 1,
|
|
313
|
+
};
|
|
314
|
+
return client.steer(message, images, { ...recovery, enqueueOrder });
|
|
315
|
+
};
|
|
159
316
|
if (property === "followUp")
|
|
160
|
-
return (message, images) =>
|
|
317
|
+
return (message, images, recovery) => {
|
|
318
|
+
const enqueueOrder = recovery?.enqueueOrder ?? Math.max(0, ...state.ordered.map((item) => item.enqueueOrder)) + 1;
|
|
319
|
+
state = {
|
|
320
|
+
...state,
|
|
321
|
+
followUp: [...state.followUp, message],
|
|
322
|
+
ordered: [...state.ordered, { text: message, mode: "followUp", enqueueOrder }],
|
|
323
|
+
pendingMessageCount: state.pendingMessageCount + 1,
|
|
324
|
+
};
|
|
325
|
+
return client.followUp(message, images, { ...recovery, enqueueOrder });
|
|
326
|
+
};
|
|
161
327
|
if (property === "waitForIdle")
|
|
162
328
|
return () => client.waitForIdle();
|
|
163
329
|
if (property === "getLastAssistantText")
|
|
164
330
|
return () => target.getLastAssistantText();
|
|
165
331
|
if (property === "setModel")
|
|
166
332
|
return async (model) => {
|
|
167
|
-
await client.setModel(model.provider, model.id);
|
|
333
|
+
const next = await client.setModel(model.provider, model.id);
|
|
334
|
+
return { systemPromptName: next.systemPromptName, model: next };
|
|
168
335
|
};
|
|
169
336
|
if (property === "cycleModel")
|
|
170
337
|
return () => client.cycleModel();
|
|
@@ -183,15 +350,59 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
183
350
|
if (property === "setAutoCompactionEnabled")
|
|
184
351
|
return (enabled) => void client.setAutoCompaction(enabled).catch(reportActionFailure("setAutoCompaction"));
|
|
185
352
|
if (property === "executeBash")
|
|
186
|
-
return (command) =>
|
|
353
|
+
return async (command, onChunk, options) => {
|
|
354
|
+
if (options?.operations && typeof options.operations.exec === "function") {
|
|
355
|
+
const abortController = new AbortController();
|
|
356
|
+
localBashAbortController = abortController;
|
|
357
|
+
localBashRunning = true;
|
|
358
|
+
updateBashState();
|
|
359
|
+
const sessionAtStart = state.sessionId;
|
|
360
|
+
const prefix = settingsManager.getShellCommandPrefix();
|
|
361
|
+
const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
|
|
362
|
+
try {
|
|
363
|
+
const result = await executeBashWithOperations(resolvedCommand, state.cwd, options.operations, { onChunk, signal: abortController.signal });
|
|
364
|
+
if (state.sessionId === sessionAtStart) {
|
|
365
|
+
await client.recordBashResult(command, result, options.excludeFromContext);
|
|
366
|
+
}
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
finally {
|
|
370
|
+
localBashAbortController = undefined;
|
|
371
|
+
localBashRunning = false;
|
|
372
|
+
updateBashState();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
bashChunk = onChunk;
|
|
376
|
+
try {
|
|
377
|
+
return await client.bash(command, {
|
|
378
|
+
excludeFromContext: options?.excludeFromContext,
|
|
379
|
+
operations: options?.operations,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
finally {
|
|
383
|
+
bashChunk = undefined;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
187
386
|
if (property === "abortBash")
|
|
188
|
-
return () =>
|
|
387
|
+
return () => {
|
|
388
|
+
if (localBashAbortController)
|
|
389
|
+
localBashAbortController.abort();
|
|
390
|
+
else
|
|
391
|
+
void client.abortBash().catch(reportActionFailure("abortBash"));
|
|
392
|
+
};
|
|
189
393
|
if (property === "getSessionStats")
|
|
190
394
|
return () => client.getSessionStats();
|
|
191
395
|
if (property === "exportToHtml")
|
|
192
396
|
return (outputPath) => client.exportHtml(outputPath).then((result) => result.path);
|
|
193
397
|
if (property === "setSessionName")
|
|
194
|
-
return (name) =>
|
|
398
|
+
return (name) => client.setSessionName(name).catch(reportActionFailure("setSessionName"));
|
|
399
|
+
if (property === "navigateTree")
|
|
400
|
+
return async (targetId, options) => {
|
|
401
|
+
const result = await client.navigateTree(targetId, options);
|
|
402
|
+
if (!result.cancelled)
|
|
403
|
+
await refresh();
|
|
404
|
+
return result;
|
|
405
|
+
};
|
|
195
406
|
if (property === "getUserMessagesForForking")
|
|
196
407
|
return () => client.getForkMessages();
|
|
197
408
|
if (property === "subscribe")
|
|
@@ -205,6 +416,45 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
205
416
|
};
|
|
206
417
|
if (property === "isStreaming")
|
|
207
418
|
return state.isStreaming;
|
|
419
|
+
if (property === "isCompacting")
|
|
420
|
+
return state.isCompacting;
|
|
421
|
+
if (property === "isFastModeActive")
|
|
422
|
+
return () => state.fastMode;
|
|
423
|
+
if (property === "pendingMessageCount")
|
|
424
|
+
return state.pendingMessageCount;
|
|
425
|
+
if (property === "getSteeringMessages")
|
|
426
|
+
return () => state.steering;
|
|
427
|
+
if (property === "getFollowUpMessages")
|
|
428
|
+
return () => state.followUp;
|
|
429
|
+
if (property === "clearQueue")
|
|
430
|
+
return (options) => {
|
|
431
|
+
const result = {
|
|
432
|
+
steering: [...state.steering],
|
|
433
|
+
followUp: [...state.followUp],
|
|
434
|
+
ordered: [...state.ordered],
|
|
435
|
+
};
|
|
436
|
+
Object.defineProperty(result, "ordered", { value: result.ordered, enumerable: false });
|
|
437
|
+
void client.clearQueue(options).catch(reportActionFailure("clearQueue"));
|
|
438
|
+
return result;
|
|
439
|
+
};
|
|
440
|
+
if (property === "abortBranchSummary")
|
|
441
|
+
return () => void client.abortBranchSummary().catch(reportActionFailure("abortBranchSummary"));
|
|
442
|
+
if (property === "recordBashResult")
|
|
443
|
+
return (command, result, options) => void client
|
|
444
|
+
.recordBashResult(command, result, options?.excludeFromContext)
|
|
445
|
+
.catch(reportActionFailure("recordBashResult"));
|
|
446
|
+
if (property === "set_label")
|
|
447
|
+
return undefined;
|
|
448
|
+
if (property === "retryAttempt")
|
|
449
|
+
return state.retryAttempt;
|
|
450
|
+
if (property === "isBashRunning")
|
|
451
|
+
return state.isBashRunning;
|
|
452
|
+
if (property === "reload")
|
|
453
|
+
return (_options) => client.reload();
|
|
454
|
+
if (property === "checkReloadVeto")
|
|
455
|
+
return () => client.checkReloadVeto();
|
|
456
|
+
if (property === "exportToJsonl")
|
|
457
|
+
return (outputPath) => client.exportJsonl(outputPath).then((result) => result.path);
|
|
208
458
|
// The footer and other renderers read session.state.*; surface the
|
|
209
459
|
// host-authoritative fields there too, not only via the direct getters.
|
|
210
460
|
if (property === "state") {
|
|
@@ -214,12 +464,21 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
214
464
|
model: state.model ?? localState.model,
|
|
215
465
|
thinkingLevel: state.thinkingLevel,
|
|
216
466
|
isStreaming: state.isStreaming,
|
|
467
|
+
isCompacting: state.isCompacting,
|
|
217
468
|
};
|
|
218
469
|
}
|
|
219
470
|
if (property === "sessionFile")
|
|
220
471
|
return state.sessionFile;
|
|
221
472
|
if (property === "sessionId")
|
|
222
473
|
return state.sessionId;
|
|
474
|
+
if (property === "sessionName")
|
|
475
|
+
return state.sessionName;
|
|
476
|
+
if (property === "serviceTier")
|
|
477
|
+
return state.serviceTier;
|
|
478
|
+
if (property === "sessionManager")
|
|
479
|
+
return remoteSessionManager;
|
|
480
|
+
if (property === "settingsManager")
|
|
481
|
+
return settingsManager;
|
|
223
482
|
if (property === "messages")
|
|
224
483
|
return target.messages;
|
|
225
484
|
if (property === "model")
|
|
@@ -229,7 +488,38 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
229
488
|
return Reflect.get(target, property, receiver);
|
|
230
489
|
},
|
|
231
490
|
});
|
|
232
|
-
return
|
|
491
|
+
return {
|
|
492
|
+
session,
|
|
493
|
+
refresh,
|
|
494
|
+
abortLocalBash: () => localBashAbortController?.abort(),
|
|
495
|
+
createReplacedSessionContext: () => {
|
|
496
|
+
const context = local.createReplacedSessionContext();
|
|
497
|
+
Object.defineProperty(context, "cwd", { value: state.cwd });
|
|
498
|
+
Object.defineProperty(context, "sessionManager", { value: remoteSessionManager });
|
|
499
|
+
context.sendMessage = (message, options) => client.sendCustomMessage(message, {
|
|
500
|
+
triggerTurn: options?.triggerTurn,
|
|
501
|
+
deliverAs: options?.deliverAs,
|
|
502
|
+
});
|
|
503
|
+
context.sendUserMessage = (content, options) => {
|
|
504
|
+
if (typeof content === "string")
|
|
505
|
+
return client.prompt(content, {
|
|
506
|
+
streamingBehavior: options?.deliverAs,
|
|
507
|
+
expandPromptTemplates: options?.expandPromptTemplates,
|
|
508
|
+
});
|
|
509
|
+
const text = content
|
|
510
|
+
.filter((part) => part.type === "text")
|
|
511
|
+
.map((part) => part.text)
|
|
512
|
+
.join("\n");
|
|
513
|
+
const images = content.filter((part) => part.type === "image");
|
|
514
|
+
return client.prompt(text, {
|
|
515
|
+
images,
|
|
516
|
+
streamingBehavior: options?.deliverAs,
|
|
517
|
+
expandPromptTemplates: options?.expandPromptTemplates,
|
|
518
|
+
});
|
|
519
|
+
};
|
|
520
|
+
return context;
|
|
521
|
+
},
|
|
522
|
+
};
|
|
233
523
|
}
|
|
234
524
|
function hydrateMessageUpdate(event, streamingAssistant) {
|
|
235
525
|
if (event.type !== "message_update" || !streamingAssistant)
|