@getpaseo/server 0.1.32 → 0.1.34
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 -451
- package/dist/server/server/agent/providers/claude-agent.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 +106 -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/persisted-config.d.ts +10 -10
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +20 -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/server/terminal/terminal.d.ts +4 -0
- package/dist/server/terminal/terminal.d.ts.map +1 -1
- package/dist/server/terminal/terminal.js +7 -30
- package/dist/server/terminal/terminal.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 -451
- package/dist/src/server/agent/providers/claude-agent.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 +106 -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 +20 -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/dist/src/terminal/terminal.js +7 -30
- package/dist/src/terminal/terminal.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,20 +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();
|
|
1249
|
+
this.query?.close?.();
|
|
1296
1250
|
await this.awaitWithTimeout(this.query?.interrupt?.(), "close query interrupt");
|
|
1297
1251
|
await this.awaitWithTimeout(this.query?.return?.(), "close query return");
|
|
1298
1252
|
this.query = null;
|
|
@@ -1343,37 +1297,6 @@ class ClaudeAgentSession {
|
|
|
1343
1297
|
? { commandName, args: rawArgs, rawInput: trimmed }
|
|
1344
1298
|
: { commandName, rawInput: trimmed };
|
|
1345
1299
|
}
|
|
1346
|
-
async *streamRewindCommand(invocation) {
|
|
1347
|
-
yield { type: "turn_started", provider: "claude" };
|
|
1348
|
-
try {
|
|
1349
|
-
const rewindAttempt = await this.attemptRewind(invocation.args);
|
|
1350
|
-
if (!rewindAttempt.messageId || !rewindAttempt.result) {
|
|
1351
|
-
yield {
|
|
1352
|
-
type: "turn_failed",
|
|
1353
|
-
provider: "claude",
|
|
1354
|
-
error: rewindAttempt.error ??
|
|
1355
|
-
"No prior user message available to rewind. Use /rewind <user_message_uuid>.",
|
|
1356
|
-
};
|
|
1357
|
-
return;
|
|
1358
|
-
}
|
|
1359
|
-
yield {
|
|
1360
|
-
type: "timeline",
|
|
1361
|
-
provider: "claude",
|
|
1362
|
-
item: {
|
|
1363
|
-
type: "assistant_message",
|
|
1364
|
-
text: this.buildRewindSuccessMessage(rewindAttempt.messageId, rewindAttempt.result),
|
|
1365
|
-
},
|
|
1366
|
-
};
|
|
1367
|
-
yield { type: "turn_completed", provider: "claude" };
|
|
1368
|
-
}
|
|
1369
|
-
catch (error) {
|
|
1370
|
-
yield {
|
|
1371
|
-
type: "turn_failed",
|
|
1372
|
-
provider: "claude",
|
|
1373
|
-
error: error instanceof Error ? error.message : "Failed to rewind tracked files",
|
|
1374
|
-
};
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
1300
|
buildRewindSuccessMessage(targetUserMessageId, rewindResult) {
|
|
1378
1301
|
const fileCount = Array.isArray(rewindResult.filesChanged)
|
|
1379
1302
|
? rewindResult.filesChanged.length
|
|
@@ -1524,22 +1447,28 @@ class ClaudeAgentSession {
|
|
|
1524
1447
|
return this.query;
|
|
1525
1448
|
}
|
|
1526
1449
|
if (this.queryRestartNeeded && this.query) {
|
|
1527
|
-
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?.();
|
|
1528
1460
|
try {
|
|
1529
|
-
await
|
|
1461
|
+
await oldQuery.return?.();
|
|
1530
1462
|
}
|
|
1531
1463
|
catch {
|
|
1532
1464
|
/* ignore */
|
|
1533
1465
|
}
|
|
1534
|
-
this.query = null;
|
|
1535
|
-
this.input = null;
|
|
1536
|
-
this.queryRestartNeeded = false;
|
|
1537
1466
|
}
|
|
1538
|
-
const input =
|
|
1467
|
+
const input = createAsyncMessageInput();
|
|
1539
1468
|
const options = this.buildOptions();
|
|
1540
1469
|
this.logger.debug({ options: summarizeClaudeOptionsForLog(options) }, "claude query");
|
|
1541
1470
|
this.input = input;
|
|
1542
|
-
this.query = this.queryFactory({ prompt: input, options });
|
|
1471
|
+
this.query = this.queryFactory({ prompt: input.iterable, options });
|
|
1543
1472
|
// Do not kick off background control-plane queries here. Methods like
|
|
1544
1473
|
// supportedCommands()/setPermissionMode() may execute immediately after
|
|
1545
1474
|
// ensureQuery() (for listCommands()/setMode()), and sharing the same query
|
|
@@ -1567,16 +1496,14 @@ class ClaudeAgentSession {
|
|
|
1567
1496
|
}
|
|
1568
1497
|
}
|
|
1569
1498
|
buildOptions() {
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
let
|
|
1575
|
-
if (thinkingOptionId
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
else if (thinkingOptionId === "off") {
|
|
1579
|
-
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;
|
|
1580
1507
|
}
|
|
1581
1508
|
const appendedSystemPrompt = [
|
|
1582
1509
|
getOrchestratorModeInstructions(),
|
|
@@ -1607,13 +1534,15 @@ class ClaudeAgentSession {
|
|
|
1607
1534
|
// Increase MCP timeouts for long-running tool calls (10 minutes)
|
|
1608
1535
|
MCP_TIMEOUT: "600000",
|
|
1609
1536
|
MCP_TOOL_TIMEOUT: "600000",
|
|
1537
|
+
...(this.launchEnv ?? {}),
|
|
1610
1538
|
},
|
|
1611
1539
|
// Required for provider-level /rewind support.
|
|
1612
1540
|
enableFileCheckpointing: true,
|
|
1613
1541
|
// If we have a session ID from a previous query (e.g., after interrupt),
|
|
1614
1542
|
// resume that session to continue the conversation history.
|
|
1615
1543
|
...(this.claudeSessionId ? { resume: this.claudeSessionId } : {}),
|
|
1616
|
-
...(
|
|
1544
|
+
...(thinking ? { thinking } : {}),
|
|
1545
|
+
...(effort ? { effort } : {}),
|
|
1617
1546
|
...this.config.extra?.claude,
|
|
1618
1547
|
};
|
|
1619
1548
|
if (this.config.mcpServers) {
|
|
@@ -1629,7 +1558,7 @@ class ClaudeAgentSession {
|
|
|
1629
1558
|
return this.applyRuntimeSettings(base);
|
|
1630
1559
|
}
|
|
1631
1560
|
applyRuntimeSettings(options) {
|
|
1632
|
-
return applyRuntimeSettingsToClaudeOptions(options, this.runtimeSettings);
|
|
1561
|
+
return applyRuntimeSettingsToClaudeOptions(options, this.runtimeSettings, this.launchEnv);
|
|
1633
1562
|
}
|
|
1634
1563
|
normalizeMcpServers(servers) {
|
|
1635
1564
|
const result = {};
|
|
@@ -1681,7 +1610,7 @@ class ClaudeAgentSession {
|
|
|
1681
1610
|
this.turnState = next;
|
|
1682
1611
|
}
|
|
1683
1612
|
syncTurnState(reason) {
|
|
1684
|
-
if (this.
|
|
1613
|
+
if (this.activeForegroundTurnId) {
|
|
1685
1614
|
this.transitionTurnState("foreground", reason);
|
|
1686
1615
|
return;
|
|
1687
1616
|
}
|
|
@@ -1691,6 +1620,10 @@ class ClaudeAgentSession {
|
|
|
1691
1620
|
}
|
|
1692
1621
|
this.transitionTurnState("idle", reason);
|
|
1693
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
|
+
}
|
|
1694
1627
|
buildTurnFailedEvent(errorMessage) {
|
|
1695
1628
|
const normalized = errorMessage.trim() || "Claude run failed";
|
|
1696
1629
|
const exitCodeMatch = normalized.match(/\bcode\s+(\d+)\b/i);
|
|
@@ -1718,6 +1651,22 @@ class ClaudeAgentSession {
|
|
|
1718
1651
|
getRecentStderrDiagnostic() {
|
|
1719
1652
|
return this.recentStderr.trim() || undefined;
|
|
1720
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
|
+
}
|
|
1721
1670
|
createTurnId(owner) {
|
|
1722
1671
|
return `${owner}-turn-${this.nextTurnOrdinal++}`;
|
|
1723
1672
|
}
|
|
@@ -1726,6 +1675,46 @@ class ClaudeAgentSession {
|
|
|
1726
1675
|
event.type === "turn_failed" ||
|
|
1727
1676
|
event.type === "turn_canceled");
|
|
1728
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
|
+
}
|
|
1729
1718
|
shouldRecoverInterruptedQueryAbort(error, consecutiveRecoveries) {
|
|
1730
1719
|
if (consecutiveRecoveries >= 3) {
|
|
1731
1720
|
return false;
|
|
@@ -1741,37 +1730,29 @@ class ClaudeAgentSession {
|
|
|
1741
1730
|
if (event.type === "turn_failed" || event.type === "turn_canceled") {
|
|
1742
1731
|
this.flushPendingToolCalls();
|
|
1743
1732
|
}
|
|
1744
|
-
this.
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
if (!foregroundTurn) {
|
|
1749
|
-
this.dispatchLiveEvents(events);
|
|
1750
|
-
return;
|
|
1751
|
-
}
|
|
1752
|
-
let terminalSeen = false;
|
|
1753
|
-
for (const event of events) {
|
|
1754
|
-
foregroundTurn.queue.push(event);
|
|
1755
|
-
terminalSeen || (terminalSeen = this.isTerminalTurnEvent(event));
|
|
1756
|
-
}
|
|
1757
|
-
if (!terminalSeen) {
|
|
1758
|
-
return;
|
|
1759
|
-
}
|
|
1760
|
-
foregroundTurn.queue.end();
|
|
1761
|
-
if (this.activeForegroundTurn === foregroundTurn) {
|
|
1762
|
-
this.activeForegroundTurn = null;
|
|
1763
|
-
}
|
|
1733
|
+
this.notifySubscribers(event);
|
|
1734
|
+
this.activeForegroundTurnId = null;
|
|
1735
|
+
this.lastForegroundPromptText = null;
|
|
1736
|
+
this.cancelCurrentTurn = null;
|
|
1764
1737
|
this.syncTurnState("foreground turn terminal");
|
|
1765
1738
|
}
|
|
1766
|
-
|
|
1739
|
+
dispatchEvents(events) {
|
|
1767
1740
|
let terminalSeen = false;
|
|
1768
1741
|
for (const event of events) {
|
|
1769
|
-
this.
|
|
1742
|
+
this.notifySubscribers(event);
|
|
1770
1743
|
terminalSeen || (terminalSeen = this.isTerminalTurnEvent(event));
|
|
1771
1744
|
}
|
|
1772
|
-
if (terminalSeen
|
|
1773
|
-
this.
|
|
1774
|
-
|
|
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
|
+
}
|
|
1775
1756
|
}
|
|
1776
1757
|
}
|
|
1777
1758
|
startAutonomousTurn() {
|
|
@@ -1781,40 +1762,26 @@ class ClaudeAgentSession {
|
|
|
1781
1762
|
this.autonomousTurn = {
|
|
1782
1763
|
id: this.createTurnId("autonomous"),
|
|
1783
1764
|
};
|
|
1784
|
-
this.
|
|
1765
|
+
this.notifySubscribers({ type: "turn_started", provider: "claude" });
|
|
1785
1766
|
this.syncTurnState("autonomous turn started");
|
|
1786
1767
|
}
|
|
1787
1768
|
completeAutonomousTurn() {
|
|
1788
1769
|
if (!this.autonomousTurn) {
|
|
1789
1770
|
return;
|
|
1790
1771
|
}
|
|
1772
|
+
this.notifySubscribers({ type: "turn_completed", provider: "claude" });
|
|
1791
1773
|
this.autonomousTurn = null;
|
|
1792
|
-
this.liveEventQueue.push({ type: "turn_completed", provider: "claude" });
|
|
1793
1774
|
this.syncTurnState("autonomous turn completed");
|
|
1794
1775
|
}
|
|
1795
|
-
cancelAutonomousTurn(reason) {
|
|
1796
|
-
if (!this.autonomousTurn) {
|
|
1797
|
-
return;
|
|
1798
|
-
}
|
|
1799
|
-
this.flushPendingToolCalls();
|
|
1800
|
-
this.autonomousTurn = null;
|
|
1801
|
-
this.liveEventQueue.push({
|
|
1802
|
-
type: "turn_canceled",
|
|
1803
|
-
provider: "claude",
|
|
1804
|
-
reason,
|
|
1805
|
-
});
|
|
1806
|
-
this.syncTurnState("autonomous turn canceled");
|
|
1807
|
-
}
|
|
1808
1776
|
failActiveTurns(errorMessage) {
|
|
1809
1777
|
const failure = this.buildTurnFailedEvent(errorMessage);
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
this.
|
|
1778
|
+
this.flushPendingToolCalls();
|
|
1779
|
+
if (this.activeForegroundTurnId) {
|
|
1780
|
+
this.finishForegroundTurn(failure);
|
|
1813
1781
|
return;
|
|
1814
1782
|
}
|
|
1815
1783
|
if (this.autonomousTurn) {
|
|
1816
|
-
this.
|
|
1817
|
-
this.dispatchLiveEvents([failure]);
|
|
1784
|
+
this.dispatchEvents([failure]);
|
|
1818
1785
|
}
|
|
1819
1786
|
}
|
|
1820
1787
|
startQueryPump() {
|
|
@@ -1846,6 +1813,12 @@ class ClaudeAgentSession {
|
|
|
1846
1813
|
while (!this.closed && this.query === activeQuery) {
|
|
1847
1814
|
try {
|
|
1848
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");
|
|
1849
1822
|
consecutiveInterruptAbortRecoveries = 0;
|
|
1850
1823
|
if (await this.handleMissingResumedConversation(message, activeQuery)) {
|
|
1851
1824
|
return;
|
|
@@ -1866,6 +1839,7 @@ class ClaudeAgentSession {
|
|
|
1866
1839
|
continue;
|
|
1867
1840
|
}
|
|
1868
1841
|
if (!this.closed && this.query === activeQuery) {
|
|
1842
|
+
await this.awaitRecentStderrAfterProcessExit(error);
|
|
1869
1843
|
this.failActiveTurns(error instanceof Error ? error.message : "Claude stream failed");
|
|
1870
1844
|
}
|
|
1871
1845
|
return;
|
|
@@ -1880,24 +1854,40 @@ class ClaudeAgentSession {
|
|
|
1880
1854
|
}
|
|
1881
1855
|
}
|
|
1882
1856
|
routeSdkMessageFromPump(message) {
|
|
1883
|
-
|
|
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);
|
|
1884
1874
|
const assistantishMessage = message.type === "assistant" ||
|
|
1885
1875
|
message.type === "stream_event" ||
|
|
1886
|
-
message.type === "tool_progress"
|
|
1887
|
-
|
|
1876
|
+
message.type === "tool_progress" ||
|
|
1877
|
+
(message.type === "system" && message.subtype === "task_notification");
|
|
1878
|
+
if (!isForeground && assistantishMessage) {
|
|
1888
1879
|
this.startAutonomousTurn();
|
|
1889
1880
|
}
|
|
1890
|
-
if (!
|
|
1881
|
+
if (!isForeground && !this.autonomousTurn && message.type === "result") {
|
|
1891
1882
|
return;
|
|
1892
1883
|
}
|
|
1893
|
-
const turnId = this.
|
|
1884
|
+
const turnId = this.activeForegroundTurnId ?? this.autonomousTurn?.id ?? null;
|
|
1894
1885
|
const identifiers = readEventIdentifiers(message);
|
|
1895
1886
|
this.logger.trace({
|
|
1896
1887
|
claudeSessionId: this.claudeSessionId,
|
|
1897
1888
|
messageType: message.type,
|
|
1898
|
-
routedTo: routeToForeground ? "foreground_queue" : "live_queue",
|
|
1899
1889
|
turnId,
|
|
1900
|
-
}, "Claude query pump
|
|
1890
|
+
}, "Claude query pump: SDK message");
|
|
1901
1891
|
const messageEvents = this.translateMessageToEvents(message, {
|
|
1902
1892
|
suppressAssistantText: true,
|
|
1903
1893
|
suppressReasoning: true,
|
|
@@ -1913,30 +1903,37 @@ class ClaudeAgentSession {
|
|
|
1913
1903
|
item,
|
|
1914
1904
|
provider: "claude",
|
|
1915
1905
|
}));
|
|
1916
|
-
|
|
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];
|
|
1917
1919
|
if (events.length === 0) {
|
|
1918
1920
|
return;
|
|
1919
1921
|
}
|
|
1920
1922
|
if (this.pendingInterruptAbort &&
|
|
1921
1923
|
message.type === "result" &&
|
|
1922
1924
|
events.some((event) => event.type === "turn_completed" || event.type === "turn_failed") &&
|
|
1923
|
-
(!this.
|
|
1925
|
+
(!this.activeForegroundTurnId || !this.foregroundHasVisibleActivity)) {
|
|
1924
1926
|
this.pendingInterruptAbort = false;
|
|
1925
1927
|
this.logger.debug("Suppressing stale Claude interrupt terminal result");
|
|
1926
1928
|
return;
|
|
1927
1929
|
}
|
|
1928
|
-
if (this.
|
|
1930
|
+
if (this.activeForegroundTurnId &&
|
|
1929
1931
|
events.some((event) => event.type === "timeline" ||
|
|
1930
1932
|
event.type === "permission_requested" ||
|
|
1931
1933
|
event.type === "permission_resolved")) {
|
|
1932
|
-
this.
|
|
1933
|
-
this.pendingInterruptAbort = false;
|
|
1934
|
+
this.foregroundHasVisibleActivity = true;
|
|
1934
1935
|
}
|
|
1935
|
-
|
|
1936
|
-
this.dispatchForegroundEvents(events);
|
|
1937
|
-
return;
|
|
1938
|
-
}
|
|
1939
|
-
this.dispatchLiveEvents(events);
|
|
1936
|
+
this.dispatchEvents(events);
|
|
1940
1937
|
}
|
|
1941
1938
|
async handleMissingResumedConversation(message, query) {
|
|
1942
1939
|
const staleResumeError = this.readMissingResumedConversationError(message);
|
|
@@ -1958,13 +1955,10 @@ class ClaudeAgentSession {
|
|
|
1958
1955
|
this.persistence = null;
|
|
1959
1956
|
this.persistedHistory = [];
|
|
1960
1957
|
this.historyPending = false;
|
|
1961
|
-
this.historyOffsetSessionId = null;
|
|
1962
|
-
this.historyReadOffsetBytes = 0;
|
|
1963
|
-
this.historyLineFragment = "";
|
|
1964
1958
|
this.cachedRuntimeInfo = null;
|
|
1965
1959
|
this.queryRestartNeeded = false;
|
|
1966
1960
|
this.autonomousTurn = null;
|
|
1967
|
-
this.
|
|
1961
|
+
this.activeForegroundTurnId = null;
|
|
1968
1962
|
this.syncTurnState("missing resumed conversation");
|
|
1969
1963
|
return true;
|
|
1970
1964
|
}
|
|
@@ -2197,15 +2191,15 @@ class ClaudeAgentSession {
|
|
|
2197
2191
|
this.currentMode = message.permissionMode;
|
|
2198
2192
|
this.persistence = null;
|
|
2199
2193
|
if (message.model) {
|
|
2200
|
-
const
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
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;
|
|
2209
2203
|
this.cachedRuntimeInfo = null;
|
|
2210
2204
|
}
|
|
2211
2205
|
return threadStartedSessionId;
|
|
@@ -2271,12 +2265,19 @@ class ClaudeAgentSession {
|
|
|
2271
2265
|
this.enqueueTimeline(item);
|
|
2272
2266
|
}
|
|
2273
2267
|
pushEvent(event) {
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
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
|
+
}
|
|
2278
2280
|
}
|
|
2279
|
-
this.liveEventQueue.push(event);
|
|
2280
2281
|
}
|
|
2281
2282
|
normalizePermissionUpdates(updates) {
|
|
2282
2283
|
if (!updates || updates.length === 0) {
|
|
@@ -2292,123 +2293,52 @@ class ClaudeAgentSession {
|
|
|
2292
2293
|
this.pendingPermissions.delete(id);
|
|
2293
2294
|
}
|
|
2294
2295
|
}
|
|
2295
|
-
loadPersistedHistory(sessionId
|
|
2296
|
+
loadPersistedHistory(sessionId) {
|
|
2296
2297
|
try {
|
|
2297
2298
|
const historyPath = this.resolveHistoryPath(sessionId);
|
|
2298
2299
|
if (!historyPath || !fs.existsSync(historyPath)) {
|
|
2299
2300
|
return;
|
|
2300
2301
|
}
|
|
2301
|
-
|
|
2302
|
-
this.historyOffsetSessionId = sessionId;
|
|
2303
|
-
this.historyReadOffsetBytes = 0;
|
|
2304
|
-
this.historyLineFragment = "";
|
|
2305
|
-
}
|
|
2306
|
-
const content = fs.readFileSync(historyPath);
|
|
2307
|
-
if (content.byteLength < this.historyReadOffsetBytes) {
|
|
2308
|
-
this.historyReadOffsetBytes = 0;
|
|
2309
|
-
this.historyLineFragment = "";
|
|
2310
|
-
}
|
|
2311
|
-
if (content.byteLength === this.historyReadOffsetBytes) {
|
|
2312
|
-
return;
|
|
2313
|
-
}
|
|
2314
|
-
const unreadChunk = content.subarray(this.historyReadOffsetBytes).toString("utf8");
|
|
2315
|
-
this.historyReadOffsetBytes = content.byteLength;
|
|
2316
|
-
this.ingestPersistedHistoryChunk(unreadChunk, {
|
|
2317
|
-
dispatchLive: options?.dispatchLive ?? false,
|
|
2318
|
-
});
|
|
2302
|
+
this.ingestPersistedHistory(fs.readFileSync(historyPath, "utf8"));
|
|
2319
2303
|
}
|
|
2320
2304
|
catch (error) {
|
|
2321
2305
|
// ignore history load failures
|
|
2322
2306
|
}
|
|
2323
2307
|
}
|
|
2324
|
-
|
|
2325
|
-
if (!
|
|
2308
|
+
ingestPersistedHistory(content) {
|
|
2309
|
+
if (!content) {
|
|
2326
2310
|
return;
|
|
2327
2311
|
}
|
|
2328
|
-
const combined = `${this.historyLineFragment}${chunk}`;
|
|
2329
|
-
this.historyLineFragment = "";
|
|
2330
|
-
const lines = combined.split(/\r?\n/);
|
|
2331
|
-
const trailing = lines.pop() ?? "";
|
|
2332
2312
|
const timeline = [];
|
|
2333
|
-
for (const line of
|
|
2334
|
-
this.ingestPersistedHistoryLine(line,
|
|
2335
|
-
dispatchLive: options.dispatchLive,
|
|
2336
|
-
timeline,
|
|
2337
|
-
});
|
|
2313
|
+
for (const line of content.split(/\r?\n/)) {
|
|
2314
|
+
this.ingestPersistedHistoryLine(line, timeline);
|
|
2338
2315
|
}
|
|
2339
|
-
if (
|
|
2340
|
-
const handled = this.ingestPersistedHistoryLine(trailing, {
|
|
2341
|
-
dispatchLive: options.dispatchLive,
|
|
2342
|
-
timeline,
|
|
2343
|
-
});
|
|
2344
|
-
if (!handled) {
|
|
2345
|
-
this.historyLineFragment = trailing;
|
|
2346
|
-
}
|
|
2347
|
-
}
|
|
2348
|
-
if (!options.dispatchLive && timeline.length > 0) {
|
|
2316
|
+
if (timeline.length > 0) {
|
|
2349
2317
|
this.persistedHistory = [...this.persistedHistory, ...timeline];
|
|
2350
2318
|
this.historyPending = true;
|
|
2351
2319
|
}
|
|
2352
2320
|
}
|
|
2353
|
-
ingestPersistedHistoryLine(line,
|
|
2321
|
+
ingestPersistedHistoryLine(line, timeline) {
|
|
2354
2322
|
const trimmed = line.trim();
|
|
2355
2323
|
if (!trimmed) {
|
|
2356
|
-
return
|
|
2324
|
+
return;
|
|
2357
2325
|
}
|
|
2358
2326
|
let entry;
|
|
2359
2327
|
try {
|
|
2360
2328
|
entry = JSON.parse(trimmed);
|
|
2361
2329
|
}
|
|
2362
2330
|
catch {
|
|
2363
|
-
return
|
|
2331
|
+
return;
|
|
2364
2332
|
}
|
|
2365
2333
|
if (entry.isSidechain) {
|
|
2366
|
-
return
|
|
2334
|
+
return;
|
|
2367
2335
|
}
|
|
2368
2336
|
if (entry.type === "user" && typeof entry.uuid === "string") {
|
|
2369
2337
|
this.rememberUserMessageId(entry.uuid);
|
|
2370
2338
|
}
|
|
2371
|
-
if (options.dispatchLive) {
|
|
2372
|
-
this.dispatchPersistedHistoryEntry(entry);
|
|
2373
|
-
return true;
|
|
2374
|
-
}
|
|
2375
2339
|
const items = this.convertHistoryEntry(entry);
|
|
2376
2340
|
if (items.length > 0) {
|
|
2377
|
-
|
|
2378
|
-
}
|
|
2379
|
-
return true;
|
|
2380
|
-
}
|
|
2381
|
-
dispatchPersistedHistoryEntry(entry) {
|
|
2382
|
-
const liveMessage = this.normalizePersistedHistoryEntryToLiveMessage(entry);
|
|
2383
|
-
if (liveMessage) {
|
|
2384
|
-
this.routeSdkMessageFromPump(liveMessage);
|
|
2385
|
-
return;
|
|
2386
|
-
}
|
|
2387
|
-
const items = this.convertHistoryEntry(entry);
|
|
2388
|
-
for (const item of items) {
|
|
2389
|
-
this.pushEvent({
|
|
2390
|
-
type: "timeline",
|
|
2391
|
-
item,
|
|
2392
|
-
provider: "claude",
|
|
2393
|
-
});
|
|
2394
|
-
}
|
|
2395
|
-
}
|
|
2396
|
-
normalizePersistedHistoryEntryToLiveMessage(entry) {
|
|
2397
|
-
const taskNotificationMessage = coerceTaskNotificationHistoryRecordToSystemMessage(entry);
|
|
2398
|
-
if (taskNotificationMessage) {
|
|
2399
|
-
return taskNotificationMessage;
|
|
2400
|
-
}
|
|
2401
|
-
const type = readTrimmedString(entry.type);
|
|
2402
|
-
switch (type) {
|
|
2403
|
-
case "assistant":
|
|
2404
|
-
case "result":
|
|
2405
|
-
case "stream_event":
|
|
2406
|
-
case "system":
|
|
2407
|
-
case "tool_progress":
|
|
2408
|
-
case "user":
|
|
2409
|
-
return entry;
|
|
2410
|
-
default:
|
|
2411
|
-
return null;
|
|
2341
|
+
timeline.push(...items);
|
|
2412
2342
|
}
|
|
2413
2343
|
}
|
|
2414
2344
|
resolveHistoryPath(sessionId) {
|
|
@@ -3016,49 +2946,50 @@ export function convertClaudeHistoryEntry(entry, mapBlocks) {
|
|
|
3016
2946
|
}
|
|
3017
2947
|
return timeline;
|
|
3018
2948
|
}
|
|
3019
|
-
|
|
3020
|
-
|
|
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
|
-
|
|
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
|
+
};
|
|
3059
2990
|
},
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
2991
|
+
},
|
|
2992
|
+
};
|
|
3062
2993
|
}
|
|
3063
2994
|
async function pathExists(target) {
|
|
3064
2995
|
try {
|