@getpaseo/server 0.1.33 → 0.1.35
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/server/server/agent/activity-curator.d.ts.map +1 -1
- package/dist/server/server/agent/activity-curator.js +15 -1
- package/dist/server/server/agent/activity-curator.js.map +1 -1
- package/dist/server/server/agent/agent-management-mcp.d.ts.map +1 -1
- package/dist/server/server/agent/agent-management-mcp.js +2 -4
- package/dist/server/server/agent/agent-management-mcp.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +25 -14
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +383 -337
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +16 -14
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-server.js +2 -4
- package/dist/server/server/agent/mcp-server.js.map +1 -1
- package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
- package/dist/server/server/agent/provider-launch-config.js +15 -1
- package/dist/server/server/agent/provider-launch-config.js.map +1 -1
- package/dist/server/server/agent/provider-manifest.d.ts +1 -1
- package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
- package/dist/server/server/agent/provider-manifest.js +4 -4
- package/dist/server/server/agent/provider-manifest.js.map +1 -1
- package/dist/server/server/agent/providers/claude/partial-json.js +3 -3
- package/dist/server/server/agent/providers/claude/partial-json.js.map +1 -1
- package/dist/server/server/agent/providers/claude/sdk-model-resolver.d.ts +11 -0
- package/dist/server/server/agent/providers/claude/sdk-model-resolver.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/sdk-model-resolver.js +104 -0
- package/dist/server/server/agent/providers/claude/sdk-model-resolver.js.map +1 -0
- package/dist/server/server/agent/providers/claude-agent.d.ts +4 -13
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +382 -453
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js +42 -7
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +5 -3
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +174 -151
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.d.ts +3 -3
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.js +212 -25
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts +3 -0
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts.map +1 -0
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js +57 -0
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js.map +1 -0
- package/dist/server/server/config.d.ts.map +1 -1
- package/dist/server/server/config.js +1 -5
- package/dist/server/server/config.js.map +1 -1
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +14 -12
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/shared/tool-call-display.js +1 -1
- package/dist/server/shared/tool-call-display.js.map +1 -1
- package/dist/src/server/agent/activity-curator.js +15 -1
- package/dist/src/server/agent/activity-curator.js.map +1 -1
- package/dist/src/server/agent/agent-manager.js +383 -337
- package/dist/src/server/agent/agent-manager.js.map +1 -1
- package/dist/src/server/agent/mcp-server.js +2 -4
- package/dist/src/server/agent/mcp-server.js.map +1 -1
- package/dist/src/server/agent/provider-launch-config.js +15 -1
- package/dist/src/server/agent/provider-launch-config.js.map +1 -1
- package/dist/src/server/agent/provider-manifest.js +4 -4
- package/dist/src/server/agent/provider-manifest.js.map +1 -1
- package/dist/src/server/agent/providers/claude/partial-json.js +3 -3
- package/dist/src/server/agent/providers/claude/partial-json.js.map +1 -1
- package/dist/src/server/agent/providers/claude/sdk-model-resolver.js +104 -0
- package/dist/src/server/agent/providers/claude/sdk-model-resolver.js.map +1 -0
- package/dist/src/server/agent/providers/claude-agent.js +382 -453
- package/dist/src/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/src/server/agent/providers/codex/tool-call-mapper.js +42 -7
- package/dist/src/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
- package/dist/src/server/agent/providers/codex-app-server-agent.js +174 -151
- package/dist/src/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/src/server/agent/providers/opencode-agent.js +212 -25
- package/dist/src/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/src/server/config.js +1 -5
- package/dist/src/server/config.js.map +1 -1
- package/dist/src/server/session.js +14 -12
- package/dist/src/server/session.js.map +1 -1
- package/dist/src/shared/tool-call-display.js +1 -1
- package/dist/src/shared/tool-call-display.js.map +1 -1
- package/package.json +4 -5
- package/agent-prompt.md +0 -339
- package/dist/server/server/agent/providers/claude/model-catalog.d.ts +0 -29
- package/dist/server/server/agent/providers/claude/model-catalog.d.ts.map +0 -1
- package/dist/server/server/agent/providers/claude/model-catalog.js +0 -70
- package/dist/server/server/agent/providers/claude/model-catalog.js.map +0 -1
- package/dist/server/server/agent/system-prompt.d.ts +0 -3
- package/dist/server/server/agent/system-prompt.d.ts.map +0 -1
- package/dist/server/server/agent/system-prompt.js +0 -19
- package/dist/server/server/agent/system-prompt.js.map +0 -1
- package/dist/server/server/terminal-mcp/index.d.ts +0 -4
- package/dist/server/server/terminal-mcp/index.d.ts.map +0 -1
- package/dist/server/server/terminal-mcp/index.js +0 -3
- package/dist/server/server/terminal-mcp/index.js.map +0 -1
- package/dist/server/server/terminal-mcp/server.d.ts +0 -10
- package/dist/server/server/terminal-mcp/server.d.ts.map +0 -1
- package/dist/server/server/terminal-mcp/server.js +0 -209
- package/dist/server/server/terminal-mcp/server.js.map +0 -1
- package/dist/server/server/terminal-mcp/terminal-manager.d.ts +0 -123
- package/dist/server/server/terminal-mcp/terminal-manager.d.ts.map +0 -1
- package/dist/server/server/terminal-mcp/terminal-manager.js +0 -339
- package/dist/server/server/terminal-mcp/terminal-manager.js.map +0 -1
- package/dist/server/server/terminal-mcp/tmux.d.ts +0 -207
- package/dist/server/server/terminal-mcp/tmux.d.ts.map +0 -1
- package/dist/server/server/terminal-mcp/tmux.js +0 -821
- package/dist/server/server/terminal-mcp/tmux.js.map +0 -1
- package/dist/src/server/agent/providers/claude/model-catalog.js +0 -70
- package/dist/src/server/agent/providers/claude/model-catalog.js.map +0 -1
|
@@ -6,116 +6,14 @@ import os from "node:os";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { query, } from "@anthropic-ai/claude-agent-sdk";
|
|
8
8
|
import { mapClaudeCanceledToolCall, mapClaudeCompletedToolCall, mapClaudeFailedToolCall, mapClaudeRunningToolCall, } from "./claude/tool-call-mapper.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { mapTaskNotificationSystemRecordToToolCall, mapTaskNotificationUserContentToToolCall, } from "./claude/task-notification-tool-call.js";
|
|
10
|
+
import { normalizeClaudeModelIdFromText, resolveClaudeModelsFromSdkModels, } from "./claude/sdk-model-resolver.js";
|
|
11
11
|
import { parsePartialJsonObject } from "./claude/partial-json.js";
|
|
12
12
|
import { ClaudeSidechainTracker } from "./claude/sidechain-tracker.js";
|
|
13
13
|
import { applyProviderEnv } from "../provider-launch-config.js";
|
|
14
14
|
import { getOrchestratorModeInstructions } from "../orchestrator-instructions.js";
|
|
15
15
|
const fsPromises = promises;
|
|
16
16
|
const CLAUDE_SETTING_SOURCES = ["user", "project"];
|
|
17
|
-
function normalizeModelIdCandidate(modelId) {
|
|
18
|
-
if (typeof modelId !== "string") {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
const trimmed = modelId.trim();
|
|
22
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
23
|
-
}
|
|
24
|
-
function pickSupportedModelId(supportedModelIds, candidate) {
|
|
25
|
-
const normalizedCandidate = normalizeModelIdCandidate(candidate);
|
|
26
|
-
if (!normalizedCandidate) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
return supportedModelIds.has(normalizedCandidate) ? normalizedCandidate : null;
|
|
30
|
-
}
|
|
31
|
-
function inferClaudeModelFamilyFromText(text) {
|
|
32
|
-
if (typeof text !== "string") {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
const lowerText = text.toLowerCase();
|
|
36
|
-
if (lowerText.includes("sonnet")) {
|
|
37
|
-
return "sonnet";
|
|
38
|
-
}
|
|
39
|
-
if (lowerText.includes("opus")) {
|
|
40
|
-
return "opus";
|
|
41
|
-
}
|
|
42
|
-
if (lowerText.includes("haiku")) {
|
|
43
|
-
return "haiku";
|
|
44
|
-
}
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
function pickFamilyAliasModelId(familyAliases, family) {
|
|
48
|
-
if (!familyAliases) {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
return normalizeModelIdCandidate(familyAliases.get(family) ?? null);
|
|
52
|
-
}
|
|
53
|
-
export function normalizeClaudeRuntimeModelId(options) {
|
|
54
|
-
const runtimeModel = options.runtimeModelId.trim();
|
|
55
|
-
if (!runtimeModel) {
|
|
56
|
-
return runtimeModel;
|
|
57
|
-
}
|
|
58
|
-
const supportedModelIds = options.supportedModelIds;
|
|
59
|
-
if (!supportedModelIds || supportedModelIds.size === 0) {
|
|
60
|
-
return runtimeModel;
|
|
61
|
-
}
|
|
62
|
-
if (supportedModelIds.has(runtimeModel)) {
|
|
63
|
-
return runtimeModel;
|
|
64
|
-
}
|
|
65
|
-
const runtimeFamily = inferClaudeModelFamilyFromText(runtimeModel);
|
|
66
|
-
const familyAlias = runtimeFamily
|
|
67
|
-
? pickFamilyAliasModelId(options.supportedModelFamilyAliases, runtimeFamily)
|
|
68
|
-
: null;
|
|
69
|
-
if (runtimeFamily === "sonnet") {
|
|
70
|
-
const explicitSonnet = pickSupportedModelId(supportedModelIds, "sonnet");
|
|
71
|
-
if (explicitSonnet) {
|
|
72
|
-
return explicitSonnet;
|
|
73
|
-
}
|
|
74
|
-
if (familyAlias && supportedModelIds.has(familyAlias)) {
|
|
75
|
-
return familyAlias;
|
|
76
|
-
}
|
|
77
|
-
const defaultAlias = pickSupportedModelId(supportedModelIds, "default");
|
|
78
|
-
if (defaultAlias) {
|
|
79
|
-
return defaultAlias;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (runtimeFamily === "opus") {
|
|
83
|
-
const alias = pickSupportedModelId(supportedModelIds, "opus");
|
|
84
|
-
if (alias) {
|
|
85
|
-
return alias;
|
|
86
|
-
}
|
|
87
|
-
if (familyAlias && supportedModelIds.has(familyAlias)) {
|
|
88
|
-
return familyAlias;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (runtimeFamily === "haiku") {
|
|
92
|
-
const alias = pickSupportedModelId(supportedModelIds, "haiku");
|
|
93
|
-
if (alias) {
|
|
94
|
-
return alias;
|
|
95
|
-
}
|
|
96
|
-
if (familyAlias && supportedModelIds.has(familyAlias)) {
|
|
97
|
-
return familyAlias;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const configuredModelId = pickSupportedModelId(supportedModelIds, options.configuredModelId);
|
|
101
|
-
if (configuredModelId) {
|
|
102
|
-
return configuredModelId;
|
|
103
|
-
}
|
|
104
|
-
const currentModelId = pickSupportedModelId(supportedModelIds, options.currentModelId);
|
|
105
|
-
if (currentModelId) {
|
|
106
|
-
return currentModelId;
|
|
107
|
-
}
|
|
108
|
-
// If Claude reports a concrete family ID we can't map directly, prefer the
|
|
109
|
-
// provider default alias for unconfigured sessions so UI model/thinking state
|
|
110
|
-
// can still reconcile against the current model catalog.
|
|
111
|
-
const defaultAlias = pickSupportedModelId(supportedModelIds, "default");
|
|
112
|
-
const hasConfiguredModel = normalizeModelIdCandidate(options.configuredModelId) !== null;
|
|
113
|
-
const hasCurrentModel = normalizeModelIdCandidate(options.currentModelId) !== null;
|
|
114
|
-
if (runtimeFamily && defaultAlias && !hasConfiguredModel && !hasCurrentModel) {
|
|
115
|
-
return defaultAlias;
|
|
116
|
-
}
|
|
117
|
-
return runtimeModel;
|
|
118
|
-
}
|
|
119
17
|
const CLAUDE_CAPABILITIES = {
|
|
120
18
|
supportsStreaming: true,
|
|
121
19
|
supportsSessionPersistence: true,
|
|
@@ -176,7 +74,7 @@ function resolveClaudeSpawnCommand(spawnOptions, runtimeSettings) {
|
|
|
176
74
|
args: [...commandConfig.argv.slice(1), ...spawnOptions.args],
|
|
177
75
|
};
|
|
178
76
|
}
|
|
179
|
-
function applyRuntimeSettingsToClaudeOptions(options, runtimeSettings) {
|
|
77
|
+
function applyRuntimeSettingsToClaudeOptions(options, runtimeSettings, launchEnv) {
|
|
180
78
|
return {
|
|
181
79
|
...options,
|
|
182
80
|
spawnClaudeCodeProcess: (spawnOptions) => {
|
|
@@ -185,16 +83,33 @@ function applyRuntimeSettingsToClaudeOptions(options, runtimeSettings) {
|
|
|
185
83
|
// running from the managed runtime bundle where node isn't in PATH.
|
|
186
84
|
// Always use process.execPath — the actual node binary running the daemon.
|
|
187
85
|
const command = resolved.command === spawnOptions.command ? process.execPath : resolved.command;
|
|
188
|
-
|
|
86
|
+
const child = spawn(command, resolved.args, {
|
|
189
87
|
cwd: spawnOptions.cwd,
|
|
190
|
-
env:
|
|
88
|
+
env: {
|
|
89
|
+
...applyProviderEnv(spawnOptions.env, runtimeSettings),
|
|
90
|
+
...(launchEnv ?? {}),
|
|
91
|
+
},
|
|
191
92
|
signal: spawnOptions.signal,
|
|
192
93
|
stdio: ["pipe", "pipe", "pipe"],
|
|
193
94
|
});
|
|
95
|
+
if (typeof options.stderr === "function") {
|
|
96
|
+
child.stderr?.on("data", (chunk) => {
|
|
97
|
+
options.stderr?.(chunk.toString());
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return child;
|
|
194
101
|
},
|
|
195
102
|
};
|
|
196
103
|
}
|
|
104
|
+
function createEmptyClaudePrompt() {
|
|
105
|
+
return (async function* empty() { })();
|
|
106
|
+
}
|
|
107
|
+
function isClaudeThinkingEffort(value) {
|
|
108
|
+
return value === "low" || value === "medium" || value === "high" || value === "max";
|
|
109
|
+
}
|
|
197
110
|
const MAX_RECENT_STDERR_CHARS = 4000;
|
|
111
|
+
const STDERR_FLUSH_WAIT_MS = 150;
|
|
112
|
+
const STDERR_FLUSH_POLL_INTERVAL_MS = 10;
|
|
198
113
|
function summarizeClaudeOptionsForLog(options) {
|
|
199
114
|
const systemPromptRaw = options.systemPrompt;
|
|
200
115
|
const systemPromptSummary = (() => {
|
|
@@ -792,16 +707,17 @@ export class ClaudeAgentClient {
|
|
|
792
707
|
this.runtimeSettings = options.runtimeSettings;
|
|
793
708
|
this.queryFactory = options.queryFactory ?? query;
|
|
794
709
|
}
|
|
795
|
-
async createSession(config) {
|
|
710
|
+
async createSession(config, launchContext) {
|
|
796
711
|
const claudeConfig = this.assertConfig(config);
|
|
797
712
|
return new ClaudeAgentSession(claudeConfig, {
|
|
798
713
|
defaults: this.defaults,
|
|
799
714
|
runtimeSettings: this.runtimeSettings,
|
|
715
|
+
launchEnv: launchContext?.env,
|
|
800
716
|
logger: this.logger,
|
|
801
717
|
queryFactory: this.queryFactory,
|
|
802
718
|
});
|
|
803
719
|
}
|
|
804
|
-
async resumeSession(handle, overrides) {
|
|
720
|
+
async resumeSession(handle, overrides, launchContext) {
|
|
805
721
|
const metadata = coerceSessionMetadata(handle.metadata);
|
|
806
722
|
const merged = { ...metadata, ...overrides };
|
|
807
723
|
if (!merged.cwd) {
|
|
@@ -813,12 +729,37 @@ export class ClaudeAgentClient {
|
|
|
813
729
|
defaults: this.defaults,
|
|
814
730
|
runtimeSettings: this.runtimeSettings,
|
|
815
731
|
handle,
|
|
732
|
+
launchEnv: launchContext?.env,
|
|
816
733
|
logger: this.logger,
|
|
817
734
|
queryFactory: this.queryFactory,
|
|
818
735
|
});
|
|
819
736
|
}
|
|
820
|
-
async listModels(
|
|
821
|
-
|
|
737
|
+
async listModels(options) {
|
|
738
|
+
const claudeQuery = this.queryFactory({
|
|
739
|
+
prompt: createEmptyClaudePrompt(),
|
|
740
|
+
options: applyRuntimeSettingsToClaudeOptions({
|
|
741
|
+
cwd: options?.cwd ?? process.cwd(),
|
|
742
|
+
permissionMode: "plan",
|
|
743
|
+
includePartialMessages: false,
|
|
744
|
+
settingSources: CLAUDE_SETTING_SOURCES,
|
|
745
|
+
}, this.runtimeSettings),
|
|
746
|
+
});
|
|
747
|
+
try {
|
|
748
|
+
const supportedModels = await claudeQuery.supportedModels();
|
|
749
|
+
return resolveClaudeModelsFromSdkModels(supportedModels);
|
|
750
|
+
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
this.logger.warn({ err: error }, "Failed to query Claude supportedModels()");
|
|
753
|
+
throw error;
|
|
754
|
+
}
|
|
755
|
+
finally {
|
|
756
|
+
try {
|
|
757
|
+
await claudeQuery.return?.();
|
|
758
|
+
}
|
|
759
|
+
catch {
|
|
760
|
+
// ignore control-plane shutdown errors
|
|
761
|
+
}
|
|
762
|
+
}
|
|
822
763
|
}
|
|
823
764
|
async listPersistedAgents(options) {
|
|
824
765
|
const configDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
|
|
@@ -865,30 +806,27 @@ class ClaudeAgentSession {
|
|
|
865
806
|
this.toolUseIndexToId = new Map();
|
|
866
807
|
this.toolUseInputBuffers = new Map();
|
|
867
808
|
this.pendingPermissions = new Map();
|
|
868
|
-
this.
|
|
809
|
+
this.activeForegroundTurnId = null;
|
|
869
810
|
this.autonomousTurn = null;
|
|
870
|
-
this.
|
|
811
|
+
this.subscribers = new Set();
|
|
871
812
|
this.timelineAssembler = new TimelineAssembler();
|
|
872
813
|
this.sidechainTracker = new ClaudeSidechainTracker({
|
|
873
814
|
getToolInput: (toolUseId) => this.toolUseCache.get(toolUseId)?.input ?? null,
|
|
874
815
|
});
|
|
875
816
|
this.persistedHistory = [];
|
|
876
817
|
this.historyPending = false;
|
|
877
|
-
this.historyOffsetSessionId = null;
|
|
878
|
-
this.historyReadOffsetBytes = 0;
|
|
879
|
-
this.historyLineFragment = "";
|
|
880
818
|
this.turnState = "idle";
|
|
881
819
|
this.nextTurnOrdinal = 1;
|
|
882
820
|
this.cancelCurrentTurn = null;
|
|
883
|
-
this.activeTurnPromise = null;
|
|
884
821
|
this.cachedRuntimeInfo = null;
|
|
885
822
|
this.lastOptionsModel = null;
|
|
886
|
-
this.
|
|
887
|
-
this.selectableModelFamilyAliases = buildClaudeModelFamilyAliases();
|
|
823
|
+
this.lastRuntimeModel = null;
|
|
888
824
|
this.compacting = false;
|
|
889
825
|
this.queryPumpPromise = null;
|
|
890
826
|
this.queryRestartNeeded = false;
|
|
891
827
|
this.pendingInterruptAbort = false;
|
|
828
|
+
this.lastForegroundPromptText = null;
|
|
829
|
+
this.foregroundHasVisibleActivity = false;
|
|
892
830
|
this.userMessageIds = [];
|
|
893
831
|
this.recentStderr = "";
|
|
894
832
|
this.closed = false;
|
|
@@ -956,6 +894,7 @@ class ClaudeAgentSession {
|
|
|
956
894
|
});
|
|
957
895
|
};
|
|
958
896
|
this.config = config;
|
|
897
|
+
this.launchEnv = options.launchEnv;
|
|
959
898
|
this.defaults = options.defaults;
|
|
960
899
|
this.runtimeSettings = options.runtimeSettings;
|
|
961
900
|
this.logger = options.logger;
|
|
@@ -992,16 +931,34 @@ class ClaudeAgentSession {
|
|
|
992
931
|
sessionId: this.claudeSessionId,
|
|
993
932
|
model: this.lastOptionsModel,
|
|
994
933
|
modeId: this.currentMode ?? null,
|
|
934
|
+
...(this.lastRuntimeModel
|
|
935
|
+
? {
|
|
936
|
+
extra: {
|
|
937
|
+
runtimeModel: this.lastRuntimeModel,
|
|
938
|
+
},
|
|
939
|
+
}
|
|
940
|
+
: {}),
|
|
995
941
|
};
|
|
996
942
|
this.cachedRuntimeInfo = info;
|
|
997
943
|
return { ...info };
|
|
998
944
|
}
|
|
999
945
|
async run(prompt, options) {
|
|
1000
|
-
const events = this.stream(prompt, options);
|
|
1001
946
|
const timeline = [];
|
|
1002
947
|
let finalText = "";
|
|
1003
948
|
let usage;
|
|
1004
|
-
|
|
949
|
+
let turnId = null;
|
|
950
|
+
const bufferedEvents = [];
|
|
951
|
+
let settled = false;
|
|
952
|
+
let resolveCompletion;
|
|
953
|
+
let rejectCompletion;
|
|
954
|
+
const processEvent = (event) => {
|
|
955
|
+
if (settled) {
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
const eventTurnId = event.turnId;
|
|
959
|
+
if (turnId && eventTurnId && eventTurnId !== turnId) {
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
1005
962
|
if (event.type === "timeline") {
|
|
1006
963
|
timeline.push(event.item);
|
|
1007
964
|
if (event.item.type === "assistant_message") {
|
|
@@ -1015,14 +972,48 @@ class ClaudeAgentSession {
|
|
|
1015
972
|
finalText += event.item.text;
|
|
1016
973
|
}
|
|
1017
974
|
}
|
|
975
|
+
return;
|
|
1018
976
|
}
|
|
1019
|
-
|
|
977
|
+
if (event.type === "turn_completed") {
|
|
1020
978
|
usage = event.usage;
|
|
979
|
+
settled = true;
|
|
980
|
+
resolveCompletion();
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
if (event.type === "turn_failed") {
|
|
984
|
+
settled = true;
|
|
985
|
+
rejectCompletion(new Error(event.error));
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
if (event.type === "turn_canceled") {
|
|
989
|
+
settled = true;
|
|
990
|
+
resolveCompletion();
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
const completion = new Promise((resolve, reject) => {
|
|
994
|
+
resolveCompletion = resolve;
|
|
995
|
+
rejectCompletion = reject;
|
|
996
|
+
});
|
|
997
|
+
const unsubscribe = this.subscribe((event) => {
|
|
998
|
+
if (!turnId) {
|
|
999
|
+
bufferedEvents.push(event);
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
processEvent(event);
|
|
1003
|
+
});
|
|
1004
|
+
try {
|
|
1005
|
+
const result = await this.startTurn(prompt, options);
|
|
1006
|
+
turnId = result.turnId;
|
|
1007
|
+
for (const event of bufferedEvents) {
|
|
1008
|
+
processEvent(event);
|
|
1021
1009
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1010
|
+
if (!settled) {
|
|
1011
|
+
await completion;
|
|
1024
1012
|
}
|
|
1025
1013
|
}
|
|
1014
|
+
finally {
|
|
1015
|
+
unsubscribe();
|
|
1016
|
+
}
|
|
1026
1017
|
this.cachedRuntimeInfo = {
|
|
1027
1018
|
provider: "claude",
|
|
1028
1019
|
sessionId: this.claudeSessionId,
|
|
@@ -1039,35 +1030,32 @@ class ClaudeAgentSession {
|
|
|
1039
1030
|
timeline,
|
|
1040
1031
|
};
|
|
1041
1032
|
}
|
|
1042
|
-
async
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1033
|
+
async startTurn(prompt, _options) {
|
|
1034
|
+
if (this.closed) {
|
|
1035
|
+
throw new Error("Claude session is closed");
|
|
1036
|
+
}
|
|
1037
|
+
if (this.activeForegroundTurnId) {
|
|
1038
|
+
throw new Error("A foreground turn is already active");
|
|
1046
1039
|
}
|
|
1047
1040
|
const slashCommand = this.resolveSlashCommandInvocation(prompt);
|
|
1048
1041
|
if (slashCommand?.commandName === REWIND_COMMAND_NAME) {
|
|
1049
|
-
|
|
1050
|
-
|
|
1042
|
+
const turnId = this.createTurnId("foreground");
|
|
1043
|
+
this.activeForegroundTurnId = turnId;
|
|
1044
|
+
this.transitionTurnState("foreground", "rewind command");
|
|
1045
|
+
void this.executeRewindTurn(turnId, slashCommand);
|
|
1046
|
+
return { turnId };
|
|
1051
1047
|
}
|
|
1052
1048
|
if (this.autonomousTurn) {
|
|
1053
1049
|
this.completeAutonomousTurn();
|
|
1054
1050
|
}
|
|
1055
1051
|
const sdkMessage = this.toSdkUserMessage(prompt);
|
|
1056
|
-
|
|
1057
|
-
const
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
};
|
|
1062
|
-
this.activeForegroundTurn = foregroundTurn;
|
|
1063
|
-
this.transitionTurnState("foreground", "foreground stream started");
|
|
1052
|
+
this.lastForegroundPromptText = this.extractPromptText(prompt);
|
|
1053
|
+
const turnId = this.createTurnId("foreground");
|
|
1054
|
+
this.activeForegroundTurnId = turnId;
|
|
1055
|
+
this.foregroundHasVisibleActivity = false;
|
|
1056
|
+
this.transitionTurnState("foreground", "foreground turn started");
|
|
1064
1057
|
this.clearRecentStderr();
|
|
1065
|
-
queue.push({ type: "turn_started", provider: "claude" });
|
|
1066
|
-
let finishedNaturally = false;
|
|
1067
1058
|
let cancelIssued = false;
|
|
1068
|
-
let queueDrainedWithoutTerminal = false;
|
|
1069
|
-
const turnPromise = Promise.resolve();
|
|
1070
|
-
this.activeTurnPromise = turnPromise;
|
|
1071
1059
|
const requestCancel = () => {
|
|
1072
1060
|
if (cancelIssued) {
|
|
1073
1061
|
return;
|
|
@@ -1087,6 +1075,7 @@ class ClaudeAgentSession {
|
|
|
1087
1075
|
});
|
|
1088
1076
|
};
|
|
1089
1077
|
this.cancelCurrentTurn = requestCancel;
|
|
1078
|
+
this.notifySubscribers({ type: "turn_started", provider: "claude" });
|
|
1090
1079
|
try {
|
|
1091
1080
|
await this.ensureQuery();
|
|
1092
1081
|
if (!this.input) {
|
|
@@ -1097,39 +1086,14 @@ class ClaudeAgentSession {
|
|
|
1097
1086
|
}
|
|
1098
1087
|
catch (error) {
|
|
1099
1088
|
this.finishForegroundTurn(this.buildTurnFailedEvent(error instanceof Error ? error.message : "Claude stream failed"));
|
|
1100
|
-
finishedNaturally = true;
|
|
1101
|
-
}
|
|
1102
|
-
try {
|
|
1103
|
-
for await (const event of queue) {
|
|
1104
|
-
const isTerminalEvent = event.type === "turn_completed" ||
|
|
1105
|
-
event.type === "turn_failed" ||
|
|
1106
|
-
event.type === "turn_canceled";
|
|
1107
|
-
if (isTerminalEvent) {
|
|
1108
|
-
finishedNaturally = true;
|
|
1109
|
-
}
|
|
1110
|
-
yield event;
|
|
1111
|
-
if (isTerminalEvent) {
|
|
1112
|
-
break;
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
if (!finishedNaturally && !cancelIssued) {
|
|
1116
|
-
queueDrainedWithoutTerminal = true;
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
finally {
|
|
1120
|
-
if (!finishedNaturally && !cancelIssued && !queueDrainedWithoutTerminal) {
|
|
1121
|
-
requestCancel();
|
|
1122
|
-
}
|
|
1123
|
-
if (this.activeForegroundTurn === foregroundTurn) {
|
|
1124
|
-
this.activeForegroundTurn = null;
|
|
1125
|
-
}
|
|
1126
|
-
if (this.cancelCurrentTurn === requestCancel) {
|
|
1127
|
-
this.cancelCurrentTurn = null;
|
|
1128
|
-
}
|
|
1129
|
-
if (this.activeTurnPromise === turnPromise) {
|
|
1130
|
-
this.activeTurnPromise = null;
|
|
1131
|
-
}
|
|
1132
1089
|
}
|
|
1090
|
+
return { turnId };
|
|
1091
|
+
}
|
|
1092
|
+
subscribe(callback) {
|
|
1093
|
+
this.subscribers.add(callback);
|
|
1094
|
+
return () => {
|
|
1095
|
+
this.subscribers.delete(callback);
|
|
1096
|
+
};
|
|
1133
1097
|
}
|
|
1134
1098
|
async interrupt() {
|
|
1135
1099
|
if (this.cancelCurrentTurn) {
|
|
@@ -1137,7 +1101,8 @@ class ClaudeAgentSession {
|
|
|
1137
1101
|
return;
|
|
1138
1102
|
}
|
|
1139
1103
|
if (this.autonomousTurn) {
|
|
1140
|
-
this.
|
|
1104
|
+
this.flushPendingToolCalls();
|
|
1105
|
+
this.completeAutonomousTurn();
|
|
1141
1106
|
}
|
|
1142
1107
|
await this.interruptActiveTurn();
|
|
1143
1108
|
}
|
|
@@ -1152,14 +1117,6 @@ class ClaudeAgentSession {
|
|
|
1152
1117
|
yield { type: "timeline", item, provider: "claude" };
|
|
1153
1118
|
}
|
|
1154
1119
|
}
|
|
1155
|
-
async *streamLiveEvents() {
|
|
1156
|
-
if (this.claudeSessionId) {
|
|
1157
|
-
this.startQueryPump();
|
|
1158
|
-
}
|
|
1159
|
-
for await (const event of this.liveEventQueue) {
|
|
1160
|
-
yield event;
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
1120
|
async getAvailableModes() {
|
|
1164
1121
|
return this.availableModes;
|
|
1165
1122
|
}
|
|
@@ -1183,6 +1140,7 @@ class ClaudeAgentSession {
|
|
|
1183
1140
|
await query.setModel(normalizedModelId ?? undefined);
|
|
1184
1141
|
this.config.model = normalizedModelId ?? undefined;
|
|
1185
1142
|
this.lastOptionsModel = normalizedModelId ?? this.lastOptionsModel;
|
|
1143
|
+
this.lastRuntimeModel = null;
|
|
1186
1144
|
this.cachedRuntimeInfo = null;
|
|
1187
1145
|
// Model change affects persistence metadata, so invalidate cached handle.
|
|
1188
1146
|
this.persistence = null;
|
|
@@ -1194,11 +1152,8 @@ class ClaudeAgentSession {
|
|
|
1194
1152
|
if (!normalizedThinkingOptionId || normalizedThinkingOptionId === "default") {
|
|
1195
1153
|
this.config.thinkingOptionId = undefined;
|
|
1196
1154
|
}
|
|
1197
|
-
else if (normalizedThinkingOptionId
|
|
1198
|
-
this.config.thinkingOptionId =
|
|
1199
|
-
}
|
|
1200
|
-
else if (normalizedThinkingOptionId === "off") {
|
|
1201
|
-
this.config.thinkingOptionId = "off";
|
|
1155
|
+
else if (isClaudeThinkingEffort(normalizedThinkingOptionId)) {
|
|
1156
|
+
this.config.thinkingOptionId = normalizedThinkingOptionId;
|
|
1202
1157
|
}
|
|
1203
1158
|
else {
|
|
1204
1159
|
throw new Error(`Unknown thinking option: ${normalizedThinkingOptionId}`);
|
|
@@ -1269,7 +1224,7 @@ class ClaudeAgentSession {
|
|
|
1269
1224
|
provider: "claude",
|
|
1270
1225
|
sessionId: this.claudeSessionId,
|
|
1271
1226
|
nativeHandle: this.claudeSessionId,
|
|
1272
|
-
metadata: this.config,
|
|
1227
|
+
metadata: { ...this.config },
|
|
1273
1228
|
};
|
|
1274
1229
|
return this.persistence;
|
|
1275
1230
|
}
|
|
@@ -1279,21 +1234,19 @@ class ClaudeAgentSession {
|
|
|
1279
1234
|
turnState: this.turnState,
|
|
1280
1235
|
hasQuery: Boolean(this.query),
|
|
1281
1236
|
hasInput: Boolean(this.input),
|
|
1282
|
-
|
|
1237
|
+
hasActiveForegroundTurnId: Boolean(this.activeForegroundTurnId),
|
|
1283
1238
|
}, "Claude session close: start");
|
|
1284
1239
|
this.closed = true;
|
|
1285
1240
|
this.rejectAllPendingPermissions(new Error("Claude session closed"));
|
|
1286
1241
|
this.cancelCurrentTurn?.();
|
|
1287
|
-
this.
|
|
1288
|
-
this.
|
|
1242
|
+
this.subscribers.clear();
|
|
1243
|
+
this.activeForegroundTurnId = null;
|
|
1289
1244
|
this.autonomousTurn = null;
|
|
1290
1245
|
this.cancelCurrentTurn = null;
|
|
1291
1246
|
this.turnState = "idle";
|
|
1292
|
-
this.liveEventQueue.end();
|
|
1293
|
-
this.activeTurnPromise = null;
|
|
1294
1247
|
this.sidechainTracker.clear();
|
|
1295
1248
|
this.input?.end();
|
|
1296
|
-
this.query?.close();
|
|
1249
|
+
this.query?.close?.();
|
|
1297
1250
|
await this.awaitWithTimeout(this.query?.interrupt?.(), "close query interrupt");
|
|
1298
1251
|
await this.awaitWithTimeout(this.query?.return?.(), "close query return");
|
|
1299
1252
|
this.query = null;
|
|
@@ -1344,37 +1297,6 @@ class ClaudeAgentSession {
|
|
|
1344
1297
|
? { commandName, args: rawArgs, rawInput: trimmed }
|
|
1345
1298
|
: { commandName, rawInput: trimmed };
|
|
1346
1299
|
}
|
|
1347
|
-
async *streamRewindCommand(invocation) {
|
|
1348
|
-
yield { type: "turn_started", provider: "claude" };
|
|
1349
|
-
try {
|
|
1350
|
-
const rewindAttempt = await this.attemptRewind(invocation.args);
|
|
1351
|
-
if (!rewindAttempt.messageId || !rewindAttempt.result) {
|
|
1352
|
-
yield {
|
|
1353
|
-
type: "turn_failed",
|
|
1354
|
-
provider: "claude",
|
|
1355
|
-
error: rewindAttempt.error ??
|
|
1356
|
-
"No prior user message available to rewind. Use /rewind <user_message_uuid>.",
|
|
1357
|
-
};
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
|
-
yield {
|
|
1361
|
-
type: "timeline",
|
|
1362
|
-
provider: "claude",
|
|
1363
|
-
item: {
|
|
1364
|
-
type: "assistant_message",
|
|
1365
|
-
text: this.buildRewindSuccessMessage(rewindAttempt.messageId, rewindAttempt.result),
|
|
1366
|
-
},
|
|
1367
|
-
};
|
|
1368
|
-
yield { type: "turn_completed", provider: "claude" };
|
|
1369
|
-
}
|
|
1370
|
-
catch (error) {
|
|
1371
|
-
yield {
|
|
1372
|
-
type: "turn_failed",
|
|
1373
|
-
provider: "claude",
|
|
1374
|
-
error: error instanceof Error ? error.message : "Failed to rewind tracked files",
|
|
1375
|
-
};
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
1300
|
buildRewindSuccessMessage(targetUserMessageId, rewindResult) {
|
|
1379
1301
|
const fileCount = Array.isArray(rewindResult.filesChanged)
|
|
1380
1302
|
? rewindResult.filesChanged.length
|
|
@@ -1525,23 +1447,28 @@ class ClaudeAgentSession {
|
|
|
1525
1447
|
return this.query;
|
|
1526
1448
|
}
|
|
1527
1449
|
if (this.queryRestartNeeded && this.query) {
|
|
1528
|
-
this.
|
|
1529
|
-
this.
|
|
1450
|
+
const oldQuery = this.query;
|
|
1451
|
+
const oldInput = this.input;
|
|
1452
|
+
// Null out query/input BEFORE awaiting the old iterator's return so the
|
|
1453
|
+
// old pump sees this.query !== activeQuery and skips failActiveTurns.
|
|
1454
|
+
this.query = null;
|
|
1455
|
+
this.input = null;
|
|
1456
|
+
this.queryPumpPromise = null;
|
|
1457
|
+
this.queryRestartNeeded = false;
|
|
1458
|
+
oldInput?.end();
|
|
1459
|
+
oldQuery.close?.();
|
|
1530
1460
|
try {
|
|
1531
|
-
await
|
|
1461
|
+
await oldQuery.return?.();
|
|
1532
1462
|
}
|
|
1533
1463
|
catch {
|
|
1534
1464
|
/* ignore */
|
|
1535
1465
|
}
|
|
1536
|
-
this.query = null;
|
|
1537
|
-
this.input = null;
|
|
1538
|
-
this.queryRestartNeeded = false;
|
|
1539
1466
|
}
|
|
1540
|
-
const input =
|
|
1467
|
+
const input = createAsyncMessageInput();
|
|
1541
1468
|
const options = this.buildOptions();
|
|
1542
1469
|
this.logger.debug({ options: summarizeClaudeOptionsForLog(options) }, "claude query");
|
|
1543
1470
|
this.input = input;
|
|
1544
|
-
this.query = this.queryFactory({ prompt: input, options });
|
|
1471
|
+
this.query = this.queryFactory({ prompt: input.iterable, options });
|
|
1545
1472
|
// Do not kick off background control-plane queries here. Methods like
|
|
1546
1473
|
// supportedCommands()/setPermissionMode() may execute immediately after
|
|
1547
1474
|
// ensureQuery() (for listCommands()/setMode()), and sharing the same query
|
|
@@ -1569,16 +1496,14 @@ class ClaudeAgentSession {
|
|
|
1569
1496
|
}
|
|
1570
1497
|
}
|
|
1571
1498
|
buildOptions() {
|
|
1572
|
-
const
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
let
|
|
1577
|
-
if (thinkingOptionId
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
else if (thinkingOptionId === "off") {
|
|
1581
|
-
maxThinkingTokens = 0;
|
|
1499
|
+
const thinkingOptionId = this.config.thinkingOptionId && this.config.thinkingOptionId !== "default"
|
|
1500
|
+
? this.config.thinkingOptionId
|
|
1501
|
+
: undefined;
|
|
1502
|
+
let thinking;
|
|
1503
|
+
let effort;
|
|
1504
|
+
if (thinkingOptionId && isClaudeThinkingEffort(thinkingOptionId)) {
|
|
1505
|
+
thinking = { type: "adaptive" };
|
|
1506
|
+
effort = thinkingOptionId;
|
|
1582
1507
|
}
|
|
1583
1508
|
const appendedSystemPrompt = [
|
|
1584
1509
|
getOrchestratorModeInstructions(),
|
|
@@ -1609,13 +1534,15 @@ class ClaudeAgentSession {
|
|
|
1609
1534
|
// Increase MCP timeouts for long-running tool calls (10 minutes)
|
|
1610
1535
|
MCP_TIMEOUT: "600000",
|
|
1611
1536
|
MCP_TOOL_TIMEOUT: "600000",
|
|
1537
|
+
...(this.launchEnv ?? {}),
|
|
1612
1538
|
},
|
|
1613
1539
|
// Required for provider-level /rewind support.
|
|
1614
1540
|
enableFileCheckpointing: true,
|
|
1615
1541
|
// If we have a session ID from a previous query (e.g., after interrupt),
|
|
1616
1542
|
// resume that session to continue the conversation history.
|
|
1617
1543
|
...(this.claudeSessionId ? { resume: this.claudeSessionId } : {}),
|
|
1618
|
-
...(
|
|
1544
|
+
...(thinking ? { thinking } : {}),
|
|
1545
|
+
...(effort ? { effort } : {}),
|
|
1619
1546
|
...this.config.extra?.claude,
|
|
1620
1547
|
};
|
|
1621
1548
|
if (this.config.mcpServers) {
|
|
@@ -1631,7 +1558,7 @@ class ClaudeAgentSession {
|
|
|
1631
1558
|
return this.applyRuntimeSettings(base);
|
|
1632
1559
|
}
|
|
1633
1560
|
applyRuntimeSettings(options) {
|
|
1634
|
-
return applyRuntimeSettingsToClaudeOptions(options, this.runtimeSettings);
|
|
1561
|
+
return applyRuntimeSettingsToClaudeOptions(options, this.runtimeSettings, this.launchEnv);
|
|
1635
1562
|
}
|
|
1636
1563
|
normalizeMcpServers(servers) {
|
|
1637
1564
|
const result = {};
|
|
@@ -1683,7 +1610,7 @@ class ClaudeAgentSession {
|
|
|
1683
1610
|
this.turnState = next;
|
|
1684
1611
|
}
|
|
1685
1612
|
syncTurnState(reason) {
|
|
1686
|
-
if (this.
|
|
1613
|
+
if (this.activeForegroundTurnId) {
|
|
1687
1614
|
this.transitionTurnState("foreground", reason);
|
|
1688
1615
|
return;
|
|
1689
1616
|
}
|
|
@@ -1693,6 +1620,10 @@ class ClaudeAgentSession {
|
|
|
1693
1620
|
}
|
|
1694
1621
|
this.transitionTurnState("idle", reason);
|
|
1695
1622
|
}
|
|
1623
|
+
isAbortError(message) {
|
|
1624
|
+
const errors = "errors" in message && Array.isArray(message.errors) ? message.errors : [];
|
|
1625
|
+
return errors.some((e) => /\baborted\b/i.test(e));
|
|
1626
|
+
}
|
|
1696
1627
|
buildTurnFailedEvent(errorMessage) {
|
|
1697
1628
|
const normalized = errorMessage.trim() || "Claude run failed";
|
|
1698
1629
|
const exitCodeMatch = normalized.match(/\bcode\s+(\d+)\b/i);
|
|
@@ -1720,6 +1651,22 @@ class ClaudeAgentSession {
|
|
|
1720
1651
|
getRecentStderrDiagnostic() {
|
|
1721
1652
|
return this.recentStderr.trim() || undefined;
|
|
1722
1653
|
}
|
|
1654
|
+
async awaitRecentStderrAfterProcessExit(error) {
|
|
1655
|
+
if (this.getRecentStderrDiagnostic()) {
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
const message = typeof error === "string" ? error : error instanceof Error ? error.message : "";
|
|
1659
|
+
if (!/\bprocess exited with code\b/i.test(message) && !/\bterminated by signal\b/i.test(message)) {
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
const startedAt = Date.now();
|
|
1663
|
+
while (!this.closed && !this.getRecentStderrDiagnostic()) {
|
|
1664
|
+
if (Date.now() - startedAt >= STDERR_FLUSH_WAIT_MS) {
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
await new Promise((resolve) => setTimeout(resolve, STDERR_FLUSH_POLL_INTERVAL_MS));
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1723
1670
|
createTurnId(owner) {
|
|
1724
1671
|
return `${owner}-turn-${this.nextTurnOrdinal++}`;
|
|
1725
1672
|
}
|
|
@@ -1728,6 +1675,46 @@ class ClaudeAgentSession {
|
|
|
1728
1675
|
event.type === "turn_failed" ||
|
|
1729
1676
|
event.type === "turn_canceled");
|
|
1730
1677
|
}
|
|
1678
|
+
extractPromptText(prompt) {
|
|
1679
|
+
if (typeof prompt === "string") {
|
|
1680
|
+
return prompt;
|
|
1681
|
+
}
|
|
1682
|
+
const textParts = prompt
|
|
1683
|
+
.filter((block) => block.type === "text")
|
|
1684
|
+
.map((block) => block.text);
|
|
1685
|
+
return textParts.length > 0 ? textParts.join("\n") : null;
|
|
1686
|
+
}
|
|
1687
|
+
async executeRewindTurn(_turnId, invocation) {
|
|
1688
|
+
this.notifySubscribers({ type: "turn_started", provider: "claude" });
|
|
1689
|
+
try {
|
|
1690
|
+
const rewindAttempt = await this.attemptRewind(invocation.args);
|
|
1691
|
+
if (!rewindAttempt.messageId || !rewindAttempt.result) {
|
|
1692
|
+
this.finishForegroundTurn({
|
|
1693
|
+
type: "turn_failed",
|
|
1694
|
+
provider: "claude",
|
|
1695
|
+
error: rewindAttempt.error ??
|
|
1696
|
+
"No prior user message available to rewind. Use /rewind <user_message_uuid>.",
|
|
1697
|
+
});
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
this.notifySubscribers({
|
|
1701
|
+
type: "timeline",
|
|
1702
|
+
provider: "claude",
|
|
1703
|
+
item: {
|
|
1704
|
+
type: "assistant_message",
|
|
1705
|
+
text: this.buildRewindSuccessMessage(rewindAttempt.messageId, rewindAttempt.result),
|
|
1706
|
+
},
|
|
1707
|
+
});
|
|
1708
|
+
this.finishForegroundTurn({ type: "turn_completed", provider: "claude" });
|
|
1709
|
+
}
|
|
1710
|
+
catch (error) {
|
|
1711
|
+
this.finishForegroundTurn({
|
|
1712
|
+
type: "turn_failed",
|
|
1713
|
+
provider: "claude",
|
|
1714
|
+
error: error instanceof Error ? error.message : "Failed to rewind tracked files",
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1731
1718
|
shouldRecoverInterruptedQueryAbort(error, consecutiveRecoveries) {
|
|
1732
1719
|
if (consecutiveRecoveries >= 3) {
|
|
1733
1720
|
return false;
|
|
@@ -1743,37 +1730,29 @@ class ClaudeAgentSession {
|
|
|
1743
1730
|
if (event.type === "turn_failed" || event.type === "turn_canceled") {
|
|
1744
1731
|
this.flushPendingToolCalls();
|
|
1745
1732
|
}
|
|
1746
|
-
this.
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
if (!foregroundTurn) {
|
|
1751
|
-
this.dispatchLiveEvents(events);
|
|
1752
|
-
return;
|
|
1753
|
-
}
|
|
1754
|
-
let terminalSeen = false;
|
|
1755
|
-
for (const event of events) {
|
|
1756
|
-
foregroundTurn.queue.push(event);
|
|
1757
|
-
terminalSeen || (terminalSeen = this.isTerminalTurnEvent(event));
|
|
1758
|
-
}
|
|
1759
|
-
if (!terminalSeen) {
|
|
1760
|
-
return;
|
|
1761
|
-
}
|
|
1762
|
-
foregroundTurn.queue.end();
|
|
1763
|
-
if (this.activeForegroundTurn === foregroundTurn) {
|
|
1764
|
-
this.activeForegroundTurn = null;
|
|
1765
|
-
}
|
|
1733
|
+
this.notifySubscribers(event);
|
|
1734
|
+
this.activeForegroundTurnId = null;
|
|
1735
|
+
this.lastForegroundPromptText = null;
|
|
1736
|
+
this.cancelCurrentTurn = null;
|
|
1766
1737
|
this.syncTurnState("foreground turn terminal");
|
|
1767
1738
|
}
|
|
1768
|
-
|
|
1739
|
+
dispatchEvents(events) {
|
|
1769
1740
|
let terminalSeen = false;
|
|
1770
1741
|
for (const event of events) {
|
|
1771
|
-
this.
|
|
1742
|
+
this.notifySubscribers(event);
|
|
1772
1743
|
terminalSeen || (terminalSeen = this.isTerminalTurnEvent(event));
|
|
1773
1744
|
}
|
|
1774
|
-
if (terminalSeen
|
|
1775
|
-
this.
|
|
1776
|
-
|
|
1745
|
+
if (terminalSeen) {
|
|
1746
|
+
if (this.activeForegroundTurnId) {
|
|
1747
|
+
this.activeForegroundTurnId = null;
|
|
1748
|
+
this.lastForegroundPromptText = null;
|
|
1749
|
+
this.cancelCurrentTurn = null;
|
|
1750
|
+
this.syncTurnState("foreground turn terminal");
|
|
1751
|
+
}
|
|
1752
|
+
else if (this.autonomousTurn) {
|
|
1753
|
+
this.autonomousTurn = null;
|
|
1754
|
+
this.syncTurnState("autonomous turn terminal");
|
|
1755
|
+
}
|
|
1777
1756
|
}
|
|
1778
1757
|
}
|
|
1779
1758
|
startAutonomousTurn() {
|
|
@@ -1783,40 +1762,26 @@ class ClaudeAgentSession {
|
|
|
1783
1762
|
this.autonomousTurn = {
|
|
1784
1763
|
id: this.createTurnId("autonomous"),
|
|
1785
1764
|
};
|
|
1786
|
-
this.
|
|
1765
|
+
this.notifySubscribers({ type: "turn_started", provider: "claude" });
|
|
1787
1766
|
this.syncTurnState("autonomous turn started");
|
|
1788
1767
|
}
|
|
1789
1768
|
completeAutonomousTurn() {
|
|
1790
1769
|
if (!this.autonomousTurn) {
|
|
1791
1770
|
return;
|
|
1792
1771
|
}
|
|
1772
|
+
this.notifySubscribers({ type: "turn_completed", provider: "claude" });
|
|
1793
1773
|
this.autonomousTurn = null;
|
|
1794
|
-
this.liveEventQueue.push({ type: "turn_completed", provider: "claude" });
|
|
1795
1774
|
this.syncTurnState("autonomous turn completed");
|
|
1796
1775
|
}
|
|
1797
|
-
cancelAutonomousTurn(reason) {
|
|
1798
|
-
if (!this.autonomousTurn) {
|
|
1799
|
-
return;
|
|
1800
|
-
}
|
|
1801
|
-
this.flushPendingToolCalls();
|
|
1802
|
-
this.autonomousTurn = null;
|
|
1803
|
-
this.liveEventQueue.push({
|
|
1804
|
-
type: "turn_canceled",
|
|
1805
|
-
provider: "claude",
|
|
1806
|
-
reason,
|
|
1807
|
-
});
|
|
1808
|
-
this.syncTurnState("autonomous turn canceled");
|
|
1809
|
-
}
|
|
1810
1776
|
failActiveTurns(errorMessage) {
|
|
1811
1777
|
const failure = this.buildTurnFailedEvent(errorMessage);
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
this.
|
|
1778
|
+
this.flushPendingToolCalls();
|
|
1779
|
+
if (this.activeForegroundTurnId) {
|
|
1780
|
+
this.finishForegroundTurn(failure);
|
|
1815
1781
|
return;
|
|
1816
1782
|
}
|
|
1817
1783
|
if (this.autonomousTurn) {
|
|
1818
|
-
this.
|
|
1819
|
-
this.dispatchLiveEvents([failure]);
|
|
1784
|
+
this.dispatchEvents([failure]);
|
|
1820
1785
|
}
|
|
1821
1786
|
}
|
|
1822
1787
|
startQueryPump() {
|
|
@@ -1848,6 +1813,12 @@ class ClaudeAgentSession {
|
|
|
1848
1813
|
while (!this.closed && this.query === activeQuery) {
|
|
1849
1814
|
try {
|
|
1850
1815
|
for await (const message of activeQuery) {
|
|
1816
|
+
this.logger.trace({
|
|
1817
|
+
claudeSessionId: this.claudeSessionId,
|
|
1818
|
+
messageType: message.type,
|
|
1819
|
+
messageSubtype: "subtype" in message ? message.subtype : undefined,
|
|
1820
|
+
messageUuid: "uuid" in message ? message.uuid : undefined,
|
|
1821
|
+
}, "Claude query pump: raw SDK message");
|
|
1851
1822
|
consecutiveInterruptAbortRecoveries = 0;
|
|
1852
1823
|
if (await this.handleMissingResumedConversation(message, activeQuery)) {
|
|
1853
1824
|
return;
|
|
@@ -1868,6 +1839,7 @@ class ClaudeAgentSession {
|
|
|
1868
1839
|
continue;
|
|
1869
1840
|
}
|
|
1870
1841
|
if (!this.closed && this.query === activeQuery) {
|
|
1842
|
+
await this.awaitRecentStderrAfterProcessExit(error);
|
|
1871
1843
|
this.failActiveTurns(error instanceof Error ? error.message : "Claude stream failed");
|
|
1872
1844
|
}
|
|
1873
1845
|
return;
|
|
@@ -1882,24 +1854,40 @@ class ClaudeAgentSession {
|
|
|
1882
1854
|
}
|
|
1883
1855
|
}
|
|
1884
1856
|
routeSdkMessageFromPump(message) {
|
|
1885
|
-
|
|
1857
|
+
// Suppress stale results from interrupted requests. The cancel path already
|
|
1858
|
+
// emitted the terminal event; this result is leftover from the killed API
|
|
1859
|
+
// request. Consume the flag on ANY result so it doesn't linger.
|
|
1860
|
+
if (message.type === "result" && this.pendingInterruptAbort) {
|
|
1861
|
+
this.pendingInterruptAbort = false;
|
|
1862
|
+
if (message.subtype !== "success") {
|
|
1863
|
+
this.logger.debug("Suppressing stale non-success result from interrupted request");
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
if (message.type === "result" &&
|
|
1868
|
+
message.subtype !== "success" &&
|
|
1869
|
+
this.isAbortError(message)) {
|
|
1870
|
+
this.logger.debug("Suppressing abort result by content");
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
const isForeground = Boolean(this.activeForegroundTurnId);
|
|
1886
1874
|
const assistantishMessage = message.type === "assistant" ||
|
|
1887
1875
|
message.type === "stream_event" ||
|
|
1888
|
-
message.type === "tool_progress"
|
|
1889
|
-
|
|
1876
|
+
message.type === "tool_progress" ||
|
|
1877
|
+
(message.type === "system" && message.subtype === "task_notification");
|
|
1878
|
+
if (!isForeground && assistantishMessage) {
|
|
1890
1879
|
this.startAutonomousTurn();
|
|
1891
1880
|
}
|
|
1892
|
-
if (!
|
|
1881
|
+
if (!isForeground && !this.autonomousTurn && message.type === "result") {
|
|
1893
1882
|
return;
|
|
1894
1883
|
}
|
|
1895
|
-
const turnId = this.
|
|
1884
|
+
const turnId = this.activeForegroundTurnId ?? this.autonomousTurn?.id ?? null;
|
|
1896
1885
|
const identifiers = readEventIdentifiers(message);
|
|
1897
1886
|
this.logger.trace({
|
|
1898
1887
|
claudeSessionId: this.claudeSessionId,
|
|
1899
1888
|
messageType: message.type,
|
|
1900
|
-
routedTo: routeToForeground ? "foreground_queue" : "live_queue",
|
|
1901
1889
|
turnId,
|
|
1902
|
-
}, "Claude query pump
|
|
1890
|
+
}, "Claude query pump: SDK message");
|
|
1903
1891
|
const messageEvents = this.translateMessageToEvents(message, {
|
|
1904
1892
|
suppressAssistantText: true,
|
|
1905
1893
|
suppressReasoning: true,
|
|
@@ -1915,30 +1903,37 @@ class ClaudeAgentSession {
|
|
|
1915
1903
|
item,
|
|
1916
1904
|
provider: "claude",
|
|
1917
1905
|
}));
|
|
1918
|
-
|
|
1906
|
+
// User message dedup: suppress echoed user messages that match the foreground prompt
|
|
1907
|
+
const filteredMessageEvents = messageEvents.filter((event) => {
|
|
1908
|
+
if (event.type === "timeline" &&
|
|
1909
|
+
event.item.type === "user_message" &&
|
|
1910
|
+
this.activeForegroundTurnId &&
|
|
1911
|
+
this.lastForegroundPromptText) {
|
|
1912
|
+
if (event.item.text.trim() === this.lastForegroundPromptText.trim()) {
|
|
1913
|
+
return false;
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
return true;
|
|
1917
|
+
});
|
|
1918
|
+
const events = [...filteredMessageEvents, ...assistantTimelineEvents];
|
|
1919
1919
|
if (events.length === 0) {
|
|
1920
1920
|
return;
|
|
1921
1921
|
}
|
|
1922
1922
|
if (this.pendingInterruptAbort &&
|
|
1923
1923
|
message.type === "result" &&
|
|
1924
1924
|
events.some((event) => event.type === "turn_completed" || event.type === "turn_failed") &&
|
|
1925
|
-
(!this.
|
|
1925
|
+
(!this.activeForegroundTurnId || !this.foregroundHasVisibleActivity)) {
|
|
1926
1926
|
this.pendingInterruptAbort = false;
|
|
1927
1927
|
this.logger.debug("Suppressing stale Claude interrupt terminal result");
|
|
1928
1928
|
return;
|
|
1929
1929
|
}
|
|
1930
|
-
if (this.
|
|
1930
|
+
if (this.activeForegroundTurnId &&
|
|
1931
1931
|
events.some((event) => event.type === "timeline" ||
|
|
1932
1932
|
event.type === "permission_requested" ||
|
|
1933
1933
|
event.type === "permission_resolved")) {
|
|
1934
|
-
this.
|
|
1935
|
-
this.pendingInterruptAbort = false;
|
|
1934
|
+
this.foregroundHasVisibleActivity = true;
|
|
1936
1935
|
}
|
|
1937
|
-
|
|
1938
|
-
this.dispatchForegroundEvents(events);
|
|
1939
|
-
return;
|
|
1940
|
-
}
|
|
1941
|
-
this.dispatchLiveEvents(events);
|
|
1936
|
+
this.dispatchEvents(events);
|
|
1942
1937
|
}
|
|
1943
1938
|
async handleMissingResumedConversation(message, query) {
|
|
1944
1939
|
const staleResumeError = this.readMissingResumedConversationError(message);
|
|
@@ -1960,13 +1955,10 @@ class ClaudeAgentSession {
|
|
|
1960
1955
|
this.persistence = null;
|
|
1961
1956
|
this.persistedHistory = [];
|
|
1962
1957
|
this.historyPending = false;
|
|
1963
|
-
this.historyOffsetSessionId = null;
|
|
1964
|
-
this.historyReadOffsetBytes = 0;
|
|
1965
|
-
this.historyLineFragment = "";
|
|
1966
1958
|
this.cachedRuntimeInfo = null;
|
|
1967
1959
|
this.queryRestartNeeded = false;
|
|
1968
1960
|
this.autonomousTurn = null;
|
|
1969
|
-
this.
|
|
1961
|
+
this.activeForegroundTurnId = null;
|
|
1970
1962
|
this.syncTurnState("missing resumed conversation");
|
|
1971
1963
|
return true;
|
|
1972
1964
|
}
|
|
@@ -2199,15 +2191,15 @@ class ClaudeAgentSession {
|
|
|
2199
2191
|
this.currentMode = message.permissionMode;
|
|
2200
2192
|
this.persistence = null;
|
|
2201
2193
|
if (message.model) {
|
|
2202
|
-
const
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
this.
|
|
2194
|
+
const normalizedRuntimeModel = normalizeClaudeModelIdFromText(message.model);
|
|
2195
|
+
this.logger.debug({ runtimeModel: message.model, normalizedRuntimeModel }, "Captured runtime model from SDK init");
|
|
2196
|
+
if (normalizedRuntimeModel) {
|
|
2197
|
+
this.lastOptionsModel = normalizedRuntimeModel;
|
|
2198
|
+
}
|
|
2199
|
+
else if (!this.lastOptionsModel) {
|
|
2200
|
+
this.lastOptionsModel = this.config.model ?? null;
|
|
2201
|
+
}
|
|
2202
|
+
this.lastRuntimeModel = message.model;
|
|
2211
2203
|
this.cachedRuntimeInfo = null;
|
|
2212
2204
|
}
|
|
2213
2205
|
return threadStartedSessionId;
|
|
@@ -2273,12 +2265,19 @@ class ClaudeAgentSession {
|
|
|
2273
2265
|
this.enqueueTimeline(item);
|
|
2274
2266
|
}
|
|
2275
2267
|
pushEvent(event) {
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2268
|
+
this.notifySubscribers(event);
|
|
2269
|
+
}
|
|
2270
|
+
notifySubscribers(event) {
|
|
2271
|
+
const turnId = this.activeForegroundTurnId ?? this.autonomousTurn?.id;
|
|
2272
|
+
const tagged = turnId ? { ...event, turnId } : event;
|
|
2273
|
+
for (const callback of this.subscribers) {
|
|
2274
|
+
try {
|
|
2275
|
+
callback(tagged);
|
|
2276
|
+
}
|
|
2277
|
+
catch (error) {
|
|
2278
|
+
this.logger.warn({ err: error }, "Subscriber callback threw");
|
|
2279
|
+
}
|
|
2280
2280
|
}
|
|
2281
|
-
this.liveEventQueue.push(event);
|
|
2282
2281
|
}
|
|
2283
2282
|
normalizePermissionUpdates(updates) {
|
|
2284
2283
|
if (!updates || updates.length === 0) {
|
|
@@ -2294,123 +2293,52 @@ class ClaudeAgentSession {
|
|
|
2294
2293
|
this.pendingPermissions.delete(id);
|
|
2295
2294
|
}
|
|
2296
2295
|
}
|
|
2297
|
-
loadPersistedHistory(sessionId
|
|
2296
|
+
loadPersistedHistory(sessionId) {
|
|
2298
2297
|
try {
|
|
2299
2298
|
const historyPath = this.resolveHistoryPath(sessionId);
|
|
2300
2299
|
if (!historyPath || !fs.existsSync(historyPath)) {
|
|
2301
2300
|
return;
|
|
2302
2301
|
}
|
|
2303
|
-
|
|
2304
|
-
this.historyOffsetSessionId = sessionId;
|
|
2305
|
-
this.historyReadOffsetBytes = 0;
|
|
2306
|
-
this.historyLineFragment = "";
|
|
2307
|
-
}
|
|
2308
|
-
const content = fs.readFileSync(historyPath);
|
|
2309
|
-
if (content.byteLength < this.historyReadOffsetBytes) {
|
|
2310
|
-
this.historyReadOffsetBytes = 0;
|
|
2311
|
-
this.historyLineFragment = "";
|
|
2312
|
-
}
|
|
2313
|
-
if (content.byteLength === this.historyReadOffsetBytes) {
|
|
2314
|
-
return;
|
|
2315
|
-
}
|
|
2316
|
-
const unreadChunk = content.subarray(this.historyReadOffsetBytes).toString("utf8");
|
|
2317
|
-
this.historyReadOffsetBytes = content.byteLength;
|
|
2318
|
-
this.ingestPersistedHistoryChunk(unreadChunk, {
|
|
2319
|
-
dispatchLive: options?.dispatchLive ?? false,
|
|
2320
|
-
});
|
|
2302
|
+
this.ingestPersistedHistory(fs.readFileSync(historyPath, "utf8"));
|
|
2321
2303
|
}
|
|
2322
2304
|
catch (error) {
|
|
2323
2305
|
// ignore history load failures
|
|
2324
2306
|
}
|
|
2325
2307
|
}
|
|
2326
|
-
|
|
2327
|
-
if (!
|
|
2308
|
+
ingestPersistedHistory(content) {
|
|
2309
|
+
if (!content) {
|
|
2328
2310
|
return;
|
|
2329
2311
|
}
|
|
2330
|
-
const combined = `${this.historyLineFragment}${chunk}`;
|
|
2331
|
-
this.historyLineFragment = "";
|
|
2332
|
-
const lines = combined.split(/\r?\n/);
|
|
2333
|
-
const trailing = lines.pop() ?? "";
|
|
2334
2312
|
const timeline = [];
|
|
2335
|
-
for (const line of
|
|
2336
|
-
this.ingestPersistedHistoryLine(line,
|
|
2337
|
-
dispatchLive: options.dispatchLive,
|
|
2338
|
-
timeline,
|
|
2339
|
-
});
|
|
2313
|
+
for (const line of content.split(/\r?\n/)) {
|
|
2314
|
+
this.ingestPersistedHistoryLine(line, timeline);
|
|
2340
2315
|
}
|
|
2341
|
-
if (
|
|
2342
|
-
const handled = this.ingestPersistedHistoryLine(trailing, {
|
|
2343
|
-
dispatchLive: options.dispatchLive,
|
|
2344
|
-
timeline,
|
|
2345
|
-
});
|
|
2346
|
-
if (!handled) {
|
|
2347
|
-
this.historyLineFragment = trailing;
|
|
2348
|
-
}
|
|
2349
|
-
}
|
|
2350
|
-
if (!options.dispatchLive && timeline.length > 0) {
|
|
2316
|
+
if (timeline.length > 0) {
|
|
2351
2317
|
this.persistedHistory = [...this.persistedHistory, ...timeline];
|
|
2352
2318
|
this.historyPending = true;
|
|
2353
2319
|
}
|
|
2354
2320
|
}
|
|
2355
|
-
ingestPersistedHistoryLine(line,
|
|
2321
|
+
ingestPersistedHistoryLine(line, timeline) {
|
|
2356
2322
|
const trimmed = line.trim();
|
|
2357
2323
|
if (!trimmed) {
|
|
2358
|
-
return
|
|
2324
|
+
return;
|
|
2359
2325
|
}
|
|
2360
2326
|
let entry;
|
|
2361
2327
|
try {
|
|
2362
2328
|
entry = JSON.parse(trimmed);
|
|
2363
2329
|
}
|
|
2364
2330
|
catch {
|
|
2365
|
-
return
|
|
2331
|
+
return;
|
|
2366
2332
|
}
|
|
2367
2333
|
if (entry.isSidechain) {
|
|
2368
|
-
return
|
|
2334
|
+
return;
|
|
2369
2335
|
}
|
|
2370
2336
|
if (entry.type === "user" && typeof entry.uuid === "string") {
|
|
2371
2337
|
this.rememberUserMessageId(entry.uuid);
|
|
2372
2338
|
}
|
|
2373
|
-
if (options.dispatchLive) {
|
|
2374
|
-
this.dispatchPersistedHistoryEntry(entry);
|
|
2375
|
-
return true;
|
|
2376
|
-
}
|
|
2377
2339
|
const items = this.convertHistoryEntry(entry);
|
|
2378
2340
|
if (items.length > 0) {
|
|
2379
|
-
|
|
2380
|
-
}
|
|
2381
|
-
return true;
|
|
2382
|
-
}
|
|
2383
|
-
dispatchPersistedHistoryEntry(entry) {
|
|
2384
|
-
const liveMessage = this.normalizePersistedHistoryEntryToLiveMessage(entry);
|
|
2385
|
-
if (liveMessage) {
|
|
2386
|
-
this.routeSdkMessageFromPump(liveMessage);
|
|
2387
|
-
return;
|
|
2388
|
-
}
|
|
2389
|
-
const items = this.convertHistoryEntry(entry);
|
|
2390
|
-
for (const item of items) {
|
|
2391
|
-
this.pushEvent({
|
|
2392
|
-
type: "timeline",
|
|
2393
|
-
item,
|
|
2394
|
-
provider: "claude",
|
|
2395
|
-
});
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
normalizePersistedHistoryEntryToLiveMessage(entry) {
|
|
2399
|
-
const taskNotificationMessage = coerceTaskNotificationHistoryRecordToSystemMessage(entry);
|
|
2400
|
-
if (taskNotificationMessage) {
|
|
2401
|
-
return taskNotificationMessage;
|
|
2402
|
-
}
|
|
2403
|
-
const type = readTrimmedString(entry.type);
|
|
2404
|
-
switch (type) {
|
|
2405
|
-
case "assistant":
|
|
2406
|
-
case "result":
|
|
2407
|
-
case "stream_event":
|
|
2408
|
-
case "system":
|
|
2409
|
-
case "tool_progress":
|
|
2410
|
-
case "user":
|
|
2411
|
-
return entry;
|
|
2412
|
-
default:
|
|
2413
|
-
return null;
|
|
2341
|
+
timeline.push(...items);
|
|
2414
2342
|
}
|
|
2415
2343
|
}
|
|
2416
2344
|
resolveHistoryPath(sessionId) {
|
|
@@ -3018,49 +2946,50 @@ export function convertClaudeHistoryEntry(entry, mapBlocks) {
|
|
|
3018
2946
|
}
|
|
3019
2947
|
return timeline;
|
|
3020
2948
|
}
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
2949
|
+
function createAsyncMessageInput() {
|
|
2950
|
+
const queue = [];
|
|
2951
|
+
const resolvers = [];
|
|
2952
|
+
let closed = false;
|
|
2953
|
+
return {
|
|
2954
|
+
push(item) {
|
|
2955
|
+
if (closed) {
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
const resolve = resolvers.shift();
|
|
2959
|
+
if (resolve) {
|
|
2960
|
+
resolve({ value: item, done: false });
|
|
2961
|
+
return;
|
|
2962
|
+
}
|
|
2963
|
+
queue.push(item);
|
|
2964
|
+
},
|
|
2965
|
+
end() {
|
|
2966
|
+
closed = true;
|
|
2967
|
+
while (resolvers.length > 0) {
|
|
2968
|
+
const resolve = resolvers.shift();
|
|
2969
|
+
resolve?.({ value: undefined, done: true });
|
|
2970
|
+
}
|
|
2971
|
+
},
|
|
2972
|
+
iterable: {
|
|
2973
|
+
[Symbol.asyncIterator]() {
|
|
2974
|
+
return {
|
|
2975
|
+
next: () => {
|
|
2976
|
+
if (queue.length > 0) {
|
|
2977
|
+
const value = queue.shift();
|
|
2978
|
+
if (value !== undefined) {
|
|
2979
|
+
return Promise.resolve({ value, done: false });
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
if (closed) {
|
|
2983
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
2984
|
+
}
|
|
2985
|
+
return new Promise((resolve) => {
|
|
2986
|
+
resolvers.push(resolve);
|
|
2987
|
+
});
|
|
2988
|
+
},
|
|
2989
|
+
};
|
|
3061
2990
|
},
|
|
3062
|
-
}
|
|
3063
|
-
}
|
|
2991
|
+
},
|
|
2992
|
+
};
|
|
3064
2993
|
}
|
|
3065
2994
|
async function pathExists(target) {
|
|
3066
2995
|
try {
|