orkestrator 2.30.0-beta → 2.31.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"]);
@@ -3764,7 +3771,86 @@ import { createHash as createHash5 } from "node:crypto";
3764
3771
 
3765
3772
  // ../../bridges/claude-bridge/src/services/plugin-config.ts
3766
3773
  import { readdir, access } from "node:fs/promises";
3767
- import { join as join3, resolve, relative, isAbsolute } from "node:path";
3774
+ import { join as join3 } from "node:path";
3775
+
3776
+ // ../protocol/src/node/claude-plugin-set.ts
3777
+ import path from "node:path";
3778
+ var CLAUDE_PLUGINS_MARKER = "/.claude/plugins/";
3779
+ function isRecord(value) {
3780
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3781
+ }
3782
+ function claudePluginConfigList(value) {
3783
+ if (!Array.isArray(value))
3784
+ return [];
3785
+ const plugins = [];
3786
+ for (const item of value) {
3787
+ if (isRecord(item) && typeof item.path === "string" && item.path) {
3788
+ plugins.push({ type: "local", path: item.path });
3789
+ }
3790
+ }
3791
+ return plugins;
3792
+ }
3793
+ function isPathWithin(parentDir, candidatePath) {
3794
+ const relativePath = path.relative(parentDir, candidatePath);
3795
+ return relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
3796
+ }
3797
+ function remapInstalledPlugins(pluginsDir, installedPlugins, onUnsafe) {
3798
+ const plugins = [];
3799
+ const pluginsRoot = path.resolve(pluginsDir);
3800
+ if (!isRecord(installedPlugins.plugins))
3801
+ return plugins;
3802
+ for (const entries of Object.values(installedPlugins.plugins)) {
3803
+ if (!Array.isArray(entries))
3804
+ continue;
3805
+ for (const entry of entries) {
3806
+ if (!isRecord(entry) || typeof entry.installPath !== "string")
3807
+ continue;
3808
+ const installPath = entry.installPath;
3809
+ const markerIdx = installPath.indexOf(CLAUDE_PLUGINS_MARKER);
3810
+ let resolvedPath;
3811
+ if (markerIdx !== -1) {
3812
+ const relativePath = installPath.substring(markerIdx + CLAUDE_PLUGINS_MARKER.length);
3813
+ const remappedPath = path.resolve(pluginsDir, relativePath);
3814
+ if (!isPathWithin(pluginsRoot, remappedPath)) {
3815
+ onUnsafe?.(installPath);
3816
+ continue;
3817
+ }
3818
+ resolvedPath = remappedPath;
3819
+ } else {
3820
+ resolvedPath = installPath;
3821
+ }
3822
+ plugins.push({ type: "local", path: resolvedPath });
3823
+ }
3824
+ }
3825
+ return plugins;
3826
+ }
3827
+ function resolveClaudePluginPath(pluginPath, cwd, home) {
3828
+ if (pluginPath.startsWith("~"))
3829
+ return path.join(home, pluginPath.slice(1));
3830
+ if (pluginPath.startsWith("/"))
3831
+ return pluginPath;
3832
+ return path.join(cwd, pluginPath);
3833
+ }
3834
+ function mergeClaudePluginLayers(layers, options) {
3835
+ const ordered = [
3836
+ claudePluginConfigList(layers.global),
3837
+ layers.cliInstalled,
3838
+ ...options.includeProjectSources ? [claudePluginConfigList(layers.projectGlobal), claudePluginConfigList(layers.projectLocal)] : []
3839
+ ];
3840
+ const seen = new Set;
3841
+ const paths = [];
3842
+ for (const layer of ordered) {
3843
+ for (const plugin of layer) {
3844
+ const resolved = resolveClaudePluginPath(plugin.path, options.cwd, options.home);
3845
+ if (seen.has(resolved))
3846
+ continue;
3847
+ seen.add(resolved);
3848
+ paths.push(resolved);
3849
+ }
3850
+ }
3851
+ return paths;
3852
+ }
3853
+ var INVALID_PLUGIN_NAME = new RegExp("[@:\\s/\\\\\\u0000-\\u001f\\u007f\\u200b-\\u200f\\u2028-\\u202e\\u2060-\\u2064\\ufeff]");
3768
3854
 
3769
3855
  // ../../bridges/claude-bridge/src/services/json-file-cache.ts
3770
3856
  import { createHash as createHash4 } from "node:crypto";
@@ -3866,10 +3952,6 @@ async function pathExists(path) {
3866
3952
  return false;
3867
3953
  }
3868
3954
  }
3869
- function isPathWithin(parentDir, candidatePath) {
3870
- const relativePath = relative(parentDir, candidatePath);
3871
- return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
3872
- }
3873
3955
  async function scanCliPluginsDirectory(pluginsDir) {
3874
3956
  if (!await pathExists(pluginsDir)) {
3875
3957
  return [];
@@ -3896,11 +3978,11 @@ async function scanCliPluginsDirectory(pluginsDir) {
3896
3978
  }
3897
3979
  }
3898
3980
  async function loadGlobalPlugins() {
3899
- const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => config?.plugins);
3981
+ const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => claudePluginConfigList(config?.plugins));
3900
3982
  return plugins ?? [];
3901
3983
  }
3902
3984
  async function loadProjectPlugins(cwd) {
3903
- const plugins = await readJsonSliceCached(join3(cwd, ".claude", "plugins.json"), "plugins", (config) => config?.plugins);
3985
+ const plugins = await readJsonSliceCached(join3(cwd, ".claude", "plugins.json"), "plugins", (config) => claudePluginConfigList(config?.plugins));
3904
3986
  return plugins ?? [];
3905
3987
  }
3906
3988
  async function loadCliInstalledPlugins() {
@@ -3910,37 +3992,13 @@ async function loadCliInstalledPlugins() {
3910
3992
  if (!plugins) {
3911
3993
  return scanCliPluginsDirectory(pluginsDir);
3912
3994
  }
3913
- return remapInstalledPlugins(pluginsDir, { plugins });
3995
+ return remapInstalledPlugins2(pluginsDir, { plugins });
3914
3996
  }
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
- }
3939
- }
3940
- return plugins;
3997
+ function remapInstalledPlugins2(pluginsDir, installedPlugins) {
3998
+ return remapInstalledPlugins(pluginsDir, installedPlugins, (installPath) => console.warn(`[plugin-config] Skipping plugin with unsafe install path: "${installPath}"`));
3941
3999
  }
3942
4000
  async function loadProjectOverridesFromGlobal(cwd) {
3943
- const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => config?.projects?.[cwd]?.plugins);
4001
+ const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => claudePluginConfigList(config?.projects?.[cwd]?.plugins));
3944
4002
  return plugins ?? [];
3945
4003
  }
3946
4004
  async function readPluginManifest(pluginPath) {
@@ -3956,42 +4014,19 @@ async function readPluginManifest(pluginPath) {
3956
4014
  };
3957
4015
  }
3958
4016
  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);
4017
+ return resolveClaudePluginPath(pluginPath, cwd, claudeHome());
3966
4018
  }
3967
- async function getMergedPlugins(cwd) {
4019
+ async function getMergedPlugins(cwd, includeProjectSources = true) {
3968
4020
  const [global2, cliInstalled, projectGlobal, projectLocal] = await Promise.all([
3969
4021
  loadGlobalPlugins(),
3970
4022
  loadCliInstalledPlugins(),
3971
- loadProjectOverridesFromGlobal(cwd),
3972
- loadProjectPlugins(cwd)
4023
+ includeProjectSources ? loadProjectOverridesFromGlobal(cwd) : [],
4024
+ includeProjectSources ? loadProjectPlugins(cwd) : []
3973
4025
  ]);
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());
4026
+ return mergeClaudePluginLayers({ global: global2, cliInstalled, projectGlobal, projectLocal }, { cwd, home: claudeHome(), includeProjectSources }).map((path) => ({ type: "local", path }));
3992
4027
  }
3993
4028
  async function getPluginsForSdk(cwd, includeProjectSources = true) {
3994
- const configs = includeProjectSources ? await getMergedPlugins(cwd) : [...await loadGlobalPlugins(), ...await loadCliInstalledPlugins()];
4029
+ const configs = await getMergedPlugins(cwd, includeProjectSources);
3995
4030
  const validPlugins = [];
3996
4031
  for (const config of configs) {
3997
4032
  if (await pathExists(config.path)) {
@@ -4117,90 +4152,18 @@ async function runtimeEnvironmentForAgentQuery(env = process.env, readTextFile =
4117
4152
  return snapshot;
4118
4153
  }
4119
4154
 
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
4155
  // ../protocol/src/native-agent-transcript-patch.ts
4202
4156
  var FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
4203
4157
 
4158
+ // ../protocol/src/native-agent-policy-effects.ts
4159
+ function appendNativeAgentPolicyNote(policy, note) {
4160
+ return { ...policy, note: policy.note ? `${policy.note} ${note}` : note };
4161
+ }
4162
+ 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.";
4163
+ function readOnlyTurnReportedPolicy(turnPolicy) {
4164
+ return appendNativeAgentPolicyNote(turnPolicy, READ_ONLY_TURN_POLICY_NOTE);
4165
+ }
4166
+
4204
4167
  // ../protocol/src/native-agent-steer-rejection.ts
4205
4168
  var NATIVE_AGENT_STEER_REJECTION_HTTP_STATUS = 429;
4206
4169
  var MAX_STEER_JOURNAL_BYTES = 1024 * 1024 * 1024;
@@ -4263,6 +4226,15 @@ var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
4263
4226
  }
4264
4227
  ]
4265
4228
  });
4229
+ var CODEX_CONTAINER_APPROVAL_DESCRIPTIONS = Object.freeze({
4230
+ ask: {
4231
+ "on-request": "Codex has no sandbox of its own in a container, so this asks before any command Codex does not trust, like Untrusted"
4232
+ },
4233
+ "auto-approve": {
4234
+ "on-request": "Codex has no sandbox of its own in a container, so this never asks; choose Untrusted to approve commands",
4235
+ "auto-review": "Codex has no sandbox of its own in a container, so the reviewer is never consulted and everything runs"
4236
+ }
4237
+ });
4266
4238
  var DISPATCH_OUTCOME_LOST_KIND = "dispatch-outcome-lost";
4267
4239
  var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
4268
4240
  var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
