@code-yeongyu/senpi 2026.8.28 → 2026.8.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +140 -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 +50 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +313 -25
- 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/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +155 -37
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/credential-accounts.d.ts.map +1 -1
- package/dist/core/credential-accounts.js +45 -21
- 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/env-slots.d.ts.map +1 -1
- package/dist/core/credential-pool/env-slots.js +2 -0
- package/dist/core/credential-pool/env-slots.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/compaction/deterministic-fallback.d.ts +8 -2
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +160 -47
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js +31 -8
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
- package/dist/core/extensions/builtin/cursor-cli-oauth/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/permission-system/external-dir.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.js +15 -5
- package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js +16 -1
- package/dist/core/extensions/builtin/permission-system/parsers.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/extensions/builtin/terminal/manager.d.ts +5 -0
- package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.js +31 -3
- package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
- package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
- package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
- package/dist/core/model-config-schema.d.ts +4 -4
- package/dist/core/model-resolver.js +2 -2
- package/dist/core/model-resolver.js.map +1 -1
- 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 +40 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +7 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +6 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +97 -15
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +8 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +76 -34
- package/dist/core/tools/output-accumulator.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/components/footer.d.ts +7 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +34 -8
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/grok/chrome.d.ts +2 -0
- package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
- package/dist/modes/interactive/grok/chrome.js.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts +51 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +545 -40
- package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +7 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +390 -239
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts +8 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +268 -63
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/host-ensure.d.ts +2 -0
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +15 -2
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +31 -12
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/host-watchdog.d.ts +2 -0
- package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
- package/dist/modes/rpc/host-watchdog.js +23 -12
- package/dist/modes/rpc/host-watchdog.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +20 -8
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +91 -16
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +129 -37
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
- package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.js +66 -0
- package/dist/modes/rpc/rpc-input-validation.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 +241 -3
- 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-binding.d.ts +1 -0
- package/dist/modes/rpc/session-binding.d.ts.map +1 -1
- package/dist/modes/rpc/session-binding.js +21 -1
- package/dist/modes/rpc/session-binding.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 +148 -16
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +4 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +58 -7
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
- package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
- package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
- package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +17 -2
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +67 -4
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
- package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
- package/dist/modes/rpc/socket-event-fanout.js +84 -0
- package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
- package/docs/rpc.md +40 -1
- package/docs/settings.md +34 -0
- package/docs/windows.md +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +62 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +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/dist/harness/env/nodejs.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +58 -31
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- 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/together.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/dist/providers/data/zai-coding-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.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/dist/components/editor.d.ts +7 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
- package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { rm } from "node:fs/promises";
|
|
3
|
+
import { executeBashWithOperations } from "../../core/bash-executor.js";
|
|
4
|
+
import { SessionManager } from "../../core/session-manager.js";
|
|
5
|
+
import { SettingsManager } from "../../core/settings-manager.js";
|
|
1
6
|
import { ensureHost } from "../rpc/host-ensure.js";
|
|
2
7
|
import { RpcClient } from "../rpc/rpc-client.js";
|
|
3
8
|
export const INTERACTIVE_HOST_FALLBACK_WARNING = "Warning: shared interactive host unavailable; continuing locally";
|
|
@@ -23,7 +28,15 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
|
|
|
23
28
|
modelId: localRuntime.session.model?.id,
|
|
24
29
|
thinkingLevel: localRuntime.session.thinkingLevel,
|
|
25
30
|
});
|
|
26
|
-
const remoteSession = createRemoteSessionProxy(localRuntime.session, client, opened.state, options.onWarning);
|
|
31
|
+
const remoteSession = createRemoteSessionProxy(localRuntime.session, localRuntime.services.agentDir, client, opened.state, options.onWarning);
|
|
32
|
+
if (opened.state.isBashRunning && !opened.attached) {
|
|
33
|
+
// Only a newly opened session can have an execution orphaned by a
|
|
34
|
+
// previous connection. An attach is an observer of the same live runtime;
|
|
35
|
+
// aborting it would cancel work owned by the surviving attachment.
|
|
36
|
+
await client.abortBash().catch(() => { });
|
|
37
|
+
}
|
|
38
|
+
if (opened.attached)
|
|
39
|
+
await remoteSession.refresh();
|
|
27
40
|
return new RemoteInteractiveRuntime(localRuntime, remoteSession, client);
|
|
28
41
|
}
|
|
29
42
|
catch (cause) {
|
|
@@ -36,25 +49,25 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
|
|
|
36
49
|
return localRuntime;
|
|
37
50
|
}
|
|
38
51
|
}
|
|
39
|
-
class RemoteInteractiveRuntime {
|
|
52
|
+
export class RemoteInteractiveRuntime {
|
|
40
53
|
#local;
|
|
41
|
-
#
|
|
54
|
+
#remoteSession;
|
|
42
55
|
#client;
|
|
43
56
|
#rebindSession;
|
|
44
57
|
#beforeSessionInvalidate;
|
|
45
|
-
constructor(local,
|
|
58
|
+
constructor(local, remoteSession, client) {
|
|
46
59
|
this.#local = local;
|
|
47
|
-
this.#
|
|
60
|
+
this.#remoteSession = remoteSession;
|
|
48
61
|
this.#client = client;
|
|
49
62
|
}
|
|
50
63
|
get session() {
|
|
51
|
-
return this.#session;
|
|
64
|
+
return this.#remoteSession.session;
|
|
52
65
|
}
|
|
53
66
|
get services() {
|
|
54
67
|
return this.#local.services;
|
|
55
68
|
}
|
|
56
69
|
get cwd() {
|
|
57
|
-
return this.#
|
|
70
|
+
return this.#remoteSession.session.sessionManager.getCwd();
|
|
58
71
|
}
|
|
59
72
|
get diagnostics() {
|
|
60
73
|
return this.#local.diagnostics;
|
|
@@ -71,37 +84,87 @@ class RemoteInteractiveRuntime {
|
|
|
71
84
|
setRebindSession(callback) {
|
|
72
85
|
this.#rebindSession = callback;
|
|
73
86
|
}
|
|
87
|
+
setHostUiHandler(callback) {
|
|
88
|
+
this.#remoteSession.setHostUiHandler(callback);
|
|
89
|
+
}
|
|
74
90
|
async dispose() {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
91
|
+
const errors = [];
|
|
92
|
+
for (const cleanup of [
|
|
93
|
+
() => this.#remoteSession.abortLocalBash?.(),
|
|
94
|
+
() => this.#client.closeSession(),
|
|
95
|
+
() => this.#client.stop(),
|
|
96
|
+
() => this.#local.dispose(),
|
|
97
|
+
]) {
|
|
98
|
+
try {
|
|
99
|
+
await cleanup();
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
errors.push(error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (errors.length > 0)
|
|
106
|
+
throw errors[0];
|
|
78
107
|
}
|
|
79
|
-
async newSession() {
|
|
80
|
-
this.#
|
|
81
|
-
|
|
82
|
-
|
|
108
|
+
async newSession(options) {
|
|
109
|
+
const result = await this.#client.newSession(options?.parentSession);
|
|
110
|
+
if (!result.cancelled) {
|
|
111
|
+
this.#beforeSessionInvalidate?.();
|
|
112
|
+
this.#remoteSession.abortLocalBash();
|
|
113
|
+
await this.#remoteSession.refresh();
|
|
114
|
+
if (options?.setup) {
|
|
115
|
+
const capture = SessionManager.inMemory(this.#remoteSession.session.sessionManager.getCwd());
|
|
116
|
+
await options.setup(capture);
|
|
117
|
+
for (const entry of capture.getEntries())
|
|
118
|
+
await this.#client.appendSessionEntry(entry);
|
|
119
|
+
await this.#remoteSession.refresh();
|
|
120
|
+
}
|
|
83
121
|
await this.#rebindSession?.();
|
|
122
|
+
if (options?.withSession)
|
|
123
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
124
|
+
}
|
|
84
125
|
return result;
|
|
85
126
|
}
|
|
86
|
-
async switchSession(sessionPath) {
|
|
87
|
-
this.#
|
|
88
|
-
|
|
89
|
-
|
|
127
|
+
async switchSession(sessionPath, options) {
|
|
128
|
+
const result = await this.#client.switchSession(sessionPath, options?.cwdOverride === undefined ? undefined : { cwdOverride: options.cwdOverride });
|
|
129
|
+
if (!result.cancelled) {
|
|
130
|
+
this.#beforeSessionInvalidate?.();
|
|
131
|
+
this.#remoteSession.abortLocalBash();
|
|
132
|
+
await this.#remoteSession.refresh();
|
|
90
133
|
await this.#rebindSession?.();
|
|
134
|
+
// The shared host already resolved trust; this callback is retained for
|
|
135
|
+
// compatibility, but its result cannot override host-authoritative state.
|
|
136
|
+
options?.projectTrustContextFactory?.(this.#remoteSession.session.sessionManager.getCwd());
|
|
137
|
+
if (options?.withSession)
|
|
138
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
139
|
+
}
|
|
91
140
|
return result;
|
|
92
141
|
}
|
|
93
|
-
async fork(entryId) {
|
|
94
|
-
this.#
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
142
|
+
async fork(entryId, options) {
|
|
143
|
+
const result = await this.#client.fork(entryId, options);
|
|
144
|
+
if (!result.cancelled) {
|
|
145
|
+
this.#beforeSessionInvalidate?.();
|
|
146
|
+
this.#remoteSession.abortLocalBash();
|
|
147
|
+
await this.#refreshAndRebind();
|
|
148
|
+
if (options?.withSession)
|
|
149
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
150
|
+
}
|
|
98
151
|
return { cancelled: result.cancelled, selectedText: result.text };
|
|
99
152
|
}
|
|
100
|
-
async importFromJsonl() {
|
|
101
|
-
|
|
153
|
+
async importFromJsonl(inputPath, cwdOverride) {
|
|
154
|
+
const result = await this.#client.importJsonl(inputPath, cwdOverride);
|
|
155
|
+
if (!result.cancelled) {
|
|
156
|
+
this.#beforeSessionInvalidate?.();
|
|
157
|
+
this.#remoteSession.abortLocalBash();
|
|
158
|
+
await this.#refreshAndRebind();
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
async #refreshAndRebind() {
|
|
163
|
+
await this.#remoteSession.refresh();
|
|
164
|
+
await this.#rebindSession?.();
|
|
102
165
|
}
|
|
103
166
|
}
|
|
104
|
-
function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
167
|
+
function createRemoteSessionProxy(local, agentDir, client, initialState, onWarning) {
|
|
105
168
|
// Fire-and-forget setters keep the sync AgentSession signature, but their RPC
|
|
106
169
|
// failures must not vanish: the matching *_changed wire event confirms success,
|
|
107
170
|
// and a rejection here is the only signal of failure.
|
|
@@ -112,36 +175,266 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
112
175
|
cause: error,
|
|
113
176
|
});
|
|
114
177
|
};
|
|
115
|
-
let state = initialState;
|
|
178
|
+
let state = { ...initialState };
|
|
179
|
+
const waitForBashCallbacks = async (promises, allowAbandonment) => {
|
|
180
|
+
if (promises.size === 0)
|
|
181
|
+
return true;
|
|
182
|
+
const settled = Promise.allSettled([...promises]).then(() => true);
|
|
183
|
+
let timeout;
|
|
184
|
+
const bounded = new Promise((resolve) => {
|
|
185
|
+
timeout = setTimeout(resolve, allowAbandonment ? 100 : 5_000, false);
|
|
186
|
+
timeout.unref?.();
|
|
187
|
+
});
|
|
188
|
+
try {
|
|
189
|
+
return await Promise.race([settled, bounded]);
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
if (timeout)
|
|
193
|
+
clearTimeout(timeout);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
const executionNamespace = randomUUID();
|
|
197
|
+
const cleanupRemoteBashOutput = async (path) => {
|
|
198
|
+
let cleanupTimeout;
|
|
199
|
+
try {
|
|
200
|
+
await Promise.race([
|
|
201
|
+
client.cleanupBashOutput(path),
|
|
202
|
+
new Promise((_, reject) => {
|
|
203
|
+
cleanupTimeout = setTimeout(() => reject(new Error("RPC cleanup timed out")), 1_000);
|
|
204
|
+
}),
|
|
205
|
+
]);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
catch (transportError) {
|
|
209
|
+
// Spill paths are shared host filesystem paths. If the RPC transport is
|
|
210
|
+
// already unavailable, remove locally as a bounded best-effort fallback;
|
|
211
|
+
// a later reconnect is not required to prevent this callback failure from
|
|
212
|
+
// permanently leaking the host-owned artifact.
|
|
213
|
+
try {
|
|
214
|
+
await rm(path, { force: true });
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
catch (localError) {
|
|
218
|
+
onWarning?.({
|
|
219
|
+
type: "interactive_host_action_failed",
|
|
220
|
+
message: `Warning: failed to clean up shared bash output spill: ${localError instanceof Error ? localError.message : String(localError)}`,
|
|
221
|
+
cause: new AggregateError([transportError, localError]),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
if (cleanupTimeout)
|
|
227
|
+
clearTimeout(cleanupTimeout);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
let nextBashExecutionId = 0;
|
|
231
|
+
const bashExecutions = new Map();
|
|
232
|
+
let hostUiHandler;
|
|
233
|
+
const pendingUiRequests = [];
|
|
234
|
+
let localBashAbortController;
|
|
235
|
+
let localBashRunning = false;
|
|
236
|
+
let hostBashRunning = initialState.isBashRunning;
|
|
237
|
+
let nextQueuedInputOrder = Math.max(0, ...initialState.ordered.map((item) => item.enqueueOrder));
|
|
238
|
+
let sessionManager = local.sessionManager;
|
|
239
|
+
let settingsManager = SettingsManager.create(initialState.cwd, agentDir, {
|
|
240
|
+
projectTrusted: initialState.projectTrusted,
|
|
241
|
+
});
|
|
242
|
+
const updateBashState = () => {
|
|
243
|
+
state = { ...state, isBashRunning: localBashRunning || hostBashRunning };
|
|
244
|
+
};
|
|
245
|
+
const remoteSessionManager = new Proxy({}, {
|
|
246
|
+
get(_target, property, _receiver) {
|
|
247
|
+
if (property === "appendLabelChange") {
|
|
248
|
+
return (entryId, label) => void client.setLabel(entryId, label);
|
|
249
|
+
}
|
|
250
|
+
if (property === "getCwd")
|
|
251
|
+
return () => state.cwd;
|
|
252
|
+
if (property === "getSessionName")
|
|
253
|
+
return () => state.sessionName;
|
|
254
|
+
if (property === "getUsageTotals")
|
|
255
|
+
return () => state.usageTotals;
|
|
256
|
+
const value = Reflect.get(sessionManager, property, sessionManager);
|
|
257
|
+
return typeof value === "function" ? value.bind(sessionManager) : value;
|
|
258
|
+
},
|
|
259
|
+
});
|
|
116
260
|
let streamingAssistant;
|
|
261
|
+
let mirroredCurrentAssistantUsage = false;
|
|
117
262
|
const listeners = new Set();
|
|
118
263
|
client.onEvent((wireEvent) => {
|
|
264
|
+
if (wireEvent.type === "extension_ui_request") {
|
|
265
|
+
const request = wireEvent;
|
|
266
|
+
if (hostUiHandler)
|
|
267
|
+
void Promise.resolve(hostUiHandler(request)).then((response) => response && client.sendExtensionUIResponse(response));
|
|
268
|
+
else
|
|
269
|
+
pendingUiRequests.push(request);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
119
272
|
if (wireEvent.type === "agent_settled")
|
|
120
|
-
state = { ...state, isStreaming: false };
|
|
273
|
+
state = { ...state, isStreaming: false, retryAttempt: 0 };
|
|
274
|
+
if (wireEvent.type === "bash_start") {
|
|
275
|
+
hostBashRunning = true;
|
|
276
|
+
updateBashState();
|
|
277
|
+
}
|
|
278
|
+
if (wireEvent.type === "bash_end") {
|
|
279
|
+
hostBashRunning = false;
|
|
280
|
+
updateBashState();
|
|
281
|
+
}
|
|
282
|
+
if (wireEvent.type === "bash_execution_update" && wireEvent.id) {
|
|
283
|
+
const execution = bashExecutions.get(wireEvent.id);
|
|
284
|
+
if (execution?.chunk && !execution.hasError) {
|
|
285
|
+
try {
|
|
286
|
+
const callbackPromise = Promise.resolve(execution.chunk(wireEvent.delta)).catch((error) => {
|
|
287
|
+
if (!execution.hasError) {
|
|
288
|
+
execution.error = error;
|
|
289
|
+
execution.hasError = true;
|
|
290
|
+
void client.abortBash().catch(() => { });
|
|
291
|
+
}
|
|
292
|
+
throw error;
|
|
293
|
+
});
|
|
294
|
+
execution.promises.add(callbackPromise);
|
|
295
|
+
void callbackPromise.catch(() => { }).finally(() => execution.promises.delete(callbackPromise));
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
if (!execution.hasError) {
|
|
299
|
+
execution.error = error;
|
|
300
|
+
execution.hasError = true;
|
|
301
|
+
void client.abortBash().catch(() => { });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
121
306
|
if (wireEvent.type === "agent_start")
|
|
122
307
|
state = { ...state, isStreaming: true };
|
|
308
|
+
if (wireEvent.type === "compaction_start")
|
|
309
|
+
state = { ...state, isCompacting: true };
|
|
310
|
+
if (wireEvent.type === "compaction_end")
|
|
311
|
+
state = { ...state, isCompacting: false };
|
|
312
|
+
if (wireEvent.type === "auto_retry_start")
|
|
313
|
+
state = { ...state, retryAttempt: wireEvent.attempt };
|
|
314
|
+
if (wireEvent.type === "auto_retry_end")
|
|
315
|
+
state = { ...state, retryAttempt: 0 };
|
|
316
|
+
if (wireEvent.type === "queue_update") {
|
|
317
|
+
nextQueuedInputOrder = Math.max(nextQueuedInputOrder, ...wireEvent.ordered.map((item) => item.enqueueOrder));
|
|
318
|
+
state = {
|
|
319
|
+
...state,
|
|
320
|
+
steering: [...wireEvent.steering],
|
|
321
|
+
followUp: [...wireEvent.followUp],
|
|
322
|
+
ordered: [...wireEvent.ordered],
|
|
323
|
+
pendingMessageCount: wireEvent.steering.length + wireEvent.followUp.length,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
123
326
|
if (wireEvent.type === "model_changed") {
|
|
124
327
|
state = { ...state, model: wireEvent.model, thinkingLevel: wireEvent.thinkingLevel };
|
|
125
328
|
}
|
|
126
329
|
if (wireEvent.type === "thinking_level_changed")
|
|
127
330
|
state = { ...state, thinkingLevel: wireEvent.level };
|
|
331
|
+
if (wireEvent.type === "service_tier_changed") {
|
|
332
|
+
state = { ...state, serviceTier: wireEvent.tier, fastMode: wireEvent.fastMode };
|
|
333
|
+
}
|
|
334
|
+
if (wireEvent.type === "session_settings_changed") {
|
|
335
|
+
state = {
|
|
336
|
+
...state,
|
|
337
|
+
steeringMode: wireEvent.steeringMode,
|
|
338
|
+
followUpMode: wireEvent.followUpMode,
|
|
339
|
+
autoCompactionEnabled: wireEvent.autoCompactionEnabled,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
if (wireEvent.type === "entry_appended") {
|
|
343
|
+
try {
|
|
344
|
+
sessionManager.appendEntry(wireEvent.entry);
|
|
345
|
+
local.agent.state.messages = sessionManager.buildSessionContext().messages;
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
// Non-fatal if the local snapshot cannot accept a concurrent entry.
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (wireEvent.type === "session_info_changed")
|
|
352
|
+
state = { ...state, sessionName: wireEvent.name };
|
|
128
353
|
if (wireEvent.type === "message_start") {
|
|
129
|
-
if (wireEvent.message.role === "assistant")
|
|
354
|
+
if (wireEvent.message.role === "assistant") {
|
|
130
355
|
streamingAssistant = structuredClone(wireEvent.message);
|
|
356
|
+
mirroredCurrentAssistantUsage = false;
|
|
357
|
+
}
|
|
131
358
|
local.agent.state.messages.push(structuredClone(wireEvent.message));
|
|
132
359
|
}
|
|
133
360
|
if (wireEvent.type === "message_end") {
|
|
134
|
-
if (wireEvent.message.role === "assistant")
|
|
361
|
+
if (wireEvent.message.role === "assistant") {
|
|
135
362
|
streamingAssistant = structuredClone(wireEvent.message);
|
|
363
|
+
const usage = wireEvent.message.usage;
|
|
364
|
+
if (usage && !mirroredCurrentAssistantUsage) {
|
|
365
|
+
mirroredCurrentAssistantUsage = true;
|
|
366
|
+
const latestPromptTokens = usage.input + usage.cacheRead + usage.cacheWrite;
|
|
367
|
+
const contextWindow = state.model?.contextWindow ?? 0;
|
|
368
|
+
const contextTokens = usage.input + usage.cacheRead + usage.cacheWrite;
|
|
369
|
+
state = {
|
|
370
|
+
...state,
|
|
371
|
+
contextUsage: contextWindow > 0
|
|
372
|
+
? { tokens: contextTokens, contextWindow, percent: (contextTokens / contextWindow) * 100 }
|
|
373
|
+
: undefined,
|
|
374
|
+
usageTotals: {
|
|
375
|
+
...state.usageTotals,
|
|
376
|
+
input: state.usageTotals.input + usage.input,
|
|
377
|
+
output: state.usageTotals.output + usage.output,
|
|
378
|
+
cacheRead: state.usageTotals.cacheRead + usage.cacheRead,
|
|
379
|
+
cacheWrite: state.usageTotals.cacheWrite + usage.cacheWrite,
|
|
380
|
+
cost: state.usageTotals.cost + (usage.cost?.total ?? 0),
|
|
381
|
+
latestCacheHitRate: latestPromptTokens > 0 ? (usage.cacheRead / latestPromptTokens) * 100 : undefined,
|
|
382
|
+
},
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
136
386
|
const messages = local.agent.state.messages;
|
|
137
387
|
const previous = messages.at(-1);
|
|
138
388
|
if (previous?.role === wireEvent.message.role)
|
|
139
389
|
messages[messages.length - 1] = structuredClone(wireEvent.message);
|
|
140
390
|
}
|
|
391
|
+
if (wireEvent.type === "compaction_end" && wireEvent.accepted && !wireEvent.aborted) {
|
|
392
|
+
try {
|
|
393
|
+
local.sessionManager.reloadFromDisk?.();
|
|
394
|
+
if (sessionManager !== local.sessionManager)
|
|
395
|
+
sessionManager.reloadFromDisk?.();
|
|
396
|
+
local.agent.state.messages = sessionManager.buildSessionContext().messages;
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
// Non-fatal if session file is transiently locked or unavailable
|
|
400
|
+
}
|
|
401
|
+
}
|
|
141
402
|
const event = hydrateMessageUpdate(wireEvent, streamingAssistant);
|
|
142
403
|
for (const listener of listeners)
|
|
143
404
|
listener(event);
|
|
144
405
|
});
|
|
406
|
+
const refresh = async () => {
|
|
407
|
+
const nextState = await client.getState();
|
|
408
|
+
state = { ...stateFromRpc(nextState) };
|
|
409
|
+
nextQueuedInputOrder = Math.max(0, ...nextState.ordered.map((item) => item.enqueueOrder));
|
|
410
|
+
let messages;
|
|
411
|
+
settingsManager = SettingsManager.create(nextState.cwd, agentDir, {
|
|
412
|
+
projectTrusted: nextState.projectTrusted,
|
|
413
|
+
});
|
|
414
|
+
if (nextState.sessionFile) {
|
|
415
|
+
// Keep the caller-owned manager identity when refreshing the same session.
|
|
416
|
+
// Navigation and host-side mutations must update every existing mirror, not
|
|
417
|
+
// leave the interactive runtime holding the pre-refresh snapshot.
|
|
418
|
+
if (sessionManager.getSessionFile() === nextState.sessionFile) {
|
|
419
|
+
sessionManager.reloadFromDisk?.();
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
// SessionManager.open retains the explicit path even when the host has
|
|
423
|
+
// deferred creating the file for a setup-only session.
|
|
424
|
+
sessionManager = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
|
|
425
|
+
}
|
|
426
|
+
if (nextState.entries?.length && !sessionManager.getEntries().length) {
|
|
427
|
+
for (const entry of nextState.entries)
|
|
428
|
+
sessionManager.appendEntry(entry);
|
|
429
|
+
}
|
|
430
|
+
messages = sessionManager.buildSessionContext().messages;
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
messages = await client.getMessages();
|
|
434
|
+
}
|
|
435
|
+
local.agent.state.messages.splice(0, local.agent.state.messages.length, ...structuredClone(messages));
|
|
436
|
+
streamingAssistant = undefined;
|
|
437
|
+
};
|
|
145
438
|
const session = new Proxy(local, {
|
|
146
439
|
get(target, property, receiver) {
|
|
147
440
|
if (property === "prompt")
|
|
@@ -149,25 +442,74 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
149
442
|
...(options?.images ? { images: options.images } : {}),
|
|
150
443
|
...(options?.streamingBehavior ? { streamingBehavior: options.streamingBehavior } : {}),
|
|
151
444
|
...(options?.thinkingLevel ? { thinkingLevel: options.thinkingLevel } : {}),
|
|
445
|
+
...(options?.sessionTitlePrompt !== undefined
|
|
446
|
+
? { sessionTitlePrompt: options.sessionTitlePrompt }
|
|
447
|
+
: {}),
|
|
448
|
+
...(options?.expandPromptTemplates !== undefined
|
|
449
|
+
? { expandPromptTemplates: options.expandPromptTemplates }
|
|
450
|
+
: {}),
|
|
152
451
|
...(options?.promptDisposition ? { promptDisposition: options.promptDisposition } : {}),
|
|
153
452
|
...(options?.preflightResult ? { preflightResult: options.preflightResult } : {}),
|
|
154
453
|
});
|
|
155
454
|
if (property === "abort")
|
|
156
455
|
return () => client.abort();
|
|
456
|
+
if (property === "abortCompaction")
|
|
457
|
+
return () => void client.abortCompaction();
|
|
157
458
|
if (property === "steer")
|
|
158
|
-
return (message, images) =>
|
|
459
|
+
return (message, images, recovery) => {
|
|
460
|
+
const enqueueOrder = recovery?.enqueueOrder ?? Math.max(0, ...state.ordered.map((item) => item.enqueueOrder)) + 1;
|
|
461
|
+
state = {
|
|
462
|
+
...state,
|
|
463
|
+
steering: [...state.steering, message],
|
|
464
|
+
ordered: [...state.ordered, { text: message, mode: "steer", enqueueOrder }],
|
|
465
|
+
pendingMessageCount: state.pendingMessageCount + 1,
|
|
466
|
+
};
|
|
467
|
+
return client.steer(message, images, { ...recovery, enqueueOrder });
|
|
468
|
+
};
|
|
159
469
|
if (property === "followUp")
|
|
160
|
-
return (message, images) =>
|
|
470
|
+
return (message, images, recovery) => {
|
|
471
|
+
const enqueueOrder = recovery?.enqueueOrder ?? Math.max(0, ...state.ordered.map((item) => item.enqueueOrder)) + 1;
|
|
472
|
+
state = {
|
|
473
|
+
...state,
|
|
474
|
+
followUp: [...state.followUp, message],
|
|
475
|
+
ordered: [...state.ordered, { text: message, mode: "followUp", enqueueOrder }],
|
|
476
|
+
pendingMessageCount: state.pendingMessageCount + 1,
|
|
477
|
+
};
|
|
478
|
+
return client.followUp(message, images, { ...recovery, enqueueOrder });
|
|
479
|
+
};
|
|
161
480
|
if (property === "waitForIdle")
|
|
162
481
|
return () => client.waitForIdle();
|
|
163
482
|
if (property === "getLastAssistantText")
|
|
164
483
|
return () => target.getLastAssistantText();
|
|
165
|
-
if (property === "setModel")
|
|
484
|
+
if (property === "setModel" || property === "setSessionModel")
|
|
166
485
|
return async (model) => {
|
|
167
|
-
await client.setModel(model.provider, model.id);
|
|
486
|
+
const next = await client.setModel(model.provider, model.id);
|
|
487
|
+
return { systemPromptName: next.systemPromptName, model: next };
|
|
488
|
+
};
|
|
489
|
+
if (property === "setSessionThinkingLevel")
|
|
490
|
+
return (level) => void client
|
|
491
|
+
.setThinkingLevel(level, { scope: "turn" })
|
|
492
|
+
.catch(reportActionFailure("setSessionThinkingLevel"));
|
|
493
|
+
if (property === "setSessionFastMode")
|
|
494
|
+
return (enabled) => void client.setFastMode(enabled).catch(reportActionFailure("setSessionFastMode"));
|
|
495
|
+
if (property === "abortRetry")
|
|
496
|
+
return () => void client.abortRetry().catch(reportActionFailure("abortRetry"));
|
|
497
|
+
if (property === "setAutoRetryEnabled")
|
|
498
|
+
return (enabled) => void client.setAutoRetry(enabled).catch(reportActionFailure("setAutoRetryEnabled"));
|
|
499
|
+
if (property === "reserveQueuedInputOrder")
|
|
500
|
+
return () => ++nextQueuedInputOrder;
|
|
501
|
+
if (property === "setFavoriteModels")
|
|
502
|
+
return (models) => {
|
|
503
|
+
state = { ...state, favoriteModels: [...models] };
|
|
504
|
+
void client.setFavoriteModels(models).catch(reportActionFailure("setFavoriteModels"));
|
|
505
|
+
};
|
|
506
|
+
if (property === "setScopedModels")
|
|
507
|
+
return (models) => {
|
|
508
|
+
state = { ...state, scopedModels: [...models] };
|
|
509
|
+
void client.setScopedModels(models).catch(reportActionFailure("setScopedModels"));
|
|
168
510
|
};
|
|
169
511
|
if (property === "cycleModel")
|
|
170
|
-
return () => client.cycleModel();
|
|
512
|
+
return (direction) => client.cycleModel(direction);
|
|
171
513
|
if (property === "setThinkingLevel")
|
|
172
514
|
return (level) => void client.setThinkingLevel(level).catch(reportActionFailure("setThinkingLevel"));
|
|
173
515
|
if (property === "cycleThinkingLevel")
|
|
@@ -183,15 +525,81 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
183
525
|
if (property === "setAutoCompactionEnabled")
|
|
184
526
|
return (enabled) => void client.setAutoCompaction(enabled).catch(reportActionFailure("setAutoCompaction"));
|
|
185
527
|
if (property === "executeBash")
|
|
186
|
-
return (command) =>
|
|
528
|
+
return async (command, onChunk, options) => {
|
|
529
|
+
if (options?.operations && typeof options.operations.exec === "function") {
|
|
530
|
+
const abortController = new AbortController();
|
|
531
|
+
localBashAbortController = abortController;
|
|
532
|
+
localBashRunning = true;
|
|
533
|
+
updateBashState();
|
|
534
|
+
const sessionAtStart = state.sessionId;
|
|
535
|
+
const prefix = settingsManager.getShellCommandPrefix();
|
|
536
|
+
const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
|
|
537
|
+
try {
|
|
538
|
+
const result = await executeBashWithOperations(resolvedCommand, state.cwd, options.operations, { onChunk, signal: abortController.signal });
|
|
539
|
+
if (state.sessionId === sessionAtStart) {
|
|
540
|
+
await client.recordBashResult(command, result, options.excludeFromContext);
|
|
541
|
+
}
|
|
542
|
+
return result;
|
|
543
|
+
}
|
|
544
|
+
finally {
|
|
545
|
+
localBashAbortController = undefined;
|
|
546
|
+
localBashRunning = false;
|
|
547
|
+
updateBashState();
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const executionId = `bash-${executionNamespace}-${++nextBashExecutionId}`;
|
|
551
|
+
const execution = {
|
|
552
|
+
chunk: onChunk,
|
|
553
|
+
promises: new Set(),
|
|
554
|
+
error: undefined,
|
|
555
|
+
hasError: false,
|
|
556
|
+
};
|
|
557
|
+
bashExecutions.set(executionId, execution);
|
|
558
|
+
try {
|
|
559
|
+
const result = await client.bash(command, {
|
|
560
|
+
excludeFromContext: options?.excludeFromContext,
|
|
561
|
+
executionId,
|
|
562
|
+
operations: options?.operations,
|
|
563
|
+
});
|
|
564
|
+
const callbacksSettled = await waitForBashCallbacks(execution.promises, false);
|
|
565
|
+
if (!callbacksSettled) {
|
|
566
|
+
if (result.fullOutputPath)
|
|
567
|
+
await cleanupRemoteBashOutput(result.fullOutputPath);
|
|
568
|
+
throw new Error("Bash output callback did not settle within 5000ms");
|
|
569
|
+
}
|
|
570
|
+
if (execution.hasError) {
|
|
571
|
+
if (result.fullOutputPath)
|
|
572
|
+
await cleanupRemoteBashOutput(result.fullOutputPath);
|
|
573
|
+
throw execution.error;
|
|
574
|
+
}
|
|
575
|
+
return result;
|
|
576
|
+
}
|
|
577
|
+
finally {
|
|
578
|
+
bashExecutions.delete(executionId);
|
|
579
|
+
}
|
|
580
|
+
};
|
|
187
581
|
if (property === "abortBash")
|
|
188
|
-
return () =>
|
|
582
|
+
return () => {
|
|
583
|
+
if (localBashAbortController)
|
|
584
|
+
localBashAbortController.abort();
|
|
585
|
+
else
|
|
586
|
+
void client.abortBash().catch(reportActionFailure("abortBash"));
|
|
587
|
+
};
|
|
588
|
+
if (property === "getContextUsage")
|
|
589
|
+
return () => state.contextUsage;
|
|
189
590
|
if (property === "getSessionStats")
|
|
190
591
|
return () => client.getSessionStats();
|
|
191
592
|
if (property === "exportToHtml")
|
|
192
|
-
return (outputPath) => client.exportHtml(outputPath).then((result) => result.path);
|
|
593
|
+
return (outputPath, options) => client.exportHtml(outputPath, options?.themeName).then((result) => result.path);
|
|
193
594
|
if (property === "setSessionName")
|
|
194
|
-
return (name) =>
|
|
595
|
+
return (name) => client.setSessionName(name).catch(reportActionFailure("setSessionName"));
|
|
596
|
+
if (property === "navigateTree")
|
|
597
|
+
return async (targetId, options) => {
|
|
598
|
+
const result = await client.navigateTree(targetId, options);
|
|
599
|
+
if (!result.cancelled)
|
|
600
|
+
await refresh();
|
|
601
|
+
return result;
|
|
602
|
+
};
|
|
195
603
|
if (property === "getUserMessagesForForking")
|
|
196
604
|
return () => client.getForkMessages();
|
|
197
605
|
if (property === "subscribe")
|
|
@@ -205,6 +613,45 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
205
613
|
};
|
|
206
614
|
if (property === "isStreaming")
|
|
207
615
|
return state.isStreaming;
|
|
616
|
+
if (property === "isIdle")
|
|
617
|
+
return !state.isStreaming;
|
|
618
|
+
if (property === "isCompacting")
|
|
619
|
+
return state.isCompacting;
|
|
620
|
+
if (property === "pendingMessageCount")
|
|
621
|
+
return state.pendingMessageCount;
|
|
622
|
+
if (property === "getSteeringMessages")
|
|
623
|
+
return () => state.steering;
|
|
624
|
+
if (property === "getFollowUpMessages")
|
|
625
|
+
return () => state.followUp;
|
|
626
|
+
if (property === "clearQueue")
|
|
627
|
+
return (options) => {
|
|
628
|
+
const result = {
|
|
629
|
+
steering: [...state.steering],
|
|
630
|
+
followUp: [...state.followUp],
|
|
631
|
+
ordered: [...state.ordered],
|
|
632
|
+
};
|
|
633
|
+
Object.defineProperty(result, "ordered", { value: result.ordered, enumerable: false });
|
|
634
|
+
void client.clearQueue(options).catch(reportActionFailure("clearQueue"));
|
|
635
|
+
return result;
|
|
636
|
+
};
|
|
637
|
+
if (property === "abortBranchSummary")
|
|
638
|
+
return () => void client.abortBranchSummary().catch(reportActionFailure("abortBranchSummary"));
|
|
639
|
+
if (property === "recordBashResult")
|
|
640
|
+
return (command, result, options) => void client
|
|
641
|
+
.recordBashResult(command, result, options?.excludeFromContext)
|
|
642
|
+
.catch(reportActionFailure("recordBashResult"));
|
|
643
|
+
if (property === "set_label")
|
|
644
|
+
return undefined;
|
|
645
|
+
if (property === "retryAttempt")
|
|
646
|
+
return state.retryAttempt;
|
|
647
|
+
if (property === "isBashRunning")
|
|
648
|
+
return state.isBashRunning;
|
|
649
|
+
if (property === "reload")
|
|
650
|
+
return (_options) => client.reload();
|
|
651
|
+
if (property === "checkReloadVeto")
|
|
652
|
+
return () => client.checkReloadVeto();
|
|
653
|
+
if (property === "exportToJsonl")
|
|
654
|
+
return (outputPath) => client.exportJsonl(outputPath).then((result) => result.path);
|
|
208
655
|
// The footer and other renderers read session.state.*; surface the
|
|
209
656
|
// host-authoritative fields there too, not only via the direct getters.
|
|
210
657
|
if (property === "state") {
|
|
@@ -214,14 +661,35 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
214
661
|
model: state.model ?? localState.model,
|
|
215
662
|
thinkingLevel: state.thinkingLevel,
|
|
216
663
|
isStreaming: state.isStreaming,
|
|
664
|
+
isCompacting: state.isCompacting,
|
|
217
665
|
};
|
|
218
666
|
}
|
|
219
667
|
if (property === "sessionFile")
|
|
220
668
|
return state.sessionFile;
|
|
221
669
|
if (property === "sessionId")
|
|
222
670
|
return state.sessionId;
|
|
671
|
+
if (property === "sessionName")
|
|
672
|
+
return state.sessionName;
|
|
673
|
+
if (property === "serviceTier")
|
|
674
|
+
return state.serviceTier;
|
|
675
|
+
if (property === "steeringMode")
|
|
676
|
+
return state.steeringMode;
|
|
677
|
+
if (property === "followUpMode")
|
|
678
|
+
return state.followUpMode;
|
|
679
|
+
if (property === "autoCompactionEnabled")
|
|
680
|
+
return state.autoCompactionEnabled;
|
|
681
|
+
if (property === "isFastModeActive")
|
|
682
|
+
return () => state.fastMode;
|
|
683
|
+
if (property === "sessionManager")
|
|
684
|
+
return remoteSessionManager;
|
|
685
|
+
if (property === "settingsManager")
|
|
686
|
+
return settingsManager;
|
|
223
687
|
if (property === "messages")
|
|
224
688
|
return target.messages;
|
|
689
|
+
if (property === "favoriteModels")
|
|
690
|
+
return state.favoriteModels;
|
|
691
|
+
if (property === "scopedModels")
|
|
692
|
+
return state.scopedModels;
|
|
225
693
|
if (property === "model")
|
|
226
694
|
return state.model ?? target.model;
|
|
227
695
|
if (property === "thinkingLevel")
|
|
@@ -229,7 +697,44 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
|
|
|
229
697
|
return Reflect.get(target, property, receiver);
|
|
230
698
|
},
|
|
231
699
|
});
|
|
232
|
-
return
|
|
700
|
+
return {
|
|
701
|
+
session,
|
|
702
|
+
setHostUiHandler: (callback) => {
|
|
703
|
+
hostUiHandler = callback;
|
|
704
|
+
if (callback)
|
|
705
|
+
for (const request of pendingUiRequests.splice(0))
|
|
706
|
+
void Promise.resolve(callback(request)).then((response) => response && client.sendExtensionUIResponse(response));
|
|
707
|
+
},
|
|
708
|
+
refresh,
|
|
709
|
+
abortLocalBash: () => localBashAbortController?.abort(),
|
|
710
|
+
createReplacedSessionContext: () => {
|
|
711
|
+
const context = local.createReplacedSessionContext();
|
|
712
|
+
Object.defineProperty(context, "cwd", { value: state.cwd });
|
|
713
|
+
Object.defineProperty(context, "sessionManager", { value: remoteSessionManager });
|
|
714
|
+
context.sendMessage = (message, options) => client.sendCustomMessage(message, {
|
|
715
|
+
triggerTurn: options?.triggerTurn,
|
|
716
|
+
deliverAs: options?.deliverAs,
|
|
717
|
+
});
|
|
718
|
+
context.sendUserMessage = (content, options) => {
|
|
719
|
+
if (typeof content === "string")
|
|
720
|
+
return client.prompt(content, {
|
|
721
|
+
streamingBehavior: options?.deliverAs,
|
|
722
|
+
expandPromptTemplates: options?.expandPromptTemplates,
|
|
723
|
+
});
|
|
724
|
+
const text = content
|
|
725
|
+
.filter((part) => part.type === "text")
|
|
726
|
+
.map((part) => part.text)
|
|
727
|
+
.join("\n");
|
|
728
|
+
const images = content.filter((part) => part.type === "image");
|
|
729
|
+
return client.prompt(text, {
|
|
730
|
+
images,
|
|
731
|
+
streamingBehavior: options?.deliverAs,
|
|
732
|
+
expandPromptTemplates: options?.expandPromptTemplates,
|
|
733
|
+
});
|
|
734
|
+
};
|
|
735
|
+
return context;
|
|
736
|
+
},
|
|
737
|
+
};
|
|
233
738
|
}
|
|
234
739
|
function hydrateMessageUpdate(event, streamingAssistant) {
|
|
235
740
|
if (event.type !== "message_update" || !streamingAssistant)
|