orkestrator 2.34.4-beta → 2.35.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.
@@ -2666,14 +2666,30 @@ import { join as join2 } from "node:path";
2666
2666
 
2667
2667
  // ../../bridges/claude-bridge/src/services/claude-home.ts
2668
2668
  import { homedir } from "node:os";
2669
- import { join } from "node:path";
2669
+ import { join, resolve } from "node:path";
2670
2670
  var overrideHome = null;
2671
+ var overrideAccountHome;
2672
+ var CLAUDE_ACCOUNT_HOME_ENV = "ORKESTRATOR_CLAUDE_ACCOUNT_HOME";
2671
2673
  function claudeHome() {
2672
2674
  return overrideHome ?? homedir();
2673
2675
  }
2674
2676
  function claudeJsonPath() {
2675
2677
  return join(claudeHome(), ".claude.json");
2676
2678
  }
2679
+ function claudeAccountJsonPath() {
2680
+ let home = overrideAccountHome;
2681
+ if (home === undefined) {
2682
+ if (overrideHome !== null)
2683
+ return null;
2684
+ const marked = process.env[CLAUDE_ACCOUNT_HOME_ENV]?.trim();
2685
+ const configDir = process.env.CLAUDE_CONFIG_DIR?.trim();
2686
+ home = marked && configDir && resolve(marked) === resolve(configDir) ? configDir : null;
2687
+ }
2688
+ if (!home)
2689
+ return null;
2690
+ const file = join(home, ".claude.json");
2691
+ return resolve(file) === resolve(claudeJsonPath()) ? null : file;
2692
+ }
2677
2693
  function claudeUserSettingsPath() {
2678
2694
  const configDir = overrideHome === null ? process.env.CLAUDE_CONFIG_DIR?.trim() : undefined;
2679
2695
  return join(configDir || join(claudeHome(), ".claude"), "settings.json");
@@ -4117,6 +4133,43 @@ import { tmpdir } from "node:os";
4117
4133
  import path from "node:path";
4118
4134
  import { promisify } from "node:util";
4119
4135
 
4136
+ // ../protocol/src/native-agent-goal.ts
4137
+ var NATIVE_AGENT_GOAL_LIMITS = Object.freeze({
4138
+ objectiveMaxChars: 4000,
4139
+ checkMaxBytes: 4 * 1024,
4140
+ reportMessageMaxBytes: 2 * 1024,
4141
+ checkOutputMaxBytes: 8 * 1024,
4142
+ statusDetailMaxBytes: 1024,
4143
+ maxIterationsDefault: 20,
4144
+ maxIterationsLimit: 100,
4145
+ noProgressLimit: 3,
4146
+ checkTimeoutDefaultMs: 10 * 60000,
4147
+ checkTimeoutMaxMs: 30 * 60000,
4148
+ verifierModelMaxChars: 200,
4149
+ verifierTimeoutDefaultMs: 30 * 60000,
4150
+ verdictSummaryMaxBytes: 2 * 1024,
4151
+ verdictReasonsMax: 16,
4152
+ verdictReasonMaxBytes: 512,
4153
+ historyLimit: 5
4154
+ });
4155
+ var AGENT_GOAL_TOOL_NAMES = Object.freeze(["report_goal"]);
4156
+ var GOAL_QUALIFIED_PLATFORMS = Object.freeze([
4157
+ "claude",
4158
+ "codex",
4159
+ "opencode",
4160
+ "cursor",
4161
+ "grok",
4162
+ "pi"
4163
+ ]);
4164
+ var GOAL_TOKEN_BUDGET_PLATFORMS = Object.freeze([
4165
+ "claude",
4166
+ "codex",
4167
+ "opencode",
4168
+ "cursor",
4169
+ "grok",
4170
+ "pi"
4171
+ ]);
4172
+
4120
4173
  // ../protocol/src/native-agent-transcript-patch.ts
4121
4174
  var FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
4122
4175
 
@@ -4517,6 +4570,7 @@ var PROJECTION_FIELD_SET = new Set([
4517
4570
  "queue",
4518
4571
  "asyncQuestionResponses",
4519
4572
  "contextUsage",
4573
+ "goal",
4520
4574
  "rateLimits",
4521
4575
  "runtime",
4522
4576
  "runtimeHealthAuthoritative",
@@ -4541,6 +4595,7 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4541
4595
  "queue",
4542
4596
  "asyncQuestionResponses",
4543
4597
  "contextUsage",
4598
+ "goal",
4544
4599
  "rateLimits",
4545
4600
  "runtime",
4546
4601
  "runtimeHealthAuthoritative",
@@ -4642,6 +4697,15 @@ async function runtimeEnvironmentForSessionQuery(policy, env = process.env, opti
4642
4697
  const ghConfigDir = await (options.ghConfigDir ?? emptyGhConfigDir)();
4643
4698
  return withheldGitHubCredentialEnvironment(env, { ghConfigDir });
4644
4699
  }
4700
+ var UNATTENDED_OVERLOAD_RETRY_ENV = {
4701
+ CLAUDE_CODE_OVERLOADED_RETRY_BASE_DELAY_MS: "4000",
4702
+ CLAUDE_CODE_OVERLOADED_RETRY_MAX_DELAY_MS: "64000"
4703
+ };
4704
+ function withUnattendedOverloadRetry(sessionPolicy, environment) {
4705
+ if (sessionPolicy?.id !== "pipeline")
4706
+ return environment;
4707
+ return { ...UNATTENDED_OVERLOAD_RETRY_ENV, ...environment };
4708
+ }
4645
4709
 
4646
4710
  // ../../bridges/claude-bridge/src/services/coordinator-skills.ts
4647
4711
  import { createHash as createHash4, randomBytes } from "node:crypto";
@@ -4669,16 +4733,19 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
4669
4733
  "add_ticket_comment",
4670
4734
  "address_multi_review",
4671
4735
  "adopt_workflow",
4736
+ "answer_worker_question",
4672
4737
  "cancel_build_pipeline",
4673
4738
  "cancel_graph_run",
4674
4739
  "cancel_multi_review",
4675
4740
  "cancel_schedule",
4741
+ "clear_worker_goal",
4676
4742
  "compare_project_files",
4677
4743
  "create_pr",
4678
4744
  "create_schedule",
4679
4745
  "create_ticket",
4680
4746
  "custom_fix_multi_review",
4681
4747
  "edit_worker_tab",
4748
+ "fork_worker",
4682
4749
  "get_build_pipeline",
4683
4750
  "get_environment",
4684
4751
  "get_feature_plan",
@@ -4691,6 +4758,9 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
4691
4758
  "get_tab_state",
4692
4759
  "get_tab_transcript",
4693
4760
  "get_ticket",
4761
+ "get_usage_limits",
4762
+ "get_worker_changes",
4763
+ "get_worker_goal",
4694
4764
  "launch_environment",
4695
4765
  "launch_feature_plan",
4696
4766
  "launch_graph",
@@ -4705,14 +4775,18 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
4705
4775
  "list_node_environments",
4706
4776
  "list_nodes",
4707
4777
  "list_projects",
4778
+ "list_provider_accounts",
4708
4779
  "list_schedules",
4709
4780
  "list_tabs",
4710
4781
  "list_tickets",
4782
+ "list_worker_commands",
4711
4783
  "list_workflows",
4712
4784
  "open_multi_review",
4713
4785
  "open_multi_review_fix",
4714
4786
  "open_worker_browser",
4715
4787
  "pause_build_pipeline",
4788
+ "pause_schedule",
4789
+ "pause_worker_goal",
4716
4790
  "propose_agents_md_update",
4717
4791
  "read_messages",
4718
4792
  "read_node_environment",
@@ -4721,18 +4795,26 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
4721
4795
  "request_project_file_update",
4722
4796
  "request_project_on_node",
4723
4797
  "request_root_changes",
4798
+ "resolve_conflicts",
4724
4799
  "resume_build_pipeline",
4800
+ "resume_schedule",
4801
+ "run_worker_command",
4725
4802
  "send_message",
4726
4803
  "send_node_message",
4727
4804
  "send_prompt_to_node_tab",
4728
4805
  "send_prompt_to_tab",
4806
+ "set_worker_goal",
4729
4807
  "start_build_pipeline",
4730
4808
  "start_environment",
4731
4809
  "start_multi_review",
4732
4810
  "start_node_environment",
4733
4811
  "stop_environment",
4734
4812
  "stop_node_environment",
4813
+ "stop_worker_turn",
4814
+ "switch_provider_account",
4815
+ "set_account_auto_switch",
4735
4816
  "transfer_files",
4817
+ "update_schedule",
4736
4818
  "update_ticket"
4737
4819
  ]);
4738
4820
 
@@ -8783,6 +8865,8 @@ function claudeLaunchConflict(runtime, request) {
8783
8865
  return "adaptive thinking";
8784
8866
  if (request.bypassCapable && !runtime.bypassCapable)
8785
8867
  return "bypass permission capability";
8868
+ if (request.repositoryInstructions !== undefined && runtime.repositoryInstructions !== request.repositoryInstructions)
8869
+ return "repository instructions";
8786
8870
  if (request.plugins !== undefined && runtime.plugins !== request.plugins)
8787
8871
  return "plugins";
8788
8872
  if (request.credentials !== undefined && runtime.credentials !== request.credentials)
@@ -8931,7 +9015,7 @@ function noMessages() {
8931
9015
  // ../../bridges/claude-bridge/src/services/session-manager-persistence.ts
8932
9016
  import { constants as constants2 } from "node:fs";
8933
9017
  import { lstat as lstat2, open as open2, realpath as realpath2 } from "node:fs/promises";
8934
- import { isAbsolute as isAbsolute3, join as join10, relative, resolve as resolve2, sep } from "node:path";
9018
+ import { isAbsolute as isAbsolute3, join as join10, relative, resolve as resolve3, sep } from "node:path";
8935
9019
 
8936
9020
  // ../protocol/src/node/command-change-journal.ts
8937
9021
  import { appendFile, mkdir as mkdir3, readFile as readFile5, rename as rename3, rm as rm3, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
@@ -9088,7 +9172,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
9088
9172
  import { rmSync } from "node:fs";
9089
9173
  import { copyFile, lstat, mkdtemp as mkdtemp2, rm as rm4 } from "node:fs/promises";
9090
9174
  import { tmpdir as tmpdir2 } from "node:os";
9091
- import { delimiter as delimiter2, isAbsolute as isAbsolute2, join as join6, resolve } from "node:path";
9175
+ import { delimiter as delimiter2, isAbsolute as isAbsolute2, join as join6, resolve as resolve2 } from "node:path";
9092
9176
  var DEFAULT_MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
9093
9177
  var DEFAULT_MAX_UNTRACKED_FILES = 5000;
9094
9178
  var DEFAULT_SLOW_SNAPSHOT_MS = 500;
@@ -9370,8 +9454,8 @@ class WorkspaceChangeProbe {
9370
9454
  return;
9371
9455
  return {
9372
9456
  root,
9373
- indexPath: isAbsolute2(index) ? index : resolve(cwd, index),
9374
- objectsPath: isAbsolute2(objects) ? objects : resolve(cwd, objects)
9457
+ indexPath: isAbsolute2(index) ? index : resolve2(cwd, index),
9458
+ objectsPath: isAbsolute2(objects) ? objects : resolve2(cwd, objects)
9375
9459
  };
9376
9460
  }).catch(() => {
9377
9461
  return;
@@ -11777,13 +11861,13 @@ class ClaudeAttachmentError extends Error {
11777
11861
  var BRIDGE_ATTACHMENT_ROOT_ENV = "ORKESTRATOR_BRIDGE_ATTACHMENT_ROOT";
11778
11862
  function configuredAttachmentRoot(env = process.env) {
11779
11863
  const value = env[BRIDGE_ATTACHMENT_ROOT_ENV]?.trim();
11780
- return value && isAbsolute3(value) ? resolve2(value) : undefined;
11864
+ return value && isAbsolute3(value) ? resolve3(value) : undefined;
11781
11865
  }
11782
11866
  function attachmentContainmentRoot(filePath, cwd, extraRoot = configuredAttachmentRoot()) {
11783
- if (extraRoot && isAbsolute3(filePath) && isPathWithin2(extraRoot, resolve2(filePath))) {
11867
+ if (extraRoot && isAbsolute3(filePath) && isPathWithin2(extraRoot, resolve3(filePath))) {
11784
11868
  return extraRoot;
11785
11869
  }
11786
- return resolve2(cwd);
11870
+ return resolve3(cwd);
11787
11871
  }
11788
11872
  function isPathWithin2(rootPath, targetPath) {
11789
11873
  const childPath = relative(rootPath, targetPath);
@@ -11831,7 +11915,7 @@ async function assertOpenedWorkspaceFile(targetPath, canonicalRoot, openedStats)
11831
11915
  }
11832
11916
  async function readWorkspaceImageAttachment(filePath, cwd, afterSymlinkValidation, afterCanonicalValidation, afterInitialValidation) {
11833
11917
  const lexicalRoot = attachmentContainmentRoot(filePath, cwd);
11834
- const targetPath = isAbsolute3(filePath) ? resolve2(filePath) : resolve2(lexicalRoot, filePath);
11918
+ const targetPath = isAbsolute3(filePath) ? resolve3(filePath) : resolve3(lexicalRoot, filePath);
11835
11919
  if (!isPathWithin2(lexicalRoot, targetPath)) {
11836
11920
  throw new ClaudeAttachmentError("attachment_outside_workspace", "Image attachment must be contained in the current workspace.");
11837
11921
  }
@@ -13605,7 +13689,11 @@ function claudeUserMcpQueryOptions(policy) {
13605
13689
  async function loadScopedMcpServers(cwd, scope) {
13606
13690
  if (scope === "none") {
13607
13691
  const sources = { user: "excluded", project: "excluded" };
13608
- return { configs: {}, revision: { fingerprint: revisionFingerprint(sources), sources, scope } };
13692
+ return {
13693
+ configs: {},
13694
+ layers: new Map,
13695
+ revision: { fingerprint: revisionFingerprint(sources), sources, scope }
13696
+ };
13609
13697
  }
13610
13698
  const path = claudeJsonPath();
13611
13699
  const [global2, projectGlobal, projectLocal] = await Promise.all([
@@ -13618,11 +13706,33 @@ async function loadScopedMcpServers(cwd, scope) {
13618
13706
  ...projectGlobal?.value,
13619
13707
  ...projectLocal?.value
13620
13708
  };
13709
+ const layers = new Map;
13710
+ for (const name of Object.keys(global2.value ?? {}))
13711
+ layers.set(name, "user");
13712
+ for (const name of Object.keys(projectGlobal?.value ?? {}))
13713
+ layers.set(name, "local");
13714
+ for (const name of Object.keys(projectLocal?.value ?? {}))
13715
+ layers.set(name, "project");
13621
13716
  const sources = {
13622
13717
  user: sourceDigest(global2.digest),
13623
13718
  project: projectLocal ? sourceDigest(projectLocal.digest) : "excluded"
13624
13719
  };
13625
- return { configs, revision: { fingerprint: revisionFingerprint(sources), sources, scope } };
13720
+ return {
13721
+ configs,
13722
+ layers,
13723
+ revision: { fingerprint: revisionFingerprint(sources), sources, scope }
13724
+ };
13725
+ }
13726
+ async function claudeAccountOnlyMcpServerNames(cwd, settingSources, declared, strictMcpConfig = false) {
13727
+ const path = claudeAccountJsonPath();
13728
+ if (!path || strictMcpConfig)
13729
+ return [];
13730
+ const [user, local] = await Promise.all([
13731
+ settingSources.includes("user") ? readJsonSliceCached(path, "mcpServers", (config) => config?.mcpServers) : undefined,
13732
+ settingSources.includes("local") ? readJsonSliceCached(path, `projects:${cwd}:mcpServers`, (config) => config?.projects?.[cwd]?.mcpServers) : undefined
13733
+ ]);
13734
+ const names = new Set([...Object.keys(user ?? {}), ...Object.keys(local ?? {})]);
13735
+ return Array.from(names).filter((name) => !declared.has(name)).sort();
13626
13736
  }
13627
13737
  function revisionFingerprint(sources) {
13628
13738
  return createHash9("sha256").update(`user=${sources.user}\x00project=${sources.project}`).digest("base64url");
@@ -13631,21 +13741,28 @@ async function getMcpRuntimeConfig(cwd, env = process.env, connection, sources =
13631
13741
  const scoped = await loadScopedMcpServers(cwd, normalizeScope(sources));
13632
13742
  const { revision } = scoped;
13633
13743
  const configs = allowConfigured ? Object.fromEntries(Object.entries(scoped.configs).filter(([name]) => allowConfigured(name))) : scoped.configs;
13634
- const agentServer = getOrkestratorAgentMcpServerFromConnection(connection) ?? getOrkestratorAgentMcpServer(env);
13635
13744
  const servers = toSdkServers(configs);
13745
+ const layers = new Map(Array.from(scoped.layers).filter(([name]) => Object.hasOwn(servers, name)));
13746
+ const agentServer = getOrkestratorAgentMcpServerFromConnection(connection) ?? getOrkestratorAgentMcpServer(env);
13636
13747
  if (agentServer) {
13637
13748
  servers[AGENT_MCP_SERVER_NAME] = agentServer;
13638
- if (connection?.design)
13749
+ layers.set(AGENT_MCP_SERVER_NAME, "orkestrator");
13750
+ if (connection?.design) {
13639
13751
  servers["orkestrator-design"] = {
13640
13752
  ...agentServer,
13641
13753
  url: new URL("/design-mcp", agentServer.url).toString()
13642
13754
  };
13755
+ layers.set("orkestrator-design", "orkestrator");
13756
+ }
13643
13757
  }
13644
13758
  const artifactServer = getOrkestratorArtifactMcpServer(connection);
13645
- if (artifactServer)
13759
+ if (artifactServer) {
13646
13760
  servers[ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME] = artifactServer;
13647
- else
13761
+ layers.set(ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME, "orkestrator");
13762
+ } else {
13648
13763
  delete servers[ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME];
13764
+ layers.delete(ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME);
13765
+ }
13649
13766
  const research = normalizeScope(sources) === "none" ? coordinatorResearchMcpServersFromEnv(env) : [];
13650
13767
  for (const server of research)
13651
13768
  servers[server.name] = { type: "http", url: server.url };
@@ -13657,6 +13774,7 @@ async function getMcpRuntimeConfig(cwd, env = process.env, connection, sources =
13657
13774
  ...artifactServer ? [ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME] : [],
13658
13775
  ...research.map((server) => server.name)
13659
13776
  ]),
13777
+ layers,
13660
13778
  revision
13661
13779
  };
13662
13780
  }
@@ -13664,130 +13782,508 @@ async function getMcpRuntimeConfig(cwd, env = process.env, connection, sources =
13664
13782
  // ../../bridges/claude-bridge/src/services/session-manager-commands.ts
13665
13783
  import { randomUUID as randomUUID4 } from "node:crypto";
13666
13784
 
13667
- // ../protocol/src/agent-command-catalogue.ts
13668
- var NATIVE_AGENT_COMMAND_CATALOGUE_VERSION = 1;
13669
- var COMMAND_CATALOGUE_LIMITS = Object.freeze({
13670
- maxCommands: 512,
13671
- maxNameBytes: 256,
13672
- maxIdBytes: 256,
13673
- maxAliasesPerCommand: 16,
13674
- maxAliasesTotal: 2048,
13675
- maxDescriptionBytes: 1000,
13676
- maxArgumentHintBytes: 512,
13677
- maxAvailabilityMessageBytes: 512,
13678
- maxBindingRevisionBytes: 128,
13679
- maxWireBytes: 512 * 1024,
13680
- maxArgumentBytes: 256 * 1024
13681
- });
13682
- var SOURCES = new Set([
13683
- "builtin",
13684
- "project",
13685
- "user",
13686
- "plugin",
13687
- "skill",
13688
- "template",
13689
- "extension",
13690
- "orkestrator",
13691
- "unknown"
13692
- ]);
13693
- var EXECUTION_KINDS = new Set(NATIVE_AGENT_COMMAND_EXECUTION_KINDS);
13694
- var ORIGINS2 = new Set(NATIVE_AGENT_COMMAND_ORIGINS);
13695
- var UNAVAILABLE_REASONS = new Set(NATIVE_AGENT_COMMAND_UNAVAILABLE_REASONS);
13696
- var ERROR_CODES = new Set(NATIVE_AGENT_COMMAND_CATALOGUE_ERROR_CODES);
13697
- var encoder = new TextEncoder;
13698
- function utf8ByteLength(value) {
13699
- if (/^[\x00-\x7f]*$/.test(value))
13700
- return value.length;
13701
- return encoder.encode(value).byteLength;
13785
+ // ../../bridges/claude-bridge/src/services/claude-model-catalog.ts
13786
+ import { homedir as homedir2 } from "node:os";
13787
+ import { join as join12 } from "node:path";
13788
+ import { stat as stat5 } from "node:fs/promises";
13789
+
13790
+ // ../../bridges/claude-bridge/src/services/live-claude-control.ts
13791
+ var LIVE_CLAUDE_READ_TIMEOUT_MS = 5000;
13792
+ function readsProbeSettings(settingSources) {
13793
+ if (settingSources === undefined)
13794
+ return false;
13795
+ try {
13796
+ const sources = JSON.parse(settingSources);
13797
+ return Array.isArray(sources) && ["user", "project", "local"].every((source) => sources.includes(source));
13798
+ } catch {
13799
+ return false;
13800
+ }
13702
13801
  }
13703
- function truncateUtf8(value, maxBytes) {
13704
- if (utf8ByteLength(value) <= maxBytes)
13705
- return value;
13706
- const marker = "…";
13707
- const budget = Math.max(0, maxBytes - utf8ByteLength(marker));
13708
- let bytes = 0;
13709
- let out = "";
13710
- for (const char of value) {
13711
- const size = utf8ByteLength(char);
13712
- if (bytes + size > budget)
13713
- break;
13714
- bytes += size;
13715
- out += char;
13802
+ function liveClaudeControls() {
13803
+ const runtimes = [];
13804
+ for (const session of sessions.values()) {
13805
+ const runtime = session.claudeRuntime;
13806
+ if (runtime?.state !== "open" || runtime.abortController.signal.aborted)
13807
+ continue;
13808
+ if (session.queryControlDraining === runtime.control || session.deleting)
13809
+ continue;
13810
+ if (!readsProbeSettings(runtime.launch.settingSources))
13811
+ continue;
13812
+ runtimes.push(runtime);
13716
13813
  }
13717
- return `${out}${marker}`;
13814
+ return runtimes.sort((left, right) => right.lastFrameAt - left.lastFrameAt).map((runtime) => runtime.control);
13718
13815
  }
13719
- function commandBindingRevision(parts) {
13720
- let hash = 0xcbf29ce484222325n;
13721
- const prime = 0x100000001b3n;
13722
- const mask = 0xffffffffffffffffn;
13723
- for (const part of parts) {
13724
- for (const byte of encoder.encode(part)) {
13725
- hash ^= BigInt(byte);
13726
- hash = hash * prime & mask;
13816
+ async function readFromLiveClaude(read, timeoutMs = LIVE_CLAUDE_READ_TIMEOUT_MS) {
13817
+ for (const control of liveClaudeControls()) {
13818
+ let timer;
13819
+ try {
13820
+ const answer = read(control);
13821
+ if (answer === undefined)
13822
+ continue;
13823
+ answer.catch(() => {
13824
+ return;
13825
+ });
13826
+ const value = await Promise.race([
13827
+ answer,
13828
+ new Promise((resolve) => {
13829
+ timer = setTimeout(() => resolve(undefined), timeoutMs);
13830
+ })
13831
+ ]);
13832
+ if (value !== undefined)
13833
+ return value;
13834
+ } catch (error) {
13835
+ debugLog("[live-claude-control] A running CLI could not answer:", error);
13836
+ } finally {
13837
+ clearTimeout(timer);
13727
13838
  }
13728
- hash ^= 0xffn;
13729
- hash = hash * prime & mask;
13730
13839
  }
13731
- return hash.toString(16).padStart(16, "0");
13732
- }
13733
- function isRecord2(value) {
13734
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
13840
+ return;
13735
13841
  }
13736
- function boundedIdentity(value, maxBytes) {
13737
- if (typeof value !== "string")
13738
- return;
13739
- if (value.length === 0 || value.trim() !== value || /\s/.test(value))
13842
+ function liveInitializationResult(control) {
13843
+ if (typeof control.initializationResult !== "function")
13740
13844
  return;
13741
- return utf8ByteLength(value) <= maxBytes ? value : undefined;
13845
+ return control.initializationResult().then((result) => result && typeof result === "object" ? result : undefined);
13742
13846
  }
13743
- function boundedText(value, maxBytes) {
13744
- if (typeof value !== "string")
13745
- return;
13746
- const trimmed = value.trim();
13747
- return trimmed ? truncateUtf8(trimmed, maxBytes) : undefined;
13847
+
13848
+ // ../../bridges/claude-bridge/src/services/claude-sign-in.ts
13849
+ var AUTH_CACHE_TTL_MS = 30000;
13850
+ var FAILED_AUTH_CACHE_TTL_MS = 5000;
13851
+ var CLAUDE_SIGN_IN = {
13852
+ kind: "terminal",
13853
+ hint: "Sign in to Claude from the sign-in card in this tab or from Settings → Claude."
13854
+ };
13855
+ var CLAUDE_SIGN_IN_REQUIRED_MESSAGE = "Claude isn't signed in. Sign in to Claude to keep working.";
13856
+ var authCache;
13857
+ var authRead;
13858
+ var authReadGeneration;
13859
+ var authRefreshPending = false;
13860
+ var authGeneration = 0;
13861
+ var turnAuthenticationFailedUntil = 0;
13862
+ var reuseLiveAccounts = true;
13863
+ function withTurnFailure(value) {
13864
+ return Date.now() < turnAuthenticationFailedUntil && value.state === "signed-in" ? { ...value, state: "expired" } : value;
13748
13865
  }
13749
- function displayCommandName(name) {
13750
- return name.startsWith("/") || name.startsWith("$") ? name : `/${name}`;
13866
+ function presentSource(value) {
13867
+ return value && value !== "none" ? value : undefined;
13751
13868
  }
13752
- function normalizeAvailability(value) {
13753
- if (!isRecord2(value))
13754
- return;
13755
- if (typeof value.state !== "string")
13756
- return;
13757
- const reason = typeof value.reason === "string" && UNAVAILABLE_REASONS.has(value.reason) ? value.reason : undefined;
13758
- const message = boundedText(value.message, COMMAND_CATALOGUE_LIMITS.maxAvailabilityMessageBytes);
13759
- if (value.state === "available")
13760
- return { state: "available" };
13869
+ function normalizeClaudeAccount(account) {
13870
+ const source = presentSource(account.apiKeySource) ?? presentSource(account.tokenSource);
13871
+ const externalProvider = account.apiProvider && account.apiProvider !== "firstParty" ? account.apiProvider : undefined;
13872
+ const signedIn = Boolean(source || account.subscriptionType || account.organization || externalProvider);
13873
+ const label = account.organization || account.subscriptionType || externalProvider;
13761
13874
  return {
13762
- state: "unavailable",
13763
- reason: reason ?? "unsupported",
13764
- ...message ? { message } : {}
13875
+ state: signedIn ? "signed-in" : "needs-auth",
13876
+ ...label ? {
13877
+ account: {
13878
+ label,
13879
+ ...account.subscriptionType ? { plan: account.subscriptionType } : {}
13880
+ }
13881
+ } : {},
13882
+ signIn: CLAUDE_SIGN_IN,
13883
+ signOut: false
13765
13884
  };
13766
13885
  }
13767
- function normalizeInputPolicy(value) {
13768
- if (!isRecord2(value))
13769
- return;
13770
- const policy = {};
13771
- if (value.arguments === "none" || value.arguments === "optional" || value.arguments === "required") {
13772
- policy.arguments = value.arguments;
13773
- }
13774
- if (value.attachments === "none" || value.attachments === "images" || value.attachments === "any") {
13775
- policy.attachments = value.attachments;
13886
+ function claudeAccountKey(status) {
13887
+ return JSON.stringify([status.state, status.account?.label, status.account?.plan]);
13888
+ }
13889
+ function claudeObservedAccountKey() {
13890
+ const value = authCache?.value;
13891
+ return value && value.state !== "unknown" ? claudeAccountKey(value) : undefined;
13892
+ }
13893
+ function liveSignedInAccount() {
13894
+ return readFromLiveClaude((control) => liveInitializationResult(control)?.then((result) => {
13895
+ const account = result?.account;
13896
+ if (!account || typeof account !== "object")
13897
+ return;
13898
+ const status = normalizeClaudeAccount(account);
13899
+ return status.state === "signed-in" ? status : undefined;
13900
+ }));
13901
+ }
13902
+ async function probeAccount() {
13903
+ if (reuseLiveAccounts) {
13904
+ const live = await liveSignedInAccount();
13905
+ if (live && reuseLiveAccounts)
13906
+ return live;
13776
13907
  }
13777
- if (value.busy === "queue" || value.busy === "idle" || value.busy === "running") {
13778
- policy.busy = value.busy;
13908
+ let probe;
13909
+ try {
13910
+ probe = controlOnlyQuery({
13911
+ cwd: process.env.CWD || process.cwd(),
13912
+ env: { ...process.env },
13913
+ ...claudeExecutableOptions()
13914
+ });
13915
+ return normalizeClaudeAccount(await probe.accountInfo());
13916
+ } finally {
13917
+ await Promise.resolve(probe?.close()).catch(() => {
13918
+ return;
13919
+ });
13779
13920
  }
13780
- return Object.keys(policy).length > 0 ? policy : undefined;
13781
13921
  }
13782
- function normalizeCommandCataloguePayload(payload, options = {}) {
13783
- const record = isRecord2(payload) ? payload : {};
13784
- const enhanced = record.catalogueVersion === NATIVE_AGENT_COMMAND_CATALOGUE_VERSION;
13785
- const rows = Array.isArray(record.commands) ? record.commands : [];
13786
- const byId = new Map;
13787
- let truncated = record.truncated === true || rows.length > COMMAND_CATALOGUE_LIMITS.maxCommands;
13788
- let rejected = 0;
13789
- let aliasBudget = COMMAND_CATALOGUE_LIMITS.maxAliasesTotal;
13790
- for (const candidate of rows.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
13922
+ function readClaudeAuthStatus() {
13923
+ if (authCache && authCache.expiresAt > Date.now()) {
13924
+ return Promise.resolve(withTurnFailure(authCache.value));
13925
+ }
13926
+ if (authRead)
13927
+ return authRead;
13928
+ const generation = authGeneration;
13929
+ const read = probeAccount().then((value) => ({ value, ttl: AUTH_CACHE_TTL_MS }), () => ({
13930
+ value: {
13931
+ state: "unknown",
13932
+ signIn: CLAUDE_SIGN_IN,
13933
+ signOut: false
13934
+ },
13935
+ ttl: FAILED_AUTH_CACHE_TTL_MS
13936
+ })).then(({ value, ttl }) => {
13937
+ if (generation === authGeneration) {
13938
+ authCache = { expiresAt: Date.now() + ttl, value };
13939
+ }
13940
+ return withTurnFailure(value);
13941
+ }).finally(() => {
13942
+ if (authRead !== read)
13943
+ return;
13944
+ authRead = undefined;
13945
+ authReadGeneration = undefined;
13946
+ if (authRefreshPending) {
13947
+ authRefreshPending = false;
13948
+ readClaudeAuthStatus();
13949
+ }
13950
+ });
13951
+ authReadGeneration = generation;
13952
+ authRead = read;
13953
+ return read;
13954
+ }
13955
+ function noteClaudeAuthenticationFailure() {
13956
+ reuseLiveAccounts = false;
13957
+ turnAuthenticationFailedUntil = Date.now() + AUTH_CACHE_TTL_MS;
13958
+ authCache = undefined;
13959
+ if (authRead && authReadGeneration !== authGeneration)
13960
+ authRefreshPending = true;
13961
+ readClaudeAuthStatus();
13962
+ }
13963
+ function noteClaudeAuthenticationWorked() {
13964
+ reuseLiveAccounts = true;
13965
+ authGeneration += 1;
13966
+ authRefreshPending = false;
13967
+ turnAuthenticationFailedUntil = 0;
13968
+ if (authCache?.value.state === "needs-auth")
13969
+ authCache = undefined;
13970
+ }
13971
+ function claudeReadiness() {
13972
+ const cached = authCache;
13973
+ if (cached?.value.state !== "needs-auth")
13974
+ return;
13975
+ if (cached.expiresAt <= Date.now())
13976
+ readClaudeAuthStatus();
13977
+ return {
13978
+ state: "authentication-required",
13979
+ message: CLAUDE_SIGN_IN_REQUIRED_MESSAGE,
13980
+ signIn: CLAUDE_SIGN_IN
13981
+ };
13982
+ }
13983
+
13984
+ // ../../bridges/claude-bridge/src/services/claude-model-catalog.ts
13985
+ var CLAUDE_MODEL_CACHE_TTL_MS = 60 * 60000;
13986
+ var CLAUDE_MODEL_PROBE_TIMEOUT_MS = 25000;
13987
+ var remembered;
13988
+ var liveAnswers = new WeakMap;
13989
+ var invalidLiveAnswers = new WeakSet;
13990
+ var generation = 0;
13991
+ var probe;
13992
+ function invalidateClaudeModelCatalog() {
13993
+ for (const control of liveClaudeControls())
13994
+ invalidLiveAnswers.add(control);
13995
+ remembered = undefined;
13996
+ generation += 1;
13997
+ }
13998
+ function catalogModels(models) {
13999
+ return models.map((model) => ({
14000
+ id: model.value,
14001
+ resolvedModel: model.resolvedModel,
14002
+ name: model.displayName,
14003
+ description: model.description,
14004
+ supportsFastMode: model.supportsFastMode,
14005
+ supportsEffort: model.supportsEffort,
14006
+ supportedEffortLevels: model.supportedEffortLevels,
14007
+ supportsAdaptiveThinking: model.supportsAdaptiveThinking,
14008
+ supportsAutoMode: model.supportsAutoMode
14009
+ }));
14010
+ }
14011
+ async function claudeCliIdentity() {
14012
+ const configDir = process.env.CLAUDE_CONFIG_DIR?.trim() || join12(homedir2(), ".claude");
14013
+ const executable = process.env.CLAUDE_CLI_PATH?.trim();
14014
+ const resolved = executable ? Bun.which(executable, { PATH: process.env.PATH }) || executable : undefined;
14015
+ const cwd = process.env.CWD || process.cwd();
14016
+ const paths = [
14017
+ resolved,
14018
+ join12(configDir, "settings.json"),
14019
+ join12(cwd, ".claude/settings.json"),
14020
+ join12(cwd, ".claude/settings.local.json")
14021
+ ];
14022
+ const files = await Promise.all(paths.map(async (path) => {
14023
+ const file = path ? await stat5(path).catch(() => {
14024
+ return;
14025
+ }) : undefined;
14026
+ return [path, file?.size, file?.mtimeMs, file?.ctimeMs];
14027
+ }));
14028
+ return JSON.stringify([configDir, cwd, files]);
14029
+ }
14030
+ function accountKeyOf(account) {
14031
+ if (!account || typeof account !== "object")
14032
+ return;
14033
+ return claudeAccountKey(normalizeClaudeAccount(account));
14034
+ }
14035
+ async function rememberModels(models, account, at = generation, cli, expiresAt) {
14036
+ if (!account || typeof account !== "object" || models.length === 0)
14037
+ return;
14038
+ const status = normalizeClaudeAccount(account);
14039
+ if (status.state !== "signed-in")
14040
+ return;
14041
+ const identity = cli ?? await claudeCliIdentity();
14042
+ if (at !== generation)
14043
+ return;
14044
+ remembered = {
14045
+ cli: identity,
14046
+ account: claudeAccountKey(status),
14047
+ models,
14048
+ expiresAt: expiresAt ?? Date.now() + CLAUDE_MODEL_CACHE_TTL_MS
14049
+ };
14050
+ }
14051
+ async function rememberedModels() {
14052
+ const entry = remembered;
14053
+ if (!entry || entry.expiresAt <= Date.now())
14054
+ return;
14055
+ const observed = claudeObservedAccountKey();
14056
+ if (observed !== undefined && observed !== entry.account)
14057
+ return;
14058
+ return entry.cli === await claudeCliIdentity() ? entry.models : undefined;
14059
+ }
14060
+ async function liveModels() {
14061
+ const observed = claudeObservedAccountKey();
14062
+ const at = generation;
14063
+ const cli = await claudeCliIdentity();
14064
+ return readFromLiveClaude(async (control) => {
14065
+ if (invalidLiveAnswers.has(control))
14066
+ return;
14067
+ let identity = liveAnswers.get(control);
14068
+ if (!identity) {
14069
+ identity = { cli, expiresAt: Date.now() + CLAUDE_MODEL_CACHE_TTL_MS };
14070
+ liveAnswers.set(control, identity);
14071
+ }
14072
+ if (identity.cli !== cli || identity.expiresAt <= Date.now())
14073
+ return;
14074
+ const result = await liveInitializationResult(control);
14075
+ if (at !== generation || invalidLiveAnswers.has(control))
14076
+ return;
14077
+ if (!Array.isArray(result?.models) || result.models.length === 0)
14078
+ return;
14079
+ const account = accountKeyOf(result.account);
14080
+ if (observed !== undefined && account !== undefined && account !== observed)
14081
+ return;
14082
+ return { models: result.models, account: result.account, at, ...identity };
14083
+ });
14084
+ }
14085
+ function fallbackCatalog() {
14086
+ return {
14087
+ source: "fallback",
14088
+ models: CLAUDE_FALLBACK_MODEL_CATALOG.map((model) => ({
14089
+ ...model,
14090
+ ...model.supportedEffortLevels ? { supportedEffortLevels: [...model.supportedEffortLevels] } : {}
14091
+ }))
14092
+ };
14093
+ }
14094
+ async function probeModels(at) {
14095
+ let q;
14096
+ let timer;
14097
+ try {
14098
+ const cwd = process.env.CWD || process.cwd();
14099
+ debugLog("[session-manager] Fetching supported models", { cwd });
14100
+ q = controlOnlyQuery({
14101
+ cwd,
14102
+ env: { ...process.env },
14103
+ ...claudeExecutableOptions()
14104
+ });
14105
+ const control = q;
14106
+ const answer = Promise.all([
14107
+ control.supportedModels(),
14108
+ typeof control.accountInfo === "function" ? control.accountInfo().catch(() => {
14109
+ return;
14110
+ }) : undefined
14111
+ ]);
14112
+ answer.catch(() => {
14113
+ return;
14114
+ });
14115
+ const [models, account] = await Promise.race([
14116
+ answer,
14117
+ new Promise((_resolve, reject) => {
14118
+ timer = setTimeout(() => reject(new Error("The model probe did not answer in time")), CLAUDE_MODEL_PROBE_TIMEOUT_MS);
14119
+ })
14120
+ ]);
14121
+ debugLog("[session-manager] Supported models fetched", { count: models.length });
14122
+ await rememberModels(models, account, at);
14123
+ return { source: "sdk", models: catalogModels(models) };
14124
+ } catch (error) {
14125
+ console.error("[session-manager] Error fetching supported models:", error);
14126
+ return fallbackCatalog();
14127
+ } finally {
14128
+ clearTimeout(timer);
14129
+ if (q?.return) {
14130
+ try {
14131
+ await q.return();
14132
+ } catch (error) {
14133
+ debugLog("[session-manager] Failed to clean up model query:", error);
14134
+ }
14135
+ }
14136
+ }
14137
+ }
14138
+ function sharedProbe() {
14139
+ if (probe?.generation === generation)
14140
+ return probe.read;
14141
+ const read = probeModels(generation).finally(() => {
14142
+ if (probe?.read === read)
14143
+ probe = undefined;
14144
+ });
14145
+ probe = { generation, read };
14146
+ return read;
14147
+ }
14148
+ async function getAvailableModelCatalog() {
14149
+ const live = liveClaudeControls().length ? await liveModels() : undefined;
14150
+ if (live) {
14151
+ debugLog("[session-manager] Supported models read from a running CLI", {
14152
+ count: live.models.length
14153
+ });
14154
+ await rememberModels(live.models, live.account, live.at, live.cli, live.expiresAt);
14155
+ return { source: "sdk", models: catalogModels(live.models) };
14156
+ }
14157
+ const known = await rememberedModels();
14158
+ if (known)
14159
+ return { source: "sdk", models: catalogModels(known) };
14160
+ return sharedProbe();
14161
+ }
14162
+
14163
+ // ../protocol/src/agent-command-catalogue.ts
14164
+ var NATIVE_AGENT_COMMAND_CATALOGUE_VERSION = 1;
14165
+ var COMMAND_CATALOGUE_LIMITS = Object.freeze({
14166
+ maxCommands: 512,
14167
+ maxNameBytes: 256,
14168
+ maxIdBytes: 256,
14169
+ maxAliasesPerCommand: 16,
14170
+ maxAliasesTotal: 2048,
14171
+ maxDescriptionBytes: 1000,
14172
+ maxArgumentHintBytes: 512,
14173
+ maxAvailabilityMessageBytes: 512,
14174
+ maxBindingRevisionBytes: 128,
14175
+ maxWireBytes: 512 * 1024,
14176
+ maxArgumentBytes: 256 * 1024
14177
+ });
14178
+ var SOURCES = new Set([
14179
+ "builtin",
14180
+ "project",
14181
+ "user",
14182
+ "plugin",
14183
+ "skill",
14184
+ "template",
14185
+ "extension",
14186
+ "orkestrator",
14187
+ "unknown"
14188
+ ]);
14189
+ var EXECUTION_KINDS = new Set(NATIVE_AGENT_COMMAND_EXECUTION_KINDS);
14190
+ var ORIGINS2 = new Set(NATIVE_AGENT_COMMAND_ORIGINS);
14191
+ var UNAVAILABLE_REASONS = new Set(NATIVE_AGENT_COMMAND_UNAVAILABLE_REASONS);
14192
+ var ERROR_CODES = new Set(NATIVE_AGENT_COMMAND_CATALOGUE_ERROR_CODES);
14193
+ var encoder = new TextEncoder;
14194
+ function utf8ByteLength(value) {
14195
+ if (/^[\x00-\x7f]*$/.test(value))
14196
+ return value.length;
14197
+ return encoder.encode(value).byteLength;
14198
+ }
14199
+ function truncateUtf8(value, maxBytes) {
14200
+ if (utf8ByteLength(value) <= maxBytes)
14201
+ return value;
14202
+ const marker = "…";
14203
+ const budget = Math.max(0, maxBytes - utf8ByteLength(marker));
14204
+ let bytes = 0;
14205
+ let out = "";
14206
+ for (const char of value) {
14207
+ const size = utf8ByteLength(char);
14208
+ if (bytes + size > budget)
14209
+ break;
14210
+ bytes += size;
14211
+ out += char;
14212
+ }
14213
+ return `${out}${marker}`;
14214
+ }
14215
+ function commandBindingRevision(parts) {
14216
+ let hash = 0xcbf29ce484222325n;
14217
+ const prime = 0x100000001b3n;
14218
+ const mask = 0xffffffffffffffffn;
14219
+ for (const part of parts) {
14220
+ for (const byte of encoder.encode(part)) {
14221
+ hash ^= BigInt(byte);
14222
+ hash = hash * prime & mask;
14223
+ }
14224
+ hash ^= 0xffn;
14225
+ hash = hash * prime & mask;
14226
+ }
14227
+ return hash.toString(16).padStart(16, "0");
14228
+ }
14229
+ function isRecord2(value) {
14230
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
14231
+ }
14232
+ function boundedIdentity(value, maxBytes) {
14233
+ if (typeof value !== "string")
14234
+ return;
14235
+ if (value.length === 0 || value.trim() !== value || /\s/.test(value))
14236
+ return;
14237
+ return utf8ByteLength(value) <= maxBytes ? value : undefined;
14238
+ }
14239
+ function boundedText(value, maxBytes) {
14240
+ if (typeof value !== "string")
14241
+ return;
14242
+ const trimmed = value.trim();
14243
+ return trimmed ? truncateUtf8(trimmed, maxBytes) : undefined;
14244
+ }
14245
+ function displayCommandName(name) {
14246
+ return name.startsWith("/") || name.startsWith("$") ? name : `/${name}`;
14247
+ }
14248
+ function normalizeAvailability(value) {
14249
+ if (!isRecord2(value))
14250
+ return;
14251
+ if (typeof value.state !== "string")
14252
+ return;
14253
+ const reason = typeof value.reason === "string" && UNAVAILABLE_REASONS.has(value.reason) ? value.reason : undefined;
14254
+ const message = boundedText(value.message, COMMAND_CATALOGUE_LIMITS.maxAvailabilityMessageBytes);
14255
+ if (value.state === "available")
14256
+ return { state: "available" };
14257
+ return {
14258
+ state: "unavailable",
14259
+ reason: reason ?? "unsupported",
14260
+ ...message ? { message } : {}
14261
+ };
14262
+ }
14263
+ function normalizeInputPolicy(value) {
14264
+ if (!isRecord2(value))
14265
+ return;
14266
+ const policy = {};
14267
+ if (value.arguments === "none" || value.arguments === "optional" || value.arguments === "required") {
14268
+ policy.arguments = value.arguments;
14269
+ }
14270
+ if (value.attachments === "none" || value.attachments === "images" || value.attachments === "any") {
14271
+ policy.attachments = value.attachments;
14272
+ }
14273
+ if (value.busy === "queue" || value.busy === "idle" || value.busy === "running") {
14274
+ policy.busy = value.busy;
14275
+ }
14276
+ return Object.keys(policy).length > 0 ? policy : undefined;
14277
+ }
14278
+ function normalizeCommandCataloguePayload(payload, options = {}) {
14279
+ const record = isRecord2(payload) ? payload : {};
14280
+ const enhanced = record.catalogueVersion === NATIVE_AGENT_COMMAND_CATALOGUE_VERSION;
14281
+ const rows = Array.isArray(record.commands) ? record.commands : [];
14282
+ const byId = new Map;
14283
+ let truncated = record.truncated === true || rows.length > COMMAND_CATALOGUE_LIMITS.maxCommands;
14284
+ let rejected = 0;
14285
+ let aliasBudget = COMMAND_CATALOGUE_LIMITS.maxAliasesTotal;
14286
+ for (const candidate of rows.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
13791
14287
  const row = typeof candidate === "string" ? { name: candidate } : candidate;
13792
14288
  if (!isRecord2(row)) {
13793
14289
  rejected += 1;
@@ -13968,7 +14464,7 @@ function idleSteerPromptReply(prompt, agentLabel) {
13968
14464
 
13969
14465
  // ../../bridges/claude-bridge/src/services/claude-terminal-commands.ts
13970
14466
  import { mkdir as mkdir5, readFile as readFile6, rename as rename5, writeFile as writeFile5 } from "node:fs/promises";
13971
- import { join as join12 } from "node:path";
14467
+ import { join as join13 } from "node:path";
13972
14468
  var FILE_NAME = "terminal-slash-commands.json";
13973
14469
  var MAX_VERSIONS = 4;
13974
14470
  var MAX_NAMES = 512;
@@ -13979,7 +14475,7 @@ var dirty = false;
13979
14475
  var entries = [];
13980
14476
  var writes = Promise.resolve();
13981
14477
  function filePath() {
13982
- return join12(claudeOrkestratorDir(), FILE_NAME);
14478
+ return join13(claudeOrkestratorDir(), FILE_NAME);
13983
14479
  }
13984
14480
  function validVersion(value) {
13985
14481
  return typeof value === "string" && value.length > 0 && value.length <= MAX_VERSION_LENGTH;
@@ -14031,84 +14527,26 @@ function recordTerminalSlashCommands(version, commands) {
14031
14527
  { version, commands: names },
14032
14528
  ...entries.filter((entry) => entry.version !== version)
14033
14529
  ].slice(0, MAX_VERSIONS);
14034
- writes = writes.then(async () => {
14035
- await load();
14036
- const directory = claudeOrkestratorDir();
14037
- await mkdir5(directory, { recursive: true });
14038
- const target = filePath();
14039
- const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
14040
- const snapshot = entries;
14041
- await writeFile5(temporary, `${JSON.stringify({ versions: snapshot })}
14042
- `, {
14043
- mode: 384
14044
- });
14045
- await rename5(temporary, target);
14046
- if (entries === snapshot)
14047
- dirty = false;
14048
- }).catch(() => {});
14049
- }
14050
-
14051
- // ../../bridges/claude-bridge/src/services/session-manager-catalog.ts
14052
- import { createHash as createHash10 } from "node:crypto";
14053
-
14054
- // ../../bridges/claude-bridge/src/services/live-claude-control.ts
14055
- var LIVE_CLAUDE_READ_TIMEOUT_MS = 5000;
14056
- function readsProbeSettings(settingSources) {
14057
- if (settingSources === undefined)
14058
- return false;
14059
- try {
14060
- const sources = JSON.parse(settingSources);
14061
- return Array.isArray(sources) && ["user", "project", "local"].every((source) => sources.includes(source));
14062
- } catch {
14063
- return false;
14064
- }
14065
- }
14066
- function liveClaudeControls() {
14067
- const runtimes = [];
14068
- for (const session of sessions.values()) {
14069
- const runtime = session.claudeRuntime;
14070
- if (runtime?.state !== "open" || runtime.abortController.signal.aborted)
14071
- continue;
14072
- if (session.queryControlDraining === runtime.control || session.deleting)
14073
- continue;
14074
- if (!readsProbeSettings(runtime.launch.settingSources))
14075
- continue;
14076
- runtimes.push(runtime);
14077
- }
14078
- return runtimes.sort((left, right) => right.lastFrameAt - left.lastFrameAt).map((runtime) => runtime.control);
14079
- }
14080
- async function readFromLiveClaude(read, timeoutMs = LIVE_CLAUDE_READ_TIMEOUT_MS) {
14081
- for (const control of liveClaudeControls()) {
14082
- let timer;
14083
- try {
14084
- const answer = read(control);
14085
- if (answer === undefined)
14086
- continue;
14087
- answer.catch(() => {
14088
- return;
14089
- });
14090
- const value = await Promise.race([
14091
- answer,
14092
- new Promise((resolve) => {
14093
- timer = setTimeout(() => resolve(undefined), timeoutMs);
14094
- })
14095
- ]);
14096
- if (value !== undefined)
14097
- return value;
14098
- } catch (error) {
14099
- debugLog("[live-claude-control] A running CLI could not answer:", error);
14100
- } finally {
14101
- clearTimeout(timer);
14102
- }
14103
- }
14104
- return;
14105
- }
14106
- function liveInitializationResult(control) {
14107
- if (typeof control.initializationResult !== "function")
14108
- return;
14109
- return control.initializationResult().then((result) => result && typeof result === "object" ? result : undefined);
14530
+ writes = writes.then(async () => {
14531
+ await load();
14532
+ const directory = claudeOrkestratorDir();
14533
+ await mkdir5(directory, { recursive: true });
14534
+ const target = filePath();
14535
+ const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
14536
+ const snapshot = entries;
14537
+ await writeFile5(temporary, `${JSON.stringify({ versions: snapshot })}
14538
+ `, {
14539
+ mode: 384
14540
+ });
14541
+ await rename5(temporary, target);
14542
+ if (entries === snapshot)
14543
+ dirty = false;
14544
+ }).catch(() => {});
14110
14545
  }
14111
14546
 
14547
+ // ../../bridges/claude-bridge/src/services/session-manager-catalog.ts
14548
+ import { createHash as createHash10 } from "node:crypto";
14549
+
14112
14550
  // ../protocol/src/secret-redaction.ts
14113
14551
  var TEXT_RULES = [
14114
14552
  {
@@ -14145,7 +14583,11 @@ function redactSecrets(value) {
14145
14583
  }
14146
14584
 
14147
14585
  // ../../bridges/claude-bridge/src/services/mcp-runtime-status.ts
14148
- function mcpScope(scope, name, source) {
14586
+ var FOREIGN_MCP_SOURCES = new Set(["plugin", "sdk", "claudeai"]);
14587
+ function mcpScope(scope, name, source, layers) {
14588
+ const layer = FOREIGN_MCP_SOURCES.has(source ?? "") ? undefined : layers?.get(name);
14589
+ if (layer)
14590
+ return layer;
14149
14591
  if (name === "orkestrator")
14150
14592
  return "orkestrator";
14151
14593
  if (source?.includes("plugin") || scope?.includes("plugin"))
@@ -14167,145 +14609,16 @@ function mcpRuntimeState(status) {
14167
14609
  return "unknown";
14168
14610
  }
14169
14611
  }
14170
- function initMcpServerStatus(raw) {
14612
+ function initMcpServerStatus(raw, layers) {
14171
14613
  const source = typeof raw.source === "string" ? raw.source : undefined;
14172
14614
  return {
14173
14615
  name: raw.name,
14174
14616
  status: mcpRuntimeState(raw.status),
14175
- ...source ? { scope: mcpScope(source, raw.name) } : {},
14617
+ ...source || layers?.has(raw.name) ? { scope: mcpScope(source, raw.name, source, layers) } : {},
14176
14618
  ...typeof raw.error === "string" && raw.error ? { error: raw.error } : {},
14177
14619
  ...Array.isArray(raw.tools) ? { tools: raw.tools.filter((tool) => typeof tool === "string") } : {}
14178
14620
  };
14179
14621
  }
14180
-
14181
- // ../../bridges/claude-bridge/src/services/claude-sign-in.ts
14182
- var AUTH_CACHE_TTL_MS = 30000;
14183
- var FAILED_AUTH_CACHE_TTL_MS = 5000;
14184
- var CLAUDE_SIGN_IN = {
14185
- kind: "terminal",
14186
- hint: "Sign in to Claude from the sign-in card in this tab or from Settings → Claude."
14187
- };
14188
- var CLAUDE_SIGN_IN_REQUIRED_MESSAGE = "Claude isn't signed in. Sign in to Claude to keep working.";
14189
- var authCache;
14190
- var authRead;
14191
- var authReadGeneration;
14192
- var authRefreshPending = false;
14193
- var authGeneration = 0;
14194
- var turnAuthenticationFailedUntil = 0;
14195
- var reuseLiveAccounts = true;
14196
- function withTurnFailure(value) {
14197
- return Date.now() < turnAuthenticationFailedUntil && value.state === "signed-in" ? { ...value, state: "expired" } : value;
14198
- }
14199
- function presentSource(value) {
14200
- return value && value !== "none" ? value : undefined;
14201
- }
14202
- function normalizeClaudeAccount(account) {
14203
- const source = presentSource(account.apiKeySource) ?? presentSource(account.tokenSource);
14204
- const externalProvider = account.apiProvider && account.apiProvider !== "firstParty" ? account.apiProvider : undefined;
14205
- const signedIn = Boolean(source || account.subscriptionType || account.organization || externalProvider);
14206
- const label = account.organization || account.subscriptionType || externalProvider;
14207
- return {
14208
- state: signedIn ? "signed-in" : "needs-auth",
14209
- ...label ? {
14210
- account: {
14211
- label,
14212
- ...account.subscriptionType ? { plan: account.subscriptionType } : {}
14213
- }
14214
- } : {},
14215
- signIn: CLAUDE_SIGN_IN,
14216
- signOut: false
14217
- };
14218
- }
14219
- function liveSignedInAccount() {
14220
- return readFromLiveClaude((control) => liveInitializationResult(control)?.then((result) => {
14221
- const account = result?.account;
14222
- if (!account || typeof account !== "object")
14223
- return;
14224
- const status = normalizeClaudeAccount(account);
14225
- return status.state === "signed-in" ? status : undefined;
14226
- }));
14227
- }
14228
- async function probeAccount() {
14229
- if (reuseLiveAccounts) {
14230
- const live = await liveSignedInAccount();
14231
- if (live && reuseLiveAccounts)
14232
- return live;
14233
- }
14234
- let probe;
14235
- try {
14236
- probe = controlOnlyQuery({
14237
- cwd: process.env.CWD || process.cwd(),
14238
- env: { ...process.env },
14239
- ...claudeExecutableOptions()
14240
- });
14241
- return normalizeClaudeAccount(await probe.accountInfo());
14242
- } finally {
14243
- await Promise.resolve(probe?.close()).catch(() => {
14244
- return;
14245
- });
14246
- }
14247
- }
14248
- function readClaudeAuthStatus() {
14249
- if (authCache && authCache.expiresAt > Date.now()) {
14250
- return Promise.resolve(withTurnFailure(authCache.value));
14251
- }
14252
- if (authRead)
14253
- return authRead;
14254
- const generation = authGeneration;
14255
- const read = probeAccount().then((value) => ({ value, ttl: AUTH_CACHE_TTL_MS }), () => ({
14256
- value: {
14257
- state: "unknown",
14258
- signIn: CLAUDE_SIGN_IN,
14259
- signOut: false
14260
- },
14261
- ttl: FAILED_AUTH_CACHE_TTL_MS
14262
- })).then(({ value, ttl }) => {
14263
- if (generation === authGeneration) {
14264
- authCache = { expiresAt: Date.now() + ttl, value };
14265
- }
14266
- return withTurnFailure(value);
14267
- }).finally(() => {
14268
- if (authRead !== read)
14269
- return;
14270
- authRead = undefined;
14271
- authReadGeneration = undefined;
14272
- if (authRefreshPending) {
14273
- authRefreshPending = false;
14274
- readClaudeAuthStatus();
14275
- }
14276
- });
14277
- authReadGeneration = generation;
14278
- authRead = read;
14279
- return read;
14280
- }
14281
- function noteClaudeAuthenticationFailure() {
14282
- reuseLiveAccounts = false;
14283
- turnAuthenticationFailedUntil = Date.now() + AUTH_CACHE_TTL_MS;
14284
- authCache = undefined;
14285
- if (authRead && authReadGeneration !== authGeneration)
14286
- authRefreshPending = true;
14287
- readClaudeAuthStatus();
14288
- }
14289
- function noteClaudeAuthenticationWorked() {
14290
- reuseLiveAccounts = true;
14291
- authGeneration += 1;
14292
- authRefreshPending = false;
14293
- turnAuthenticationFailedUntil = 0;
14294
- if (authCache?.value.state === "needs-auth")
14295
- authCache = undefined;
14296
- }
14297
- function claudeReadiness() {
14298
- const cached = authCache;
14299
- if (cached?.value.state !== "needs-auth")
14300
- return;
14301
- if (cached.expiresAt <= Date.now())
14302
- readClaudeAuthStatus();
14303
- return {
14304
- state: "authentication-required",
14305
- message: CLAUDE_SIGN_IN_REQUIRED_MESSAGE,
14306
- signIn: CLAUDE_SIGN_IN
14307
- };
14308
- }
14309
14622
  // ../../bridges/claude-bridge/src/services/session-manager-catalog.ts
14310
14623
  function readableControl(session) {
14311
14624
  if (!session)
@@ -14456,7 +14769,7 @@ function mcpTransport(status) {
14456
14769
  function mcpError(error) {
14457
14770
  return redactSecrets(error).slice(0, 2000);
14458
14771
  }
14459
- function normalizeMcp(status) {
14772
+ function normalizeMcp(status, layers) {
14460
14773
  const mappedStatus = status.status === "pending" ? "connecting" : status.status;
14461
14774
  const tools = (status.tools ?? []).map((tool) => tool.name).filter(Boolean).slice(0, 256);
14462
14775
  const actions = status.status === "disabled" ? ["enable"] : status.status === "needs-auth" ? ["sign-in", "reconnect", "disable"] : ["reconnect", "disable"];
@@ -14465,7 +14778,7 @@ function normalizeMcp(status) {
14465
14778
  id: status.name,
14466
14779
  name: status.serverInfo?.name || status.name,
14467
14780
  status: mappedStatus,
14468
- scope: mcpScope(status.scope, status.name, status.source),
14781
+ scope: mcpScope(status.scope, status.name, status.source, layers),
14469
14782
  ...transport ? { transport } : {},
14470
14783
  toolCount: tools.length,
14471
14784
  ...tools.length ? { tools } : {},
@@ -14473,18 +14786,11 @@ function normalizeMcp(status) {
14473
14786
  actions
14474
14787
  };
14475
14788
  }
14476
- async function readSessionMcpServers(sessionId) {
14477
- const session = sessions.get(sessionId);
14478
- if (!session)
14479
- return [];
14480
- const control = readableControl(session);
14481
- if (control?.mcpServerStatus) {
14482
- try {
14483
- session.mcpInventory = (await control.mcpServerStatus()).slice(0, 128).map(normalizeMcp);
14484
- } catch (error) {
14485
- rethrowUnlessClosedTransport(error);
14486
- }
14487
- } else if (!session.mcpInventory && session.initData?.mcpServers) {
14789
+ function liveMcpInventory(session, statuses) {
14790
+ return statuses.slice(0, 128).map((status) => normalizeMcp(status, session.mcpServerLayers));
14791
+ }
14792
+ function retainedMcpInventory(session) {
14793
+ if (!session.mcpInventory && session.initData?.mcpServers) {
14488
14794
  session.mcpInventory = session.initData.mcpServers.slice(0, 128).map((server) => ({
14489
14795
  id: server.name,
14490
14796
  name: server.name,
@@ -14496,7 +14802,34 @@ async function readSessionMcpServers(sessionId) {
14496
14802
  ...server.error ? { error: mcpError(server.error) } : {}
14497
14803
  }));
14498
14804
  }
14499
- return session.mcpInventory ?? [];
14805
+ return session.mcpInventory;
14806
+ }
14807
+ function applyProbeMcpStatus(session, serverId, statuses) {
14808
+ const status = statuses.find((candidate) => candidate.name === serverId);
14809
+ if (!status)
14810
+ return;
14811
+ const inventory = retainedMcpInventory(session);
14812
+ session.mcpInventory = inventory?.map((row) => {
14813
+ if (row.id !== serverId)
14814
+ return row;
14815
+ const updated = normalizeMcp(status, session.mcpServerLayers);
14816
+ return { ...updated, scope: row.scope ?? updated.scope };
14817
+ });
14818
+ }
14819
+ async function readSessionMcpServers(sessionId) {
14820
+ const session = sessions.get(sessionId);
14821
+ if (!session)
14822
+ return [];
14823
+ const control = readableControl(session);
14824
+ if (control?.mcpServerStatus) {
14825
+ try {
14826
+ session.mcpInventory = liveMcpInventory(session, await control.mcpServerStatus());
14827
+ } catch (error) {
14828
+ rethrowUnlessClosedTransport(error);
14829
+ }
14830
+ return session.mcpInventory ?? [];
14831
+ }
14832
+ return retainedMcpInventory(session) ?? [];
14500
14833
  }
14501
14834
  async function performSessionMcpAction(sessionId, serverId, action) {
14502
14835
  const session = sessions.get(sessionId);
@@ -14519,7 +14852,11 @@ async function performSessionMcpAction(sessionId, serverId, action) {
14519
14852
  await control.reconnectMcpServer?.(serverId);
14520
14853
  }
14521
14854
  if (control.mcpServerStatus) {
14522
- session.mcpInventory = (await control.mcpServerStatus()).slice(0, 128).map(normalizeMcp);
14855
+ const statuses = await control.mcpServerStatus();
14856
+ if (probe)
14857
+ applyProbeMcpStatus(session, serverId, statuses);
14858
+ else
14859
+ session.mcpInventory = liveMcpInventory(session, statuses);
14523
14860
  }
14524
14861
  return {};
14525
14862
  } finally {
@@ -15140,6 +15477,7 @@ async function refreshClaudeCommandCatalogue(sessionId) {
15140
15477
  }
15141
15478
  }
15142
15479
  async function refreshClaudeCatalogs() {
15480
+ invalidateClaudeModelCatalog();
15143
15481
  const sessionIds = Array.from(sessions.keys()).slice(0, 128);
15144
15482
  let next = 0;
15145
15483
  await Promise.allSettled(Array.from({ length: Math.min(8, sessionIds.length) }, async () => {
@@ -15229,10 +15567,17 @@ function appendLocalCommandResult(session, content, key) {
15229
15567
  }
15230
15568
 
15231
15569
  // ../../bridges/claude-bridge/src/services/session-manager-prompt-mcp-sync.ts
15232
- function createClaudeRuntimeMcp(servers, declared) {
15233
- return { servers, claimed: false, declared: new Set(declared), stale: new Set };
15570
+ function createClaudeRuntimeMcp(servers, declared, accountOnly = []) {
15571
+ const declaredNames = new Set(declared);
15572
+ const loadedOnlyByAccount = new Set(Array.from(accountOnly).filter((name) => !declaredNames.has(name)));
15573
+ return {
15574
+ servers,
15575
+ claimed: false,
15576
+ declared: declaredNames,
15577
+ stale: new Set(loadedOnlyByAccount),
15578
+ accountOnly: loadedOnlyByAccount
15579
+ };
15234
15580
  }
15235
- var FOREIGN_SOURCES = new Set(["plugin", "sdk", "claudeai"]);
15236
15581
  var MAX_REPORTED_STALE_SERVERS = 256;
15237
15582
  function conflict(message) {
15238
15583
  return sessionOperationError("conflict", message);
@@ -15287,10 +15632,12 @@ async function syncClaudeRuntimeMcp(runtime, next) {
15287
15632
  runtime.mcpServers = "";
15288
15633
  throw conflict("Claude required MCP server is not connected; retry the prompt");
15289
15634
  }
15290
- const stillRuns = (name) => statuses ? statuses.some((server) => server.name === name && server.status !== "disabled" && !FOREIGN_SOURCES.has(server.source ?? "")) : !removed?.has(name);
15635
+ const stillRuns = (name) => statuses ? statuses.some((server) => server.name === name && server.status !== "disabled" && !FOREIGN_MCP_SOURCES.has(server.source ?? "")) : !removed?.has(name);
15291
15636
  for (const name of Array.from(mcp.stale)) {
15292
- if (next.names.has(name) || !stillRuns(name))
15637
+ if (next.names.has(name) || !stillRuns(name)) {
15293
15638
  mcp.stale.delete(name);
15639
+ mcp.accountOnly.delete(name);
15640
+ }
15294
15641
  }
15295
15642
  for (const name of undeclared)
15296
15643
  if (stillRuns(name))
@@ -15332,7 +15679,7 @@ function createStaleMcpServerHook(mcp) {
15332
15679
  hookSpecificOutput: {
15333
15680
  hookEventName: "PreToolUse",
15334
15681
  permissionDecision: "deny",
15335
- permissionDecisionReason: `The MCP server "${server}" was removed from this session's configuration, so ${input.tool_name} is not available. This Claude process loaded it at launch and cannot unload it; it is gone once the process closes after an idle period, or when the session is stopped.`
15682
+ permissionDecisionReason: mcp.accountOnly.has(server) ? `The MCP server "${server}" is only in this Claude account's own configuration, not in the user's Claude configuration, so ${input.tool_name} is not available. Orkestrator replaces an account's MCP servers with the user's when the account's Claude starts; add the server to the user's Claude configuration to use it.` : `The MCP server "${server}" was removed from this session's configuration, so ${input.tool_name} is not available. This Claude process loaded it at launch and cannot unload it; it is gone once the process closes after an idle period, or when the session is stopped.`
15336
15683
  }
15337
15684
  };
15338
15685
  };
@@ -15584,6 +15931,60 @@ function claudeArtifactServerTools(servers) {
15584
15931
  return ARTIFACT_TOOL_NAMES.map((name) => `mcp__${ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME}__${name}`);
15585
15932
  }
15586
15933
 
15934
+ // ../protocol/src/node/repository-instructions.ts
15935
+ import { open as open3, realpath as realpath3 } from "node:fs/promises";
15936
+ import { join as join14, relative as relative2, isAbsolute as isAbsolute4 } from "node:path";
15937
+
15938
+ // ../protocol/src/agent-system-instructions.ts
15939
+ var AGENT_IMAGE_PREVIEW_INSTRUCTION = "Orkestrator renders Markdown images inline in the transcript. When you show the user an image in your reply (a screenshot, rendered preview, or chart) using Markdown image syntax, reference it by its absolute path. In a managed worktree or container environment, save the file inside the working directory, preferably in a git-ignored folder so preview files stay out of the change set. Local image previews are limited to Orkestrator-managed workspace storage or the container's /workspace. Project-home checkouts are excluded from host preview reads, even when they are the working directory, so images saved there cannot be previewed from a local Markdown path. Paths outside previewable storage, such as /tmp, also show a placeholder.";
15940
+ var AGENT_DOCUMENT_INSTRUCTION = "When the user asks for a document (a report, design doc, plan, analysis, guide, or other writing to keep), prefer writing it as a Markdown file in a `docs/` folder at the root of the working directory, and tell the user the file's path. Project instructions (such as AGENTS.md or CLAUDE.md), local skills, and the user's own request take priority over this default when they say where or how to write documents.";
15941
+ var GENERAL_AGENT_INSTRUCTIONS = [
15942
+ AGENT_IMAGE_PREVIEW_INSTRUCTION,
15943
+ AGENT_DOCUMENT_INSTRUCTION
15944
+ ];
15945
+ function agentSystemInstructions(providerSpecific = []) {
15946
+ return [...GENERAL_AGENT_INSTRUCTIONS, ...providerSpecific].filter((line) => line.trim().length > 0).join(`
15947
+
15948
+ `);
15949
+ }
15950
+ var REPOSITORY_INSTRUCTIONS_FILE = "AGENTS.md";
15951
+ function repositoryInstructionsBlock(path, content, truncated) {
15952
+ return [
15953
+ `# Repository instructions (${REPOSITORY_INSTRUCTIONS_FILE})`,
15954
+ `This session does not load the repository's ${REPOSITORY_INSTRUCTIONS_FILE} on its own, so Orkestrator includes it here. Follow it as project instructions. Contents of ${path}:`,
15955
+ content.trimEnd(),
15956
+ ...truncated ? [
15957
+ `[${REPOSITORY_INSTRUCTIONS_FILE} is longer than this session can hold, so it is cut here. Read the rest from ${path} before relying on anything it may say.]`
15958
+ ] : []
15959
+ ].join(`
15960
+
15961
+ `);
15962
+ }
15963
+
15964
+ // ../protocol/src/node/repository-instructions.ts
15965
+ var MAX_REPOSITORY_INSTRUCTIONS_BYTES = 256 * 1024;
15966
+ async function readRepositoryInstructions(cwd, maxBytes = MAX_REPOSITORY_INSTRUCTIONS_BYTES) {
15967
+ const path = join14(cwd, REPOSITORY_INSTRUCTIONS_FILE);
15968
+ try {
15969
+ const [root, target] = await Promise.all([realpath3(cwd), realpath3(path)]);
15970
+ const inside = relative2(root, target);
15971
+ if (!inside || inside.startsWith("..") || isAbsolute4(inside))
15972
+ return;
15973
+ const handle = await open3(target, "r");
15974
+ try {
15975
+ const buffer = Buffer.alloc(maxBytes + 1);
15976
+ const { bytesRead } = await handle.read(buffer, 0, maxBytes + 1, 0);
15977
+ const truncated = bytesRead > maxBytes;
15978
+ const text = new TextDecoder().decode(buffer.subarray(0, Math.min(bytesRead, maxBytes))).replace(/�$/, "");
15979
+ return text.trim() ? repositoryInstructionsBlock(path, text, truncated) : undefined;
15980
+ } finally {
15981
+ await handle.close();
15982
+ }
15983
+ } catch {
15984
+ return;
15985
+ }
15986
+ }
15987
+
15587
15988
  // ../protocol/src/claude-background-commands.ts
15588
15989
  var CLAUDE_BASH_MAX_TIMEOUT_ENV = "BASH_MAX_TIMEOUT_MS";
15589
15990
  var CLAUDE_BASH_DEFAULT_TIMEOUT_ENV = "BASH_DEFAULT_TIMEOUT_MS";
@@ -15632,6 +16033,17 @@ function backgroundCommandInstruction(env) {
15632
16033
  return `Background shell commands in this session are stopped when they reach their timeout (${defaultText} by default, ${maxText} at most). Pass a longer \`timeout\` when a background command needs more than ${defaultText}. For a dev server or watcher that must keep running longer than ${maxText}, tell the user to start it in an Orkestrator terminal tab instead of running it yourself.`;
15633
16034
  }
15634
16035
 
16036
+ // ../../bridges/claude-bridge/src/services/system-prompt-append.ts
16037
+ var READ_BEFORE_EDIT_INSTRUCTION = "IMPORTANT: You MUST read a file before editing or writing to it. The Edit and Write tools will fail if you have not first used the Read tool to read the file in this conversation. Always read files before attempting to modify them.";
16038
+ var CLAUDE_DOCS_INSTRUCTION = "Write a requested document as that Markdown file rather than as a Claude Doc or claude.ai artifact: Orkestrator cannot open those, so the user would never see it.";
16039
+ function claudeSystemPromptAppend(env) {
16040
+ return agentSystemInstructions([
16041
+ CLAUDE_DOCS_INSTRUCTION,
16042
+ READ_BEFORE_EDIT_INSTRUCTION,
16043
+ backgroundCommandInstruction(env)
16044
+ ]);
16045
+ }
16046
+
15635
16047
  // ../protocol/src/agent-interaction-deadline.ts
15636
16048
  var SYSTEM_CLOCK = {
15637
16049
  now: () => Date.now(),
@@ -15777,7 +16189,7 @@ function canAdmitInteraction(candidate, family) {
15777
16189
  // ../../bridges/claude-bridge/src/services/session-manager-interactions.ts
15778
16190
  import { readFile as readFile7 } from "node:fs/promises";
15779
16191
 
15780
- // ../../bridges/claude-bridge/src/services/claude-elicitation-schema.ts
16192
+ // ../protocol/src/mcp-elicitation-form.ts
15781
16193
  function record2(value) {
15782
16194
  return value !== null && typeof value === "object" && !Array.isArray(value);
15783
16195
  }
@@ -15802,7 +16214,7 @@ var FIELD_KEYS = new Set([
15802
16214
  "minimum",
15803
16215
  "maximum"
15804
16216
  ]);
15805
- function isSupportedClaudeForm(schema) {
16217
+ function isSupportedMcpForm(schema) {
15806
16218
  if (!record2(schema) || schema.type !== "object" || Object.keys(schema).some((key) => !ROOT_KEYS.has(key)))
15807
16219
  return false;
15808
16220
  if (schema.additionalProperties !== undefined && schema.additionalProperties !== false)
@@ -15832,8 +16244,8 @@ function isSupportedClaudeForm(schema) {
15832
16244
  return true;
15833
16245
  });
15834
16246
  }
15835
- function isValidClaudeFormContent(schema, content) {
15836
- if (!isSupportedClaudeForm(schema) || !record2(content))
16247
+ function isValidMcpFormContent(schema, content) {
16248
+ if (!isSupportedMcpForm(schema) || !record2(content))
15837
16249
  return false;
15838
16250
  const properties = schema.properties ?? {};
15839
16251
  if (schema.required?.some((key) => !Object.hasOwn(content, key)))
@@ -15954,7 +16366,7 @@ function respondToInteraction(requestId, response, sessionId) {
15954
16366
  const resolve = interactionResolvers.get(key);
15955
16367
  if (!resolve)
15956
16368
  return false;
15957
- if (response.action === "accept" && request.kind === "mcp-form" && !isValidClaudeFormContent(request.schema, response.content))
16369
+ if (response.action === "accept" && request.kind === "mcp-form" && !isValidMcpFormContent(request.schema, response.content))
15958
16370
  return false;
15959
16371
  pendingInteractions.delete(key);
15960
16372
  interactionResolvers.delete(key);
@@ -15974,61 +16386,6 @@ function getSessionInitData(sessionId) {
15974
16386
  const session = sessions.get(sessionId);
15975
16387
  return session?.initData;
15976
16388
  }
15977
- function catalogModels(models) {
15978
- return models.map((model) => ({
15979
- id: model.value,
15980
- resolvedModel: model.resolvedModel,
15981
- name: model.displayName,
15982
- description: model.description,
15983
- supportsFastMode: model.supportsFastMode,
15984
- supportsEffort: model.supportsEffort,
15985
- supportedEffortLevels: model.supportedEffortLevels,
15986
- supportsAdaptiveThinking: model.supportsAdaptiveThinking,
15987
- supportsAutoMode: model.supportsAutoMode
15988
- }));
15989
- }
15990
- function liveModels() {
15991
- return readFromLiveClaude((control) => liveInitializationResult(control)?.then((result) => Array.isArray(result?.models) && result.models.length > 0 ? result.models : undefined));
15992
- }
15993
- async function getAvailableModelCatalog() {
15994
- const live = await liveModels();
15995
- if (live) {
15996
- debugLog("[session-manager] Supported models read from a running CLI", {
15997
- count: live.length
15998
- });
15999
- return { source: "sdk", models: catalogModels(live) };
16000
- }
16001
- let q;
16002
- try {
16003
- const cwd = process.env.CWD || process.cwd();
16004
- debugLog("[session-manager] Fetching supported models", { cwd });
16005
- q = controlOnlyQuery({
16006
- cwd,
16007
- env: { ...process.env },
16008
- ...claudeExecutableOptions()
16009
- });
16010
- const models = await q.supportedModels();
16011
- debugLog("[session-manager] Supported models fetched", { count: models.length });
16012
- return { source: "sdk", models: catalogModels(models) };
16013
- } catch (error) {
16014
- console.error("[session-manager] Error fetching supported models:", error);
16015
- return {
16016
- source: "fallback",
16017
- models: CLAUDE_FALLBACK_MODEL_CATALOG.map((model) => ({
16018
- ...model,
16019
- ...model.supportedEffortLevels ? { supportedEffortLevels: [...model.supportedEffortLevels] } : {}
16020
- }))
16021
- };
16022
- } finally {
16023
- if (q?.return) {
16024
- try {
16025
- await q.return();
16026
- } catch (error) {
16027
- debugLog("[session-manager] Failed to clean up model query:", error);
16028
- }
16029
- }
16030
- }
16031
- }
16032
16389
  async function getClaudeRuntimeVersions() {
16033
16390
  let sdkVersion;
16034
16391
  let bundledCliVersion;
@@ -16182,7 +16539,7 @@ async function answerClaudeElicitation(session, sessionId, request, options) {
16182
16539
  const url = request.mode === "url" ? bounded2(request.url) : undefined;
16183
16540
  const schema = request.mode === "url" ? undefined : request.requestedSchema ?? { type: "object" };
16184
16541
  const schemaText = schema === undefined ? "" : JSON.stringify(schema);
16185
- if (request.mode === "url" && (!url || url !== request.url) || new TextEncoder().encode(schemaText).byteLength > AGENT_INTERACTION_LIMITS.maxTextLength || schema !== undefined && !isSupportedClaudeForm(schema)) {
16542
+ if (request.mode === "url" && (!url || url !== request.url) || new TextEncoder().encode(schemaText).byteLength > AGENT_INTERACTION_LIMITS.maxTextLength || schema !== undefined && !isSupportedMcpForm(schema)) {
16186
16543
  appendTranscriptNotice(session, sessionId, (event) => eventEmitter.emit(event), {
16187
16544
  type: "status",
16188
16545
  severity: "warning",
@@ -16230,9 +16587,9 @@ function proposedBashPrefix(suggestions) {
16230
16587
  const { toolName, ruleContent } = rule;
16231
16588
  if (toolName !== "Bash" || typeof ruleContent !== "string")
16232
16589
  continue;
16233
- const prefix = ruleContent.endsWith(":*") ? ruleContent.slice(0, -2) : ruleContent;
16590
+ const prefix = ruleContent.replace(/(?::| )\*$/, "");
16234
16591
  const words = prefix.trim().split(/\s+/);
16235
- if (words.length === 0 || words.length > MAX_PROPOSED_PREFIX_TOKENS || words.some((word) => word.length === 0 || word.length > MAX_PROPOSED_PREFIX_TOKEN_LENGTH)) {
16592
+ if (words.length === 0 || words.length > MAX_PROPOSED_PREFIX_TOKENS || words.some((word) => word.length === 0 || word.length > MAX_PROPOSED_PREFIX_TOKEN_LENGTH || word.includes("*"))) {
16236
16593
  continue;
16237
16594
  }
16238
16595
  return words;
@@ -16443,6 +16800,9 @@ function turnHook(turn, hook) {
16443
16800
  }
16444
16801
 
16445
16802
  // ../../bridges/claude-bridge/src/services/session-manager-prompt-query.ts
16803
+ function withNativeGoalClosed(disallowed) {
16804
+ return [...disallowed ?? [], "ProposeGoal"];
16805
+ }
16446
16806
  var PLAN_MODE_INSTRUCTIONS = `The user has enabled planning mode. Use this phase to:
16447
16807
  1. Thoroughly explore the codebase and its existing patterns.
16448
16808
  2. Consider viable approaches and their trade-offs.
@@ -16463,15 +16823,22 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
16463
16823
  };
16464
16824
  const settingSources = claudeTurnSettingSources(session, options);
16465
16825
  session.commandDiscoveryInputs.includeLocalSettings = settingSources.includes("local");
16826
+ const repositoryInstructions = options?.readOnly || policy?.id === "coordinator-read-only" || settingSources.includes("project") ? undefined : await readRepositoryInstructions(cwd);
16466
16827
  const readOnlyCommandSandbox = isCoordinatorReadOnlyPolicy(policy) ? claudeReadOnlyCommandSandbox() : undefined;
16467
16828
  const [
16468
- { servers: mcpServers, names: mcpServerNames, revision: mcpConfigRevision },
16829
+ {
16830
+ servers: mcpServers,
16831
+ names: mcpServerNames,
16832
+ layers: mcpServerLayers,
16833
+ revision: mcpConfigRevision
16834
+ },
16469
16835
  { plugins, coordinatorSkills }
16470
16836
  ] = await Promise.all([
16471
16837
  getMcpRuntimeConfig(cwd, process.env, options?.agentMcp, mcpSourceScopeForPolicy(policy), userMcpServerFilter(policy)),
16472
16838
  resolveClaudeTurnPlugins(cwd, policy, settingSources)
16473
16839
  ]);
16474
16840
  turn.mcpServerNames = mcpServerNames;
16841
+ const accountOnlyMcpServers = await claudeAccountOnlyMcpServerNames(cwd, settingSources, mcpServerNames, claudeUserMcpQueryOptions(policy).strictMcpConfig === true);
16475
16842
  const mcpServerCount = Object.keys(mcpServers).length;
16476
16843
  const pluginCount = plugins.length;
16477
16844
  const policyPermissionMode = claudePolicyPermissionMode(policy);
@@ -16492,7 +16859,7 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
16492
16859
  ];
16493
16860
  const coordinatorReadOnly = isCoordinatorReadOnlyPolicy(policy);
16494
16861
  const grantedMcpServerNames = claudeAsksForApproval(policy, options) ? [] : claudeGrantedMcpServerNames(policy, mcpServerNames);
16495
- const agentMailTools = mcpServerNames.has(AGENT_MCP_SERVER_NAME) ? [...AGENT_MAIL_TOOL_NAMES, ...ARTIFACT_TOOL_NAMES].map((name) => `mcp__${AGENT_MCP_SERVER_NAME}__${name}`) : [];
16862
+ const agentMailTools = mcpServerNames.has(AGENT_MCP_SERVER_NAME) ? [...AGENT_MAIL_TOOL_NAMES, ...ARTIFACT_TOOL_NAMES, ...AGENT_GOAL_TOOL_NAMES].map((name) => `mcp__${AGENT_MCP_SERVER_NAME}__${name}`) : [];
16496
16863
  const allowedTools = coordinatorReadOnly ? claudeReadOnlyAllowedTools({
16497
16864
  agentMcpServerNames: mcpServerNames.has(AGENT_MCP_SERVER_NAME) ? [AGENT_MCP_SERVER_NAME] : [],
16498
16865
  policy,
@@ -16506,7 +16873,7 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
16506
16873
  ...claudeArtifactServerTools(mcpServerNames),
16507
16874
  ...grantedMcpServerNames.map((name) => `${claudeMcpToolPrefix(name)}*`)
16508
16875
  ];
16509
- const disallowedTools = coordinatorReadOnly ? claudeDeniedTools(policy) : claudeDisallowedTools(policy);
16876
+ const disallowedTools = withNativeGoalClosed(coordinatorReadOnly ? claudeDeniedTools(policy) : claudeDisallowedTools(policy));
16510
16877
  const fastMode = options?.fastMode === true;
16511
16878
  const ultracode = requestsUltracode(options?.parameterValues);
16512
16879
  session.ultracode = ultracode;
@@ -16528,7 +16895,7 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
16528
16895
  const envPath = process.env.PATH;
16529
16896
  debugLog("[session-manager] SDK env PATH", { path: envPath });
16530
16897
  const sdkPrompt = await buildSdkPrompt(sdkTextPrompt, options?.attachments, cwd, testHooks?.afterAttachmentSymlinkValidation, testHooks?.afterAttachmentCanonicalValidation, testHooks?.afterAttachmentInitialValidation);
16531
- const queryEnvironment = await runtimeEnvironmentForSessionQuery(policy);
16898
+ const queryEnvironment = withUnattendedOverloadRetry(session.executionPolicy, await runtimeEnvironmentForSessionQuery(policy));
16532
16899
  const liveSettings = claudeLiveSettings({
16533
16900
  model: options?.model,
16534
16901
  permissionMode,
@@ -16539,11 +16906,14 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
16539
16906
  const mcpServersKey = createHash11("sha256").update(JSON.stringify(mcpServers)).digest("hex");
16540
16907
  return {
16541
16908
  settingSources,
16909
+ repositoryInstructions,
16542
16910
  cwd,
16543
16911
  policy,
16544
16912
  effortLevel,
16545
16913
  mcpServers,
16546
16914
  mcpServerNames,
16915
+ mcpServerLayers,
16916
+ accountOnlyMcpServers,
16547
16917
  mcpConfigRevision,
16548
16918
  plugins,
16549
16919
  mcpServerCount,
@@ -16564,6 +16934,7 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
16564
16934
  }
16565
16935
  function recordMcpConfigRevision(session, plan, mcp) {
16566
16936
  const { mcpConfigRevision } = plan;
16937
+ session.mcpServerLayers = plan.mcpServerLayers;
16567
16938
  if (!mcpConfigRevision)
16568
16939
  return;
16569
16940
  const staleServers = reportedStaleServers(mcp);
@@ -16646,11 +17017,9 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
16646
17017
  systemPrompt: {
16647
17018
  type: "preset",
16648
17019
  preset: "claude_code",
16649
- append: coordinatorReadOnly ? "You are a read-only coordinator. Inspect the checkout with reading tools and use the trusted research tools available for this turn. Delegate all mutations and artifact creation to worker environments through Orkestrator tools. Present findings directly in your response." : [
16650
- "IMPORTANT: You MUST read a file before editing or writing to it. The Edit and Write tools will fail if you have not first used the Read tool to read the file in this conversation. Always read files before attempting to modify them.",
16651
- backgroundCommandInstruction(queryEnvironment),
16652
- "When the user asks for a document (a report, design doc, plan, analysis, guide, or other writing to keep), prefer writing it as a Markdown file in a `docs/` folder at the root of the working directory over creating a Claude Doc or claude.ai artifact, and tell the user the file's path. Project instructions (such as CLAUDE.md or AGENTS.md), local skills, and the user's own request take priority over this default when they say where or how to write documents.",
16653
- "When you show the user an image in your reply (a screenshot, rendered preview, or chart) using Markdown image syntax, reference it by its absolute path. In a managed worktree or container environment, save the file inside the working directory, preferably in a git-ignored folder so preview files stay out of the change set. Local image previews are limited to Orkestrator-managed workspace storage or the container's /workspace. Project-home checkouts are excluded from host preview reads, even when they are the working directory, so images saved there cannot be previewed from a local Markdown path. Paths outside previewable storage, such as /tmp, also show a placeholder."
17020
+ append: [
17021
+ coordinatorReadOnly ? "You are a read-only coordinator. Inspect the checkout with reading tools and use the trusted research tools available for this turn. Delegate all mutations and artifact creation to worker environments through Orkestrator tools. Present findings directly in your response." : claudeSystemPromptAppend(queryEnvironment),
17022
+ ...plan.repositoryInstructions ? [plan.repositoryInstructions] : []
16654
17023
  ].join(`
16655
17024
 
16656
17025
  `)
@@ -16985,7 +17354,7 @@ async function handleInitMessage(turn, message) {
16985
17354
  }
16986
17355
  const allMcpServers = initMsg.mcp_servers || [];
16987
17356
  const regularMcpServers = allMcpServers.filter((s) => !s.name?.startsWith("plugin:"));
16988
- const mcpServerStatuses = regularMcpServers.map(initMcpServerStatus);
17357
+ const mcpServerStatuses = regularMcpServers.map((server) => initMcpServerStatus(server, session.mcpServerLayers));
16989
17358
  const { statuses: pluginStatuses, errors: pluginErrors } = pluginStatusesFromInit(initMsg);
16990
17359
  const previousPluginErrors = new Set(session.pluginErrorSignatures);
16991
17360
  session.pluginErrorSignatures = pluginErrors.map(({ plugin, type, message, path, loaded }) => JSON.stringify([plugin, type, message, path, loaded]));
@@ -17172,6 +17541,28 @@ var LEAF_MESSAGE_HANDLERS = {
17172
17541
  conversation_reset: handleConversationReset
17173
17542
  };
17174
17543
 
17544
+ // ../protocol/src/agent-usage-limits.ts
17545
+ var USAGE_LIMIT_PATTERNS = [
17546
+ /usage[\s_-]?limit/i,
17547
+ /hit your (?:usage |session |weekly )?limit/i,
17548
+ /\b(?:5|five)[\s-]?hour limit\b/i,
17549
+ /\bweekly limit\b/i,
17550
+ /\b(?:usage|quota|credits?|allowance) (?:is |has been )?(?:exhausted|exceeded|used up)\b/i,
17551
+ /\bquota exceeded\b|\binsufficient_quota\b|\bout of (?:credits|usage)\b/i,
17552
+ /\bUSAGE_EXHAUSTED\b|\bPAYMENT_REQUIRED\b/,
17553
+ /\b(?:usage|plan|session|daily|weekly|monthly|go) limit reached\b/i
17554
+ ];
17555
+ function classifyUsageLimitError(text) {
17556
+ if (typeof text !== "string" || !text.trim())
17557
+ return null;
17558
+ const bounded = text.slice(0, 2000);
17559
+ if (!USAGE_LIMIT_PATTERNS.some((pattern) => pattern.test(bounded)))
17560
+ return null;
17561
+ const reset = bounded.match(/\b(?:resets?|try again)(?:\s+(?:at|in|after))?\s+([^.\n·]{1,60})/i);
17562
+ const hint = reset?.[1]?.trim();
17563
+ return hint ? { resetHint: hint } : {};
17564
+ }
17565
+
17175
17566
  // ../../bridges/claude-bridge/src/services/session-manager-prompt-handlers/result.ts
17176
17567
  function claudeStartupFailureMessage(reason) {
17177
17568
  switch (reason) {
@@ -17213,8 +17604,11 @@ function claudeStartupFailureMessage(reason) {
17213
17604
  return "Claude could not load the policy limits and managed settings your organization requires. Check connectivity and any proxy, then retry.";
17214
17605
  case "org_config_refused":
17215
17606
  return "Anthropic refused your organization's required Claude settings for this sign-in. Sign in to Claude again; if that does not help, ask an administrator to allow Claude Code for this account.";
17607
+ case "sandbox_required_unavailable":
17608
+ return "Claude requires a working command sandbox, and it cannot run here (for example, bubblewrap is missing or the platform is unsupported). Make the sandbox available or have the requirement changed, then retry.";
17216
17609
  }
17217
17610
  }
17611
+ var MAX_USAGE_LIMIT_ERROR_CHARS = 500;
17218
17612
  var STOPPED_TERMINAL_REASONS = new Set([
17219
17613
  "aborted_streaming",
17220
17614
  "aborted_tools"
@@ -17347,8 +17741,9 @@ async function handleResultMessage(turn, message) {
17347
17741
  isError: resultIsError
17348
17742
  });
17349
17743
  const startupFailure = resultMsg.startup_failure_reason ? claudeStartupFailureMessage(resultMsg.startup_failure_reason) : undefined;
17744
+ const usageLimit = resultMsg.subtype === "success" && classifyUsageLimitError(resultMsg.result) ? resultMsg.result.trim().slice(0, MAX_USAGE_LIMIT_ERROR_CHARS) : undefined;
17350
17745
  const resultError = startupFailure || resultMsg.errors?.filter(Boolean).join(`
17351
- `) || (resultMsg.subtype === "success" ? "Claude ended the turn on an API error." : `Claude query failed: ${resultMsg.subtype}`);
17746
+ `) || usageLimit || (resultMsg.subtype === "success" ? "Claude ended the turn on an API error." : `Claude query failed: ${resultMsg.subtype}`);
17352
17747
  if (startupFailure) {
17353
17748
  appendTranscriptNotice(session, sessionId, (event) => eventEmitter.emit(event), {
17354
17749
  type: "status",
@@ -18280,7 +18675,8 @@ async function prepareJoin(turn, plan, running) {
18280
18675
  const required = mcpServers[AGENT_MCP_SERVER_NAME] ? [AGENT_MCP_SERVER_NAME, ...options?.agentMcp?.design ? ["orkestrator-design"] : []] : [];
18281
18676
  await assertClaudeRuntimeAccepts(session, options, {
18282
18677
  ...claudeLaunchResourceProfile(plugins, queryEnvironment),
18283
- settingSources: JSON.stringify(plan.settingSources)
18678
+ settingSources: JSON.stringify(plan.settingSources),
18679
+ repositoryInstructions: plan.repositoryInstructions ?? ""
18284
18680
  }, plan.readOnlyCommandSandbox);
18285
18681
  session.queryControl = running.control;
18286
18682
  turn.queryIteratorControl = running.control;
@@ -18321,7 +18717,7 @@ function launchClaudeRuntime(turn, plan) {
18321
18717
  const { session, options, abortController, stream } = turn;
18322
18718
  const { plugins, sdkPrompt, queryEnvironment, mcpServers, mcpServerNames, mcpServersKey } = plan;
18323
18719
  const heldSdkPrompt = holdSdkPromptOpen(sdkPrompt, abortController.signal, turn.promptUuid);
18324
- const mcp = createClaudeRuntimeMcp(mcpServers, mcpServerNames);
18720
+ const mcp = createClaudeRuntimeMcp(mcpServers, mcpServerNames, plan.accountOnlyMcpServers);
18325
18721
  const turnFor = (toolUseId) => (toolUseId ? turn.runtime?.toolOwners.get(toolUseId) : undefined) ?? turn.runtime?.latestTurn ?? turn.thisTurn;
18326
18722
  const queryIterator = query2({
18327
18723
  prompt: heldSdkPrompt.prompt,
@@ -18340,6 +18736,7 @@ function launchClaudeRuntime(turn, plan) {
18340
18736
  launch: {
18341
18737
  ...claudeLaunchedProfile(session, options, plan.readOnlyCommandSandbox),
18342
18738
  settingSources: JSON.stringify(plan.settingSources),
18739
+ repositoryInstructions: plan.repositoryInstructions ?? "",
18343
18740
  ...claudeLaunchResourceProfile(plugins, queryEnvironment)
18344
18741
  },
18345
18742
  mcpServers: mcpServersKey,
@@ -18396,6 +18793,7 @@ function joinableRuntime(turn, plan) {
18396
18793
  const requested = {
18397
18794
  ...claudeLaunchProfile(turn.session, turn.options, plan.readOnlyCommandSandbox),
18398
18795
  settingSources: JSON.stringify(plan.settingSources),
18796
+ repositoryInstructions: plan.repositoryInstructions ?? "",
18399
18797
  ...claudeLaunchResourceProfile(plan.plugins, plan.queryEnvironment)
18400
18798
  };
18401
18799
  return claudeLaunchConflict(running.launch, requested) ? undefined : running;
@@ -18790,6 +19188,8 @@ async function settlesBy(work, deadline) {
18790
19188
  // ../../bridges/claude-bridge/src/routes/config.ts
18791
19189
  var config = new Hono2;
18792
19190
  config.get("/models", async (c) => {
19191
+ if (c.req.query("forceRefresh") === "true")
19192
+ invalidateClaudeModelCatalog();
18793
19193
  const startedAt = Date.now();
18794
19194
  console.log("[config] /models requested");
18795
19195
  const [{ models, source }, versions] = await Promise.all([
@@ -19093,7 +19493,7 @@ function parseSessionActivityObservation(value) {
19093
19493
  if (!isRecord4(value))
19094
19494
  return;
19095
19495
  const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
19096
- const { contentRevision } = value;
19496
+ const { contentRevision, turnFailed } = value;
19097
19497
  if (typeof activity !== "string" || !SESSION_ACTIVITY_STATES.has(activity))
19098
19498
  return;
19099
19499
  if (readyForInput !== undefined && typeof readyForInput !== "boolean")
@@ -19102,6 +19502,8 @@ function parseSessionActivityObservation(value) {
19102
19502
  return;
19103
19503
  if (closing !== undefined && typeof closing !== "boolean")
19104
19504
  return;
19505
+ if (turnFailed !== undefined && typeof turnFailed !== "boolean")
19506
+ return;
19105
19507
  if (contentRevision !== undefined && !isContentRevision(contentRevision))
19106
19508
  return;
19107
19509
  let itemIds;
@@ -19117,6 +19519,7 @@ function parseSessionActivityObservation(value) {
19117
19519
  ...completionPending !== undefined ? { completionPending } : {},
19118
19520
  ...itemIds !== undefined ? { asyncQuestionItemIds: itemIds } : {},
19119
19521
  ...closing !== undefined ? { closing } : {},
19522
+ ...turnFailed === true ? { turnFailed } : {},
19120
19523
  ...contentRevision !== undefined ? { contentRevision } : {}
19121
19524
  };
19122
19525
  }
@@ -19248,6 +19651,7 @@ async function readSessionActivityObservation(sessionId) {
19248
19651
  return {
19249
19652
  activity: activity === "idle" && completionPending ? "working" : activity,
19250
19653
  ...resident?.status === "idle" ? { readyForInput: true, completionPending } : {},
19654
+ ...resident?.status === "error" ? { turnFailed: true } : {},
19251
19655
  ...activity === "missing" ? {} : contentRevisionField(resident)
19252
19656
  };
19253
19657
  }
@@ -20725,7 +21129,7 @@ session.post("/:id/interactions/:interactionId", interactionBodyLimit, async (c)
20725
21129
  } else if (action === "accept" && pending.kind === "mcp-form") {
20726
21130
  return c.json({ error: "An MCP form answer requires 'content'" }, 400);
20727
21131
  }
20728
- if (action === "accept" && pending.kind === "mcp-form" && !isValidClaudeFormContent(pending.schema, content)) {
21132
+ if (action === "accept" && pending.kind === "mcp-form" && !isValidMcpFormContent(pending.schema, content)) {
20729
21133
  return c.json({ error: "The answer does not match the MCP form schema" }, 400);
20730
21134
  }
20731
21135
  const response = action === "accept" ? content ? { action, content } : { action } : { action };