@@ -4414,6 +4386,87 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4414
4386
  "slashCommandCatalogue"
4415
4387
  ]);
4416
4388
 
4389
+ // ../protocol/src/coordinator-web-research.ts
4390
+ var COORDINATOR_WEB_RESEARCH_MODES = ["off", "search", "search-and-mcp"];
4391
+ var DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS = Object.freeze([{ name: "context7", url: "https://mcp.context7.com/mcp" }]);
4392
+ var MAX_COORDINATOR_RESEARCH_MCP_SERVERS = 8;
4393
+ var COORDINATOR_WEB_RESEARCH_ENV = "ORKESTRATOR_COORDINATOR_WEB_RESEARCH";
4394
+ var COORDINATOR_RESEARCH_MCP_ENV = "ORKESTRATOR_COORDINATOR_RESEARCH_MCP";
4395
+ var RESERVED_SERVER_NAME = /^orkestrator/;
4396
+ var SERVER_NAME = /^[a-z0-9][a-z0-9-]{0,31}$/;
4397
+ function isCoordinatorWebResearch(value) {
4398
+ return typeof value === "string" && COORDINATOR_WEB_RESEARCH_MODES.includes(value);
4399
+ }
4400
+ function coordinatorResearchMcpServerProblem(server) {
4401
+ if (!SERVER_NAME.test(server.name)) {
4402
+ return "Use 1 to 32 lowercase letters, digits or hyphens for the name.";
4403
+ }
4404
+ if (RESERVED_SERVER_NAME.test(server.name))
4405
+ return "Names starting with orkestrator are reserved.";
4406
+ let url;
4407
+ try {
4408
+ url = new URL(server.url);
4409
+ } catch {
4410
+ return "The URL is not valid.";
4411
+ }
4412
+ if (url.protocol !== "https:")
4413
+ return "Only https:// research servers are allowed.";
4414
+ if (url.username || url.password)
4415
+ return "Credentials in the URL are not allowed.";
4416
+ return;
4417
+ }
4418
+ function normalizeCoordinatorResearchMcpServers(value) {
4419
+ if (value === undefined)
4420
+ return DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS.map((s) => ({ ...s }));
4421
+ if (!Array.isArray(value))
4422
+ return [];
4423
+ const servers = [];
4424
+ const names = new Set;
4425
+ for (const entry of value) {
4426
+ if (servers.length >= MAX_COORDINATOR_RESEARCH_MCP_SERVERS)
4427
+ break;
4428
+ if (!entry || typeof entry !== "object")
4429
+ continue;
4430
+ const { name, url } = entry;
4431
+ if (typeof name !== "string" || typeof url !== "string")
4432
+ continue;
4433
+ const server = { name: name.trim(), url: url.trim() };
4434
+ if (coordinatorResearchMcpServerProblem(server) || names.has(server.name))
4435
+ continue;
4436
+ names.add(server.name);
4437
+ servers.push(server);
4438
+ }
4439
+ return servers;
4440
+ }
4441
+ function coordinatorWebResearchFromEnv(env) {
4442
+ const value = env[COORDINATOR_WEB_RESEARCH_ENV];
4443
+ return isCoordinatorWebResearch(value) ? value : "off";
4444
+ }
4445
+ function coordinatorResearchMcpServersFromEnv(env) {
4446
+ if (coordinatorWebResearchFromEnv(env) !== "search-and-mcp")
4447
+ return [];
4448
+ const raw = env[COORDINATOR_RESEARCH_MCP_ENV];
4449
+ if (!raw)
4450
+ return [];
4451
+ try {
4452
+ return normalizeCoordinatorResearchMcpServers(JSON.parse(raw));
4453
+ } catch {
4454
+ return [];
4455
+ }
4456
+ }
4457
+ var COORDINATOR_DENIED = Object.freeze([
4458
+ "file.write",
4459
+ "file.patch",
4460
+ "shell.mutate",
4461
+ "network"
4462
+ ]);
4463
+ function coordinatorCapabilityPolicy(mode) {
4464
+ return {
4465
+ deny: [...COORDINATOR_DENIED],
4466
+ ...mode === "off" ? {} : { allow: ["web.search"] }
4467
+ };
4468
+ }
4469
+
4417
4470
  // ../../bridges/claude-bridge/src/services/mcp-tool-prefix.ts
