orkestrator 2.30.0-beta → 2.32.0-beta

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.
@@ -1866,7 +1866,14 @@ var QUESTION_KEYS = new Set([
1866
1866
  "allowFreeText",
1867
1867
  "options"
1868
1868
  ]);
1869
- var OPTION_KEYS = new Set(["id", "label", "providerValue", "description", "amendment"]);
1869
+ var OPTION_KEYS = new Set([
1870
+ "id",
1871
+ "label",
1872
+ "providerValue",
1873
+ "description",
1874
+ "preview",
1875
+ "amendment"
1876
+ ]);
1870
1877
  var AMENDMENT_KEYS = new Set(["kind", "detail"]);
1871
1878
  var ANSWER_KEYS = new Set(["version", "interactionId", "sessionId", "answers"]);
1872
1879
  var QUESTION_ANSWER_KEYS = new Set(["questionId", "optionIds", "freeText"]);
@@ -2522,6 +2529,10 @@ function claudeHome() {
2522
2529
  function claudeJsonPath() {
2523
2530
  return join(claudeHome(), ".claude.json");
2524
2531
  }
2532
+ function claudeUserSettingsPath() {
2533
+ const configDir = overrideHome === null ? process.env.CLAUDE_CONFIG_DIR?.trim() : undefined;
2534
+ return join(configDir || join(claudeHome(), ".claude"), "settings.json");
2535
+ }
2525
2536
  function claudePluginsDir() {
2526
2537
  return join(claudeHome(), ".claude", "plugins");
2527
2538
  }
@@ -2549,7 +2560,8 @@ function parseTurnInFlightMark(value) {
2549
2560
  return {
2550
2561
  bridgeInstanceId: record.bridgeInstanceId,
2551
2562
  pid: record.pid,
2552
- startedAt: record.startedAt
2563
+ startedAt: record.startedAt,
2564
+ ...typeof record.requestId === "string" && record.requestId.length > 0 && record.requestId.length <= 200 ? { requestId: record.requestId } : {}
2553
2565
  };
2554
2566
  }
2555
2567
  var MAX_SHELL_TASK_DETAILS = 256;
@@ -3764,7 +3776,106 @@ import { createHash as createHash5 } from "node:crypto";
3764
3776
 
3765
3777
  // ../../bridges/claude-bridge/src/services/plugin-config.ts
3766
3778
  import { readdir, access } from "node:fs/promises";
3767
- import { join as join3, resolve, relative, isAbsolute } from "node:path";
3779
+ import { join as join3 } from "node:path";
3780
+
3781
+ // ../protocol/src/node/claude-plugin-set.ts
3782
+ import path from "node:path";
3783
+ var CLAUDE_PLUGINS_MARKER = "/.claude/plugins/";
3784
+ function isRecord(value) {
3785
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3786
+ }
3787
+ function claudePluginConfigList(value) {
3788
+ if (!Array.isArray(value))
3789
+ return [];
3790
+ const plugins = [];
3791
+ for (const item of value) {
3792
+ if (isRecord(item) && typeof item.path === "string" && item.path) {
3793
+ plugins.push({ type: "local", path: item.path });
3794
+ }
3795
+ }
3796
+ return plugins;
3797
+ }
3798
+ function isPathWithin(parentDir, candidatePath) {
3799
+ const relativePath = path.relative(parentDir, candidatePath);
3800
+ return relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
3801
+ }
3802
+ function remapInstalledPlugins(pluginsDir, installedPlugins, onUnsafe) {
3803
+ const plugins = [];
3804
+ const pluginsRoot = path.resolve(pluginsDir);
3805
+ if (!isRecord(installedPlugins.plugins))
3806
+ return plugins;
3807
+ for (const [installedId, entries] of Object.entries(installedPlugins.plugins)) {
3808
+ if (!Array.isArray(entries))
3809
+ continue;
3810
+ for (const entry of entries) {
3811
+ if (!isRecord(entry) || typeof entry.installPath !== "string")
3812
+ continue;
3813
+ const installPath = entry.installPath;
3814
+ const markerIdx = installPath.indexOf(CLAUDE_PLUGINS_MARKER);
3815
+ let resolvedPath;
3816
+ if (markerIdx !== -1) {
3817
+ const relativePath = installPath.substring(markerIdx + CLAUDE_PLUGINS_MARKER.length);
3818
+ const remappedPath = path.resolve(pluginsDir, relativePath);
3819
+ if (!isPathWithin(pluginsRoot, remappedPath)) {
3820
+ onUnsafe?.(installPath);
3821
+ continue;
3822
+ }
3823
+ resolvedPath = remappedPath;
3824
+ } else {
3825
+ resolvedPath = installPath;
3826
+ }
3827
+ plugins.push({ type: "local", path: resolvedPath, installedId });
3828
+ }
3829
+ }
3830
+ return plugins;
3831
+ }
3832
+ function resolveClaudePluginPath(pluginPath, cwd, home) {
3833
+ if (pluginPath.startsWith("~"))
3834
+ return path.join(home, pluginPath.slice(1));
3835
+ if (pluginPath.startsWith("/"))
3836
+ return pluginPath;
3837
+ return path.join(cwd, pluginPath);
3838
+ }
3839
+ function mergeClaudePluginLayers(layers, options) {
3840
+ const ordered = [
3841
+ claudePluginConfigList(layers.global),
3842
+ layers.cliInstalled,
3843
+ ...options.includeProjectSources ? [claudePluginConfigList(layers.projectGlobal), claudePluginConfigList(layers.projectLocal)] : []
3844
+ ];
3845
+ const seen = new Set;
3846
+ const paths = [];
3847
+ for (const layer of ordered) {
3848
+ for (const plugin of layer) {
3849
+ const resolved = resolveClaudePluginPath(plugin.path, options.cwd, options.home);
3850
+ if (seen.has(resolved))
3851
+ continue;
3852
+ seen.add(resolved);
3853
+ paths.push(resolved);
3854
+ }
3855
+ }
3856
+ return paths;
3857
+ }
3858
+ var INVALID_PLUGIN_NAME = new RegExp("[@:\\s/\\\\\\u0000-\\u001f\\u007f\\u200b-\\u200f\\u2028-\\u202e\\u2060-\\u2064\\ufeff]");
3859
+ var CLAUDE_PLUGIN_SETTING_SOURCES = ["local", "project", "user"];
3860
+ function claudeInstalledPluginIdEnablement(id, settings, loadedSources) {
3861
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3862
+ for (const source of ["local", "project", "user"]) {
3863
+ if (!loadedSources.includes(source))
3864
+ continue;
3865
+ const document = settings[source];
3866
+ const enabledPlugins = isRecord(document) ? document.enabledPlugins : undefined;
3867
+ if (!isRecord(enabledPlugins) || !Object.hasOwn(enabledPlugins, id))
3868
+ continue;
3869
+ return { enabled: enabledPlugins[id] === true, decidedBy: { source, key: id } };
3870
+ }
3871
+ return { enabled: false };
3872
+ }
3873
+ function enabledInstalledPlugins(plugins, settings, loadedSources) {
3874
+ return plugins.filter((plugin) => plugin.installedId === undefined || claudeInstalledPluginIdEnablement(plugin.installedId, settings, loadedSources).enabled);
3875
+ }
3876
+ function claudePluginSettingSources(settingSources) {
3877
+ return CLAUDE_PLUGIN_SETTING_SOURCES.filter((source) => settingSources.includes(source));
3878
+ }
3768
3879
 
3769
3880
  // ../../bridges/claude-bridge/src/services/json-file-cache.ts
3770
3881
  import { createHash as createHash4 } from "node:crypto";
@@ -3866,10 +3977,6 @@ async function pathExists(path) {
3866
3977
  return false;
3867
3978
  }
3868
3979
  }
3869
- function isPathWithin(parentDir, candidatePath) {
3870
- const relativePath = relative(parentDir, candidatePath);
3871
- return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
3872
- }
3873
3980
  async function scanCliPluginsDirectory(pluginsDir) {
3874
3981
  if (!await pathExists(pluginsDir)) {
3875
3982
  return [];
@@ -3896,11 +4003,11 @@ async function scanCliPluginsDirectory(pluginsDir) {
3896
4003
  }
3897
4004
  }
3898
4005
  async function loadGlobalPlugins() {
3899
- const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => config?.plugins);
4006
+ const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => claudePluginConfigList(config?.plugins));
3900
4007
  return plugins ?? [];
3901
4008
  }
3902
4009
  async function loadProjectPlugins(cwd) {
3903
- const plugins = await readJsonSliceCached(join3(cwd, ".claude", "plugins.json"), "plugins", (config) => config?.plugins);
4010
+ const plugins = await readJsonSliceCached(join3(cwd, ".claude", "plugins.json"), "plugins", (config) => claudePluginConfigList(config?.plugins));
3904
4011
  return plugins ?? [];
3905
4012
  }