4418
4471
  function claudeMcpToolPrefix(name) {
4419
4472
  let normalized = name.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -4882,6 +4935,14 @@ function claudeWorkspaceCwd() {
4882
4935
  function claudeTurnPolicy(sessionPolicy, readOnly) {
4883
4936
  return effectiveExecutionPolicy(readOnly ? structuredClone(READ_ONLY_TURN_POLICY) : sessionPolicy);
4884
4937
  }
4938
+ function claudeReportedPolicy(session) {
4939
+ if (session.status === "running" && session.commandDiscoveryInputs?.readOnly === true) {
4940
+ const turnPolicy = claudeTurnPolicy(session.executionPolicy, true);
4941
+ if (turnPolicy)
4942
+ return readOnlyTurnReportedPolicy(turnPolicy);
4943
+ }
4944
+ return session.executionPolicy;
4945
+ }
4885
4946
  function claudePolicyPermissionMode(policy) {
4886
4947
  return isRestrictivePolicy(policy) ? "default" : "bypassPermissions";
4887
4948
  }
@@ -7385,7 +7446,6 @@ function claudeLaunchProfile(session, options) {
7385
7446
  context1m: effectiveClaudeContext1m(options),
7386
7447
  promptSuggestions: options?.promptSuggestions === true,
7387
7448
  chrome: effectiveClaudeChrome(policy, options),
7388
- maxBudgetUsd: typeof options?.maxBudgetUsd === "number" && options.maxBudgetUsd > 0 ? options.maxBudgetUsd : null,
7389
7449
  thinking: typeof options?.parameterValues?.thinking === "string" ? options.parameterValues.thinking : "adaptive",
7390
7450
  bypassCapable: claudePolicyPermissionMode(policy) === "bypassPermissions",
7391
7451
  cwd: claudeWorkspaceCwd(),
@@ -7487,8 +7547,6 @@ function claudeRuntimeOwesWork(runtime) {
7487
7547
  function claudeLaunchConflict(runtime, request) {
7488
7548
  if (runtime.chrome !== request.chrome)
7489
7549
  return "Chrome setting";
7490
- if (runtime.maxBudgetUsd !== request.maxBudgetUsd)
7491
- return "cost budget";
7492
7550
  if (runtime.context1m !== request.context1m)
7493
7551
  return "1M context setting";
7494
7552
  if (runtime.promptSuggestions !== request.promptSuggestions)
@@ -7645,7 +7703,7 @@ function noMessages() {
7645
7703
  // ../../bridges/claude-bridge/src/services/session-manager-persistence.ts
7646
7704
  import { constants } from "node:fs";
7647
7705
  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";
7706
+ import { isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve2, sep } from "node:path";
7649
7707
 
7650
7708
  // ../protocol/src/node/command-change-journal.ts
7651
7709
  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 +7860,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
7802
7860
  import { rmSync } from "node:fs";
7803
7861
  import { copyFile, lstat, mkdtemp, rm as rm3 } from "node:fs/promises";
7804
7862
  import { tmpdir } from "node:os";
7805
- import { delimiter, isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "node:path";
7863
+ import { delimiter, isAbsolute, join as join4, resolve } from "node:path";
7806
7864
  var DEFAULT_MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
7807
7865
  var DEFAULT_MAX_UNTRACKED_FILES = 5000;
7808
7866
  var DEFAULT_SLOW_SNAPSHOT_MS = 500;
@@ -8084,8 +8142,8 @@ class WorkspaceChangeProbe {
8084
8142
  return;
8085
8143
  return {
8086
8144
  root,
8087
- indexPath: isAbsolute2(index) ? index : resolve2(cwd, index),
8088
- objectsPath: isAbsolute2(objects) ? objects : resolve2(cwd, objects)
8145
+ indexPath: isAbsolute(index) ? index : resolve(cwd, index),
8146
+ objectsPath: isAbsolute(objects) ? objects : resolve(cwd, objects)
8089
8147
  };
8090
8148
  }).catch(() => {
8091
8149
  return;
@@ -9413,14 +9471,14 @@ function record(value) {
9413
9471
  function text(value) {
9414
9472
  return typeof value === "string" ? value : undefined;
9415
9473
  }
9416
- function path(value) {
9474
+ function path2(value) {
9417
9475
  return typeof value === "string" && value.length > 0 ? value : undefined;
9418
9476
  }
9419
9477
  function claudeProposalEvidence(toolName, input) {
9420
9478
  const fields = record(input);
9421
9479
  switch (toolName) {
9422
9480
  case "Edit": {
9423
- const filePath = path(fields.file_path);
9481
+ const filePath = path2(fields.file_path);
9424
9482
  if (!filePath)
9425
9483
  return [];
9426
9484
  return [
@@ -9438,7 +9496,7 @@ function claudeProposalEvidence(toolName, input) {
9438
9496
  ];
9439
9497
  }
9440
9498
  case "MultiEdit": {
9441
- const filePath = path(fields.file_path);
9499
+ const filePath = path2(fields.file_path);
9442
9500
  if (!filePath || !Array.isArray(fields.edits))
9443
9501
  return [];
9444
9502
  return fields.edits.map((edit, index) => {
@@ -9457,7 +9515,7 @@ function claudeProposalEvidence(toolName, input) {
9457
9515
  });
9458
9516
  }
9459
9517
  case "Write": {
9460
- const filePath = path(fields.file_path);
9518
+ const filePath = path2(fields.file_path);
9461
9519
  if (!filePath)
9462
9520
  return [];
9463
9521
  return [
@@ -9475,7 +9533,7 @@ function claudeProposalEvidence(toolName, input) {
9475
9533
  ];
9476
9534
  }
9477
9535
  case "NotebookEdit": {
9478
- const filePath = path(fields.notebook_path);
9536
+ const filePath = path2(fields.notebook_path);
9479
9537
  if (!filePath)
9480
9538
  return [];
9481
9539
  const deleting = fields.edit_mode === "delete";
@@ -10446,17 +10504,17 @@ function parseBase64ImageData(value) {
10446
10504
  var BRIDGE_ATTACHMENT_ROOT_ENV = "ORKESTRATOR_BRIDGE_ATTACHMENT_ROOT";
10447
10505
  function configuredAttachmentRoot(env = process.env) {
10448
10506
  const value = env[BRIDGE_ATTACHMENT_ROOT_ENV]?.trim();
10449
- return value && isAbsolute3(value) ? resolve3(value) : undefined;
10507
+ return value && isAbsolute2(value) ? resolve2(value) : undefined;
10450
10508
  }
10451
10509
  function attachmentContainmentRoot(filePath, cwd, extraRoot = configuredAttachmentRoot()) {
10452
- if (extraRoot && isAbsolute3(filePath) && isPathWithin2(extraRoot, resolve3(filePath))) {
10510
+ if (extraRoot && isAbsolute2(filePath) && isPathWithin2(extraRoot, resolve2(filePath))) {
10453
10511
  return extraRoot;
10454
10512
  }
10455
- return resolve3(cwd);
10513
+ return resolve2(cwd);
10456
10514
  }
10457
10515
  function isPathWithin2(rootPath, targetPath) {
10458
- const childPath = relative2(rootPath, targetPath);
10459
- return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !isAbsolute3(childPath);
10516
+ const childPath = relative(rootPath, targetPath);
10517
+ return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !isAbsolute2(childPath);
10460
10518
  }
10461
10519
  function attachmentErrorForFsFailure(error) {
10462
10520
  const code = error?.code;
@@ -10469,7 +10527,7 @@ function attachmentErrorForFsFailure(error) {
10469
10527
  return new ClaudeAttachmentError("attachment_read_failed", "Image attachment could not be read safely from the workspace.");
10470
10528
  }
10471
10529
  async function assertNoSymlinkComponents(lexicalRoot, targetPath) {
10472
- const childPath = relative2(lexicalRoot, targetPath);
10530
+ const childPath = relative(lexicalRoot, targetPath);
10473
10531
  let currentPath = lexicalRoot;
10474
10532
  for (const segment of childPath.split(sep).filter(Boolean)) {
10475
10533
  currentPath = join8(currentPath, segment);
@@ -10500,7 +10558,7 @@ async function assertOpenedWorkspaceFile(targetPath, canonicalRoot, openedStats)
10500
10558
  }
10501
10559
  async function readWorkspaceImageAttachment(filePath, cwd, afterSymlinkValidation, afterCanonicalValidation, afterInitialValidation) {
10502
10560
  const lexicalRoot = attachmentContainmentRoot(filePath, cwd);
10503
- const targetPath = isAbsolute3(filePath) ? resolve3(filePath) : resolve3(lexicalRoot, filePath);
10561
+ const targetPath = isAbsolute2(filePath) ? resolve2(filePath) : resolve2(lexicalRoot, filePath);
10504
10562
  if (!isPathWithin2(lexicalRoot, targetPath)) {
10505
10563
  throw new ClaudeAttachmentError("attachment_outside_workspace", "Image attachment must be contained in the current workspace.");
10506
10564
  }
@@ -10731,57 +10789,97 @@ var MAX_TRACKED_STREAM_ROWS = 256;
10731
10789
  var MAX_SUBAGENT_PARTS_PER_CALL = 128;
10732
10790
  var sessionStreamRows = new WeakMap;
10733
10791
  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);
10792
+ return new PromptStream(session, sessionId, options);
10793
+ }
10794
+ function explicitParentTaskUseId(partialMessage) {
10795
+ return typeof partialMessage.parent_tool_use_id === "string" && partialMessage.parent_tool_use_id.length > 0 ? partialMessage.parent_tool_use_id : undefined;
10796
+ }
10797
+ function materializeEntryValue(entry) {
10798
+ if (entry.pendingChunks) {
10799
+ entry.value += entry.pendingChunks.join("");
10800
+ entry.pendingChunks = undefined;
10801
+ }
10802
+ }
10803
+
10804
+ class PromptStream {
10805
+ session;
10806
+ sessionId;
10807
+ toolTracker;
10808
+ taskRegistry;
10809
+ activeTaskIds;
10810
+ pendingApiRetryMessageId;
10811
+ flushIntervalMs;
10812
+ maxBlockIndex;
10813
+ streamEventFlushTimer = null;
10814
+ rows = [];
10815
+ sharedRows;
10816
+ rowByApiMessage = new Map;
10817
+ rowByToolUseId = new Map;
10818
+ currentRow = null;
10819
+ rowSequence = 0;
10820
+ parentTaskByApiMessage = new Map;
10821
+ currentStreamApiMessageId = null;
10822
+ syntheticMessageKeyCounter = 0;
10823
+ dirtyRows = new Set;
10824
+ rowOfMessage = new Map;
10825
+ outputSeen = false;
10826
+ blockTarget = null;
10827
+ flushOnTimer = () => {
10828
+ this.streamEventFlushTimer = null;
10829
+ this.flushStreamedAssistantMessage();
10830
+ };
10831
+ constructor(session, sessionId, options) {
10832
+ this.session = session;
10833
+ this.sessionId = sessionId;
10834
+ this.flushIntervalMs = options.flushIntervalMs;
10835
+ this.maxBlockIndex = options.maxBlockIndex;
10836
+ this.toolTracker = options.toolTracker ?? new ToolTracker;
10837
+ this.taskRegistry = session.taskRegistry ??= new TaskRegistry;
10838
+ this.activeTaskIds = options.activeTaskIds ?? new Set;
10839
+ let sharedState = sessionStreamRows.get(session);
10840
+ if (!sharedState || sharedState.messages !== session.messages) {
10841
+ sharedState = { messages: session.messages, rows: new Map };
10842
+ sessionStreamRows.set(session, sharedState);
10843
+ }
10844
+ this.sharedRows = sharedState.rows;
10845
+ }
10846
+ get currentAssistantMessage() {
10847
+ return this.currentRow?.message ?? null;
10848
+ }
10849
+ get assistantOutputSeen() {
10850
+ return this.outputSeen;
10851
+ }
10852
+ forgetRow(row) {
10853
+ if (this.dirtyRows.delete(row))
10854
+ this.materializeRow(row, true);
10758
10855
  for (const apiMessageId of row.blocksByApiMessage.keys()) {
10759
- if (rowByApiMessage.get(apiMessageId) !== row)
10856
+ if (this.rowByApiMessage.get(apiMessageId) !== row)
10760
10857
  continue;
10761
- rowByApiMessage.delete(apiMessageId);
10762
- parentTaskByApiMessage.delete(apiMessageId);
10858
+ this.rowByApiMessage.delete(apiMessageId);
10859
+ this.parentTaskByApiMessage.delete(apiMessageId);
10763
10860
  }
10764
10861
  for (const toolUseId of row.toolUseIds) {
10765
- if (rowByToolUseId.get(toolUseId) === row)
10766
- rowByToolUseId.delete(toolUseId);
10862
+ if (this.rowByToolUseId.get(toolUseId) === row)
10863
+ this.rowByToolUseId.delete(toolUseId);
10767
10864
  }
10768
10865
  if (row.message)
10769
- rowOfMessage.delete(row.message);
10770
- };
10771
- const createRow = (key, modelId) => {
10866
+ this.rowOfMessage.delete(row.message);
10867
+ }
10868
+ createRow(key, modelId) {
10869
+ const { rows, sharedRows, session } = this;
10772
10870
  const shared = sharedRows.get(key);
10773
10871
  if (shared && (!shared.message || session.messages.includes(shared.message))) {
10774
10872
  rows.push(shared);
10775
10873
  if (shared.message)
10776
- rowOfMessage.set(shared.message, shared);
10874
+ this.rowOfMessage.set(shared.message, shared);
10777
10875
  while (rows.length > MAX_TRACKED_STREAM_ROWS)
10778
- forgetRow(rows.shift());
10876
+ this.forgetRow(rows.shift());
10779
10877
  for (const id of shared.toolUseIds)
10780
- rowByToolUseId.set(id, shared);
10878
+ this.rowByToolUseId.set(id, shared);
10781
10879
  return shared;
10782
10880
  }
10783
10881
  const row = {
10784
- sequence: rowSequence += 1,
10882
+ sequence: this.rowSequence += 1,
10785
10883
  key,
10786
10884
  message: null,
10787
10885
  blocksByApiMessage: new Map,
@@ -10803,41 +10901,35 @@ function createPromptStreamState(session, sessionId, options) {
10803
10901
  rows.push(row);
10804
10902
  while (rows.length > MAX_TRACKED_STREAM_ROWS) {
10805
10903
  const oldest = rows.shift();
10806
- forgetRow(oldest);
10904
+ this.forgetRow(oldest);
10807
10905
  }
10808
10906
  return row;
10809
- };
10810
- const rowForApiMessage = (messageKey, opensRow, parentToolUseId, modelId) => {
10811
- const known = rowByApiMessage.get(messageKey);
10907
+ }
10908
+ rowForApiMessage(messageKey, opensRow, parentToolUseId, modelId) {
10909
+ const known = this.rowByApiMessage.get(messageKey);
10812
10910
  if (known)
10813
10911
  return known;
10814
10912
  let row;
10815
10913
  if (!opensRow) {
10816
- row = (parentToolUseId ? rowByToolUseId.get(parentToolUseId) : undefined) ?? currentRow ?? undefined;
10914
+ row = (parentToolUseId ? this.rowByToolUseId.get(parentToolUseId) : undefined) ?? this.currentRow ?? undefined;
10817
10915
  }
10818
10916
  if (!row) {
10819
- flushStreamedAssistantMessage();
10820
- row = createRow(messageKey, modelId);
10821
- currentRow = row;
10917
+ this.flushStreamedAssistantMessage();
10918
+ row = this.createRow(messageKey, modelId);
10919
+ this.currentRow = row;
10822
10920
  }
10823
- rowByApiMessage.set(messageKey, row);
10921
+ this.rowByApiMessage.set(messageKey, row);
10824
10922
  return row;
10825
- };
10826
- const getBlocks = (row, messageKey) => {
10923
+ }
10924
+ getBlocks(row, messageKey) {
10827
10925
  let blocks = row.blocksByApiMessage.get(messageKey);
10828
10926
  if (!blocks) {
10829
10927
  blocks = new Map;
10830
10928
  row.blocksByApiMessage.set(messageKey, blocks);
10831
10929
  }
10832
10930
  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) => {
10931
+ }
10932
+ rebuildOrderedParts(row) {
10841
10933
  const located = [];
10842
10934
  for (const [messageKey, blocks] of row.blocksByApiMessage) {
10843
10935
  const blockIndices = Array.from(blocks.keys()).sort((a, b) => a - b);
@@ -10864,17 +10956,14 @@ function createPromptStreamState(session, sessionId, options) {
10864
10956
  blocks.delete(blockIndex);
10865
10957
  if (entry.type === "tool-ref") {
10866
10958
  row.toolUseIds.delete(entry.value);
10867
- if (rowByToolUseId.get(entry.value) === row)
10868
- rowByToolUseId.delete(entry.value);
10959
+ if (this.rowByToolUseId.get(entry.value) === row)
10960
+ this.rowByToolUseId.delete(entry.value);
10869
10961
  }
10870
10962
  }
10871
10963
  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) => {
10964
+ }
10965
+ emitRow(row) {
10966
+ const { session, sessionId } = this;
10878
10967
  const message = row.message;
10879
10968
  if (!message)
10880
10969
  return;
@@ -10912,34 +11001,36 @@ function createPromptStreamState(session, sessionId, options) {
10912
11001
  revision: message.revision
10913
11002
  }
10914
11003
  });
10915
- };
10916
- const newestRowMessage = () => {
10917
- for (let index = rows.length - 1;index >= 0; index -= 1) {
10918
- const message = rows[index].message;
11004
+ }
11005
+ newestRowMessage() {
11006
+ for (let index = this.rows.length - 1;index >= 0; index -= 1) {
11007
+ const message = this.rows[index].message;
10919
11008
  if (message)
10920
11009
  return message;
10921
11010
  }
10922
11011
  return null;
10923
- };
10924
- const insertAfterAnchor = (row, message) => {
10925
- const anchorIndex = row.anchor ? session.messages.lastIndexOf(row.anchor) : -1;
11012
+ }
11013
+ insertAfterAnchor(row, message) {
11014
+ const messages = this.session.messages;
11015
+ const anchorIndex = row.anchor ? messages.lastIndexOf(row.anchor) : -1;
10926
11016
  if (anchorIndex < 0) {
10927
- session.messages.push(message);
11017
+ messages.push(message);
10928
11018
  return;
10929
11019
  }
10930
11020
  let index = anchorIndex + 1;
10931
- while (index < session.messages.length) {
10932
- const owner = rowOfMessage.get(session.messages[index]);
11021
+ while (index < messages.length) {
11022
+ const owner = this.rowOfMessage.get(messages[index]);
10933
11023
  if (!owner || owner.sequence > row.sequence)
10934
11024
  break;
10935
11025
  index += 1;
10936
11026
  }
10937
- session.messages.splice(index, 0, message);
10938
- };
10939
- const materializeRow = (row, rebuildBlocks) => {
11027
+ messages.splice(index, 0, message);
11028
+ }
11029
+ materializeRow(row, rebuildBlocks) {
11030
+ const { session } = this;
10940
11031
  if (rebuildBlocks)
10941
- rebuildOrderedParts(row);
10942
- const parts = buildMessageParts(row.orderedParts, toolTracker);
11032
+ this.rebuildOrderedParts(row);
11033
+ const parts = buildMessageParts(row.orderedParts, this.toolTracker);
10943
11034
  const content = getMessageTextFromParts(parts);
10944
11035
  if (!row.message) {
10945
11036
  if (parts.length === 0 && !content)
@@ -10953,9 +11044,9 @@ function createPromptStreamState(session, sessionId, options) {
10953
11044
  ...row.modelId ? { modelId: row.modelId } : {},
10954
11045
  ...row.sdkUuid ? { sdkUuid: row.sdkUuid } : {}
10955
11046
  };
10956
- rowOfMessage.set(row.message, row);
10957
- insertAfterAnchor(row, row.message);
10958
- assistantOutputSeen = true;
11047
+ this.rowOfMessage.set(row.message, row);
11048
+ this.insertAfterAnchor(row, row.message);
11049
+ this.outputSeen = true;
10959
11050
  } else {
10960
11051
  row.message.content = content;
10961
11052
  row.message.parts = parts;
@@ -10968,32 +11059,26 @@ function createPromptStreamState(session, sessionId, options) {
10968
11059
  (session.revisedRows ??= new RecentlyRevisedRows).note(row.message.id);
10969
11060
  }
10970
11061
  markTranscriptChanged(session);
10971
- emitRow(row);
10972
- };
10973
- function flushStreamedAssistantMessage() {
10974
- if (streamEventFlushTimer) {
10975
- clearTimeout(streamEventFlushTimer);
10976
- streamEventFlushTimer = null;
10977
- }
10978
- if (dirtyRows.size === 0)
11062
+ this.emitRow(row);
11063
+ }
11064
+ flushStreamedAssistantMessage() {
11065
+ this.clearFlushTimer();
11066
+ if (this.dirtyRows.size === 0)
10979
11067
  return;
10980
- const pending = [...dirtyRows].sort((a, b) => a.sequence - b.sequence);
10981
- dirtyRows.clear();
11068
+ const pending = [...this.dirtyRows].sort((a, b) => a.sequence - b.sequence);
11069
+ this.dirtyRows.clear();
10982
11070
  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}`;
11071
+ this.materializeRow(row, true);
11072
+ }
11073
+ scheduleStreamedAssistantMessageFlush(row) {
11074
+ this.dirtyRows.add(row);
11075
+ this.streamEventFlushTimer ??= setTimeout(this.flushOnTimer, this.flushIntervalMs);
11076
+ }
11077
+ applyAssistantRecord(record) {
11078
+ const messageKey = record.apiMessageId ?? record.uuid ?? `assistant-${this.syntheticMessageKeyCounter += 1}`;
10994
11079
  const opensRow = record.isRoot && record.apiMessageId !== undefined;
10995
- const row = rowForApiMessage(messageKey, opensRow, record.parentToolUseId, record.modelId);
10996
- const blocks = getBlocks(row, messageKey);
11080
+ const row = this.rowForApiMessage(messageKey, opensRow, record.parentToolUseId, record.modelId);
11081
+ const blocks = this.getBlocks(row, messageKey);
10997
11082
  const blockIndexBase = row.finalizedBlockCounts.get(messageKey) ?? 0;
10998
11083
  for (const part of record.orderedParts) {
10999
11084
  const blockIndex = blockIndexBase + (part.blockOffset ?? 0);
@@ -11005,7 +11090,7 @@ function createPromptStreamState(session, sessionId, options) {
11005
11090
  });
11006
11091
  if (part.type === "tool-ref") {
11007
11092
  row.toolUseIds.add(part.value);
11008
- rowByToolUseId.set(part.value, row);
11093
+ this.rowByToolUseId.set(part.value, row);
11009
11094
  }
11010
11095
  }
11011
11096
  row.finalizedBlockCounts.set(messageKey, blockIndexBase + record.contentBlockCount);
@@ -11013,15 +11098,15 @@ function createPromptStreamState(session, sessionId, options) {
11013
11098
  row.modelId = record.modelId;
11014
11099
  if (record.isRoot && record.uuid)
11015
11100
  row.sdkUuid = record.uuid;
11016
- dirtyRows.delete(row);
11017
- materializeRow(row, true);
11018
- };
11019
- const refreshToolRows = (toolUseIds) => {
11101
+ this.dirtyRows.delete(row);
11102
+ this.materializeRow(row, true);
11103
+ }
11104
+ refreshToolRows(toolUseIds) {
11020
11105
  const affected = new Set;
11021
- if (currentRow)
11022
- affected.add(currentRow);
11106
+ if (this.currentRow)
11107
+ affected.add(this.currentRow);
11023
11108
  for (const toolUseId of toolUseIds) {
11024
- const row = rowByToolUseId.get(toolUseId);
11109
+ const row = this.rowByToolUseId.get(toolUseId);
11025
11110
  if (row)
11026
11111
  affected.add(row);
11027
11112
  }
@@ -11029,150 +11114,145 @@ function createPromptStreamState(session, sessionId, options) {
11029
11114
  for (const row of [...affected].sort((a, b) => a.sequence - b.sequence)) {
11030
11115
  if (!row.message)
11031
11116
  continue;
11032
- const rebuildBlocks = dirtyRows.delete(row);
11033
- materializeRow(row, rebuildBlocks);
11117
+ const rebuildBlocks = this.dirtyRows.delete(row);
11118
+ this.materializeRow(row, rebuildBlocks);
11034
11119
  published.add(row.message);
11035
11120
  }
11036
11121
  return published;
11037
- };
11038
- const applyPartialAssistantMessage = (partialMessage) => {
11122
+ }
11123
+ rowHoldingTool(toolUseId) {
11124
+ return this.rowByToolUseId.get(toolUseId)?.message ?? this.currentRow?.message ?? this.newestRowMessage();
11125
+ }
11126
+ publishRow(message) {
11127
+ const row = this.rowOfMessage.get(message);
11128
+ if (row)
11129
+ this.emitRow(row);
11130
+ }
11131
+ applyPartialAssistantMessage(partialMessage) {
11039
11132
  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);
11133
+ switch (streamEvent?.type) {
11134
+ case "message_start":
11135
+ return this.applyMessageStart(partialMessage, streamEvent);
11136
+ case "message_stop":
11137
+ return this.applyMessageStop();
11138
+ case "content_block_start": {
11139
+ const target = this.locateStreamBlock(partialMessage, streamEvent);
11140
+ return target ? this.applyContentBlockStart(target, streamEvent.content_block) : false;
11055
11141
  }
11056
- return false;
11057
- }
11058
- if (eventType === "message_stop") {
11059
- currentStreamApiMessageId = null;
11060
- return false;
11142
+ case "content_block_delta": {
11143
+ const target = this.locateStreamBlock(partialMessage, streamEvent);
11144
+ return target ? this.applyContentBlockDelta(target, streamEvent.delta) : false;
11145
+ }
11146
+ default:
11147
+ this.locateStreamBlock(partialMessage, streamEvent);
11148
+ return false;
11061
11149
  }
11062
- const blockIndex = Number.isInteger(streamEvent?.index) && streamEvent.index >= 0 && streamEvent.index <= maxBlockIndex ? streamEvent.index : undefined;
11063
- if (blockIndex === undefined) {
11064
- return false;
11150
+ }
11151
+ applyMessageStart(partialMessage, streamEvent) {
11152
+ const apiMessageId = typeof streamEvent.message?.id === "string" ? streamEvent.message.id : undefined;
11153
+ this.currentStreamApiMessageId = apiMessageId ?? null;
11154
+ const isRootAssistant = isRootAssistantRecord(partialMessage.parent_tool_use_id, partialMessage.isSidechain);
11155
+ const modelId = isRootAssistant ? normalizeBackendModelId(streamEvent.message?.model) : undefined;
11156
+ if (apiMessageId) {
11157
+ const parentTaskUseId = explicitParentTaskUseId(partialMessage);
11158
+ if (parentTaskUseId)
11159
+ this.parentTaskByApiMessage.set(apiMessageId, parentTaskUseId);
11160
+ const row = this.rowForApiMessage(apiMessageId, isRootAssistant, parentTaskUseId, modelId);
11161
+ if (modelId)
11162
+ row.modelId = modelId;
11163
+ this.getBlocks(row, apiMessageId);
11065
11164
  }
11066
- const messageKey = currentStreamApiMessageId ?? (typeof partialMessage.uuid === "string" ? partialMessage.uuid : undefined);
11067
- if (!messageKey) {
11165
+ return false;
11166
+ }
11167
+ applyMessageStop() {
11168
+ this.currentStreamApiMessageId = null;
11169
+ return false;
11170
+ }
11171
+ locateStreamBlock(partialMessage, streamEvent) {
11172
+ const blockIndex = Number.isInteger(streamEvent?.index) && streamEvent.index >= 0 && streamEvent.index <= this.maxBlockIndex ? streamEvent.index : undefined;
11173
+ if (blockIndex === undefined)
11174
+ return null;
11175
+ const messageKey = this.currentStreamApiMessageId ?? (typeof partialMessage.uuid === "string" ? partialMessage.uuid : undefined);
11176
+ if (!messageKey)
11177
+ return null;
11178
+ const explicitParent = explicitParentTaskUseId(partialMessage);
11179
+ const parentTaskUseId = explicitParent ?? this.parentTaskByApiMessage.get(messageKey);
11180
+ if (explicitParent)
11181
+ this.parentTaskByApiMessage.set(messageKey, explicitParent);
11182
+ const row = this.rowForApiMessage(messageKey, false, parentTaskUseId);
11183
+ const blocks = this.getBlocks(row, messageKey);
11184
+ const target = this.blockTarget ??= { row, blocks, blockIndex, messageKey, parentTaskUseId };
11185
+ target.row = row;
11186
+ target.blocks = blocks;
11187
+ target.blockIndex = blockIndex;
11188
+ target.messageKey = messageKey;
11189
+ target.parentTaskUseId = parentTaskUseId;
11190
+ return target;
11191
+ }
11192
+ applyContentBlockStart(target, contentBlock) {
11193
+ let type;
11194
+ let value;
11195
+ if (contentBlock?.type === "text") {
11196
+ type = "text";
11197
+ value = typeof contentBlock.text === "string" ? contentBlock.text : "";
11198
+ } else if (contentBlock?.type === "thinking") {
11199
+ type = "thinking";
11200
+ value = typeof contentBlock.thinking === "string" ? contentBlock.thinking : "";
11201
+ } else {
11068
11202
  return false;
11069
11203
  }
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
- }
11204
+ const existing = target.blocks.get(target.blockIndex);
11205
+ this.storeStreamedEntry(target, {
11206
+ type,
11207
+ value,
11208
+ timestamp: existing?.timestamp ?? new Date().toISOString(),
11209
+ messageUuid: target.messageKey,
11210
+ parentTaskUseId: target.parentTaskUseId
11211
+ });
11212
+ return true;
11213
+ }
11214
+ applyContentBlockDelta(target, delta) {
11215
+ if (delta?.type === "text_delta") {
11216
+ const chunk = typeof delta.text === "string" ? delta.text : "";
11217
+ this.storeStreamedEntry(target, this.appendStreamedDelta(target, "text", chunk));
11218
+ } else if (delta?.type === "thinking_delta") {
11219
+ const chunk = typeof delta.thinking === "string" ? delta.thinking : "";
11220
+ this.storeStreamedEntry(target, this.appendStreamedDelta(target, "thinking", chunk));
11123
11221
  } else {
11124
11222
  return false;
11125
11223
  }
11126
- entriesForMessage.set(blockIndex, entry);
11127
- scheduleStreamedAssistantMessageFlush(row);
11128
11224
  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
- }
11225
+ }
11226
+ appendStreamedDelta(target, type, chunk) {
11227
+ const entry = target.blocks.get(target.blockIndex);
11228
+ if (entry?.type === type) {
11229
+ entry.parentTaskUseId ??= target.parentTaskUseId;
11230
+ (entry.pendingChunks ??= []).push(chunk);
11231
+ return entry;
11174
11232
  }
11175
- };
11233
+ if (entry)
11234
+ materializeEntryValue(entry);
11235
+ return {
11236
+ type,
11237
+ value: `${entry?.value ?? ""}${chunk}`,
11238
+ timestamp: entry?.timestamp ?? new Date().toISOString(),
11239
+ messageUuid: target.messageKey,
11240
+ parentTaskUseId: entry?.parentTaskUseId ?? target.parentTaskUseId
11241
+ };
11242
+ }
11243
+ storeStreamedEntry(target, entry) {
11244
+ target.blocks.set(target.blockIndex, entry);
11245
+ this.scheduleStreamedAssistantMessageFlush(target.row);
11246
+ }
11247
+ beginPostSteerScope() {
11248
+ this.flushStreamedAssistantMessage();
11249
+ }
11250
+ clearFlushTimer() {
11251
+ if (this.streamEventFlushTimer) {
11252
+ clearTimeout(this.streamEventFlushTimer);
11253
+ this.streamEventFlushTimer = null;
11254
+ }
11255
+ }
11176
11256
  }
11177
11257
 
11178
11258
  // ../protocol/src/structured-output-text.ts
@@ -11769,7 +11849,10 @@ function createTurnState(session, sessionId, init) {
11769
11849
  settlingResult: false,
11770
11850
  localCommandOutputSeen: false,
11771
11851
  lastThinkingTokensEmitAt: 0,
11772
- compactedTokensBefore: undefined
11852
+ compactedTokensBefore: undefined,
11853
+ pendingPlanRejectionFeedback: null,
11854
+ planApprovedThisTurn: false,
11855
+ pendingPlanApprovalContinuation: null
11773
11856
  };
11774
11857
  const diagnostics = createBridgeDiagnostics("claude", {
11775
11858
  id: sessionId,
@@ -12332,7 +12415,7 @@ function commandBindingRevision(parts) {
12332
12415
  }
12333
12416
  return hash.toString(16).padStart(16, "0");
12334
12417
  }
12335
- function isRecord(value) {
12418
+ function isRecord2(value) {
12336
12419
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
12337
12420
  }
12338
12421
  function boundedIdentity(value, maxBytes) {
@@ -12352,7 +12435,7 @@ function displayCommandName(name) {
12352
12435
  return name.startsWith("/") || name.startsWith("$") ? name : `/${name}`;
12353
12436
  }
12354
12437
  function normalizeAvailability(value) {
12355
- if (!isRecord(value))
12438
+ if (!isRecord2(value))
12356
12439
  return;
12357
12440
  if (typeof value.state !== "string")
12358
12441
  return;
@@ -12367,7 +12450,7 @@ function normalizeAvailability(value) {
12367
12450
  };
12368
12451
  }
12369
12452
  function normalizeInputPolicy(value) {
12370
- if (!isRecord(value))
12453
+ if (!isRecord2(value))
12371
12454
  return;
12372
12455
  const policy = {};
12373
12456
  if (value.arguments === "none" || value.arguments === "optional" || value.arguments === "required") {
@@ -12382,7 +12465,7 @@ function normalizeInputPolicy(value) {
12382
12465
  return Object.keys(policy).length > 0 ? policy : undefined;
12383
12466
  }
12384
12467
  function normalizeCommandCataloguePayload(payload, options = {}) {
12385
- const record = isRecord(payload) ? payload : {};
12468
+ const record = isRecord2(payload) ? payload : {};
12386
12469
  const enhanced = record.catalogueVersion === NATIVE_AGENT_COMMAND_CATALOGUE_VERSION;
12387
12470
  const rows = Array.isArray(record.commands) ? record.commands : [];
12388
12471
  const byId = new Map;
@@ -12391,7 +12474,7 @@ function normalizeCommandCataloguePayload(payload, options = {}) {
12391
12474
  let aliasBudget = COMMAND_CATALOGUE_LIMITS.maxAliasesTotal;
12392
12475
  for (const candidate of rows.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
12393
12476
  const row = typeof candidate === "string" ? { name: candidate } : candidate;
12394
- if (!isRecord(row)) {
12477
+ if (!isRecord2(row)) {
12395
12478
  rejected += 1;
12396
12479
  continue;
12397
12480
  }
@@ -12471,7 +12554,8 @@ function normalizeCommandCataloguePayload(payload, options = {}) {
12471
12554
  ...status ? { status } : {},
12472
12555
  ...revision !== undefined ? { revision } : {},
12473
12556
  ...generation ? { generation } : {},
12474
- ...freshness ? { freshness } : {}
12557
+ ...freshness ? { freshness } : {},
12558
+ ...enhanced && status === "stale" && record.attachPending === true ? { attachPending: true } : {}
12475
12559
  };
12476
12560
  }
12477
12561
  var LEGACY_ID_PREFIX = "legacy:";
@@ -12484,7 +12568,7 @@ function commandCatalogueBytes(commands) {
12484
12568
  function parseBridgeCommandInvocation(value) {
12485
12569
  if (value === undefined || value === null)
12486
12570
  return { ok: true };
12487
- if (!isRecord(value))
12571
+ if (!isRecord2(value))
12488
12572
  return { ok: false, error: "command must be an object" };
12489
12573
  const id = boundedIdentity(value.id, COMMAND_CATALOGUE_LIMITS.maxIdBytes);
12490
12574
  const name = boundedIdentity(value.name, COMMAND_CATALOGUE_LIMITS.maxNameBytes);
@@ -12687,6 +12771,40 @@ function redactSecrets(value) {
12687
12771
  return result;
12688
12772
  }
12689
12773
 
12774
+ // ../../bridges/claude-bridge/src/services/mcp-runtime-status.ts
12775
+ function mcpScope(scope, name, source) {
12776
+ if (name === "orkestrator")
12777
+ return "orkestrator";
12778
+ if (source?.includes("plugin") || scope?.includes("plugin"))
12779
+ return "plugin";
12780
+ if (scope === "project" || scope === "local")
12781
+ return scope;
12782
+ return "user";
12783
+ }
12784
+ function mcpRuntimeState(status) {
12785
+ switch (status) {
12786
+ case "connected":
12787
+ case "failed":
12788
+ case "needs-auth":
12789
+ case "disabled":
12790
+ return status;
12791
+ case "pending":
12792
+ return "connecting";
12793
+ default:
12794
+ return "unknown";
12795
+ }
12796
+ }
12797
+ function initMcpServerStatus(raw) {
12798
+ const source = typeof raw.source === "string" ? raw.source : undefined;
12799
+ return {
12800
+ name: raw.name,
12801
+ status: mcpRuntimeState(raw.status),
12802
+ ...source ? { scope: mcpScope(source, raw.name) } : {},
12803
+ ...typeof raw.error === "string" && raw.error ? { error: raw.error } : {},
12804
+ ...Array.isArray(raw.tools) ? { tools: raw.tools.filter((tool) => typeof tool === "string") } : {}
12805
+ };
12806
+ }
12807
+
12690
12808
  // ../../bridges/claude-bridge/src/services/claude-sign-in.ts
12691
12809
  var AUTH_CACHE_TTL_MS = 30000;
12692
12810
  var FAILED_AUTH_CACHE_TTL_MS = 5000;
@@ -12935,15 +13053,6 @@ function createProbe() {
12935
13053
  ...claudeExecutableOptions()
12936
13054
  });
12937
13055
  }
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
13056
  function mcpTransport(status) {
12948
13057
  const type = status.config?.type;
12949
13058
  if (type === "stdio" || type === "sse" || type === "http")
@@ -12966,7 +13075,7 @@ function normalizeMcp(status) {
12966
13075
  id: status.name,
12967
13076
  name: status.serverInfo?.name || status.name,
12968
13077
  status: mappedStatus,
12969
- scope: mcpScope(status.scope, status.name),
13078
+ scope: mcpScope(status.scope, status.name, status.source),
12970
13079
  ...transport ? { transport } : {},
12971
13080
  toolCount: tools.length,
12972
13081
  ...tools.length ? { tools } : {},
@@ -13003,8 +13112,12 @@ async function performSessionMcpAction(sessionId, serverId, action) {
13003
13112
  const session = sessions.get(sessionId);
13004
13113
  if (!session)
13005
13114
  throw new Error("Session not found");
13115
+ const live = readableControl(session);
13116
+ if (!live && action !== "reconnect" && action !== "enable" && action !== "disable") {
13117
+ 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.");
13118
+ }
13006
13119
  let probe;
13007
- const control = readableControl(session) ?? (probe = createProbe());
13120
+ const control = live ?? (probe = createProbe());
13008
13121
  try {
13009
13122
  if (action === "reconnect")
13010
13123
  await control.reconnectMcpServer?.(serverId);
@@ -13248,7 +13361,7 @@ function boundedMessage(error, fallback) {
13248
13361
  const message = error instanceof Error && error.message ? error.message : fallback;
13249
13362
  return truncateUtf8(message, MAX_MESSAGE_BYTES);
13250
13363
  }
13251
- function isRecord2(value) {
13364
+ function isRecord3(value) {
13252
13365
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
13253
13366
  }
13254
13367
  function sdkRows(value) {
@@ -13256,7 +13369,7 @@ function sdkRows(value) {
13256
13369
  return [];
13257
13370
  const rows = [];
13258
13371
  for (const candidate of value.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
13259
- if (!isRecord2(candidate) || typeof candidate.name !== "string" || !candidate.name)
13372
+ if (!isRecord3(candidate) || typeof candidate.name !== "string" || !candidate.name)
13260
13373
  continue;
13261
13374
  rows.push(candidate);
13262
13375
  }
@@ -13438,7 +13551,7 @@ function recordInitCommandInventory(session) {
13438
13551
  reannotate(session);
13439
13552
  }
13440
13553
  function recordCommandsChanged(session, message) {
13441
- if (!isRecord2(message) || !Array.isArray(message.commands))
13554
+ if (!isRecord3(message) || !Array.isArray(message.commands))
13442
13555
  return;
13443
13556
  applyInventory(session, "replacement", sdkRows(message.commands));
13444
13557
  }
@@ -13587,10 +13700,10 @@ async function refreshClaudeCommandCatalogue(sessionId) {
13587
13700
  else
13588
13701
  failures.push(`${label}: ${boundedMessage(result.reason, "reload failed")}`);
13589
13702
  }
13590
- if (skills.status === "fulfilled" && isRecord2(skills.value)) {
13703
+ if (skills.status === "fulfilled" && isRecord3(skills.value)) {
13591
13704
  session.commandSkillNames = sdkRows(skills.value.skills).map((skill) => skill.name);
13592
13705
  }
13593
- const errorCount = plugins.status === "fulfilled" && isRecord2(plugins.value) ? plugins.value.error_count : undefined;
13706
+ const errorCount = plugins.status === "fulfilled" && isRecord3(plugins.value) ? plugins.value.error_count : undefined;
13594
13707
  if (typeof errorCount === "number" && errorCount > 0) {
13595
13708
  failures.push(`plugins: ${errorCount} failed to load`);
13596
13709
  }
@@ -13973,7 +14086,9 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
13973
14086
  "adopt_workflow",
13974
14087
  "cancel_build_pipeline",
13975
14088
  "cancel_multi_review",
14089
+ "cancel_schedule",
13976
14090
  "compare_project_files",
14091
+ "create_schedule",
13977
14092
  "create_ticket",
13978
14093
  "get_build_pipeline",
13979
14094
  "get_environment",
@@ -13995,6 +14110,7 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
13995
14110
  "list_node_environments",
13996
14111
  "list_nodes",
13997
14112
  "list_projects",
14113
+ "list_schedules",
13998
14114
  "list_tabs",
13999
14115
  "list_tickets",
14000
14116
  "list_workflows",
@@ -14968,7 +15084,6 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
14968
15084
  } : { type: "adaptive", display: "summarized" },
14969
15085
  ...effectiveClaudeContext1m(options) ? { betas: ["context-1m-2025-08-07"] } : {},
14970
15086
  extraArgs: effectiveClaudeChrome(policy, options) ? { chrome: null } : { "no-chrome": null },
14971
- ...typeof options?.maxBudgetUsd === "number" && options.maxBudgetUsd > 0 ? { maxBudgetUsd: options.maxBudgetUsd } : {},
14972
15087
  includePartialMessages: true,
14973
15088
  forwardSubagentText: true,
14974
15089
  allowedTools,
@@ -15022,6 +15137,7 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
15022
15137
  PostCompact: [{ hooks: [latestTurnHook((target) => target.recordPostCompact)] }]
15023
15138
  },
15024
15139
  includeHookEvents: true,
15140
+ toolConfig: { askUserQuestion: { previewFormat: "markdown" } },
15025
15141
  canUseTool: (toolName, input, permissionContext) => turnFor(permissionContext?.toolUseID).canUseTool(toolName, input, permissionContext),
15026
15142
  onElicitation: (request, elicitationOptions) => answerClaudeElicitation(session, sessionId, request, elicitationOptions),
15027
15143
  onUserDialog: async () => ({ behavior: "cancelled" })
@@ -15162,11 +15278,11 @@ function handleAssistantMessage(turn, message) {
15162
15278
  state: "success"
15163
15279
  });
15164
15280
  reclaimReleasedTurnForAssistant(turn, message);
15165
- if (stream.pendingPlanRejectionFeedback) {
15281
+ if (turn.pendingPlanRejectionFeedback) {
15166
15282
  debugLog("[session-manager] Claude responded after plan denial, clearing re-prompt feedback", {
15167
15283
  sessionId
15168
15284
  });
15169
- stream.pendingPlanRejectionFeedback = null;
15285
+ turn.pendingPlanRejectionFeedback = null;
15170
15286
  }
15171
15287
  const { orderedParts, newTaskIds, contentBlockCount } = parseMessageContent(message, toolTracker, turn.mcpServerNames, activeTaskIds, taskRegistry, receivedAt, recordUnknownContentBlock);
15172
15288
  const evidenceParent = message.parent_tool_use_id;
@@ -15309,12 +15425,7 @@ async function handleInitMessage(turn, message) {
15309
15425
  }
15310
15426
  const allMcpServers = initMsg.mcp_servers || [];
15311
15427
  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
- }));
15428
+ const mcpServerStatuses = regularMcpServers.map(initMcpServerStatus);
15318
15429
  const { statuses: pluginStatuses, errors: pluginErrors } = pluginStatusesFromInit(initMsg);
15319
15430
  const previousPluginErrors = new Set(session.pluginErrorSignatures);
15320
15431
  session.pluginErrorSignatures = pluginErrors.map(({ plugin, type, message, path, loaded }) => JSON.stringify([plugin, type, message, path, loaded]));
@@ -15686,14 +15797,6 @@ async function handleResultMessage(turn, message) {
15686
15797
  createdAt: new Date().toISOString()
15687
15798
  });
15688
15799
  }
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
15800
  if (turn.options?.outputSchema) {
15698
15801
  const failure = structuredOutputFailure("claude", resultMsg.subtype === "error_max_structured_output_retries" ? "schema_retry_exhausted" : "provider_error", resultError, {
15699
15802
  requestId: turn.structuredRequestId,
@@ -16140,7 +16243,7 @@ function handleUserMessage(turn, message) {
16140
16243
  for (const taskId of completedTaskIds) {
16141
16244
  activeTaskIds.delete(taskId);
16142
16245
  }
16143
- if (stream.planApprovedThisTurn) {
16246
+ if (turn.planApprovedThisTurn) {
16144
16247
  for (const tool of toolTracker.getTools()) {
16145
16248
  if (tool.toolName === "ExitPlanMode" && tool.toolState === "failure" && tool.toolUseId) {
16146
16249
  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 +16252,8 @@ function handleUserMessage(turn, message) {
16149
16252
  output: "Plan approved by the user. Proceeding with implementation.",
16150
16253
  error: undefined
16151
16254
  });
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.";
16255
+ if (!turn.pendingPlanApprovalContinuation) {
16256
+ 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
16257
  }
16155
16258
  }
16156
16259
  }
@@ -16226,214 +16329,223 @@ function boundedPlan(content) {
16226
16329
  planTruncated: true
16227
16330
  };
16228
16331
  }
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
16332
+ async function askUserQuestion(turn, input, permissionContext) {
16333
+ const { sessionId } = turn;
16334
+ const questions = Array.isArray(input.questions) ? input.questions : [];
16335
+ const questionTexts = questions.map((question) => question.question);
16336
+ if (new Set(questionTexts).size !== questionTexts.length) {
16337
+ return {
16338
+ behavior: "deny",
16339
+ message: "AskUserQuestion contains duplicate question text. Ask the questions again with distinct wording."
16249
16340
  };
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 });
16341
+ }
16342
+ const questionId = permissionContext?.requestId || generateMessageId();
16343
+ const requestedAt = Date.now();
16344
+ const questionRequest = {
16345
+ id: questionId,
16346
+ sessionId,
16347
+ questions,
16348
+ toolUseId: permissionContext?.toolUseID ?? questionId,
16349
+ requestedAt,
16350
+ expiresAt: requestedAt + QUESTION_TIMEOUT_MS
16351
+ };
16352
+ if (!canAdmitInteraction(questionRequest, "questions")) {
16353
+ return {
16354
+ behavior: "deny",
16355
+ message: "Too many pending interactions. Answer an existing request first."
16356
+ };
16357
+ }
16358
+ let rejectOnExpiry;
16359
+ const timeoutPromise = new Promise((_, reject) => {
16360
+ rejectOnExpiry = reject;
16361
+ });
16362
+ const deadline = new InteractionDeadline({
16363
+ requestedAt,
16364
+ windowMs: QUESTION_TIMEOUT_MS,
16365
+ onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Question", deadline)))
16366
+ });
16367
+ const releaseDeadline = registerInteractionDeadline("questions", questionRequest, deadline);
16368
+ pendingQuestions.set(questionId, questionRequest);
16369
+ eventEmitter.emit({
16370
+ type: "question.asked",
16371
+ sessionId,
16372
+ data: questionRequest
16373
+ });
16374
+ const answerPromise = new Promise((resolve, reject) => {
16375
+ questionResolvers.set(questionId, { resolve, reject });
16376
+ });
16377
+ try {
16378
+ const answers = await Promise.race([answerPromise, timeoutPromise]);
16379
+ console.log("[session-manager] Received question answers", {
16380
+ questionId,
16381
+ answerCount: Object.keys(answers).length
16274
16382
  });
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
- });
16383
+ return {
16384
+ behavior: "allow",
16385
+ updatedInput: {
16386
+ questions: input.questions,
16387
+ answers
16297
16388
  }
16298
- return { behavior: "deny", message };
16299
- } finally {
16300
- releaseDeadline();
16301
- pendingQuestions.delete(questionId);
16302
- questionResolvers.delete(questionId);
16389
+ };
16390
+ } catch (error) {
16391
+ console.error("[session-manager] Error waiting for answer:", error);
16392
+ const message = error instanceof Error ? error.message : "Question was cancelled";
16393
+ if (pendingQuestions.has(questionId)) {
16394
+ eventEmitter.emit({
16395
+ type: "question.answered",
16396
+ sessionId,
16397
+ data: { requestId: questionId, cancelled: true }
16398
+ });
16303
16399
  }
16400
+ return { behavior: "deny", message };
16401
+ } finally {
16402
+ releaseDeadline();
16403
+ pendingQuestions.delete(questionId);
16404
+ questionResolvers.delete(questionId);
16304
16405
  }
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
- });
16406
+ }
16407
+ async function enterPlanMode(turn, input) {
16408
+ const { session, sessionId } = turn;
16409
+ debugLog("[session-manager] EnterPlanMode requested", { sessionId });
16410
+ try {
16411
+ await applySessionPlanMode(session, true);
16412
+ } catch (error) {
16413
+ const message = error instanceof Error ? error.message : "Failed to persist plan mode";
16321
16414
  return {
16322
- behavior: "allow",
16323
- updatedInput: input
16415
+ behavior: "deny",
16416
+ message: `Plan mode could not be persisted safely: ${message}`
16324
16417
  };
16325
16418
  }
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
16419
+ eventEmitter.emit({
16420
+ type: "plan.enter-requested",
16421
+ sessionId,
16422
+ data: { sessionId }
16423
+ });
16424
+ return {
16425
+ behavior: "allow",
16426
+ updatedInput: input
16427
+ };
16428
+ }
16429
+ async function requestPlanApproval(turn, input, permissionContext) {
16430
+ const { session, sessionId } = turn;
16431
+ debugLog("[session-manager] ExitPlanMode requested, waiting for user approval", {
16432
+ sessionId
16433
+ });
16434
+ const approvalId = permissionContext?.requestId || generateMessageId();
16435
+ const directPlan = boundedPlan(input?.plan);
16436
+ const requestedAt = Date.now();
16437
+ const approvalRequest = {
16438
+ id: approvalId,
16439
+ sessionId,
16440
+ toolUseId: permissionContext?.toolUseID ?? approvalId,
16441
+ ...directPlan,
16442
+ requestedAt,
16443
+ expiresAt: requestedAt + PLAN_APPROVAL_TIMEOUT_MS
16444
+ };
16445
+ if (!canAdmitInteraction(approvalRequest, "approvals")) {
16446
+ return {
16447
+ behavior: "deny",
16448
+ message: "Too many pending interactions. Answer an existing request first."
16340
16449
  };
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
16450
+ }
16451
+ let rejectOnExpiry;
16452
+ const timeoutPromise = new Promise((_, reject) => {
16453
+ rejectOnExpiry = reject;
16454
+ });
16455
+ const deadline = new InteractionDeadline({
16456
+ requestedAt,
16457
+ windowMs: PLAN_APPROVAL_TIMEOUT_MS,
16458
+ onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Plan approval", deadline)))
16459
+ });
16460
+ const releaseDeadline = registerInteractionDeadline("plan-approvals", approvalRequest, deadline);
16461
+ pendingPlanApprovals.set(approvalId, approvalRequest);
16462
+ const approvalPromise = new Promise((resolve, reject) => {
16463
+ planApprovalResolvers.set(approvalId, { resolve, reject });
16464
+ });
16465
+ eventEmitter.emit({
16466
+ type: "plan.approval-requested",
16467
+ sessionId,
16468
+ data: approvalRequest
16469
+ });
16470
+ try {
16471
+ const response = await Promise.race([approvalPromise, timeoutPromise]);
16472
+ console.log("[session-manager] Plan approval result", {
16473
+ approvalId,
16474
+ approved: response.approved,
16475
+ hasFeedback: typeof response.feedback === "string" && response.feedback.length > 0
16365
16476
  });
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) {
16477
+ if (response.approved) {
16478
+ try {
16479
+ const control = session.queryControl;
16480
+ if (!control?.setPermissionMode)
16481
+ throw new Error("Claude permission control is unavailable");
16482
+ await control.setPermissionMode(claudePermissionMode(claudeTurnPolicy(session.executionPolicy, turn.options?.readOnly), turn.options?.approvalMode));
16483
+ await applySessionPlanMode(session, false);
16484
+ } catch (error) {
16374
16485
  try {
16375
16486
  const control = session.queryControl;
16376
16487
  if (!control?.setPermissionMode)
16377
16488
  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
- };
16489
+ await control.setPermissionMode("plan");
16490
+ } catch {
16491
+ abortSession(sessionId);
16394
16492
  }
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.`;
16493
+ const message = error instanceof Error ? error.message : "Failed to persist plan mode";
16411
16494
  return {
16412
16495
  behavior: "deny",
16413
- message: denyMessage
16496
+ message: `Plan mode could not be exited safely: ${message}`
16414
16497
  };
16415
16498
  }
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);
16499
+ turn.planApprovedThisTurn = true;
16500
+ eventEmitter.emit({
16501
+ type: "plan.exit-requested",
16502
+ sessionId,
16503
+ data: { sessionId }
16504
+ });
16505
+ return {
16506
+ behavior: "allow",
16507
+ updatedInput: input
16508
+ };
16509
+ } else {
16510
+ const feedbackMessage = response.feedback ? `User feedback: "${response.feedback}"` : "No specific feedback was provided.";
16511
+ const denyMessage = `User rejected the plan. ${feedbackMessage} Please revise your approach based on this feedback.`;
16512
+ turn.pendingPlanRejectionFeedback = response.feedback ? `I've reviewed the plan and I'd like changes: ${response.feedback}
16513
+
16514
+ 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.`;
16515
+ return {
16516
+ behavior: "deny",
16517
+ message: denyMessage
16518
+ };
16519
+ }
16520
+ } catch (error) {
16521
+ console.error("[session-manager] Error waiting for plan approval:", error);
16522
+ const errorMessage = error instanceof Error ? error.message : "Plan approval was cancelled";
16523
+ if (pendingPlanApprovals.has(approvalId)) {
16524
+ eventEmitter.emit({
16525
+ type: "plan.approval-responded",
16526
+ sessionId,
16527
+ data: {
16528
+ requestId: approvalId,
16529
+ approved: false,
16530
+ cancelled: true
16531
+ }
16532
+ });
16435
16533
  }
16534
+ return { behavior: "deny", message: errorMessage };
16535
+ } finally {
16536
+ releaseDeadline();
16537
+ pendingPlanApprovals.delete(approvalId);
16538
+ planApprovalResolvers.delete(approvalId);
16436
16539
  }
16540
+ }
16541
+ async function turnCanUseTool(turn, toolName, input, permissionContext) {
16542
+ const { session, sessionId } = turn;
16543
+ if (toolName === "AskUserQuestion")
16544
+ return askUserQuestion(turn, input, permissionContext);
16545
+ if (toolName === "EnterPlanMode")
16546
+ return enterPlanMode(turn, input);
16547
+ if (toolName === "ExitPlanMode")
16548
+ return requestPlanApproval(turn, input, permissionContext);
16437
16549
  if ((claudeWorkflowResultTools(turn.mcpServerNames).includes(toolName) || claudeArtifactServerTools(turn.mcpServerNames).includes(toolName)) && !session.executionPolicy?.toolPolicy?.deny?.includes(toolName)) {
16438
16550
  return { behavior: "allow", updatedInput: input };
16439
16551
  }
@@ -16735,9 +16847,9 @@ function settleTurnEnd(turn) {
16735
16847
  }
16736
16848
  return;
16737
16849
  }
16738
- if (stream.pendingPlanRejectionFeedback && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
16739
- const feedbackPrompt = stream.pendingPlanRejectionFeedback;
16740
- stream.pendingPlanRejectionFeedback = null;
16850
+ if (turn.pendingPlanRejectionFeedback && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
16851
+ const feedbackPrompt = turn.pendingPlanRejectionFeedback;
16852
+ turn.pendingPlanRejectionFeedback = null;
16741
16853
  debugLog("[session-manager] Re-prompting with plan rejection feedback", { sessionId });
16742
16854
  session.status = "idle";
16743
16855
  session.abortController = undefined;
@@ -16757,9 +16869,9 @@ function settleTurnEnd(turn) {
16757
16869
  failureLog: "[session-manager] Failed to re-prompt with plan feedback:"
16758
16870
  };
16759
16871
  }
16760
- if (stream.pendingPlanApprovalContinuation && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
16761
- const continuationPrompt = stream.pendingPlanApprovalContinuation;
16762
- stream.pendingPlanApprovalContinuation = null;
16872
+ if (turn.pendingPlanApprovalContinuation && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
16873
+ const continuationPrompt = turn.pendingPlanApprovalContinuation;
16874
+ turn.pendingPlanApprovalContinuation = null;
16763
16875
  debugLog("[session-manager] Re-prompting after approved-plan ExitPlanMode failure", {
16764
16876
  sessionId
16765
16877
  });
@@ -17326,14 +17438,14 @@ var SESSION_ACTIVITY_BATCH_LIMITS = {
17326
17438
  };
17327
17439
  var SESSION_ACTIVITY_STATES = new Set(["idle", "working", "waiting", "missing"]);
17328
17440
  var encoder3 = new TextEncoder;
17329
- function isRecord3(value) {
17441
+ function isRecord4(value) {
17330
17442
  return typeof value === "object" && value !== null && !Array.isArray(value);
17331
17443
  }
17332
17444
  function isBatchableSessionId(value) {
17333
17445
  return typeof value === "string" && value.length > 0 && encoder3.encode(value).byteLength <= SESSION_ACTIVITY_BATCH_LIMITS.maxSessionIdBytes;
17334
17446
  }
17335
17447
  function parseSessionActivityObservation(value) {
17336
- if (!isRecord3(value))
17448
+ if (!isRecord4(value))
17337
17449
  return;
17338
17450
  const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
17339
17451
  const { contentRevision } = value;
@@ -17367,7 +17479,7 @@ function isContentRevision(value) {
17367
17479
  return typeof value === "string" && value.length > 0 && value.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxContentRevisionLength;
17368
17480
  }
17369
17481
  function parseSessionActivityBatchRequest(value) {
17370
- if (!isRecord3(value))
17482
+ if (!isRecord4(value))
17371
17483
  return { ok: false, error: "Expected a JSON object" };
17372
17484
  if (value.version !== SESSION_ACTIVITY_BATCH_VERSION) {
17373
17485
  return { ok: false, error: "Unsupported activity batch version" };
@@ -17610,7 +17722,7 @@ function decodeLocator(locator) {
17610
17722
  }
17611
17723
  try {
17612
17724
  const body = JSON.parse(Buffer3.from(locator.slice(LOCATOR_PREFIX.length), "base64url").toString("utf8"));
17613
- if (!isRecord4(body))
17725
+ if (!isRecord5(body))
17614
17726
  return;
17615
17727
  const { m, p, d } = body;
17616
17728
  if (typeof m !== "string" || typeof d !== "string" || !Array.isArray(p))
@@ -17630,12 +17742,12 @@ var DETAIL_FIELDS = new Set([
17630
17742
  "toolDiff",
17631
17743
  "fileDataUrl"
17632
17744
  ]);
17633
- function isRecord4(value) {
17745
+ function isRecord5(value) {
17634
17746
  return value !== null && typeof value === "object" && !Array.isArray(value);
17635
17747
  }
17636
17748
  function heavyDiff(part) {
17637
17749
  const diff = part.toolDiff;
17638
- if (!isRecord4(diff))
17750
+ if (!isRecord5(diff))
17639
17751
  return;
17640
17752
  return typeof diff.diff === "string" || typeof diff.before === "string" || typeof diff.after === "string" ? diff : undefined;
17641
17753
  }
@@ -17657,7 +17769,7 @@ function detailPayload(part) {
17657
17769
  return Object.keys(payload).length > 0 ? payload : undefined;
17658
17770
  }
17659
17771
  function detailInputs(part) {
17660
- const diff = isRecord4(part.toolDiff) ? part.toolDiff : undefined;
17772
+ const diff = isRecord5(part.toolDiff) ? part.toolDiff : undefined;
17661
17773
  return [
17662
17774
  part.type,
17663
17775
  part.content,
@@ -17687,7 +17799,7 @@ function digestPayload(payload) {
17687
17799
  };
17688
17800
  }
17689
17801
  function topLevelKey(part, index) {
17690
- if (isRecord4(part)) {
17802
+ if (isRecord5(part)) {
17691
17803
  if (typeof part.sourcePartId === "string" && part.sourcePartId.length <= 512) {
17692
17804
  return `s:${part.sourcePartId}`;
17693
17805
  }
@@ -17698,7 +17810,7 @@ function topLevelKey(part, index) {
17698
17810
  return `i:${index}`;
17699
17811
  }
17700
17812
  function summarizePart(raw, messageId, path, depth) {
17701
- if (!isRecord4(raw) || depth > MAX_SUMMARY_DEPTH)
17813
+ if (!isRecord5(raw) || depth > MAX_SUMMARY_DEPTH)
17702
17814
  return raw;
17703
17815
  const part = raw;
17704
17816
  let summary;
@@ -17750,7 +17862,7 @@ function summarizePart(raw, messageId, path, depth) {
17750
17862
  if (changed)
17751
17863
  writable()[field] = next;
17752
17864
  }
17753
- if (isRecord4(part.task)) {
17865
+ if (isRecord5(part.task)) {
17754
17866
  const task = summarizePart(part.task, messageId, [...path, "task"], depth + 1);
17755
17867
  if (task !== part.task)
17756
17868
  writable().task = task;
@@ -17758,7 +17870,7 @@ function summarizePart(raw, messageId, path, depth) {
17758
17870
  return summary ?? part;
17759
17871
  }
17760
17872
  function isSummarizable(message) {
17761
- return isRecord4(message) && typeof message.id === "string" && Array.isArray(message.parts);
17873
+ return isRecord5(message) && typeof message.id === "string" && Array.isArray(message.parts);
17762
17874
  }
17763
17875
  function summarizeBridgeMessage(message) {
17764
17876
  if (!isSummarizable(message))
@@ -17784,15 +17896,15 @@ function resolvePath2(message, path) {
17784
17896
  let current;
17785
17897
  if (top.startsWith("s:")) {
17786
17898
  const id = top.slice(2);
17787
- current = message.parts.find((part) => isRecord4(part) && part.sourcePartId === id);
17899
+ current = message.parts.find((part) => isRecord5(part) && part.sourcePartId === id);
17788
17900
  } else if (top.startsWith("t:")) {
17789
17901
  const id = top.slice(2);
17790
- current = message.parts.find((part) => isRecord4(part) && part.toolUseId === id && typeof part.sourcePartId !== "string");
17902
+ current = message.parts.find((part) => isRecord5(part) && part.toolUseId === id && typeof part.sourcePartId !== "string");
17791
17903
  } else if (top.startsWith("i:")) {
17792
17904
  current = message.parts[Number(top.slice(2))];
17793
17905
  }
17794
17906
  for (let index = 0;index < rest.length; index += 1) {
17795
- if (!isRecord4(current))
17907
+ if (!isRecord5(current))
17796
17908
  return;
17797
17909
  const field = rest[index];
17798
17910
  if (field === "task") {
@@ -17825,7 +17937,7 @@ function readBridgeTranscriptDetail(messages, locator) {
17825
17937
  if (!message)
17826
17938
  return { version: 1, status: "missing" };
17827
17939
  const part = resolvePath2(message, body.p);
17828
- if (!isRecord4(part))
17940
+ if (!isRecord5(part))
17829
17941
  return { version: 1, status: "missing" };
17830
17942
  const payload = detailPayload(part);
17831
17943
  if (!payload)
@@ -17928,7 +18040,7 @@ function decodePageCursor(cursor) {
17928
18040
  }
17929
18041
  try {
17930
18042
  const body = JSON.parse(Buffer3.from(cursor.slice(PAGE_CURSOR_PREFIX.length), "base64url").toString("utf8"));
17931
- if (!isRecord4(body))
18043
+ if (!isRecord5(body))
17932
18044
  return;
17933
18045
  const { g, e, b } = body;
17934
18046
  if (typeof g !== "string" || typeof e !== "string")
@@ -18229,7 +18341,7 @@ session.get("/:id", async (c) => {
18229
18341
  structuredOutputRequestId: sessionData.structuredOutputRequestId,
18230
18342
  structuredOutput: sessionData.structuredOutput,
18231
18343
  contextUsage: sessionData.inProgressUsage ?? sessionData.usage,
18232
- policy: sessionData.executionPolicy,
18344
+ policy: claudeReportedPolicy(sessionData),
18233
18345
  rateLimits: sessionData.rateLimits,
18234
18346
  promptSuggestion: sessionData.promptSuggestion,
18235
18347
  activity: sessionData.activity,
@@ -18329,7 +18441,7 @@ session.post("/:id/config", async (c) => {
18329
18441
  } else if (permissionMode === "plan" || permissionMode === "bypassPermissions") {
18330
18442
  await setSessionPreferences(sessionData.id, { planMode: permissionMode === "plan" });
18331
18443
  }
18332
- return c.json({ ok: true, policy: sessionData.executionPolicy });
18444
+ return c.json({ ok: true, policy: claudeReportedPolicy(sessionData) });
18333
18445
  });
18334
18446
  session.get("/:id/commands", async (c) => {
18335
18447
  try {