3906
4013
  async function loadCliInstalledPlugins() {
@@ -3910,37 +4017,30 @@ async function loadCliInstalledPlugins() {
3910
4017
  if (!plugins) {
3911
4018
  return scanCliPluginsDirectory(pluginsDir);
3912
4019
  }
3913
- return remapInstalledPlugins(pluginsDir, { plugins });
4020
+ return remapInstalledPlugins2(pluginsDir, { plugins });
3914
4021
  }
3915
- function remapInstalledPlugins(pluginsDir, installedPlugins) {
3916
- const plugins = [];
3917
- const CLAUDE_PLUGINS_MARKER = "/.claude/plugins/";
3918
- const pluginsRoot = resolve(pluginsDir);
3919
- for (const entries of Object.values(installedPlugins.plugins)) {
3920
- for (const entry of entries) {
3921
- const markerIdx = entry.installPath.indexOf(CLAUDE_PLUGINS_MARKER);
3922
- let resolvedPath;
3923
- if (markerIdx !== -1) {
3924
- const relativePath = entry.installPath.substring(markerIdx + CLAUDE_PLUGINS_MARKER.length);
3925
- const remappedPath = resolve(pluginsDir, relativePath);
3926
- if (!isPathWithin(pluginsRoot, remappedPath)) {
3927
- console.warn(`[plugin-config] Skipping plugin with unsafe install path: "${entry.installPath}"`);
3928
- continue;
3929
- }
3930
- resolvedPath = remappedPath;
3931
- } else {
3932
- resolvedPath = entry.installPath;
3933
- }
3934
- plugins.push({
3935
- type: "local",
3936
- path: resolvedPath
3937
- });
3938
- }
4022
+ function pluginSettingsPath(source, cwd) {
4023
+ switch (source) {
4024
+ case "user":
4025
+ return claudeUserSettingsPath();
4026
+ case "project":
4027
+ return join3(cwd, ".claude", "settings.json");
4028
+ case "local":
4029
+ return join3(cwd, ".claude", "settings.local.json");
3939
4030
  }
3940
- return plugins;
4031
+ }
4032
+ async function loadPluginSettings(cwd, sources) {
4033
+ const entries = await Promise.all(sources.map(async (source) => {
4034
+ const enabledPlugins = await readJsonSliceCached(pluginSettingsPath(source, cwd), "enabledPlugins", (config) => config?.enabledPlugins ?? null);
4035
+ return [source, enabledPlugins === null ? undefined : { enabledPlugins }];
4036
+ }));
4037
+ return Object.fromEntries(entries);
4038
+ }
4039
+ function remapInstalledPlugins2(pluginsDir, installedPlugins) {
4040
+ return remapInstalledPlugins(pluginsDir, installedPlugins, (installPath) => console.warn(`[plugin-config] Skipping plugin with unsafe install path: "${installPath}"`));
3941
4041
  }
3942
4042
  async function loadProjectOverridesFromGlobal(cwd) {
3943
- const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => config?.projects?.[cwd]?.plugins);
4043
+ const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => claudePluginConfigList(config?.projects?.[cwd]?.plugins));
3944
4044
  return plugins ?? [];
3945
4045
  }
3946
4046
  async function readPluginManifest(pluginPath) {
@@ -3956,42 +4056,29 @@ async function readPluginManifest(pluginPath) {
3956
4056
  };
3957
4057
  }
3958
4058
  function resolvePath(pluginPath, cwd) {
3959
- if (pluginPath.startsWith("~")) {
3960
- return join3(claudeHome(), pluginPath.slice(1));
3961
- }
3962
- if (pluginPath.startsWith("/")) {
3963
- return pluginPath;
3964
- }
3965
- return join3(cwd, pluginPath);
4059
+ return resolveClaudePluginPath(pluginPath, cwd, claudeHome());
3966
4060
  }
3967
- async function getMergedPlugins(cwd) {
3968
- const [global2, cliInstalled, projectGlobal, projectLocal] = await Promise.all([
4061
+ function defaultSettingSources(includeProjectSources) {
4062
+ return includeProjectSources ? ["user", "project"] : ["user"];
4063
+ }
4064
+ async function getMergedPlugins(cwd, includeProjectSources = true, settingSources = defaultSettingSources(includeProjectSources)) {
4065
+ const sources = claudePluginSettingSources(settingSources);
4066
+ const [global2, cliInstalled, projectGlobal, projectLocal, settings] = await Promise.all([
3969
4067
  loadGlobalPlugins(),
3970
4068
  loadCliInstalledPlugins(),
3971
- loadProjectOverridesFromGlobal(cwd),
3972
- loadProjectPlugins(cwd)
4069
+ includeProjectSources ? loadProjectOverridesFromGlobal(cwd) : [],
4070
+ includeProjectSources ? loadProjectPlugins(cwd) : [],
4071
+ loadPluginSettings(cwd, sources)
3973
4072
  ]);
3974
- const pluginMap = new Map;
3975
- for (const plugin of global2) {
3976
- const resolved = resolvePath(plugin.path, cwd);
3977
- pluginMap.set(resolved, { ...plugin, path: resolved });
3978
- }
3979
- for (const plugin of cliInstalled) {
3980
- const resolved = resolvePath(plugin.path, cwd);
3981
- pluginMap.set(resolved, { ...plugin, path: resolved });
3982
- }
3983
- for (const plugin of projectGlobal) {
3984
- const resolved = resolvePath(plugin.path, cwd);
3985
- pluginMap.set(resolved, { ...plugin, path: resolved });
3986
- }
3987
- for (const plugin of projectLocal) {
3988
- const resolved = resolvePath(plugin.path, cwd);
3989
- pluginMap.set(resolved, { ...plugin, path: resolved });
3990
- }
3991
- return Array.from(pluginMap.values());
3992
- }
3993
- async function getPluginsForSdk(cwd, includeProjectSources = true) {
3994
- const configs = includeProjectSources ? await getMergedPlugins(cwd) : [...await loadGlobalPlugins(), ...await loadCliInstalledPlugins()];
4073
+ return mergeClaudePluginLayers({
4074
+ global: global2,
4075
+ cliInstalled: enabledInstalledPlugins(cliInstalled, settings, sources),
4076
+ projectGlobal,
4077
+ projectLocal
4078
+ }, { cwd, home: claudeHome(), includeProjectSources }).map((path) => ({ type: "local", path }));
4079
+ }
4080
+ async function getPluginsForSdk(cwd, includeProjectSources = true, settingSources = defaultSettingSources(includeProjectSources)) {
4081
+ const configs = await getMergedPlugins(cwd, includeProjectSources, settingSources);
3995
4082
  const validPlugins = [];
3996
4083
  for (const config of configs) {
3997
4084
  if (await pathExists(config.path)) {
@@ -4117,90 +4204,18 @@ async function runtimeEnvironmentForAgentQuery(env = process.env, readTextFile =
4117
4204
  return snapshot;
4118
4205
  }
4119
4206
 
4120
- // ../protocol/src/coordinator-web-research.ts
4121
- var COORDINATOR_WEB_RESEARCH_MODES = ["off", "search", "search-and-mcp"];
4122
- var DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS = Object.freeze([{ name: "context7", url: "https://mcp.context7.com/mcp" }]);
4123
- var MAX_COORDINATOR_RESEARCH_MCP_SERVERS = 8;
4124
- var COORDINATOR_WEB_RESEARCH_ENV = "ORKESTRATOR_COORDINATOR_WEB_RESEARCH";
4125
- var COORDINATOR_RESEARCH_MCP_ENV = "ORKESTRATOR_COORDINATOR_RESEARCH_MCP";
4126
- var RESERVED_SERVER_NAME = /^orkestrator/;
4127
- var SERVER_NAME = /^[a-z0-9][a-z0-9-]{0,31}$/;
4128
- function isCoordinatorWebResearch(value) {
4129
- return typeof value === "string" && COORDINATOR_WEB_RESEARCH_MODES.includes(value);
4130
- }
4131
- function coordinatorResearchMcpServerProblem(server) {
4132
- if (!SERVER_NAME.test(server.name)) {
4133
- return "Use 1 to 32 lowercase letters, digits or hyphens for the name.";
4134
- }
4135
- if (RESERVED_SERVER_NAME.test(server.name))
4136
- return "Names starting with orkestrator are reserved.";
4137
- let url;
4138
- try {
4139
- url = new URL(server.url);
4140
- } catch {
4141
- return "The URL is not valid.";
4142
- }
4143
- if (url.protocol !== "https:")
4144
- return "Only https:// research servers are allowed.";
4145
- if (url.username || url.password)
4146
- return "Credentials in the URL are not allowed.";
4147
- return;
4148
- }
4149
- function normalizeCoordinatorResearchMcpServers(value) {
4150
- if (value === undefined)
4151
- return DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS.map((s) => ({ ...s }));
4152
- if (!Array.isArray(value))
4153
- return [];
4154
- const servers = [];
4155
- const names = new Set;
4156
- for (const entry of value) {
4157
- if (servers.length >= MAX_COORDINATOR_RESEARCH_MCP_SERVERS)
4158
- break;
4159
- if (!entry || typeof entry !== "object")
4160
- continue;
4161
- const { name, url } = entry;
4162
- if (typeof name !== "string" || typeof url !== "string")
4163
- continue;
4164
- const server = { name: name.trim(), url: url.trim() };
4165
- if (coordinatorResearchMcpServerProblem(server) || names.has(server.name))
4166
- continue;
4167
- names.add(server.name);
4168
- servers.push(server);
4169
- }
4170
- return servers;
4171
- }
4172
- function coordinatorWebResearchFromEnv(env) {
4173
- const value = env[COORDINATOR_WEB_RESEARCH_ENV];
4174
- return isCoordinatorWebResearch(value) ? value : "off";
4175
- }
4176
- function coordinatorResearchMcpServersFromEnv(env) {
4177
- if (coordinatorWebResearchFromEnv(env) !== "search-and-mcp")
4178
- return [];
4179
- const raw = env[COORDINATOR_RESEARCH_MCP_ENV];
4180
- if (!raw)
4181
- return [];
4182
- try {
4183
- return normalizeCoordinatorResearchMcpServers(JSON.parse(raw));
4184
- } catch {
4185
- return [];
4186
- }
4187
- }
4188
- var COORDINATOR_DENIED = Object.freeze([
4189
- "file.write",
4190
- "file.patch",
4191
- "shell.mutate",
4192
- "network"
4193
- ]);
4194
- function coordinatorCapabilityPolicy(mode) {
4195
- return {
4196
- deny: [...COORDINATOR_DENIED],
4197
- ...mode === "off" ? {} : { allow: ["web.search"] }
4198
- };
4199
- }
4200
-
4201
4207
  // ../protocol/src/native-agent-transcript-patch.ts
4202
4208
  var FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
4203
4209
 
4210
+ // ../protocol/src/native-agent-policy-effects.ts
4211
+ function appendNativeAgentPolicyNote(policy, note) {
4212
+ return { ...policy, note: policy.note ? `${policy.note} ${note}` : note };
4213
+ }
4214
+ var READ_ONLY_TURN_POLICY_NOTE = "The current turn is read-only, so it runs under the read-only policy rather than this session's own.";
4215
+ function readOnlyTurnReportedPolicy(turnPolicy) {
4216
+ return appendNativeAgentPolicyNote(turnPolicy, READ_ONLY_TURN_POLICY_NOTE);
4217
+ }
4218
+
4204
4219
  // ../protocol/src/native-agent-steer-rejection.ts
4205
4220
  var NATIVE_AGENT_STEER_REJECTION_HTTP_STATUS = 429;
4206
4221
  var MAX_STEER_JOURNAL_BYTES = 1024 * 1024 * 1024;
@@ -4263,6 +4278,15 @@ var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
4263
4278
  }
4264
4279
  ]
4265
4280
  });
4281
+ var CODEX_CONTAINER_APPROVAL_DESCRIPTIONS = Object.freeze({
4282
+ ask: {
4283
+ "on-request": "Codex has no sandbox of its own in a container, so this asks before any command Codex does not trust, like Untrusted"
4284
+ },
4285
+ "auto-approve": {
4286
+ "on-request": "Codex has no sandbox of its own in a container, so this never asks; choose Untrusted to approve commands",
4287
+ "auto-review": "Codex has no sandbox of its own in a container, so the reviewer is never consulted and everything runs"
4288
+ }
4289
+ });
4266
4290
  var DISPATCH_OUTCOME_LOST_KIND = "dispatch-outcome-lost";
4267
4291
  var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
4268
4292
  var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
@@ -4414,6 +4438,87 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4414
4438
  "slashCommandCatalogue"
4415
4439
  ]);
4416
4440
 
4441
+ // ../protocol/src/coordinator-web-research.ts
4442
+ var COORDINATOR_WEB_RESEARCH_MODES = ["off", "search", "search-and-mcp"];
4443
+ var DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS = Object.freeze([{ name: "context7", url: "https://mcp.context7.com/mcp" }]);
4444
+ var MAX_COORDINATOR_RESEARCH_MCP_SERVERS = 8;
4445
+ var COORDINATOR_WEB_RESEARCH_ENV = "ORKESTRATOR_COORDINATOR_WEB_RESEARCH";
4446
+ var COORDINATOR_RESEARCH_MCP_ENV = "ORKESTRATOR_COORDINATOR_RESEARCH_MCP";
4447
+ var RESERVED_SERVER_NAME = /^orkestrator/;
4448
+ var SERVER_NAME = /^[a-z0-9][a-z0-9-]{0,31}$/;
4449
+ function isCoordinatorWebResearch(value) {
4450
+ return typeof value === "string" && COORDINATOR_WEB_RESEARCH_MODES.includes(value);
4451
+ }
4452
+ function coordinatorResearchMcpServerProblem(server) {
4453
+ if (!SERVER_NAME.test(server.name)) {
4454
+ return "Use 1 to 32 lowercase letters, digits or hyphens for the name.";
4455
+ }
4456
+ if (RESERVED_SERVER_NAME.test(server.name))
4457
+ return "Names starting with orkestrator are reserved.";
4458
+ let url;
4459
+ try {
4460
+ url = new URL(server.url);
4461
+ } catch {
4462
+ return "The URL is not valid.";
4463
+ }
4464
+ if (url.protocol !== "https:")
4465
+ return "Only https:// research servers are allowed.";
4466
+ if (url.username || url.password)
4467
+ return "Credentials in the URL are not allowed.";
4468
+ return;
4469
+ }
4470
+ function normalizeCoordinatorResearchMcpServers(value) {
4471
+ if (value === undefined)
4472
+ return DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS.map((s) => ({ ...s }));
4473
+ if (!Array.isArray(value))
4474
+ return [];
4475
+ const servers = [];
4476
+ const names = new Set;
4477
+ for (const entry of value) {
4478
+ if (servers.length >= MAX_COORDINATOR_RESEARCH_MCP_SERVERS)
4479
+ break;
4480
+ if (!entry || typeof entry !== "object")
4481
+ continue;
4482
+ const { name, url } = entry;
4483
+ if (typeof name !== "string" || typeof url !== "string")
4484
+ continue;
4485
+ const server = { name: name.trim(), url: url.trim() };
4486
+ if (coordinatorResearchMcpServerProblem(server) || names.has(server.name))
4487
+ continue;
4488
+ names.add(server.name);
4489
+ servers.push(server);
4490
+ }
4491
+ return servers;
4492
+ }
4493
+ function coordinatorWebResearchFromEnv(env) {
4494
+ const value = env[COORDINATOR_WEB_RESEARCH_ENV];
4495
+ return isCoordinatorWebResearch(value) ? value : "off";
4496
+ }
4497
+ function coordinatorResearchMcpServersFromEnv(env) {
4498
+ if (coordinatorWebResearchFromEnv(env) !== "search-and-mcp")
4499
+ return [];
4500
+ const raw = env[COORDINATOR_RESEARCH_MCP_ENV];
4501
+ if (!raw)
4502
+ return [];
4503
+ try {
4504
+ return normalizeCoordinatorResearchMcpServers(JSON.parse(raw));
4505
+ } catch {
4506
+ return [];
4507
+ }
4508
+ }
4509
+ var COORDINATOR_DENIED = Object.freeze([
4510
+ "file.write",
4511
+ "file.patch",
4512
+ "shell.mutate",
4513
+ "network"
4514
+ ]);
4515
+ function coordinatorCapabilityPolicy(mode) {
4516
+ return {
4517
+ deny: [...COORDINATOR_DENIED],
4518
+ ...mode === "off" ? {} : { allow: ["web.search"] }
4519
+ };
4520
+ }
4521
+
4417
4522
  // ../../bridges/claude-bridge/src/services/mcp-tool-prefix.ts
4418
4523
  function claudeMcpToolPrefix(name) {
4419
4524
  let normalized = name.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -4684,7 +4789,7 @@ function claudeReadOnlyAllowedTools(options) {
4684
4789
  }
4685
4790
  function claudeReadOnlySandbox(policy, options = {}) {
4686
4791
  return {
4687
- enabled: true,
4792
+ enabled: options.commandSandbox !== "unavailable",
4688
4793
  failIfUnavailable: options.commandSandbox !== "unavailable",
4689
4794
  autoAllowBashIfSandboxed: true,
4690
4795
  allowUnsandboxedCommands: false,
@@ -4882,6 +4987,14 @@ function claudeWorkspaceCwd() {
4882
4987
  function claudeTurnPolicy(sessionPolicy, readOnly) {
4883
4988
  return effectiveExecutionPolicy(readOnly ? structuredClone(READ_ONLY_TURN_POLICY) : sessionPolicy);
4884
4989
  }
4990
+ function claudeReportedPolicy(session) {
4991
+ if (session.status === "running" && session.commandDiscoveryInputs?.readOnly === true) {
4992
+ const turnPolicy = claudeTurnPolicy(session.executionPolicy, true);
4993
+ if (turnPolicy)
4994
+ return readOnlyTurnReportedPolicy(turnPolicy);
4995
+ }
4996
+ return session.executionPolicy;
4997
+ }
4885
4998
  function claudePolicyPermissionMode(policy) {
4886
4999
  return isRestrictivePolicy(policy) ? "default" : "bypassPermissions";
4887
5000
  }
@@ -4924,7 +5037,7 @@ async function resolveClaudeDiscoveryConfig(sessionPolicy, inputs) {
4924
5037
  const policy = claudeTurnPolicy(sessionPolicy, inputs.readOnly);
4925
5038
  const cwd = claudeWorkspaceCwd();
4926
5039
  const settingSources = claudeSettingSources(policy, inputs.includeLocalSettings);
4927
- const plugins = await getPluginsForSdk(cwd, policy?.projectResources !== false);
5040
+ const plugins = await getPluginsForSdk(cwd, policy?.projectResources !== false, settingSources);
4928
5041
  return {
4929
5042
  cwd,
4930
5043
  settingSources,
@@ -6813,21 +6926,31 @@ function turnInFlight(session) {
6813
6926
  function syncTurnInFlightMark(session, options = {}) {
6814
6927
  if (session.deleting)
6815
6928
  return;
6929
+ if (options.started)
6930
+ session.turnInFlightRequestId = options.requestId;
6816
6931
  writeTurnInFlightMark(session, options.stopped !== true && turnInFlight(session), options.started);
6817
6932
  }
6818
6933
  function clearTurnInFlightMarkForClose(session) {
6819
6934
  writeTurnInFlightMark(session, false);
6820
6935
  }
6821
6936
  function writeTurnInFlightMark(session, wanted, claim = false) {
6822
- if (wanted === (session.turnInFlightMarked === true) && !(wanted && claim && !session.turnInFlightMarkOwned))
6937
+ const renamed = claim && session.turnInFlightMarkOwned === true && session.turnInFlightMarkRequestId !== session.turnInFlightRequestId;
6938
+ if (wanted === (session.turnInFlightMarked === true) && !(wanted && claim && (!session.turnInFlightMarkOwned || renamed)))
6823
6939
  return;
6824
6940
  const sdkSessionId = session.sdkSessionId ?? sdkSessionIdFromBridgeId(session.id);
6825
6941
  if (!sdkSessionId)
6826
6942
  return;
6827
6943
  session.turnInFlightMarked = wanted;
6828
6944
  session.turnInFlightMarkOwned = wanted;
6945
+ const requestId = wanted ? session.turnInFlightRequestId : undefined;
6946
+ session.turnInFlightMarkRequestId = requestId;
6829
6947
  const update = {
6830
- turnInFlight: wanted ? { bridgeInstanceId: BRIDGE_INSTANCE_ID, pid: process.pid, startedAt: Date.now() } : undefined
6948
+ turnInFlight: wanted ? {
6949
+ bridgeInstanceId: BRIDGE_INSTANCE_ID,
6950
+ pid: process.pid,
6951
+ startedAt: Date.now(),
6952
+ ...requestId ? { requestId } : {}
6953
+ } : undefined
6831
6954
  };
6832
6955
  updateSessionPreferences(sdkSessionId, update).catch((error) => {
6833
6956
  debugLog("[session-manager] Failed to record the turn-in-flight mark:", error instanceof Error ? error.message : String(error));
@@ -6846,6 +6969,7 @@ function restoredTurnInterruption(preferences) {
6846
6969
  status: "error",
6847
6970
  error: CLAUDE_RESTARTED_MID_TURN_MESSAGE,
6848
6971
  interruption: "restart",
6972
+ ...mark.requestId ? { interruptedRequestId: mark.requestId } : {},
6849
6973
  turnInFlightMarked: true
6850
6974
  };
6851
6975
  }
@@ -7385,7 +7509,6 @@ function claudeLaunchProfile(session, options) {
7385
7509
  context1m: effectiveClaudeContext1m(options),
7386
7510
  promptSuggestions: options?.promptSuggestions === true,
7387
7511
  chrome: effectiveClaudeChrome(policy, options),
7388
- maxBudgetUsd: typeof options?.maxBudgetUsd === "number" && options.maxBudgetUsd > 0 ? options.maxBudgetUsd : null,
7389
7512
  thinking: typeof options?.parameterValues?.thinking === "string" ? options.parameterValues.thinking : "adaptive",
7390
7513
  bypassCapable: claudePolicyPermissionMode(policy) === "bypassPermissions",
7391
7514
  cwd: claudeWorkspaceCwd(),
@@ -7421,6 +7544,13 @@ function claudeLaunchedProfile(session, options) {
7421
7544
  settingSources: JSON.stringify(claudeSettingSources(claudeTurnPolicy(session.executionPolicy, options?.readOnly), options?.includeLocalSettings))
7422
7545
  };
7423
7546
  }
7547
+ function claudeTurnSettingSources(session, options) {
7548
+ const policy = claudeTurnPolicy(session.executionPolicy, options?.readOnly);
7549
+ const running = openClaudeRuntime(session);
7550
+ if (options?.includeLocalSettings === undefined && running?.launch.settingSources !== undefined && !claudeLaunchConflict(running.launch, claudeLaunchProfile(session, options)))
7551
+ return JSON.parse(running.launch.settingSources);
7552
+ return claudeSettingSources(policy, options?.includeLocalSettings);
7553
+ }
7424
7554
  function assertClaudeRuntimeStaticAccepts(session, options) {
7425
7555
  const runtime = openClaudeRuntime(session);
7426
7556
  if (!runtime || !claudeRuntimeOwesWork(runtime))
@@ -7442,7 +7572,7 @@ async function assertClaudeRuntimeAccepts(session, options, resources) {
7442
7572
  if (!resources) {
7443
7573
  const policy = claudeTurnPolicy(session.executionPolicy, options?.readOnly);
7444
7574
  const [plugins, environment] = await Promise.all([
7445
- getPluginsForSdk(request.cwd, policy?.projectResources !== false),
7575
+ getPluginsForSdk(request.cwd, policy?.projectResources !== false, claudeTurnSettingSources(session, options)),
7446
7576
  runtimeEnvironmentForAgentQuery()
7447
7577
  ]);
7448
7578
  resources = claudeLaunchResourceProfile(plugins, environment);
@@ -7487,8 +7617,6 @@ function claudeRuntimeOwesWork(runtime) {
7487
7617
  function claudeLaunchConflict(runtime, request) {
7488
7618
  if (runtime.chrome !== request.chrome)
7489
7619
  return "Chrome setting";
7490
- if (runtime.maxBudgetUsd !== request.maxBudgetUsd)
7491
- return "cost budget";
7492
7620
  if (runtime.context1m !== request.context1m)
7493
7621
  return "1M context setting";
7494
7622
  if (runtime.promptSuggestions !== request.promptSuggestions)
@@ -7645,7 +7773,7 @@ function noMessages() {
7645
7773
  // ../../bridges/claude-bridge/src/services/session-manager-persistence.ts
7646
7774
  import { constants } from "node:fs";
7647
7775
  import { lstat as lstat2, open as open2, realpath } from "node:fs/promises";
7648
- import { isAbsolute as isAbsolute3, join as join8, relative as relative2, resolve as resolve3, sep } from "node:path";
7776
+ import { isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve2, sep } from "node:path";
7649
7777
 
7650
7778
  // ../protocol/src/node/command-change-journal.ts
7651
7779
  import { appendFile, mkdir as mkdir2, readFile as readFile4, rename as rename2, rm as rm2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
@@ -7802,7 +7930,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
7802
7930
  import { rmSync } from "node:fs";
7803
7931
  import { copyFile, lstat, mkdtemp, rm as rm3 } from "node:fs/promises";
7804
7932
  import { tmpdir } from "node:os";
7805
- import { delimiter, isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "node:path";
7933
+ import { delimiter, isAbsolute, join as join4, resolve } from "node:path";
7806
7934
  var DEFAULT_MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
7807
7935
  var DEFAULT_MAX_UNTRACKED_FILES = 5000;
7808
7936
  var DEFAULT_SLOW_SNAPSHOT_MS = 500;
@@ -8084,8 +8212,8 @@ class WorkspaceChangeProbe {
8084
8212
  return;
8085
8213
  return {
8086
8214
  root,
8087
- indexPath: isAbsolute2(index) ? index : resolve2(cwd, index),
8088
- objectsPath: isAbsolute2(objects) ? objects : resolve2(cwd, objects)
8215
+ indexPath: isAbsolute(index) ? index : resolve(cwd, index),
8216
+ objectsPath: isAbsolute(objects) ? objects : resolve(cwd, objects)
8089
8217
  };
8090
8218
  }).catch(() => {
8091
8219
  return;
@@ -9413,14 +9541,14 @@ function record(value) {
9413
9541
  function text(value) {
9414
9542
  return typeof value === "string" ? value : undefined;
9415
9543
  }
9416
- function path(value) {
9544
+ function path2(value) {
9417
9545
  return typeof value === "string" && value.length > 0 ? value : undefined;
9418
9546
  }
9419
9547
  function claudeProposalEvidence(toolName, input) {
9420
9548
  const fields = record(input);
9421
9549
  switch (toolName) {
9422
9550
  case "Edit": {
9423
- const filePath = path(fields.file_path);
9551
+ const filePath = path2(fields.file_path);
9424
9552
  if (!filePath)
9425
9553
  return [];
9426
9554
  return [
@@ -9438,7 +9566,7 @@ function claudeProposalEvidence(toolName, input) {
9438
9566
  ];
9439
9567
  }
9440
9568
  case "MultiEdit": {
9441
- const filePath = path(fields.file_path);
9569
+ const filePath = path2(fields.file_path);
9442
9570
  if (!filePath || !Array.isArray(fields.edits))
9443
9571
  return [];
9444
9572
  return fields.edits.map((edit, index) => {
@@ -9457,7 +9585,7 @@ function claudeProposalEvidence(toolName, input) {
9457
9585
  });
9458
9586
  }
9459
9587
  case "Write": {
9460
- const filePath = path(fields.file_path);
9588
+ const filePath = path2(fields.file_path);
9461
9589
  if (!filePath)
9462
9590
  return [];
9463
9591
  return [
@@ -9475,7 +9603,7 @@ function claudeProposalEvidence(toolName, input) {
9475
9603
  ];
9476
9604
  }
9477
9605
  case "NotebookEdit": {
9478
- const filePath = path(fields.notebook_path);
9606
+ const filePath = path2(fields.notebook_path);
9479
9607
  if (!filePath)
9480
9608
  return [];
9481
9609
  const deleting = fields.edit_mode === "delete";
@@ -10446,17 +10574,17 @@ function parseBase64ImageData(value) {
10446
10574
  var BRIDGE_ATTACHMENT_ROOT_ENV = "ORKESTRATOR_BRIDGE_ATTACHMENT_ROOT";
10447
10575
  function configuredAttachmentRoot(env = process.env) {
10448
10576
  const value = env[BRIDGE_ATTACHMENT_ROOT_ENV]?.trim();
10449
- return value && isAbsolute3(value) ? resolve3(value) : undefined;
10577
+ return value && isAbsolute2(value) ? resolve2(value) : undefined;
10450
10578
  }
10451
10579
  function attachmentContainmentRoot(filePath, cwd, extraRoot = configuredAttachmentRoot()) {
10452
- if (extraRoot && isAbsolute3(filePath) && isPathWithin2(extraRoot, resolve3(filePath))) {
10580
+ if (extraRoot && isAbsolute2(filePath) && isPathWithin2(extraRoot, resolve2(filePath))) {
10453
10581
  return extraRoot;
10454
10582
  }
10455
- return resolve3(cwd);
10583
+ return resolve2(cwd);
10456
10584
  }
10457
10585
  function isPathWithin2(rootPath, targetPath) {
10458
- const childPath = relative2(rootPath, targetPath);
10459
- return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !isAbsolute3(childPath);
10586
+ const childPath = relative(rootPath, targetPath);
10587
+ return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !isAbsolute2(childPath);
10460
10588
  }
10461
10589
  function attachmentErrorForFsFailure(error) {
10462
10590
  const code = error?.code;
@@ -10469,7 +10597,7 @@ function attachmentErrorForFsFailure(error) {
10469
10597
  return new ClaudeAttachmentError("attachment_read_failed", "Image attachment could not be read safely from the workspace.");
10470
10598
  }
10471
10599
  async function assertNoSymlinkComponents(lexicalRoot, targetPath) {
10472
- const childPath = relative2(lexicalRoot, targetPath);
10600
+ const childPath = relative(lexicalRoot, targetPath);
10473
10601
  let currentPath = lexicalRoot;
10474
10602
  for (const segment of childPath.split(sep).filter(Boolean)) {
10475
10603
  currentPath = join8(currentPath, segment);
@@ -10500,7 +10628,7 @@ async function assertOpenedWorkspaceFile(targetPath, canonicalRoot, openedStats)
10500
10628
  }
10501
10629
  async function readWorkspaceImageAttachment(filePath, cwd, afterSymlinkValidation, afterCanonicalValidation, afterInitialValidation) {
10502
10630
  const lexicalRoot = attachmentContainmentRoot(filePath, cwd);
10503
- const targetPath = isAbsolute3(filePath) ? resolve3(filePath) : resolve3(lexicalRoot, filePath);
10631
+ const targetPath = isAbsolute2(filePath) ? resolve2(filePath) : resolve2(lexicalRoot, filePath);
10504
10632
  if (!isPathWithin2(lexicalRoot, targetPath)) {
10505
10633
  throw new ClaudeAttachmentError("attachment_outside_workspace", "Image attachment must be contained in the current workspace.");
10506
10634
  }
@@ -10731,57 +10859,97 @@ var MAX_TRACKED_STREAM_ROWS = 256;
10731
10859
  var MAX_SUBAGENT_PARTS_PER_CALL = 128;
10732
10860
  var sessionStreamRows = new WeakMap;
10733
10861
  function createPromptStreamState(session, sessionId, options) {
10734
- let streamEventFlushTimer = null;
10735
- const { flushIntervalMs, maxBlockIndex } = options;
10736
- const toolTracker = options.toolTracker ?? new ToolTracker;
10737
- const taskRegistry = session.taskRegistry ??= new TaskRegistry;
10738
- const rows = [];
10739
- let sharedState = sessionStreamRows.get(session);
10740
- if (!sharedState || sharedState.messages !== session.messages) {
10741
- sharedState = { messages: session.messages, rows: new Map };
10742
- sessionStreamRows.set(session, sharedState);
10743
- }
10744
- const sharedRows = sharedState.rows;
10745
- const rowByApiMessage = new Map;
10746
- const rowByToolUseId = new Map;
10747
- let currentRow = null;
10748
- let rowSequence = 0;
10749
- const parentTaskByApiMessage = new Map;
10750
- let currentStreamApiMessageId = null;
10751
- let syntheticMessageKeyCounter = 0;
10752
- const dirtyRows = new Set;
10753
- const rowOfMessage = new Map;
10754
- let assistantOutputSeen = false;
10755
- const forgetRow = (row) => {
10756
- if (dirtyRows.delete(row))
10757
- materializeRow(row, true);
10862
+ return new PromptStream(session, sessionId, options);
10863
+ }
10864
+ function explicitParentTaskUseId(partialMessage) {
10865
+ return typeof partialMessage.parent_tool_use_id === "string" && partialMessage.parent_tool_use_id.length > 0 ? partialMessage.parent_tool_use_id : undefined;
10866
+ }
10867
+ function materializeEntryValue(entry) {
10868
+ if (entry.pendingChunks) {
10869
+ entry.value += entry.pendingChunks.join("");
10870
+ entry.pendingChunks = undefined;
10871
+ }
10872
+ }
10873
+
10874
+ class PromptStream {
10875
+ session;
10876
+ sessionId;
10877
+ toolTracker;
10878
+ taskRegistry;
10879
+ activeTaskIds;
10880
+ pendingApiRetryMessageId;
10881
+ flushIntervalMs;
10882
+ maxBlockIndex;
10883
+ streamEventFlushTimer = null;
10884
+ rows = [];
10885
+ sharedRows;
10886
+ rowByApiMessage = new Map;
10887
+ rowByToolUseId = new Map;
10888
+ currentRow = null;
10889
+ rowSequence = 0;
10890
+ parentTaskByApiMessage = new Map;
10891
+ currentStreamApiMessageId = null;
10892
+ syntheticMessageKeyCounter = 0;
10893
+ dirtyRows = new Set;
10894
+ rowOfMessage = new Map;
10895
+ outputSeen = false;
10896
+ blockTarget = null;
10897
+ flushOnTimer = () => {
10898
+ this.streamEventFlushTimer = null;
10899
+ this.flushStreamedAssistantMessage();
10900
+ };
10901
+ constructor(session, sessionId, options) {
10902
+ this.session = session;
10903
+ this.sessionId = sessionId;
10904
+ this.flushIntervalMs = options.flushIntervalMs;
10905
+ this.maxBlockIndex = options.maxBlockIndex;
10906
+ this.toolTracker = options.toolTracker ?? new ToolTracker;
10907
+ this.taskRegistry = session.taskRegistry ??= new TaskRegistry;
10908
+ this.activeTaskIds = options.activeTaskIds ?? new Set;
10909
+ let sharedState = sessionStreamRows.get(session);
10910
+ if (!sharedState || sharedState.messages !== session.messages) {
10911
+ sharedState = { messages: session.messages, rows: new Map };
10912
+ sessionStreamRows.set(session, sharedState);
10913
+ }
10914
+ this.sharedRows = sharedState.rows;
10915
+ }
10916
+ get currentAssistantMessage() {
10917
+ return this.currentRow?.message ?? null;
10918
+ }
10919
+ get assistantOutputSeen() {
10920
+ return this.outputSeen;
10921
+ }
10922
+ forgetRow(row) {
10923
+ if (this.dirtyRows.delete(row))
10924
+ this.materializeRow(row, true);
10758
10925
  for (const apiMessageId of row.blocksByApiMessage.keys()) {
10759
- if (rowByApiMessage.get(apiMessageId) !== row)
10926
+ if (this.rowByApiMessage.get(apiMessageId) !== row)
10760
10927
  continue;
10761
- rowByApiMessage.delete(apiMessageId);
10762
- parentTaskByApiMessage.delete(apiMessageId);
10928
+ this.rowByApiMessage.delete(apiMessageId);
10929
+ this.parentTaskByApiMessage.delete(apiMessageId);
10763
10930
  }
10764
10931
  for (const toolUseId of row.toolUseIds) {
10765
- if (rowByToolUseId.get(toolUseId) === row)
10766
- rowByToolUseId.delete(toolUseId);
10932
+ if (this.rowByToolUseId.get(toolUseId) === row)
10933
+ this.rowByToolUseId.delete(toolUseId);
10767
10934
  }
10768
10935
  if (row.message)
10769
- rowOfMessage.delete(row.message);
10770
- };
10771
- const createRow = (key, modelId) => {
10936
+ this.rowOfMessage.delete(row.message);
10937
+ }
10938
+ createRow(key, modelId) {
10939
+ const { rows, sharedRows, session } = this;
10772
10940
  const shared = sharedRows.get(key);
10773
10941
  if (shared && (!shared.message || session.messages.includes(shared.message))) {
10774
10942
  rows.push(shared);
10775
10943
  if (shared.message)
10776
- rowOfMessage.set(shared.message, shared);
10944
+ this.rowOfMessage.set(shared.message, shared);
10777
10945
  while (rows.length > MAX_TRACKED_STREAM_ROWS)
10778
- forgetRow(rows.shift());
10946
+ this.forgetRow(rows.shift());
10779
10947
  for (const id of shared.toolUseIds)
10780
- rowByToolUseId.set(id, shared);
10948
+ this.rowByToolUseId.set(id, shared);
10781
10949
  return shared;
10782
10950
  }
10783
10951
  const row = {
10784
- sequence: rowSequence += 1,
10952
+ sequence: this.rowSequence += 1,
10785
10953
  key,
10786
10954
  message: null,
10787
10955
  blocksByApiMessage: new Map,
@@ -10803,41 +10971,35 @@ function createPromptStreamState(session, sessionId, options) {
10803
10971
  rows.push(row);
10804
10972
  while (rows.length > MAX_TRACKED_STREAM_ROWS) {
10805
10973
  const oldest = rows.shift();
10806
- forgetRow(oldest);
10974
+ this.forgetRow(oldest);
10807
10975
  }
10808
10976
  return row;
10809
- };
10810
- const rowForApiMessage = (messageKey, opensRow, parentToolUseId, modelId) => {
10811
- const known = rowByApiMessage.get(messageKey);
10977
+ }
10978
+ rowForApiMessage(messageKey, opensRow, parentToolUseId, modelId) {
10979
+ const known = this.rowByApiMessage.get(messageKey);
10812
10980
  if (known)
10813
10981
  return known;
10814
10982
  let row;
10815
10983
  if (!opensRow) {
10816
- row = (parentToolUseId ? rowByToolUseId.get(parentToolUseId) : undefined) ?? currentRow ?? undefined;
10984
+ row = (parentToolUseId ? this.rowByToolUseId.get(parentToolUseId) : undefined) ?? this.currentRow ?? undefined;
10817
10985
  }
10818
10986
  if (!row) {
10819
- flushStreamedAssistantMessage();
10820
- row = createRow(messageKey, modelId);
10821
- currentRow = row;
10987
+ this.flushStreamedAssistantMessage();
10988
+ row = this.createRow(messageKey, modelId);
10989
+ this.currentRow = row;
10822
10990
  }
10823
- rowByApiMessage.set(messageKey, row);
10991
+ this.rowByApiMessage.set(messageKey, row);
10824
10992
  return row;
10825
- };
10826
- const getBlocks = (row, messageKey) => {
10993
+ }
10994
+ getBlocks(row, messageKey) {
10827
10995
  let blocks = row.blocksByApiMessage.get(messageKey);
10828
10996
  if (!blocks) {
10829
10997
  blocks = new Map;
10830
10998
  row.blocksByApiMessage.set(messageKey, blocks);
10831
10999
  }
10832
11000
  return blocks;
10833
- };
10834
- const materializeEntryValue = (entry) => {
10835
- if (entry.pendingChunks) {
10836
- entry.value += entry.pendingChunks.join("");
10837
- entry.pendingChunks = undefined;
10838
- }
10839
- };
10840
- const rebuildOrderedParts = (row) => {
11001
+ }
11002
+ rebuildOrderedParts(row) {
10841
11003
  const located = [];
10842
11004
  for (const [messageKey, blocks] of row.blocksByApiMessage) {
10843
11005
  const blockIndices = Array.from(blocks.keys()).sort((a, b) => a - b);
@@ -10864,17 +11026,14 @@ function createPromptStreamState(session, sessionId, options) {
10864
11026
  blocks.delete(blockIndex);
10865
11027
  if (entry.type === "tool-ref") {
10866
11028
  row.toolUseIds.delete(entry.value);
10867
- if (rowByToolUseId.get(entry.value) === row)
10868
- rowByToolUseId.delete(entry.value);
11029
+ if (this.rowByToolUseId.get(entry.value) === row)
11030
+ this.rowByToolUseId.delete(entry.value);
10869
11031
  }
10870
11032
  }
10871
11033
  row.orderedParts = shed.size === 0 ? located.map(({ entry }) => entry) : located.filter(({ entry }) => !shed.has(entry)).map(({ entry }) => entry);
10872
- };
10873
- const activeTaskIds = options.activeTaskIds ?? new Set;
10874
- let pendingPlanRejectionFeedback = null;
10875
- let planApprovedThisTurn = false;
10876
- let pendingPlanApprovalContinuation = null;
10877
- const emitRow = (row) => {
11034
+ }
11035
+ emitRow(row) {
11036
+ const { session, sessionId } = this;
10878
11037
  const message = row.message;
10879
11038
  if (!message)
10880
11039
  return;
@@ -10912,34 +11071,36 @@ function createPromptStreamState(session, sessionId, options) {
10912
11071
  revision: message.revision
10913
11072
  }
10914
11073
  });
10915
- };
10916
- const newestRowMessage = () => {
10917
- for (let index = rows.length - 1;index >= 0; index -= 1) {
10918
- const message = rows[index].message;
11074
+ }
11075
+ newestRowMessage() {
11076
+ for (let index = this.rows.length - 1;index >= 0; index -= 1) {
11077
+ const message = this.rows[index].message;
10919
11078
  if (message)
10920
11079
  return message;
10921
11080
  }
10922
11081
  return null;
10923
- };
10924
- const insertAfterAnchor = (row, message) => {
10925
- const anchorIndex = row.anchor ? session.messages.lastIndexOf(row.anchor) : -1;
11082
+ }
11083
+ insertAfterAnchor(row, message) {
11084
+ const messages = this.session.messages;
11085
+ const anchorIndex = row.anchor ? messages.lastIndexOf(row.anchor) : -1;
10926
11086
  if (anchorIndex < 0) {
10927
- session.messages.push(message);
11087
+ messages.push(message);
10928
11088
  return;
10929
11089
  }
10930
11090
  let index = anchorIndex + 1;
10931
- while (index < session.messages.length) {
10932
- const owner = rowOfMessage.get(session.messages[index]);
11091
+ while (index < messages.length) {
11092
+ const owner = this.rowOfMessage.get(messages[index]);
10933
11093
  if (!owner || owner.sequence > row.sequence)
10934
11094
  break;
10935
11095
  index += 1;
10936
11096
  }
10937
- session.messages.splice(index, 0, message);
10938
- };
10939
- const materializeRow = (row, rebuildBlocks) => {
11097
+ messages.splice(index, 0, message);
11098
+ }
11099
+ materializeRow(row, rebuildBlocks) {
11100
+ const { session } = this;
10940
11101
  if (rebuildBlocks)
10941
- rebuildOrderedParts(row);
10942
- const parts = buildMessageParts(row.orderedParts, toolTracker);
11102
+ this.rebuildOrderedParts(row);
11103
+ const parts = buildMessageParts(row.orderedParts, this.toolTracker);
10943
11104
  const content = getMessageTextFromParts(parts);
10944
11105
  if (!row.message) {
10945
11106
  if (parts.length === 0 && !content)
@@ -10953,9 +11114,9 @@ function createPromptStreamState(session, sessionId, options) {
10953
11114
  ...row.modelId ? { modelId: row.modelId } : {},
10954
11115
  ...row.sdkUuid ? { sdkUuid: row.sdkUuid } : {}
10955
11116
  };
10956
- rowOfMessage.set(row.message, row);
10957
- insertAfterAnchor(row, row.message);
10958
- assistantOutputSeen = true;
11117
+ this.rowOfMessage.set(row.message, row);
11118
+ this.insertAfterAnchor(row, row.message);
11119
+ this.outputSeen = true;
10959
11120
  } else {
10960
11121
  row.message.content = content;
10961
11122
  row.message.parts = parts;
@@ -10968,32 +11129,26 @@ function createPromptStreamState(session, sessionId, options) {
10968
11129
  (session.revisedRows ??= new RecentlyRevisedRows).note(row.message.id);
10969
11130
  }
10970
11131
  markTranscriptChanged(session);
10971
- emitRow(row);
10972
- };
10973
- function flushStreamedAssistantMessage() {
10974
- if (streamEventFlushTimer) {
10975
- clearTimeout(streamEventFlushTimer);
10976
- streamEventFlushTimer = null;
10977
- }
10978
- if (dirtyRows.size === 0)
11132
+ this.emitRow(row);
11133
+ }
11134
+ flushStreamedAssistantMessage() {
11135
+ this.clearFlushTimer();
11136
+ if (this.dirtyRows.size === 0)
10979
11137
  return;
10980
- const pending = [...dirtyRows].sort((a, b) => a.sequence - b.sequence);
10981
- dirtyRows.clear();
11138
+ const pending = [...this.dirtyRows].sort((a, b) => a.sequence - b.sequence);
11139
+ this.dirtyRows.clear();
10982
11140
  for (const row of pending)
10983
- materializeRow(row, true);
10984
- }
10985
- const scheduleStreamedAssistantMessageFlush = (row) => {
10986
- dirtyRows.add(row);
10987
- streamEventFlushTimer ??= setTimeout(() => {
10988
- streamEventFlushTimer = null;
10989
- flushStreamedAssistantMessage();
10990
- }, flushIntervalMs);
10991
- };
10992
- const applyAssistantRecord = (record) => {
10993
- const messageKey = record.apiMessageId ?? record.uuid ?? `assistant-${syntheticMessageKeyCounter += 1}`;
11141
+ this.materializeRow(row, true);
11142
+ }
11143
+ scheduleStreamedAssistantMessageFlush(row) {
11144
+ this.dirtyRows.add(row);
11145
+ this.streamEventFlushTimer ??= setTimeout(this.flushOnTimer, this.flushIntervalMs);
11146
+ }
11147
+ applyAssistantRecord(record) {
11148
+ const messageKey = record.apiMessageId ?? record.uuid ?? `assistant-${this.syntheticMessageKeyCounter += 1}`;
10994
11149
  const opensRow = record.isRoot && record.apiMessageId !== undefined;
10995
- const row = rowForApiMessage(messageKey, opensRow, record.parentToolUseId, record.modelId);
10996
- const blocks = getBlocks(row, messageKey);
11150
+ const row = this.rowForApiMessage(messageKey, opensRow, record.parentToolUseId, record.modelId);
11151
+ const blocks = this.getBlocks(row, messageKey);
10997
11152
  const blockIndexBase = row.finalizedBlockCounts.get(messageKey) ?? 0;
10998
11153
  for (const part of record.orderedParts) {
10999
11154
  const blockIndex = blockIndexBase + (part.blockOffset ?? 0);
@@ -11005,7 +11160,7 @@ function createPromptStreamState(session, sessionId, options) {
11005
11160
  });
11006
11161
  if (part.type === "tool-ref") {
11007
11162
  row.toolUseIds.add(part.value);
11008
- rowByToolUseId.set(part.value, row);
11163
+ this.rowByToolUseId.set(part.value, row);
11009
11164
  }
11010
11165
  }
11011
11166
  row.finalizedBlockCounts.set(messageKey, blockIndexBase + record.contentBlockCount);
@@ -11013,15 +11168,15 @@ function createPromptStreamState(session, sessionId, options) {
11013
11168
  row.modelId = record.modelId;
11014
11169
  if (record.isRoot && record.uuid)
11015
11170
  row.sdkUuid = record.uuid;
11016
- dirtyRows.delete(row);
11017
- materializeRow(row, true);
11018
- };
11019
- const refreshToolRows = (toolUseIds) => {
11171
+ this.dirtyRows.delete(row);
11172
+ this.materializeRow(row, true);
11173
+ }
11174
+ refreshToolRows(toolUseIds) {
11020
11175
  const affected = new Set;
11021
- if (currentRow)
11022
- affected.add(currentRow);
11176
+ if (this.currentRow)
11177
+ affected.add(this.currentRow);
11023
11178
  for (const toolUseId of toolUseIds) {
11024
- const row = rowByToolUseId.get(toolUseId);
11179
+ const row = this.rowByToolUseId.get(toolUseId);
11025
11180
  if (row)
11026
11181
  affected.add(row);
11027
11182
  }
@@ -11029,150 +11184,145 @@ function createPromptStreamState(session, sessionId, options) {
11029
11184
  for (const row of [...affected].sort((a, b) => a.sequence - b.sequence)) {
11030
11185
  if (!row.message)
11031
11186
  continue;
11032
- const rebuildBlocks = dirtyRows.delete(row);
11033
- materializeRow(row, rebuildBlocks);
11187
+ const rebuildBlocks = this.dirtyRows.delete(row);
11188
+ this.materializeRow(row, rebuildBlocks);
11034
11189
  published.add(row.message);
11035
11190
  }
11036
11191
  return published;
11037
- };
11038
- const applyPartialAssistantMessage = (partialMessage) => {
11192
+ }
11193
+ rowHoldingTool(toolUseId) {
11194
+ return this.rowByToolUseId.get(toolUseId)?.message ?? this.currentRow?.message ?? this.newestRowMessage();
11195
+ }
11196
+ publishRow(message) {
11197
+ const row = this.rowOfMessage.get(message);
11198
+ if (row)
11199
+ this.emitRow(row);
11200
+ }
11201
+ applyPartialAssistantMessage(partialMessage) {
11039
11202
  const streamEvent = partialMessage.event;
11040
- const eventType = streamEvent?.type;
11041
- const explicitParentTaskUseId = typeof partialMessage.parent_tool_use_id === "string" && partialMessage.parent_tool_use_id.length > 0 ? partialMessage.parent_tool_use_id : undefined;
11042
- if (eventType === "message_start") {
11043
- const apiMessageId = typeof streamEvent.message?.id === "string" ? streamEvent.message.id : undefined;
11044
- currentStreamApiMessageId = apiMessageId ?? null;
11045
- const isRootAssistant = isRootAssistantRecord(partialMessage.parent_tool_use_id, partialMessage.isSidechain);
11046
- const modelId = isRootAssistant ? normalizeBackendModelId(streamEvent.message?.model) : undefined;
11047
- if (apiMessageId) {
11048
- if (explicitParentTaskUseId) {
11049
- parentTaskByApiMessage.set(apiMessageId, explicitParentTaskUseId);
11050
- }
11051
- const row = rowForApiMessage(apiMessageId, isRootAssistant, explicitParentTaskUseId, modelId);
11052
- if (modelId)
11053
- row.modelId = modelId;
11054
- getBlocks(row, apiMessageId);
11203
+ switch (streamEvent?.type) {
11204
+ case "message_start":
11205
+ return this.applyMessageStart(partialMessage, streamEvent);
11206
+ case "message_stop":
11207
+ return this.applyMessageStop();
11208
+ case "content_block_start": {
11209
+ const target = this.locateStreamBlock(partialMessage, streamEvent);
11210
+ return target ? this.applyContentBlockStart(target, streamEvent.content_block) : false;
11055
11211
  }
11056
- return false;
11057
- }
11058
- if (eventType === "message_stop") {
11059
- currentStreamApiMessageId = null;
11060
- return false;
11212
+ case "content_block_delta": {
11213
+ const target = this.locateStreamBlock(partialMessage, streamEvent);
11214
+ return target ? this.applyContentBlockDelta(target, streamEvent.delta) : false;
11215
+ }
11216
+ default:
11217
+ this.locateStreamBlock(partialMessage, streamEvent);
11218
+ return false;
11061
11219
  }
11062
- const blockIndex = Number.isInteger(streamEvent?.index) && streamEvent.index >= 0 && streamEvent.index <= maxBlockIndex ? streamEvent.index : undefined;
11063
- if (blockIndex === undefined) {
11064
- return false;
11220
+ }
11221
+ applyMessageStart(partialMessage, streamEvent) {
11222
+ const apiMessageId = typeof streamEvent.message?.id === "string" ? streamEvent.message.id : undefined;
11223
+ this.currentStreamApiMessageId = apiMessageId ?? null;
11224
+ const isRootAssistant = isRootAssistantRecord(partialMessage.parent_tool_use_id, partialMessage.isSidechain);
11225
+ const modelId = isRootAssistant ? normalizeBackendModelId(streamEvent.message?.model) : undefined;
11226
+ if (apiMessageId) {
11227
+ const parentTaskUseId = explicitParentTaskUseId(partialMessage);
11228
+ if (parentTaskUseId)
11229
+ this.parentTaskByApiMessage.set(apiMessageId, parentTaskUseId);
11230
+ const row = this.rowForApiMessage(apiMessageId, isRootAssistant, parentTaskUseId, modelId);
11231
+ if (modelId)
11232
+ row.modelId = modelId;
11233
+ this.getBlocks(row, apiMessageId);
11065
11234
  }
11066
- const messageKey = currentStreamApiMessageId ?? (typeof partialMessage.uuid === "string" ? partialMessage.uuid : undefined);
11067
- if (!messageKey) {
11235
+ return false;
11236
+ }
11237
+ applyMessageStop() {
11238
+ this.currentStreamApiMessageId = null;
11239
+ return false;
11240
+ }
11241
+ locateStreamBlock(partialMessage, streamEvent) {
11242
+ const blockIndex = Number.isInteger(streamEvent?.index) && streamEvent.index >= 0 && streamEvent.index <= this.maxBlockIndex ? streamEvent.index : undefined;
11243
+ if (blockIndex === undefined)
11244
+ return null;
11245
+ const messageKey = this.currentStreamApiMessageId ?? (typeof partialMessage.uuid === "string" ? partialMessage.uuid : undefined);
11246
+ if (!messageKey)
11247
+ return null;
11248
+ const explicitParent = explicitParentTaskUseId(partialMessage);
11249
+ const parentTaskUseId = explicitParent ?? this.parentTaskByApiMessage.get(messageKey);
11250
+ if (explicitParent)
11251
+ this.parentTaskByApiMessage.set(messageKey, explicitParent);
11252
+ const row = this.rowForApiMessage(messageKey, false, parentTaskUseId);
11253
+ const blocks = this.getBlocks(row, messageKey);
11254
+ const target = this.blockTarget ??= { row, blocks, blockIndex, messageKey, parentTaskUseId };
11255
+ target.row = row;
11256
+ target.blocks = blocks;
11257
+ target.blockIndex = blockIndex;
11258
+ target.messageKey = messageKey;
11259
+ target.parentTaskUseId = parentTaskUseId;
11260
+ return target;
11261
+ }
11262
+ applyContentBlockStart(target, contentBlock) {
11263
+ let type;
11264
+ let value;
11265
+ if (contentBlock?.type === "text") {
11266
+ type = "text";
11267
+ value = typeof contentBlock.text === "string" ? contentBlock.text : "";
11268
+ } else if (contentBlock?.type === "thinking") {
11269
+ type = "thinking";
11270
+ value = typeof contentBlock.thinking === "string" ? contentBlock.thinking : "";
11271
+ } else {
11068
11272
  return false;
11069
11273
  }
11070
- const parentTaskUseId = explicitParentTaskUseId ?? parentTaskByApiMessage.get(messageKey);
11071
- if (explicitParentTaskUseId) {
11072
- parentTaskByApiMessage.set(messageKey, explicitParentTaskUseId);
11073
- }
11074
- const row = rowForApiMessage(messageKey, false, parentTaskUseId);
11075
- const entriesForMessage = getBlocks(row, messageKey);
11076
- let entry = entriesForMessage.get(blockIndex);
11077
- const appendStreamedDelta = (type, chunk) => {
11078
- if (entry?.type === type) {
11079
- entry.parentTaskUseId ??= parentTaskUseId;
11080
- (entry.pendingChunks ??= []).push(chunk);
11081
- return entry;
11082
- }
11083
- if (entry)
11084
- materializeEntryValue(entry);
11085
- return {
11086
- type,
11087
- value: `${entry?.value ?? ""}${chunk}`,
11088
- timestamp: entry?.timestamp ?? new Date().toISOString(),
11089
- messageUuid: messageKey,
11090
- parentTaskUseId: entry?.parentTaskUseId ?? parentTaskUseId
11091
- };
11092
- };
11093
- if (eventType === "content_block_start") {
11094
- const contentBlock = streamEvent.content_block;
11095
- if (contentBlock?.type === "text") {
11096
- entry = {
11097
- type: "text",
11098
- value: typeof contentBlock.text === "string" ? contentBlock.text : "",
11099
- timestamp: entry?.timestamp ?? new Date().toISOString(),
11100
- messageUuid: messageKey,
11101
- parentTaskUseId
11102
- };
11103
- } else if (contentBlock?.type === "thinking") {
11104
- entry = {
11105
- type: "thinking",
11106
- value: typeof contentBlock.thinking === "string" ? contentBlock.thinking : "",
11107
- timestamp: entry?.timestamp ?? new Date().toISOString(),
11108
- messageUuid: messageKey,
11109
- parentTaskUseId
11110
- };
11111
- } else {
11112
- return false;
11113
- }
11114
- } else if (eventType === "content_block_delta") {
11115
- const delta = streamEvent.delta;
11116
- if (delta?.type === "text_delta") {
11117
- entry = appendStreamedDelta("text", typeof delta.text === "string" ? delta.text : "");
11118
- } else if (delta?.type === "thinking_delta") {
11119
- entry = appendStreamedDelta("thinking", typeof delta.thinking === "string" ? delta.thinking : "");
11120
- } else {
11121
- return false;
11122
- }
11274
+ const existing = target.blocks.get(target.blockIndex);
11275
+ this.storeStreamedEntry(target, {
11276
+ type,
11277
+ value,
11278
+ timestamp: existing?.timestamp ?? new Date().toISOString(),
11279
+ messageUuid: target.messageKey,
11280
+ parentTaskUseId: target.parentTaskUseId
11281
+ });
11282
+ return true;
11283
+ }
11284
+ applyContentBlockDelta(target, delta) {
11285
+ if (delta?.type === "text_delta") {
11286
+ const chunk = typeof delta.text === "string" ? delta.text : "";
11287
+ this.storeStreamedEntry(target, this.appendStreamedDelta(target, "text", chunk));
11288
+ } else if (delta?.type === "thinking_delta") {
11289
+ const chunk = typeof delta.thinking === "string" ? delta.thinking : "";
11290
+ this.storeStreamedEntry(target, this.appendStreamedDelta(target, "thinking", chunk));
11123
11291
  } else {
11124
11292
  return false;
11125
11293
  }
11126
- entriesForMessage.set(blockIndex, entry);
11127
- scheduleStreamedAssistantMessageFlush(row);
11128
11294
  return true;
11129
- };
11130
- return {
11131
- get currentAssistantMessage() {
11132
- return currentRow?.message ?? null;
11133
- },
11134
- get assistantOutputSeen() {
11135
- return assistantOutputSeen;
11136
- },
11137
- toolTracker,
11138
- taskRegistry,
11139
- activeTaskIds,
11140
- get pendingPlanRejectionFeedback() {
11141
- return pendingPlanRejectionFeedback;
11142
- },
11143
- set pendingPlanRejectionFeedback(value) {
11144
- pendingPlanRejectionFeedback = value;
11145
- },
11146
- get planApprovedThisTurn() {
11147
- return planApprovedThisTurn;
11148
- },
11149
- set planApprovedThisTurn(value) {
11150
- planApprovedThisTurn = value;
11151
- },
11152
- get pendingPlanApprovalContinuation() {
11153
- return pendingPlanApprovalContinuation;
11154
- },
11155
- set pendingPlanApprovalContinuation(value) {
11156
- pendingPlanApprovalContinuation = value;
11157
- },
11158
- applyAssistantRecord,
11159
- refreshToolRows,
11160
- rowHoldingTool: (toolUseId) => rowByToolUseId.get(toolUseId)?.message ?? currentRow?.message ?? newestRowMessage(),
11161
- publishRow: (message) => {
11162
- const row = rowOfMessage.get(message);
11163
- if (row)
11164
- emitRow(row);
11165
- },
11166
- flushStreamedAssistantMessage,
11167
- applyPartialAssistantMessage,
11168
- beginPostSteerScope: flushStreamedAssistantMessage,
11169
- clearFlushTimer: () => {
11170
- if (streamEventFlushTimer) {
11171
- clearTimeout(streamEventFlushTimer);
11172
- streamEventFlushTimer = null;
11173
- }
11295
+ }
11296
+ appendStreamedDelta(target, type, chunk) {
11297
+ const entry = target.blocks.get(target.blockIndex);
11298
+ if (entry?.type === type) {
11299
+ entry.parentTaskUseId ??= target.parentTaskUseId;
11300
+ (entry.pendingChunks ??= []).push(chunk);
11301
+ return entry;
11174
11302
  }
11175
- };
11303
+ if (entry)
11304
+ materializeEntryValue(entry);
11305
+ return {
11306
+ type,
11307
+ value: `${entry?.value ?? ""}${chunk}`,
11308
+ timestamp: entry?.timestamp ?? new Date().toISOString(),
11309
+ messageUuid: target.messageKey,
11310
+ parentTaskUseId: entry?.parentTaskUseId ?? target.parentTaskUseId
11311
+ };
11312
+ }
11313
+ storeStreamedEntry(target, entry) {
11314
+ target.blocks.set(target.blockIndex, entry);
11315
+ this.scheduleStreamedAssistantMessageFlush(target.row);
11316
+ }
11317
+ beginPostSteerScope() {
11318
+ this.flushStreamedAssistantMessage();
11319
+ }
11320
+ clearFlushTimer() {
11321
+ if (this.streamEventFlushTimer) {
11322
+ clearTimeout(this.streamEventFlushTimer);
11323
+ this.streamEventFlushTimer = null;
11324
+ }
11325
+ }
11176
11326
  }
11177
11327
 
11178
11328
  // ../protocol/src/structured-output-text.ts
@@ -11711,6 +11861,7 @@ function captureTurnStartup(session) {
11711
11861
  abortController: session.abortController,
11712
11862
  error: session.error,
11713
11863
  interruption: session.interruption,
11864
+ interruptedRequestId: session.interruptedRequestId,
11714
11865
  lastActivity: session.lastActivity,
11715
11866
  persistedMessagesLoaded: session.persistedMessagesLoaded,
11716
11867
  structuredOutput: session.structuredOutput,
@@ -11769,7 +11920,10 @@ function createTurnState(session, sessionId, init) {
11769
11920
  settlingResult: false,
11770
11921
  localCommandOutputSeen: false,
11771
11922
  lastThinkingTokensEmitAt: 0,
11772
- compactedTokensBefore: undefined
11923
+ compactedTokensBefore: undefined,
11924
+ pendingPlanRejectionFeedback: null,
11925
+ planApprovedThisTurn: false,
11926
+ pendingPlanApprovalContinuation: null
11773
11927
  };
11774
11928
  const diagnostics = createBridgeDiagnostics("claude", {
11775
11929
  id: sessionId,
@@ -11880,6 +12034,7 @@ async function applyPromptPlanMode(admission, planMode) {
11880
12034
  session.turnStartedAt = beforeStartup.turnStartedAt;
11881
12035
  session.error = beforeStartup.error;
11882
12036
  session.interruption = beforeStartup.interruption;
12037
+ session.interruptedRequestId = beforeStartup.interruptedRequestId;
11883
12038
  session.lastActivity = beforeStartup.lastActivity;
11884
12039
  session.persistedMessagesLoaded = beforeStartup.persistedMessagesLoaded;
11885
12040
  if (needsTranscriptHydration)
@@ -11900,6 +12055,7 @@ function beginPromptActivity(admission) {
11900
12055
  const { session, sessionId } = admission;
11901
12056
  session.error = undefined;
11902
12057
  session.interruption = undefined;
12058
+ session.interruptedRequestId = undefined;
11903
12059
  session.lastActivity = new Date;
11904
12060
  if (session.promptSuggestion !== undefined) {
11905
12061
  session.promptSuggestion = undefined;
@@ -12332,7 +12488,7 @@ function commandBindingRevision(parts) {
12332
12488
  }
12333
12489
  return hash.toString(16).padStart(16, "0");
12334
12490
  }
12335
- function isRecord(value) {
12491
+ function isRecord2(value) {
12336
12492
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
12337
12493
  }
12338
12494
  function boundedIdentity(value, maxBytes) {
@@ -12352,7 +12508,7 @@ function displayCommandName(name) {
12352
12508
  return name.startsWith("/") || name.startsWith("$") ? name : `/${name}`;
12353
12509
  }
12354
12510
  function normalizeAvailability(value) {
12355
- if (!isRecord(value))
12511
+ if (!isRecord2(value))
12356
12512
  return;
12357
12513
  if (typeof value.state !== "string")
12358
12514
  return;
@@ -12367,7 +12523,7 @@ function normalizeAvailability(value) {
12367
12523
  };
12368
12524
  }
12369
12525
  function normalizeInputPolicy(value) {
12370
- if (!isRecord(value))
12526
+ if (!isRecord2(value))
12371
12527
  return;
12372
12528
  const policy = {};
12373
12529
  if (value.arguments === "none" || value.arguments === "optional" || value.arguments === "required") {
@@ -12382,7 +12538,7 @@ function normalizeInputPolicy(value) {
12382
12538
  return Object.keys(policy).length > 0 ? policy : undefined;
12383
12539
  }
12384
12540
  function normalizeCommandCataloguePayload(payload, options = {}) {
12385
- const record = isRecord(payload) ? payload : {};
12541
+ const record = isRecord2(payload) ? payload : {};
12386
12542
  const enhanced = record.catalogueVersion === NATIVE_AGENT_COMMAND_CATALOGUE_VERSION;
12387
12543
  const rows = Array.isArray(record.commands) ? record.commands : [];
12388
12544
  const byId = new Map;
@@ -12391,7 +12547,7 @@ function normalizeCommandCataloguePayload(payload, options = {}) {
12391
12547
  let aliasBudget = COMMAND_CATALOGUE_LIMITS.maxAliasesTotal;
12392
12548
  for (const candidate of rows.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
12393
12549
  const row = typeof candidate === "string" ? { name: candidate } : candidate;
12394
- if (!isRecord(row)) {
12550
+ if (!isRecord2(row)) {
12395
12551
  rejected += 1;
12396
12552
  continue;
12397
12553
  }
@@ -12471,7 +12627,8 @@ function normalizeCommandCataloguePayload(payload, options = {}) {
12471
12627
  ...status ? { status } : {},
12472
12628
  ...revision !== undefined ? { revision } : {},
12473
12629
  ...generation ? { generation } : {},
12474
- ...freshness ? { freshness } : {}
12630
+ ...freshness ? { freshness } : {},
12631
+ ...enhanced && status === "stale" && record.attachPending === true ? { attachPending: true } : {}
12475
12632
  };
12476
12633
  }
12477
12634
  var LEGACY_ID_PREFIX = "legacy:";
@@ -12484,7 +12641,7 @@ function commandCatalogueBytes(commands) {
12484
12641
  function parseBridgeCommandInvocation(value) {
12485
12642
  if (value === undefined || value === null)
12486
12643
  return { ok: true };
12487
- if (!isRecord(value))
12644
+ if (!isRecord2(value))
12488
12645
  return { ok: false, error: "command must be an object" };
12489
12646
  const id = boundedIdentity(value.id, COMMAND_CATALOGUE_LIMITS.maxIdBytes);
12490
12647
  const name = boundedIdentity(value.name, COMMAND_CATALOGUE_LIMITS.maxNameBytes);
@@ -12687,6 +12844,40 @@ function redactSecrets(value) {
12687
12844
  return result;
12688
12845
  }
12689
12846
 
12847
+ // ../../bridges/claude-bridge/src/services/mcp-runtime-status.ts
12848
+ function mcpScope(scope, name, source) {
12849
+ if (name === "orkestrator")
12850
+ return "orkestrator";
12851
+ if (source?.includes("plugin") || scope?.includes("plugin"))
12852
+ return "plugin";
12853
+ if (scope === "project" || scope === "local")
12854
+ return scope;
12855
+ return "user";
12856
+ }
12857
+ function mcpRuntimeState(status) {
12858
+ switch (status) {
12859
+ case "connected":
12860
+ case "failed":
12861
+ case "needs-auth":
12862
+ case "disabled":
12863
+ return status;
12864
+ case "pending":
12865
+ return "connecting";
12866
+ default:
12867
+ return "unknown";
12868
+ }
12869
+ }
12870
+ function initMcpServerStatus(raw) {
12871
+ const source = typeof raw.source === "string" ? raw.source : undefined;
12872
+ return {
12873
+ name: raw.name,
12874
+ status: mcpRuntimeState(raw.status),
12875
+ ...source ? { scope: mcpScope(source, raw.name) } : {},
12876
+ ...typeof raw.error === "string" && raw.error ? { error: raw.error } : {},
12877
+ ...Array.isArray(raw.tools) ? { tools: raw.tools.filter((tool) => typeof tool === "string") } : {}
12878
+ };
12879
+ }
12880
+
12690
12881
  // ../../bridges/claude-bridge/src/services/claude-sign-in.ts
12691
12882
  var AUTH_CACHE_TTL_MS = 30000;
12692
12883
  var FAILED_AUTH_CACHE_TTL_MS = 5000;
@@ -12935,15 +13126,6 @@ function createProbe() {
12935
13126
  ...claudeExecutableOptions()
12936
13127
  });
12937
13128
  }
12938
- function mcpScope(scope, name) {
12939
- if (name === "orkestrator")
12940
- return "orkestrator";
12941
- if (scope?.includes("plugin"))
12942
- return "plugin";
12943
- if (scope === "project" || scope === "local")
12944
- return "project";
12945
- return "user";
12946
- }
12947
13129
  function mcpTransport(status) {
12948
13130
  const type = status.config?.type;
12949
13131
  if (type === "stdio" || type === "sse" || type === "http")
@@ -12966,7 +13148,7 @@ function normalizeMcp(status) {
12966
13148
  id: status.name,
12967
13149
  name: status.serverInfo?.name || status.name,
12968
13150
  status: mappedStatus,
12969
- scope: mcpScope(status.scope, status.name),
13151
+ scope: mcpScope(status.scope, status.name, status.source),
12970
13152
  ...transport ? { transport } : {},
12971
13153
  toolCount: tools.length,
12972
13154
  ...tools.length ? { tools } : {},
@@ -13003,8 +13185,12 @@ async function performSessionMcpAction(sessionId, serverId, action) {
13003
13185
  const session = sessions.get(sessionId);
13004
13186
  if (!session)
13005
13187
  throw new Error("Session not found");
13188
+ const live = readableControl(session);
13189
+ if (!live && action !== "reconnect" && action !== "enable" && action !== "disable") {
13190
+ throw new Error("Claude can sign in to an MCP server only while this session's Claude is running. Send a message, then sign in again; the sign-in link appears as a card in the conversation.");
13191
+ }
13006
13192
  let probe;
13007
- const control = readableControl(session) ?? (probe = createProbe());
13193
+ const control = live ?? (probe = createProbe());
13008
13194
  try {
13009
13195
  if (action === "reconnect")
13010
13196
  await control.reconnectMcpServer?.(serverId);
@@ -13248,7 +13434,7 @@ function boundedMessage(error, fallback) {
13248
13434
  const message = error instanceof Error && error.message ? error.message : fallback;
13249
13435
  return truncateUtf8(message, MAX_MESSAGE_BYTES);
13250
13436
  }
13251
- function isRecord2(value) {
13437
+ function isRecord3(value) {
13252
13438
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
13253
13439
  }
13254
13440
  function sdkRows(value) {
@@ -13256,7 +13442,7 @@ function sdkRows(value) {
13256
13442
  return [];
13257
13443
  const rows = [];
13258
13444
  for (const candidate of value.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
13259
- if (!isRecord2(candidate) || typeof candidate.name !== "string" || !candidate.name)
13445
+ if (!isRecord3(candidate) || typeof candidate.name !== "string" || !candidate.name)
13260
13446
  continue;
13261
13447
  rows.push(candidate);
13262
13448
  }
@@ -13438,7 +13624,7 @@ function recordInitCommandInventory(session) {
13438
13624
  reannotate(session);
13439
13625
  }
13440
13626
  function recordCommandsChanged(session, message) {
13441
- if (!isRecord2(message) || !Array.isArray(message.commands))
13627
+ if (!isRecord3(message) || !Array.isArray(message.commands))
13442
13628
  return;
13443
13629
  applyInventory(session, "replacement", sdkRows(message.commands));
13444
13630
  }
@@ -13587,10 +13773,10 @@ async function refreshClaudeCommandCatalogue(sessionId) {
13587
13773
  else
13588
13774
  failures.push(`${label}: ${boundedMessage(result.reason, "reload failed")}`);
13589
13775
  }
13590
- if (skills.status === "fulfilled" && isRecord2(skills.value)) {
13776
+ if (skills.status === "fulfilled" && isRecord3(skills.value)) {
13591
13777
  session.commandSkillNames = sdkRows(skills.value.skills).map((skill) => skill.name);
13592
13778
  }
13593
- const errorCount = plugins.status === "fulfilled" && isRecord2(plugins.value) ? plugins.value.error_count : undefined;
13779
+ const errorCount = plugins.status === "fulfilled" && isRecord3(plugins.value) ? plugins.value.error_count : undefined;
13594
13780
  if (typeof errorCount === "number" && errorCount > 0) {
13595
13781
  failures.push(`plugins: ${errorCount} failed to load`);
13596
13782
  }
@@ -13961,6 +14147,132 @@ function backgroundCommandInstruction(env) {
13961
14147
  // ../../bridges/claude-bridge/src/services/claude-execution-sandbox.ts
13962
14148
  import { realpathSync } from "node:fs";
13963
14149
 
14150
+ // ../protocol/src/node/claude-linux-sandbox.ts
14151
+ import { spawnSync } from "node:child_process";
14152
+ import { accessSync, constants as constants2, existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
14153
+ import { delimiter as delimiter2, join as join11 } from "node:path";
14154
+ var CLAUDE_LINUX_SANDBOX_PROGRAMS = Object.freeze(["bwrap", "socat"]);
14155
+ var APPARMOR_BWRAP_PROFILE = "/etc/apparmor.d/bwrap-userns-restrict";
14156
+ var CLAUDE_LINUX_SANDBOX_PROBE_TIMEOUT_MS = 5000;
14157
+ var CLAUDE_LINUX_SANDBOX_PROBE_TTL_MS = 5 * 60000;
14158
+ var BWRAP_ARGS = Object.freeze([
14159
+ "--new-session",
14160
+ "--die-with-parent",
14161
+ "--unshare-user",
14162
+ "--unshare-pid",
14163
+ "--ro-bind",
14164
+ "/",
14165
+ "/",
14166
+ "--dev",
14167
+ "/dev",
14168
+ "--proc",
14169
+ "/proc",
14170
+ "--"
14171
+ ]);
14172
+ var NESTED_ARGS = Object.freeze([
14173
+ "unshare",
14174
+ "--user",
14175
+ "--map-root-user",
14176
+ "--pid",
14177
+ "--mount",
14178
+ "--fork",
14179
+ "--mount-proc",
14180
+ "true"
14181
+ ]);
14182
+ function executableOnPath(name, path = process.env.PATH) {
14183
+ for (const directory of (path ?? "").split(delimiter2)) {
14184
+ if (!directory)
14185
+ continue;
14186
+ try {
14187
+ accessSync(join11(directory, name), constants2.X_OK);
14188
+ return true;
14189
+ } catch {}
14190
+ }
14191
+ return false;
14192
+ }
14193
+ function runProbeCommand(command, args) {
14194
+ const result = spawnSync(command, [...args], {
14195
+ timeout: CLAUDE_LINUX_SANDBOX_PROBE_TIMEOUT_MS,
14196
+ killSignal: "SIGKILL",
14197
+ stdio: ["ignore", "ignore", "pipe"],
14198
+ encoding: "utf8",
14199
+ maxBuffer: 64 * 1024
14200
+ });
14201
+ if (result.error)
14202
+ return { ok: false, stderr: result.error.message };
14203
+ return { ok: result.status === 0, stderr: result.stderr ?? "" };
14204
+ }
14205
+ function appArmorBwrapProfileInstalled() {
14206
+ try {
14207
+ const enabled = readFileSync3("/sys/module/apparmor/parameters/enabled", "utf8");
14208
+ return enabled.trim().startsWith("Y") && existsSync2(APPARMOR_BWRAP_PROFILE);
14209
+ } catch {
14210
+ return false;
14211
+ }
14212
+ }
14213
+ function probeDetail(stderr) {
14214
+ const line = stderr.split(`
14215
+ `).map((entry) => entry.trim()).find(Boolean) ?? "no error output";
14216
+ return line.length > 200 ? `${line.slice(0, 199)}…` : line;
14217
+ }
14218
+ var UNAVAILABLE = "Claude's command sandbox cannot run commands on this host";
14219
+ function probeClaudeLinuxSandbox(dependencies = {}) {
14220
+ const onPath = dependencies.onPath ?? ((name) => executableOnPath(name));
14221
+ const run = dependencies.run ?? runProbeCommand;
14222
+ const appArmorBwrapProfile = dependencies.appArmorBwrapProfile ?? appArmorBwrapProfileInstalled;
14223
+ const missing = CLAUDE_LINUX_SANDBOX_PROGRAMS.filter((name) => !onPath(name));
14224
+ if (missing.length > 0) {
14225
+ return {
14226
+ available: false,
14227
+ failure: "missing-programs",
14228
+ reason: `${UNAVAILABLE}: the Claude CLI's Linux sandbox needs bubblewrap and socat on PATH, and ${missing.join(" and ")} ${missing.length === 1 ? "is" : "are"} missing. Install the bubblewrap and socat packages.`
14229
+ };
14230
+ }
14231
+ if (!onPath("unshare")) {
14232
+ return {
14233
+ available: false,
14234
+ failure: "probe-unavailable",
14235
+ reason: `${UNAVAILABLE}: whether a user namespace can be nested inside bubblewrap's, which the Claude CLI needs, could not be checked because unshare (util-linux) is not on PATH.`
14236
+ };
14237
+ }
14238
+ const nested = run("bwrap", [...BWRAP_ARGS, ...NESTED_ARGS]);
14239
+ if (nested.ok)
14240
+ return { available: true };
14241
+ const outer = run("bwrap", [...BWRAP_ARGS, "true"]);
14242
+ if (!outer.ok) {
14243
+ return {
14244
+ available: false,
14245
+ failure: "user-namespace",
14246
+ reason: `${UNAVAILABLE}: bubblewrap cannot create a user namespace (${probeDetail(outer.stderr)}). Unprivileged user namespaces are turned off or restricted here, for example by kernel.unprivileged_userns_clone, AppArmor's kernel.apparmor_restrict_unprivileged_userns, or a container's seccomp profile.`
14247
+ };
14248
+ }
14249
+ const detail = probeDetail(nested.stderr);
14250
+ if (appArmorBwrapProfile()) {
14251
+ return {
14252
+ available: false,
14253
+ failure: "nested-user-namespace",
14254
+ reason: `${UNAVAILABLE}: bubblewrap starts, but AppArmor's bwrap-userns-restrict profile strips capabilities from the user namespace the Claude CLI nests inside it, so every sandboxed command would fail (${detail}). Allow nested namespaces for bubblewrap with a local AppArmor profile, or remove bwrap-userns-restrict on a dedicated host; Orkestrator does not change AppArmor policy itself.`
14255
+ };
14256
+ }
14257
+ return {
14258
+ available: false,
14259
+ failure: "nested-user-namespace",
14260
+ reason: `${UNAVAILABLE}: bubblewrap starts, but a user namespace nested inside it is refused (${detail}), and the Claude CLI's command helper needs one, so every sandboxed command would fail. In a container this is usually the container's seccomp profile or missing capabilities.`
14261
+ };
14262
+ }
14263
+ var cached;
14264
+ var pinned;
14265
+ function claudeLinuxSandboxStatus(now = Date.now()) {
14266
+ if (pinned)
14267
+ return pinned;
14268
+ if (cached && now - cached.at >= 0 && now - cached.at < CLAUDE_LINUX_SANDBOX_PROBE_TTL_MS) {
14269
+ return cached.status;
14270
+ }
14271
+ const status = probeClaudeLinuxSandbox();
14272
+ cached = { status, at: now };
14273
+ return status;
14274
+ }
14275
+
13964
14276
  // ../protocol/src/coordinator.ts
13965
14277
  var COORDINATOR_COMMAND_SANDBOX_ENV = "ORKESTRATOR_COORDINATOR_COMMAND_SANDBOX";
13966
14278
  function coordinatorCommandSandboxFromEnv(env) {
@@ -13973,7 +14285,9 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
13973
14285
  "adopt_workflow",
13974
14286
  "cancel_build_pipeline",
13975
14287
  "cancel_multi_review",
14288
+ "cancel_schedule",
13976
14289
  "compare_project_files",
14290
+ "create_schedule",
13977
14291
  "create_ticket",
13978
14292
  "get_build_pipeline",
13979
14293
  "get_environment",
@@ -13995,6 +14309,7 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
13995
14309
  "list_node_environments",
13996
14310
  "list_nodes",
13997
14311
  "list_projects",
14312
+ "list_schedules",
13998
14313
  "list_tabs",
13999
14314
  "list_tickets",
14000
14315
  "list_workflows",
@@ -14052,6 +14367,16 @@ function claudeExecutionSandbox(policy, asksForApproval, readOnly = {}) {
14052
14367
  }
14053
14368
  };
14054
14369
  }
14370
+ function claudeSandboxRefusal(policy, options = {}) {
14371
+ if ((options.platform ?? process.platform) !== "linux" || !policy)
14372
+ return;
14373
+ const launcher = "commandSandbox" in options ? options.commandSandbox : process.env[COORDINATOR_COMMAND_SANDBOX_ENV];
14374
+ const required = isCoordinatorReadOnlyPolicy(policy) ? launcher !== undefined && launcher !== "unavailable" : policy.sandbox === "provider";
14375
+ if (!required)
14376
+ return;
14377
+ const status = (options.linuxSandbox ?? claudeLinuxSandboxStatus)();
14378
+ return status.available ? undefined : status.reason;
14379
+ }
14055
14380
 
14056
14381
  // ../protocol/src/agent-interaction-deadline.ts
14057
14382
  var SYSTEM_CLOCK = {
@@ -14813,14 +15138,19 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
14813
15138
  const effortLevel = options?.effort ?? "high";
14814
15139
  const cwd = claudeWorkspaceCwd();
14815
15140
  const policy = claudeTurnPolicy(session.executionPolicy, options?.readOnly);
15141
+ const sandboxRefusal = claudeSandboxRefusal(policy);
15142
+ if (sandboxRefusal)
15143
+ throw sessionOperationError("invalid", sandboxRefusal);
14816
15144
  session.commandDiscoveryInputs = {
14817
15145
  readOnly: options?.readOnly === true,
14818
15146
  includeLocalSettings: options?.includeLocalSettings === true
14819
15147
  };
14820
15148
  const includeProjectResources = policy?.projectResources !== false;
15149
+ const settingSources = claudeTurnSettingSources(session, options);
15150
+ session.commandDiscoveryInputs.includeLocalSettings = settingSources.includes("local");
14821
15151
  const [{ servers: mcpServers, names: mcpServerNames, revision: mcpConfigRevision }, plugins] = await Promise.all([
14822
15152
  getMcpRuntimeConfig(cwd, process.env, options?.agentMcp, mcpSourceScopeForPolicy(policy)),
14823
- getPluginsForSdk(cwd, includeProjectResources)
15153
+ getPluginsForSdk(cwd, includeProjectResources, settingSources)
14824
15154
  ]);
14825
15155
  turn.mcpServerNames = mcpServerNames;
14826
15156
  const mcpServerCount = Object.keys(mcpServers).length;
@@ -14885,6 +15215,7 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
14885
15215
  });
14886
15216
  const mcpServersKey = createHash10("sha256").update(JSON.stringify(mcpServers)).digest("hex");
14887
15217
  return {
15218
+ settingSources,
14888
15219
  cwd,
14889
15220
  policy,
14890
15221
  effortLevel,
@@ -14968,7 +15299,6 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
14968
15299
  } : { type: "adaptive", display: "summarized" },
14969
15300
  ...effectiveClaudeContext1m(options) ? { betas: ["context-1m-2025-08-07"] } : {},
14970
15301
  extraArgs: effectiveClaudeChrome(policy, options) ? { chrome: null } : { "no-chrome": null },
14971
- ...typeof options?.maxBudgetUsd === "number" && options.maxBudgetUsd > 0 ? { maxBudgetUsd: options.maxBudgetUsd } : {},
14972
15302
  includePartialMessages: true,
14973
15303
  forwardSubagentText: true,
14974
15304
  allowedTools,
@@ -14996,7 +15326,7 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
14996
15326
 
14997
15327
  `)
14998
15328
  },
14999
- settingSources: claudeSettingSources(policy, options?.includeLocalSettings),
15329
+ settingSources: plan.settingSources,
15000
15330
  ...(fastMode || ultracode) && {
15001
15331
  settings: {
15002
15332
  ...fastMode && { fastMode: true },
@@ -15022,6 +15352,7 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
15022
15352
  PostCompact: [{ hooks: [latestTurnHook((target) => target.recordPostCompact)] }]
15023
15353
  },
15024
15354
  includeHookEvents: true,
15355
+ toolConfig: { askUserQuestion: { previewFormat: "markdown" } },
15025
15356
  canUseTool: (toolName, input, permissionContext) => turnFor(permissionContext?.toolUseID).canUseTool(toolName, input, permissionContext),
15026
15357
  onElicitation: (request, elicitationOptions) => answerClaudeElicitation(session, sessionId, request, elicitationOptions),
15027
15358
  onUserDialog: async () => ({ behavior: "cancelled" })
@@ -15162,11 +15493,11 @@ function handleAssistantMessage(turn, message) {
15162
15493
  state: "success"
15163
15494
  });
15164
15495
  reclaimReleasedTurnForAssistant(turn, message);
15165
- if (stream.pendingPlanRejectionFeedback) {
15496
+ if (turn.pendingPlanRejectionFeedback) {
15166
15497
  debugLog("[session-manager] Claude responded after plan denial, clearing re-prompt feedback", {
15167
15498
  sessionId
15168
15499
  });
15169
- stream.pendingPlanRejectionFeedback = null;
15500
+ turn.pendingPlanRejectionFeedback = null;
15170
15501
  }
15171
15502
  const { orderedParts, newTaskIds, contentBlockCount } = parseMessageContent(message, toolTracker, turn.mcpServerNames, activeTaskIds, taskRegistry, receivedAt, recordUnknownContentBlock);
15172
15503
  const evidenceParent = message.parent_tool_use_id;
@@ -15309,12 +15640,7 @@ async function handleInitMessage(turn, message) {
15309
15640
  }
15310
15641
  const allMcpServers = initMsg.mcp_servers || [];
15311
15642
  const regularMcpServers = allMcpServers.filter((s) => !s.name?.startsWith("plugin:"));
15312
- const mcpServerStatuses = regularMcpServers.map((s) => ({
15313
- name: s.name,
15314
- status: s.status === "connected" ? "connected" : "failed",
15315
- error: s.error,
15316
- tools: s.tools
15317
- }));
15643
+ const mcpServerStatuses = regularMcpServers.map(initMcpServerStatus);
15318
15644
  const { statuses: pluginStatuses, errors: pluginErrors } = pluginStatusesFromInit(initMsg);
15319
15645
  const previousPluginErrors = new Set(session.pluginErrorSignatures);
15320
15646
  session.pluginErrorSignatures = pluginErrors.map(({ plugin, type, message, path, loaded }) => JSON.stringify([plugin, type, message, path, loaded]));
@@ -15686,14 +16012,6 @@ async function handleResultMessage(turn, message) {
15686
16012
  createdAt: new Date().toISOString()
15687
16013
  });
15688
16014
  }
15689
- if (resultMsg.subtype === "error_max_budget_usd") {
15690
- appendTranscriptNotice(session, sessionId, (event) => eventEmitter.emit(event), {
15691
- type: "status",
15692
- content: "Claude stopped because this session reached its spending cap.",
15693
- severity: "warning",
15694
- createdAt: new Date().toISOString()
15695
- });
15696
- }
15697
16015
  if (turn.options?.outputSchema) {
15698
16016
  const failure = structuredOutputFailure("claude", resultMsg.subtype === "error_max_structured_output_retries" ? "schema_retry_exhausted" : "provider_error", resultError, {
15699
16017
  requestId: turn.structuredRequestId,
@@ -16140,7 +16458,7 @@ function handleUserMessage(turn, message) {
16140
16458
  for (const taskId of completedTaskIds) {
16141
16459
  activeTaskIds.delete(taskId);
16142
16460
  }
16143
- if (stream.planApprovedThisTurn) {
16461
+ if (turn.planApprovedThisTurn) {
16144
16462
  for (const tool of toolTracker.getTools()) {
16145
16463
  if (tool.toolName === "ExitPlanMode" && tool.toolState === "failure" && tool.toolUseId) {
16146
16464
  console.warn("[session-manager] ExitPlanMode reported failure despite user approval — overriding to success and scheduling continuation re-prompt", { sessionId, toolUseId: tool.toolUseId, sdkError: tool.toolError });
@@ -16149,8 +16467,8 @@ function handleUserMessage(turn, message) {
16149
16467
  output: "Plan approved by the user. Proceeding with implementation.",
16150
16468
  error: undefined
16151
16469
  });
16152
- if (!stream.pendingPlanApprovalContinuation) {
16153
- stream.pendingPlanApprovalContinuation = "The user has approved your plan. Please proceed with implementing it now. You are no longer in plan mode and may write, edit, and run commands as needed.";
16470
+ if (!turn.pendingPlanApprovalContinuation) {
16471
+ turn.pendingPlanApprovalContinuation = "The user has approved your plan. Please proceed with implementing it now. You are no longer in plan mode and may write, edit, and run commands as needed.";
16154
16472
  }
16155
16473
  }
16156
16474
  }
@@ -16226,214 +16544,223 @@ function boundedPlan(content) {
16226
16544
  planTruncated: true
16227
16545
  };
16228
16546
  }
16229
- async function turnCanUseTool(turn, toolName, input, permissionContext) {
16230
- const { session, sessionId, stream } = turn;
16231
- if (toolName === "AskUserQuestion") {
16232
- const questions = Array.isArray(input.questions) ? input.questions : [];
16233
- const questionTexts = questions.map((question) => question.question);
16234
- if (new Set(questionTexts).size !== questionTexts.length) {
16235
- return {
16236
- behavior: "deny",
16237
- message: "AskUserQuestion contains duplicate question text. Ask the questions again with distinct wording."
16238
- };
16239
- }
16240
- const questionId = permissionContext?.requestId || generateMessageId();
16241
- const requestedAt = Date.now();
16242
- const questionRequest = {
16243
- id: questionId,
16244
- sessionId,
16245
- questions,
16246
- toolUseId: permissionContext?.toolUseID ?? questionId,
16247
- requestedAt,
16248
- expiresAt: requestedAt + QUESTION_TIMEOUT_MS
16547
+ async function askUserQuestion(turn, input, permissionContext) {
16548
+ const { sessionId } = turn;
16549
+ const questions = Array.isArray(input.questions) ? input.questions : [];
16550
+ const questionTexts = questions.map((question) => question.question);
16551
+ if (new Set(questionTexts).size !== questionTexts.length) {
16552
+ return {
16553
+ behavior: "deny",
16554
+ message: "AskUserQuestion contains duplicate question text. Ask the questions again with distinct wording."
16249
16555
  };
16250
- if (!canAdmitInteraction(questionRequest, "questions")) {
16251
- return {
16252
- behavior: "deny",
16253
- message: "Too many pending interactions. Answer an existing request first."
16254
- };
16255
- }
16256
- let rejectOnExpiry;
16257
- const timeoutPromise = new Promise((_, reject) => {
16258
- rejectOnExpiry = reject;
16259
- });
16260
- const deadline = new InteractionDeadline({
16261
- requestedAt,
16262
- windowMs: QUESTION_TIMEOUT_MS,
16263
- onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Question", deadline)))
16264
- });
16265
- const releaseDeadline = registerInteractionDeadline("questions", questionRequest, deadline);
16266
- pendingQuestions.set(questionId, questionRequest);
16267
- eventEmitter.emit({
16268
- type: "question.asked",
16269
- sessionId,
16270
- data: questionRequest
16271
- });
16272
- const answerPromise = new Promise((resolve, reject) => {
16273
- questionResolvers.set(questionId, { resolve, reject });
16556
+ }
16557
+ const questionId = permissionContext?.requestId || generateMessageId();
16558
+ const requestedAt = Date.now();
16559
+ const questionRequest = {
16560
+ id: questionId,
16561
+ sessionId,
16562
+ questions,
16563
+ toolUseId: permissionContext?.toolUseID ?? questionId,
16564
+ requestedAt,
16565
+ expiresAt: requestedAt + QUESTION_TIMEOUT_MS
16566
+ };
16567
+ if (!canAdmitInteraction(questionRequest, "questions")) {
16568
+ return {
16569
+ behavior: "deny",
16570
+ message: "Too many pending interactions. Answer an existing request first."
16571
+ };
16572
+ }
16573
+ let rejectOnExpiry;
16574
+ const timeoutPromise = new Promise((_, reject) => {
16575
+ rejectOnExpiry = reject;
16576
+ });
16577
+ const deadline = new InteractionDeadline({
16578
+ requestedAt,
16579
+ windowMs: QUESTION_TIMEOUT_MS,
16580
+ onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Question", deadline)))
16581
+ });
16582
+ const releaseDeadline = registerInteractionDeadline("questions", questionRequest, deadline);
16583
+ pendingQuestions.set(questionId, questionRequest);
16584
+ eventEmitter.emit({
16585
+ type: "question.asked",
16586
+ sessionId,
16587
+ data: questionRequest
16588
+ });
16589
+ const answerPromise = new Promise((resolve, reject) => {
16590
+ questionResolvers.set(questionId, { resolve, reject });
16591
+ });
16592
+ try {
16593
+ const answers = await Promise.race([answerPromise, timeoutPromise]);
16594
+ console.log("[session-manager] Received question answers", {
16595
+ questionId,
16596
+ answerCount: Object.keys(answers).length
16274
16597
  });
16275
- try {
16276
- const answers = await Promise.race([answerPromise, timeoutPromise]);
16277
- console.log("[session-manager] Received question answers", {
16278
- questionId,
16279
- answerCount: Object.keys(answers).length
16280
- });
16281
- return {
16282
- behavior: "allow",
16283
- updatedInput: {
16284
- questions: input.questions,
16285
- answers
16286
- }
16287
- };
16288
- } catch (error) {
16289
- console.error("[session-manager] Error waiting for answer:", error);
16290
- const message = error instanceof Error ? error.message : "Question was cancelled";
16291
- if (pendingQuestions.has(questionId)) {
16292
- eventEmitter.emit({
16293
- type: "question.answered",
16294
- sessionId,
16295
- data: { requestId: questionId, cancelled: true }
16296
- });
16598
+ return {
16599
+ behavior: "allow",
16600
+ updatedInput: {
16601
+ questions: input.questions,
16602
+ answers
16297
16603
  }
16298
- return { behavior: "deny", message };
16299
- } finally {
16300
- releaseDeadline();
16301
- pendingQuestions.delete(questionId);
16302
- questionResolvers.delete(questionId);
16604
+ };
16605
+ } catch (error) {
16606
+ console.error("[session-manager] Error waiting for answer:", error);
16607
+ const message = error instanceof Error ? error.message : "Question was cancelled";
16608
+ if (pendingQuestions.has(questionId)) {
16609
+ eventEmitter.emit({
16610
+ type: "question.answered",
16611
+ sessionId,
16612
+ data: { requestId: questionId, cancelled: true }
16613
+ });
16303
16614
  }
16615
+ return { behavior: "deny", message };
16616
+ } finally {
16617
+ releaseDeadline();
16618
+ pendingQuestions.delete(questionId);
16619
+ questionResolvers.delete(questionId);
16304
16620
  }
16305
- if (toolName === "EnterPlanMode") {
16306
- debugLog("[session-manager] EnterPlanMode requested", { sessionId });
16307
- try {
16308
- await applySessionPlanMode(session, true);
16309
- } catch (error) {
16310
- const message = error instanceof Error ? error.message : "Failed to persist plan mode";
16311
- return {
16312
- behavior: "deny",
16313
- message: `Plan mode could not be persisted safely: ${message}`
16314
- };
16315
- }
16316
- eventEmitter.emit({
16317
- type: "plan.enter-requested",
16318
- sessionId,
16319
- data: { sessionId }
16320
- });
16621
+ }
16622
+ async function enterPlanMode(turn, input) {
16623
+ const { session, sessionId } = turn;
16624
+ debugLog("[session-manager] EnterPlanMode requested", { sessionId });
16625
+ try {
16626
+ await applySessionPlanMode(session, true);
16627
+ } catch (error) {
16628
+ const message = error instanceof Error ? error.message : "Failed to persist plan mode";
16321
16629
  return {
16322
- behavior: "allow",
16323
- updatedInput: input
16630
+ behavior: "deny",
16631
+ message: `Plan mode could not be persisted safely: ${message}`
16324
16632
  };
16325
16633
  }
16326
- if (toolName === "ExitPlanMode") {
16327
- debugLog("[session-manager] ExitPlanMode requested, waiting for user approval", {
16328
- sessionId
16329
- });
16330
- const approvalId = permissionContext?.requestId || generateMessageId();
16331
- const directPlan = boundedPlan(input?.plan);
16332
- const requestedAt = Date.now();
16333
- const approvalRequest = {
16334
- id: approvalId,
16335
- sessionId,
16336
- toolUseId: permissionContext?.toolUseID ?? approvalId,
16337
- ...directPlan,
16338
- requestedAt,
16339
- expiresAt: requestedAt + PLAN_APPROVAL_TIMEOUT_MS
16634
+ eventEmitter.emit({
16635
+ type: "plan.enter-requested",
16636
+ sessionId,
16637
+ data: { sessionId }
16638
+ });
16639
+ return {
16640
+ behavior: "allow",
16641
+ updatedInput: input
16642
+ };
16643
+ }
16644
+ async function requestPlanApproval(turn, input, permissionContext) {
16645
+ const { session, sessionId } = turn;
16646
+ debugLog("[session-manager] ExitPlanMode requested, waiting for user approval", {
16647
+ sessionId
16648
+ });
16649
+ const approvalId = permissionContext?.requestId || generateMessageId();
16650
+ const directPlan = boundedPlan(input?.plan);
16651
+ const requestedAt = Date.now();
16652
+ const approvalRequest = {
16653
+ id: approvalId,
16654
+ sessionId,
16655
+ toolUseId: permissionContext?.toolUseID ?? approvalId,
16656
+ ...directPlan,
16657
+ requestedAt,
16658
+ expiresAt: requestedAt + PLAN_APPROVAL_TIMEOUT_MS
16659
+ };
16660
+ if (!canAdmitInteraction(approvalRequest, "approvals")) {
16661
+ return {
16662
+ behavior: "deny",
16663
+ message: "Too many pending interactions. Answer an existing request first."
16340
16664
  };
16341
- if (!canAdmitInteraction(approvalRequest, "approvals")) {
16342
- return {
16343
- behavior: "deny",
16344
- message: "Too many pending interactions. Answer an existing request first."
16345
- };
16346
- }
16347
- let rejectOnExpiry;
16348
- const timeoutPromise = new Promise((_, reject) => {
16349
- rejectOnExpiry = reject;
16350
- });
16351
- const deadline = new InteractionDeadline({
16352
- requestedAt,
16353
- windowMs: PLAN_APPROVAL_TIMEOUT_MS,
16354
- onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Plan approval", deadline)))
16355
- });
16356
- const releaseDeadline = registerInteractionDeadline("plan-approvals", approvalRequest, deadline);
16357
- pendingPlanApprovals.set(approvalId, approvalRequest);
16358
- const approvalPromise = new Promise((resolve, reject) => {
16359
- planApprovalResolvers.set(approvalId, { resolve, reject });
16360
- });
16361
- eventEmitter.emit({
16362
- type: "plan.approval-requested",
16363
- sessionId,
16364
- data: approvalRequest
16665
+ }
16666
+ let rejectOnExpiry;
16667
+ const timeoutPromise = new Promise((_, reject) => {
16668
+ rejectOnExpiry = reject;
16669
+ });
16670
+ const deadline = new InteractionDeadline({
16671
+ requestedAt,
16672
+ windowMs: PLAN_APPROVAL_TIMEOUT_MS,
16673
+ onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Plan approval", deadline)))
16674
+ });
16675
+ const releaseDeadline = registerInteractionDeadline("plan-approvals", approvalRequest, deadline);
16676
+ pendingPlanApprovals.set(approvalId, approvalRequest);
16677
+ const approvalPromise = new Promise((resolve, reject) => {
16678
+ planApprovalResolvers.set(approvalId, { resolve, reject });
16679
+ });
16680
+ eventEmitter.emit({
16681
+ type: "plan.approval-requested",
16682
+ sessionId,
16683
+ data: approvalRequest
16684
+ });
16685
+ try {
16686
+ const response = await Promise.race([approvalPromise, timeoutPromise]);
16687
+ console.log("[session-manager] Plan approval result", {
16688
+ approvalId,
16689
+ approved: response.approved,
16690
+ hasFeedback: typeof response.feedback === "string" && response.feedback.length > 0
16365
16691
  });
16366
- try {
16367
- const response = await Promise.race([approvalPromise, timeoutPromise]);
16368
- console.log("[session-manager] Plan approval result", {
16369
- approvalId,
16370
- approved: response.approved,
16371
- hasFeedback: typeof response.feedback === "string" && response.feedback.length > 0
16372
- });
16373
- if (response.approved) {
16692
+ if (response.approved) {
16693
+ try {
16694
+ const control = session.queryControl;
16695
+ if (!control?.setPermissionMode)
16696
+ throw new Error("Claude permission control is unavailable");
16697
+ await control.setPermissionMode(claudePermissionMode(claudeTurnPolicy(session.executionPolicy, turn.options?.readOnly), turn.options?.approvalMode));
16698
+ await applySessionPlanMode(session, false);
16699
+ } catch (error) {
16374
16700
  try {
16375
16701
  const control = session.queryControl;
16376
16702
  if (!control?.setPermissionMode)
16377
16703
  throw new Error("Claude permission control is unavailable");
16378
- await control.setPermissionMode(claudePermissionMode(claudeTurnPolicy(session.executionPolicy, turn.options?.readOnly), turn.options?.approvalMode));
16379
- await applySessionPlanMode(session, false);
16380
- } catch (error) {
16381
- try {
16382
- const control = session.queryControl;
16383
- if (!control?.setPermissionMode)
16384
- throw new Error("Claude permission control is unavailable");
16385
- await control.setPermissionMode("plan");
16386
- } catch {
16387
- abortSession(sessionId);
16388
- }
16389
- const message = error instanceof Error ? error.message : "Failed to persist plan mode";
16390
- return {
16391
- behavior: "deny",
16392
- message: `Plan mode could not be exited safely: ${message}`
16393
- };
16704
+ await control.setPermissionMode("plan");
16705
+ } catch {
16706
+ abortSession(sessionId);
16394
16707
  }
16395
- stream.planApprovedThisTurn = true;
16396
- eventEmitter.emit({
16397
- type: "plan.exit-requested",
16398
- sessionId,
16399
- data: { sessionId }
16400
- });
16401
- return {
16402
- behavior: "allow",
16403
- updatedInput: input
16404
- };
16405
- } else {
16406
- const feedbackMessage = response.feedback ? `User feedback: "${response.feedback}"` : "No specific feedback was provided.";
16407
- const denyMessage = `User rejected the plan. ${feedbackMessage} Please revise your approach based on this feedback.`;
16408
- stream.pendingPlanRejectionFeedback = response.feedback ? `I've reviewed the plan and I'd like changes: ${response.feedback}
16409
-
16410
- Please revise the plan based on this feedback.` : `I've reviewed the plan and I don't approve it as-is. Please revise your approach.`;
16708
+ const message = error instanceof Error ? error.message : "Failed to persist plan mode";
16411
16709
  return {
16412
16710
  behavior: "deny",
16413
- message: denyMessage
16711
+ message: `Plan mode could not be exited safely: ${message}`
16414
16712
  };
16415
16713
  }
16416
- } catch (error) {
16417
- console.error("[session-manager] Error waiting for plan approval:", error);
16418
- const errorMessage = error instanceof Error ? error.message : "Plan approval was cancelled";
16419
- if (pendingPlanApprovals.has(approvalId)) {
16420
- eventEmitter.emit({
16421
- type: "plan.approval-responded",
16422
- sessionId,
16423
- data: {
16424
- requestId: approvalId,
16425
- approved: false,
16426
- cancelled: true
16427
- }
16428
- });
16429
- }
16430
- return { behavior: "deny", message: errorMessage };
16431
- } finally {
16432
- releaseDeadline();
16433
- pendingPlanApprovals.delete(approvalId);
16434
- planApprovalResolvers.delete(approvalId);
16714
+ turn.planApprovedThisTurn = true;
16715
+ eventEmitter.emit({
16716
+ type: "plan.exit-requested",
16717
+ sessionId,
16718
+ data: { sessionId }
16719
+ });
16720
+ return {
16721
+ behavior: "allow",
16722
+ updatedInput: input
16723
+ };
16724
+ } else {
16725
+ const feedbackMessage = response.feedback ? `User feedback: "${response.feedback}"` : "No specific feedback was provided.";
16726
+ const denyMessage = `User rejected the plan. ${feedbackMessage} Please revise your approach based on this feedback.`;
16727
+ turn.pendingPlanRejectionFeedback = response.feedback ? `I've reviewed the plan and I'd like changes: ${response.feedback}
16728
+
16729
+ Please revise the plan based on this feedback.` : `I've reviewed the plan and I don't approve it as-is. Please revise your approach.`;
16730
+ return {
16731
+ behavior: "deny",
16732
+ message: denyMessage
16733
+ };
16734
+ }
16735
+ } catch (error) {
16736
+ console.error("[session-manager] Error waiting for plan approval:", error);
16737
+ const errorMessage = error instanceof Error ? error.message : "Plan approval was cancelled";
16738
+ if (pendingPlanApprovals.has(approvalId)) {
16739
+ eventEmitter.emit({
16740
+ type: "plan.approval-responded",
16741
+ sessionId,
16742
+ data: {
16743
+ requestId: approvalId,
16744
+ approved: false,
16745
+ cancelled: true
16746
+ }
16747
+ });
16435
16748
  }
16749
+ return { behavior: "deny", message: errorMessage };
16750
+ } finally {
16751
+ releaseDeadline();
16752
+ pendingPlanApprovals.delete(approvalId);
16753
+ planApprovalResolvers.delete(approvalId);
16436
16754
  }
16755
+ }
16756
+ async function turnCanUseTool(turn, toolName, input, permissionContext) {
16757
+ const { session, sessionId } = turn;
16758
+ if (toolName === "AskUserQuestion")
16759
+ return askUserQuestion(turn, input, permissionContext);
16760
+ if (toolName === "EnterPlanMode")
16761
+ return enterPlanMode(turn, input);
16762
+ if (toolName === "ExitPlanMode")
16763
+ return requestPlanApproval(turn, input, permissionContext);
16437
16764
  if ((claudeWorkflowResultTools(turn.mcpServerNames).includes(toolName) || claudeArtifactServerTools(turn.mcpServerNames).includes(toolName)) && !session.executionPolicy?.toolPolicy?.deny?.includes(toolName)) {
16438
16765
  return { behavior: "allow", updatedInput: input };
16439
16766
  }
@@ -16527,6 +16854,7 @@ function endClaudeRuntime(runtime, failure) {
16527
16854
  session.status = "error";
16528
16855
  session.error = claudeProcessKilledMessage(killedBy);
16529
16856
  session.interruption = "restart";
16857
+ session.interruptedRequestId = session.turnInFlightRequestId;
16530
16858
  session.turnStartedAt = undefined;
16531
16859
  session.abortController = undefined;
16532
16860
  session.completionBlockedByBackgroundTasks = false;
@@ -16585,7 +16913,10 @@ async function prepareJoin(turn, plan, running) {
16585
16913
  const { session, options } = turn;
16586
16914
  const { mcpServers, plugins, sdkPrompt, queryEnvironment, liveSettings, mcpServersKey } = plan;
16587
16915
  const required = mcpServers[AGENT_MCP_SERVER_NAME] ? [AGENT_MCP_SERVER_NAME, ...options?.agentMcp?.design ? ["orkestrator-design"] : []] : [];
16588
- await assertClaudeRuntimeAccepts(session, options, claudeLaunchResourceProfile(plugins, queryEnvironment));
16916
+ await assertClaudeRuntimeAccepts(session, options, {
16917
+ ...claudeLaunchResourceProfile(plugins, queryEnvironment),
16918
+ settingSources: JSON.stringify(plan.settingSources)
16919
+ });
16589
16920
  session.queryControl = running.control;
16590
16921
  turn.queryIteratorControl = running.control;
16591
16922
  try {
@@ -16643,6 +16974,7 @@ function launchClaudeRuntime(turn, plan) {
16643
16974
  activeTaskIds: stream.activeTaskIds,
16644
16975
  launch: {
16645
16976
  ...claudeLaunchedProfile(session, options),
16977
+ settingSources: JSON.stringify(plan.settingSources),
16646
16978
  ...claudeLaunchResourceProfile(plugins, queryEnvironment)
16647
16979
  },
16648
16980
  mcpServers: mcpServersKey,
@@ -16679,7 +17011,7 @@ function markQueryStarted(turn, testHooks) {
16679
17011
  }, { once: true });
16680
17012
  session.finishTurnInputIfSettled = turn.finishTurnInputForThisTurn;
16681
17013
  turn.queryStarted = true;
16682
- syncTurnInFlightMark(session, { started: true });
17014
+ syncTurnInFlightMark(session, { started: true, requestId: turn.dispatchRequestId });
16683
17015
  testHooks?.onQueryStarted?.();
16684
17016
  readLiveCommandInventory(session, control);
16685
17017
  reportUltracodeRuntime(session, control);
@@ -16693,6 +17025,7 @@ function joinableRuntime(turn, plan) {
16693
17025
  return running;
16694
17026
  const requested = {
16695
17027
  ...claudeLaunchProfile(turn.session, turn.options),
17028
+ settingSources: JSON.stringify(plan.settingSources),
16696
17029
  ...claudeLaunchResourceProfile(plan.plugins, plan.queryEnvironment)
16697
17030
  };
16698
17031
  return claudeLaunchConflict(running.launch, requested) ? undefined : running;
@@ -16735,9 +17068,9 @@ function settleTurnEnd(turn) {
16735
17068
  }
16736
17069
  return;
16737
17070
  }
16738
- if (stream.pendingPlanRejectionFeedback && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
16739
- const feedbackPrompt = stream.pendingPlanRejectionFeedback;
16740
- stream.pendingPlanRejectionFeedback = null;
17071
+ if (turn.pendingPlanRejectionFeedback && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
17072
+ const feedbackPrompt = turn.pendingPlanRejectionFeedback;
17073
+ turn.pendingPlanRejectionFeedback = null;
16741
17074
  debugLog("[session-manager] Re-prompting with plan rejection feedback", { sessionId });
16742
17075
  session.status = "idle";
16743
17076
  session.abortController = undefined;
@@ -16757,9 +17090,9 @@ function settleTurnEnd(turn) {
16757
17090
  failureLog: "[session-manager] Failed to re-prompt with plan feedback:"
16758
17091
  };
16759
17092
  }
16760
- if (stream.pendingPlanApprovalContinuation && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
16761
- const continuationPrompt = stream.pendingPlanApprovalContinuation;
16762
- stream.pendingPlanApprovalContinuation = null;
17093
+ if (turn.pendingPlanApprovalContinuation && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
17094
+ const continuationPrompt = turn.pendingPlanApprovalContinuation;
17095
+ turn.pendingPlanApprovalContinuation = null;
16763
17096
  debugLog("[session-manager] Re-prompting after approved-plan ExitPlanMode failure", {
16764
17097
  sessionId
16765
17098
  });
@@ -16826,6 +17159,7 @@ function settleFailedTurn(turn, error) {
16826
17159
  session.abortController = turn.beforeStartup.abortController;
16827
17160
  session.error = turn.beforeStartup.error;
16828
17161
  session.interruption = turn.beforeStartup.interruption;
17162
+ session.interruptedRequestId = turn.beforeStartup.interruptedRequestId;
16829
17163
  session.inProgressUsage = turn.beforeStartup.inProgressUsage;
16830
17164
  session.inProgressUsageGeneration = turn.beforeStartup.inProgressUsageGeneration;
16831
17165
  const index = session.messages.indexOf(userMessage);
@@ -16854,6 +17188,7 @@ function settleFailedTurn(turn, error) {
16854
17188
  session.turnStartedAt = undefined;
16855
17189
  session.error = failure;
16856
17190
  session.interruption = killedBy ? "restart" : undefined;
17191
+ session.interruptedRequestId = killedBy ? turn.dispatchRequestId : undefined;
16857
17192
  session.abortController = undefined;
16858
17193
  session.completionBlockedByBackgroundTasks = false;
16859
17194
  cleanupPendingInteractions(sessionId);
@@ -17326,14 +17661,14 @@ var SESSION_ACTIVITY_BATCH_LIMITS = {
17326
17661
  };
17327
17662
  var SESSION_ACTIVITY_STATES = new Set(["idle", "working", "waiting", "missing"]);
17328
17663
  var encoder3 = new TextEncoder;
17329
- function isRecord3(value) {
17664
+ function isRecord4(value) {
17330
17665
  return typeof value === "object" && value !== null && !Array.isArray(value);
17331
17666
  }
17332
17667
  function isBatchableSessionId(value) {
17333
17668
  return typeof value === "string" && value.length > 0 && encoder3.encode(value).byteLength <= SESSION_ACTIVITY_BATCH_LIMITS.maxSessionIdBytes;
17334
17669
  }
17335
17670
  function parseSessionActivityObservation(value) {
17336
- if (!isRecord3(value))
17671
+ if (!isRecord4(value))
17337
17672
  return;
17338
17673
  const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
17339
17674
  const { contentRevision } = value;
@@ -17367,7 +17702,7 @@ function isContentRevision(value) {
17367
17702
  return typeof value === "string" && value.length > 0 && value.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxContentRevisionLength;
17368
17703
  }
17369
17704
  function parseSessionActivityBatchRequest(value) {
17370
- if (!isRecord3(value))
17705
+ if (!isRecord4(value))
17371
17706
  return { ok: false, error: "Expected a JSON object" };
17372
17707
  if (value.version !== SESSION_ACTIVITY_BATCH_VERSION) {
17373
17708
  return { ok: false, error: "Unsupported activity batch version" };
@@ -17610,7 +17945,7 @@ function decodeLocator(locator) {
17610
17945
  }
17611
17946
  try {
17612
17947
  const body = JSON.parse(Buffer3.from(locator.slice(LOCATOR_PREFIX.length), "base64url").toString("utf8"));
17613
- if (!isRecord4(body))
17948
+ if (!isRecord5(body))
17614
17949
  return;
17615
17950
  const { m, p, d } = body;
17616
17951
  if (typeof m !== "string" || typeof d !== "string" || !Array.isArray(p))
@@ -17630,12 +17965,12 @@ var DETAIL_FIELDS = new Set([
17630
17965
  "toolDiff",
17631
17966
  "fileDataUrl"
17632
17967
  ]);
17633
- function isRecord4(value) {
17968
+ function isRecord5(value) {
17634
17969
  return value !== null && typeof value === "object" && !Array.isArray(value);
17635
17970
  }
17636
17971
  function heavyDiff(part) {
17637
17972
  const diff = part.toolDiff;
17638
- if (!isRecord4(diff))
17973
+ if (!isRecord5(diff))
17639
17974
  return;
17640
17975
  return typeof diff.diff === "string" || typeof diff.before === "string" || typeof diff.after === "string" ? diff : undefined;
17641
17976
  }
@@ -17657,7 +17992,7 @@ function detailPayload(part) {
17657
17992
  return Object.keys(payload).length > 0 ? payload : undefined;
17658
17993
  }
17659
17994
  function detailInputs(part) {
17660
- const diff = isRecord4(part.toolDiff) ? part.toolDiff : undefined;
17995
+ const diff = isRecord5(part.toolDiff) ? part.toolDiff : undefined;
17661
17996
  return [
17662
17997
  part.type,
17663
17998
  part.content,
@@ -17687,7 +18022,7 @@ function digestPayload(payload) {
17687
18022
  };
17688
18023
  }
17689
18024
  function topLevelKey(part, index) {
17690
- if (isRecord4(part)) {
18025
+ if (isRecord5(part)) {
17691
18026
  if (typeof part.sourcePartId === "string" && part.sourcePartId.length <= 512) {
17692
18027
  return `s:${part.sourcePartId}`;
17693
18028
  }
@@ -17698,7 +18033,7 @@ function topLevelKey(part, index) {
17698
18033
  return `i:${index}`;
17699
18034
  }
17700
18035
  function summarizePart(raw, messageId, path, depth) {
17701
- if (!isRecord4(raw) || depth > MAX_SUMMARY_DEPTH)
18036
+ if (!isRecord5(raw) || depth > MAX_SUMMARY_DEPTH)
17702
18037
  return raw;
17703
18038
  const part = raw;
17704
18039
  let summary;
@@ -17750,7 +18085,7 @@ function summarizePart(raw, messageId, path, depth) {
17750
18085
  if (changed)
17751
18086
  writable()[field] = next;
17752
18087
  }
17753
- if (isRecord4(part.task)) {
18088
+ if (isRecord5(part.task)) {
17754
18089
  const task = summarizePart(part.task, messageId, [...path, "task"], depth + 1);
17755
18090
  if (task !== part.task)
17756
18091
  writable().task = task;
@@ -17758,7 +18093,7 @@ function summarizePart(raw, messageId, path, depth) {
17758
18093
  return summary ?? part;
17759
18094
  }
17760
18095
  function isSummarizable(message) {
17761
- return isRecord4(message) && typeof message.id === "string" && Array.isArray(message.parts);
18096
+ return isRecord5(message) && typeof message.id === "string" && Array.isArray(message.parts);
17762
18097
  }
17763
18098
  function summarizeBridgeMessage(message) {
17764
18099
  if (!isSummarizable(message))
@@ -17784,15 +18119,15 @@ function resolvePath2(message, path) {
17784
18119
  let current;
17785
18120
  if (top.startsWith("s:")) {
17786
18121
  const id = top.slice(2);
17787
- current = message.parts.find((part) => isRecord4(part) && part.sourcePartId === id);
18122
+ current = message.parts.find((part) => isRecord5(part) && part.sourcePartId === id);
17788
18123
  } else if (top.startsWith("t:")) {
17789
18124
  const id = top.slice(2);
17790
- current = message.parts.find((part) => isRecord4(part) && part.toolUseId === id && typeof part.sourcePartId !== "string");
18125
+ current = message.parts.find((part) => isRecord5(part) && part.toolUseId === id && typeof part.sourcePartId !== "string");
17791
18126
  } else if (top.startsWith("i:")) {
17792
18127
  current = message.parts[Number(top.slice(2))];
17793
18128
  }
17794
18129
  for (let index = 0;index < rest.length; index += 1) {
17795
- if (!isRecord4(current))
18130
+ if (!isRecord5(current))
17796
18131
  return;
17797
18132
  const field = rest[index];
17798
18133
  if (field === "task") {
@@ -17825,7 +18160,7 @@ function readBridgeTranscriptDetail(messages, locator) {
17825
18160
  if (!message)
17826
18161
  return { version: 1, status: "missing" };
17827
18162
  const part = resolvePath2(message, body.p);
17828
- if (!isRecord4(part))
18163
+ if (!isRecord5(part))
17829
18164
  return { version: 1, status: "missing" };
17830
18165
  const payload = detailPayload(part);
17831
18166
  if (!payload)
@@ -17928,7 +18263,7 @@ function decodePageCursor(cursor) {
17928
18263
  }
17929
18264
  try {
17930
18265
  const body = JSON.parse(Buffer3.from(cursor.slice(PAGE_CURSOR_PREFIX.length), "base64url").toString("utf8"));
17931
- if (!isRecord4(body))
18266
+ if (!isRecord5(body))
17932
18267
  return;
17933
18268
  const { g, e, b } = body;
17934
18269
  if (typeof g !== "string" || typeof e !== "string")
@@ -18056,9 +18391,14 @@ function compressTranscriptReads(app) {
18056
18391
  function query3(c) {
18057
18392
  return (name) => c.req.query(name);
18058
18393
  }
18059
- function registerSessionTranscriptRoutes(app, generation) {
18060
- app.get("/:id/transcript", (c) => {
18394
+ function registerSessionTranscriptRoutes(app, generation, materialize) {
18395
+ app.get("/:id/transcript", async (c) => {
18061
18396
  const id = c.req.param("id");
18397
+ if (!peekSession(id)) {
18398
+ const resolved = await materialize(id);
18399
+ if (!resolved.ok)
18400
+ return c.json(resolved.body, resolved.status);
18401
+ }
18062
18402
  const read = claudeTranscriptSource(id, generation);
18063
18403
  if (!read)
18064
18404
  return c.json({ error: "Session not found" }, 404);
@@ -18225,11 +18565,14 @@ session.get("/:id", async (c) => {
18225
18565
  createdAt: sessionData.createdAt.toISOString(),
18226
18566
  lastActivity: sessionData.lastActivity.toISOString(),
18227
18567
  error: sessionData.error,
18228
- ...sessionData.status === "error" && sessionData.interruption ? { interruption: sessionData.interruption } : {},
18568
+ ...sessionData.status === "error" && sessionData.interruption ? {
18569
+ interruption: sessionData.interruption,
18570
+ ...sessionData.interruptedRequestId ? { interruptedRequestId: sessionData.interruptedRequestId } : {}
18571
+ } : {},
18229
18572
  structuredOutputRequestId: sessionData.structuredOutputRequestId,
18230
18573
  structuredOutput: sessionData.structuredOutput,
18231
18574
  contextUsage: sessionData.inProgressUsage ?? sessionData.usage,
18232
- policy: sessionData.executionPolicy,
18575
+ policy: claudeReportedPolicy(sessionData),
18233
18576
  rateLimits: sessionData.rateLimits,
18234
18577
  promptSuggestion: sessionData.promptSuggestion,
18235
18578
  activity: sessionData.activity,
@@ -18329,7 +18672,7 @@ session.post("/:id/config", async (c) => {
18329
18672
  } else if (permissionMode === "plan" || permissionMode === "bypassPermissions") {
18330
18673
  await setSessionPreferences(sessionData.id, { planMode: permissionMode === "plan" });
18331
18674
  }
18332
- return c.json({ ok: true, policy: sessionData.executionPolicy });
18675
+ return c.json({ ok: true, policy: claudeReportedPolicy(sessionData) });
18333
18676
  });
18334
18677
  session.get("/:id/commands", async (c) => {
18335
18678
  try {
@@ -18421,7 +18764,7 @@ session.get("/:id/messages", async (c) => {
18421
18764
  const limit = parseTranscriptTailLimit(c.req.query("limit") ?? null);
18422
18765
  return c.json(boundClaudeTranscriptResponse(messages, limit ?? undefined));
18423
18766
  });
18424
- registerSessionTranscriptRoutes(session, TRANSCRIPT_GENERATION);
18767
+ registerSessionTranscriptRoutes(session, TRANSCRIPT_GENERATION, (id) => resolveSession(id, "Failed to load session transcript"));
18425
18768
  session.post("/:id/prompt", async (c) => {
18426
18769
  const id = c.req.param("id");
18427
18770
  const resolved = await resolveSession(id, "Failed to send prompt");
@@ -18527,6 +18870,10 @@ session.post("/:id/prompt", async (c) => {
18527
18870
  await answerIdleSteerPrompt(sessionData, prompt, requestId);
18528
18871
  return c.json({ status: "accepted", requestId, local: true });
18529
18872
  }
18873
+ const sandboxRefusal = claudeSandboxRefusal(claudeTurnPolicy(sessionData.executionPolicy, typeof readOnly === "boolean" ? readOnly : undefined));
18874
+ if (sandboxRefusal) {
18875
+ return c.json({ error: sandboxRefusal, kind: "sandbox-unavailable" }, 422);
18876
+ }
18530
18877
  await assertClaudeRuntimeAccepts(sessionData, {
18531
18878
  ...typeof readOnly === "boolean" ? { readOnly } : {},
18532
18879
  agent,
@@ -18779,14 +19126,14 @@ session.get("/:id/runtime-health", (c) => {
18779
19126
  return c.json(sessionRuntimeHealthBody(peekSession(c.req.param("id"))));
18780
19127
  });
18781
19128
  session.get("/:id/dispatch", async (c) => {
18782
- const answer = (dispatch) => c.json({ dispatch });
19129
+ const answer = (dispatch, pending) => c.json({ dispatch, ...pending !== undefined ? { pending } : {} });
18783
19130
  const requestId = c.req.query("requestId")?.trim();
18784
19131
  if (!requestId)
18785
19132
  return answer("unknown");
18786
19133
  const id = c.req.param("id");
18787
19134
  const state = getPromptDispatchState(id, requestId);
18788
19135
  if (state === "processing" || state === "already-processed")
18789
- return answer("dispatched");
19136
+ return answer("dispatched", state === "processing");
18790
19137
  if (state === "new") {
18791
19138
  return answer(restoredDispatchClaimLost(peekSession(id), requestId) ? "lost" : "unknown");
18792
19139
  }
@@ -19629,7 +19976,7 @@ var X_ALREADY_SENT = "x-hono-already-sent";
19629
19976
 
19630
19977
  // ../../node_modules/.bun/@hono+node-server@2.1.5+c539cad7f494f5c9/node_modules/@hono/node-server/dist/index.mjs
19631
19978
  import { STATUS_CODES, ServerResponse, createServer } from "node:http";
19632
- import { Http2ServerRequest, constants as constants2 } from "node:http2";
19979
+ import { Http2ServerRequest, constants as constants3 } from "node:http2";
19633
19980
  import { Readable } from "node:stream";
19634
19981
 
19635
19982
  // ../../node_modules/.bun/hono@4.13.13/node_modules/hono/dist/helper/websocket/index.js
@@ -20548,7 +20895,7 @@ var drainIncoming = (incoming) => {
20548
20895
  incomingWithDrainState[incomingDraining] = true;
20549
20896
  if (incoming instanceof Http2ServerRequest) {
20550
20897
  try {
20551
- incoming.stream?.close?.(constants2.NGHTTP2_NO_ERROR);
20898
+ incoming.stream?.close?.(constants3.NGHTTP2_NO_ERROR);
20552
20899
  } catch {}
20553
20900
  return;
20554
20901
  }