orkestrator 2.28.0-beta → 2.30.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.
@@ -4349,7 +4349,8 @@ var NATIVE_AGENT_COMMAND_CATALOGUE_ERROR_CODES = [
4349
4349
  "rejected",
4350
4350
  "malformed",
4351
4351
  "too-large",
4352
- "provider-error"
4352
+ "provider-error",
4353
+ "outdated-bridge"
4353
4354
  ];
4354
4355
  var DEFAULT_NATIVE_AGENT_LIVE_WINDOW = {
4355
4356
  messages: 100,
@@ -4495,6 +4496,7 @@ var READ_ONLY_COMMANDS = Object.freeze(new Set([
4495
4496
  var MUTATING_ARGUMENTS = new Map([
4496
4497
  ["date", new Set(["-s", "--set"])],
4497
4498
  ["fd", new Set(["-x", "-X", "--exec", "--exec-batch"])],
4499
+ ["file", new Set(["-C", "--compile"])],
4498
4500
  [
4499
4501
  "find",
4500
4502
  new Set([
@@ -4509,9 +4511,48 @@ var MUTATING_ARGUMENTS = new Map([
4509
4511
  "-okdir"
4510
4512
  ])
4511
4513
  ],
4514
+ ["hostname", new Set(["-F", "--file", "-b", "--boot"])],
4512
4515
  ["rg", new Set(["--hostname-bin", "--pre"])],
4513
- ["sort", new Set(["-o", "--output"])],
4514
- ["yq", new Set(["-i", "--in-place", "--inplace"])]
4516
+ ["sort", new Set(["-o", "--output", "-T", "--temporary-directory", "--compress-program"])],
4517
+ ["yq", new Set(["-i", "--in-place", "--inplace", "-s", "--split-exp"])]
4518
+ ]);
4519
+ var POSITIONAL_LIMITS = new Map([
4520
+ ["hostname", { max: 0, valueOptions: new Set }],
4521
+ ["uniq", { max: 1, valueOptions: new Set(["-f", "-s", "-w"]) }],
4522
+ [
4523
+ "xxd",
4524
+ {
4525
+ max: 1,
4526
+ valueOptions: new Set([
4527
+ "-c",
4528
+ "-cols",
4529
+ "-g",
4530
+ "-groupsize",
4531
+ "-l",
4532
+ "-len",
4533
+ "-n",
4534
+ "-name",
4535
+ "-o",
4536
+ "-offset",
4537
+ "-s",
4538
+ "-seek"
4539
+ ])
4540
+ }
4541
+ ]
4542
+ ]);
4543
+ var OPTION_ALLOWLISTS = new Map([
4544
+ ["awk", new Set(["-F", "-v", "--field-separator", "--assign"])]
4545
+ ]);
4546
+ var GIT_MUTATING_ARGUMENTS = new Set([
4547
+ "--config-env",
4548
+ "--exec-path",
4549
+ "--ext-diff",
4550
+ "--filters",
4551
+ "--output",
4552
+ "--textconv"
4553
+ ]);
4554
+ var GIT_SUBCOMMAND_MUTATING_ARGUMENTS = new Map([
4555
+ ["grep", new Set(["-O", "--open-files-in-pager"])]
4515
4556
  ]);
4516
4557
  var READ_ONLY_GIT_SUBCOMMANDS = Object.freeze(new Set([
4517
4558
  "blame",
@@ -4521,7 +4562,6 @@ var READ_ONLY_GIT_SUBCOMMANDS = Object.freeze(new Set([
4521
4562
  "grep",
4522
4563
  "log",
4523
4564
  "ls-files",
4524
- "ls-remote",
4525
4565
  "ls-tree",
4526
4566
  "merge-base",
4527
4567
  "name-rev",
@@ -4575,9 +4615,10 @@ var GIT_CONFIG_WRITE_FLAGS = Object.freeze(new Set([
4575
4615
  ]));
4576
4616
  var GIT_CONFIG_READ_SUBCOMMANDS = Object.freeze(new Set(["get", "list"]));
4577
4617
  var GIT_CONFIG_WRITE_SUBCOMMANDS = Object.freeze(new Set(["edit", "remove-section", "rename-section", "set", "unset"]));
4578
- var GIT_REMOTE_READ_SUBCOMMANDS = Object.freeze(new Set(["get-url", "show"]));
4618
+ var GIT_REMOTE_READ_SUBCOMMANDS = Object.freeze(new Set(["get-url"]));
4579
4619
  function hasFlag(args, flags) {
4580
- return args.some((arg) => flags.has(arg) || flags.has(arg.split("=", 1)[0] ?? arg));
4620
+ const letters = [...flags].flatMap((flag) => /^-[A-Za-z0-9]$/.test(flag) ? [flag[1]] : []);
4621
+ return args.some((arg) => flags.has(arg) || flags.has(arg.split("=", 1)[0] ?? arg) || arg.startsWith("--") && [...flags].some((flag) => flag.startsWith("--") && flag.startsWith(arg.split("=", 1)[0])) || /^-[^-]/.test(arg) && letters.some((letter) => arg.slice(1).includes(letter)));
4581
4622
  }
4582
4623
  function listsRefsOnly(args) {
4583
4624
  if (hasFlag(args, GIT_REF_WRITE_FLAGS))
@@ -4610,8 +4651,9 @@ var CONDITIONAL_GIT_SUBCOMMANDS = new Map([
4610
4651
  ],
4611
4652
  ["tag", listsRefsOnly]
4612
4653
  ]);
4613
- var READ_ONLY_GH_SUBCOMMANDS = Object.freeze(new Set(["api", "browse", "search", "status"]));
4614
- var COMPOSITION_PATTERN = /[;&|><`$(){}\n\r]|\bsudo\b|\bexec\b/;
4654
+ var NETWORK_COMMANDS = new Set(["curl", "gh", "wget"]);
4655
+ var NETWORK_GIT_SUBCOMMANDS = new Set(["fetch", "ls-remote", "pull"]);
4656
+ var COMPOSITION_PATTERN = /[;&|><`$(){}\n\r'"\\*?[\]]|\bsudo\b|\bexec\b/;
4615
4657
  function isCoordinatorReadOnlyPolicy(policy) {
4616
4658
  return policy?.id === "coordinator-read-only";
4617
4659
  }
@@ -4640,13 +4682,14 @@ function claudeReadOnlyAllowedTools(options) {
4640
4682
  ...(options.researchMcpServerNames ?? []).map((name) => `${claudeMcpToolPrefix(name)}*`)
4641
4683
  ];
4642
4684
  }
4643
- function claudeReadOnlySandbox(policy) {
4685
+ function claudeReadOnlySandbox(policy, options = {}) {
4644
4686
  return {
4645
4687
  enabled: true,
4646
- failIfUnavailable: true,
4688
+ failIfUnavailable: options.commandSandbox !== "unavailable",
4647
4689
  autoAllowBashIfSandboxed: true,
4648
4690
  allowUnsandboxedCommands: false,
4649
4691
  excludedCommands: [],
4692
+ filesystem: { denyWrite: [...new Set(options.workspaces ?? [])] },
4650
4693
  network: {
4651
4694
  allowLocalBinding: policy.networkAccess === "full",
4652
4695
  strictAllowlist: true,
@@ -4658,32 +4701,54 @@ function isReadOnlyShellCommand(command) {
4658
4701
  const trimmed = command.trim();
4659
4702
  if (!trimmed)
4660
4703
  return { allowed: true };
4661
- if (COMPOSITION_PATTERN.test(trimmed)) {
4704
+ if (COMPOSITION_PATTERN.test(trimmed) || /(?:^|\s)~/.test(trimmed)) {
4662
4705
  return {
4663
4706
  allowed: false,
4664
- reason: "Coordinator shells cannot use pipes, redirection, substitution or command chaining, because the command that runs would not be the one checked."
4707
+ reason: "Coordinator shells cannot use quotes, escapes, globs, pipes, redirection, substitution or command chaining, because the command that runs would not be the one checked."
4665
4708
  };
4666
4709
  }
4667
4710
  const tokens = trimmed.split(/\s+/).filter(Boolean);
4668
- const program = tokens[0]?.split("/").at(-1) ?? "";
4669
- if (!program)
4711
+ const first = tokens[0] ?? "";
4712
+ if (!first)
4670
4713
  return { allowed: true };
4671
- if (program === "git" || program === "gh") {
4672
- const subcommandIndex = tokens.findIndex((token, index) => index > 0 && !token.startsWith("-"));
4673
- const subcommand = subcommandIndex > 0 ? tokens[subcommandIndex] : undefined;
4674
- const args = subcommandIndex > 0 ? tokens.slice(subcommandIndex + 1) : [];
4675
- const permitted = program === "git" ? subcommand !== undefined && (READ_ONLY_GIT_SUBCOMMANDS.has(subcommand) || (CONDITIONAL_GIT_SUBCOMMANDS.get(subcommand)?.(args) ?? false)) : subcommand !== undefined && READ_ONLY_GH_SUBCOMMANDS.has(subcommand);
4676
- if (permitted)
4677
- return { allowed: true };
4678
- const described = [program, subcommand].filter(Boolean).join(" ");
4714
+ if (first.includes("/") || first.includes("\\")) {
4679
4715
  return {
4680
4716
  allowed: false,
4681
- reason: `Coordinator is read-only, so \`${described}\` is not available here. Delegate it to a worker environment.`
4717
+ reason: `Coordinator shells run reading programs by name only, so \`${first}\` is not available here. Use the program name, or delegate it to a worker environment.`
4682
4718
  };
4683
4719
  }
4720
+ const program = first;
4721
+ if (NETWORK_COMMANDS.has(program))
4722
+ return networkRefusal(program);
4723
+ if (program === "git") {
4724
+ const subcommandIndex = tokens.findIndex((token, index) => index > 0 && !token.startsWith("-"));
4725
+ const subcommand = subcommandIndex > 0 ? tokens[subcommandIndex] : undefined;
4726
+ const args = subcommandIndex > 0 ? tokens.slice(subcommandIndex + 1) : [];
4727
+ if (subcommand && (NETWORK_GIT_SUBCOMMANDS.has(subcommand) || subcommand === "remote" && args.some((arg) => arg === "show"))) {
4728
+ return networkRefusal(`git ${subcommand}`);
4729
+ }
4730
+ const permitted = subcommand !== undefined && (READ_ONLY_GIT_SUBCOMMANDS.has(subcommand) || (CONDITIONAL_GIT_SUBCOMMANDS.get(subcommand)?.(args) ?? false));
4731
+ const described = ["git", subcommand].filter(Boolean).join(" ");
4732
+ if (!permitted) {
4733
+ return {
4734
+ allowed: false,
4735
+ reason: `Coordinator is read-only, so \`${described}\` is not available here. Delegate it to a worker environment.`
4736
+ };
4737
+ }
4738
+ if (hasFlag(tokens.slice(1), GIT_MUTATING_ARGUMENTS) || hasFlag(args, GIT_SUBCOMMAND_MUTATING_ARGUMENTS.get(subcommand) ?? new Set)) {
4739
+ return {
4740
+ allowed: false,
4741
+ reason: `Coordinator is read-only, so \`${described}\` cannot be used with an argument that writes a file or runs another program. Delegate it to a worker environment.`
4742
+ };
4743
+ }
4744
+ return { allowed: true };
4745
+ }
4684
4746
  if (READ_ONLY_COMMANDS.has(program)) {
4747
+ const args = tokens.slice(1);
4685
4748
  const mutating = MUTATING_ARGUMENTS.get(program);
4686
- if (mutating && hasFlag(tokens.slice(1), mutating)) {
4749
+ const options = OPTION_ALLOWLISTS.get(program);
4750
+ const limit = POSITIONAL_LIMITS.get(program);
4751
+ if (mutating && hasFlag(args, mutating) || options && !optionsAllowed(args, options) || limit && positionalCount(args, limit.valueOptions) > limit.max) {
4687
4752
  return {
4688
4753
  allowed: false,
4689
4754
  reason: `Coordinator is read-only, so \`${program}\` cannot be used with an argument that writes or runs another program. Delegate it to a worker environment.`
@@ -4696,6 +4761,48 @@ function isReadOnlyShellCommand(command) {
4696
4761
  reason: `Coordinator is read-only, so \`${program}\` is not available here. Delegate commands that change the checkout to a worker environment.`
4697
4762
  };
4698
4763
  }
4764
+ function networkRefusal(described) {
4765
+ return {
4766
+ allowed: false,
4767
+ reason: `Coordinator commands have no network access, so \`${described}\` is not available here. Delegate it to a worker environment.`
4768
+ };
4769
+ }
4770
+ function optionsAllowed(args, allowed) {
4771
+ for (let index = 0;index < args.length; index += 1) {
4772
+ const arg = args[index];
4773
+ if (arg === "--")
4774
+ return true;
4775
+ if (!arg.startsWith("-") || arg === "-")
4776
+ continue;
4777
+ if (allowed.has(arg)) {
4778
+ index += 1;
4779
+ continue;
4780
+ }
4781
+ const long = arg.startsWith("--") ? arg.split("=", 1)[0] : undefined;
4782
+ const attached = long ?? arg.slice(0, 2);
4783
+ if (!allowed.has(attached))
4784
+ return false;
4785
+ }
4786
+ return true;
4787
+ }
4788
+ function positionalCount(args, valueOptions) {
4789
+ let count = 0;
4790
+ let optionsEnded = false;
4791
+ for (let index = 0;index < args.length; index += 1) {
4792
+ const arg = args[index];
4793
+ if (!optionsEnded && arg === "--") {
4794
+ optionsEnded = true;
4795
+ continue;
4796
+ }
4797
+ if (!optionsEnded && arg.startsWith("-") && arg !== "-") {
4798
+ if (valueOptions.has(arg))
4799
+ index += 1;
4800
+ continue;
4801
+ }
4802
+ count += 1;
4803
+ }
4804
+ return count;
4805
+ }
4699
4806
  function deny(reason) {
4700
4807
  return {
4701
4808
  hookSpecificOutput: {
@@ -12585,7 +12692,7 @@ var AUTH_CACHE_TTL_MS = 30000;
12585
12692
  var FAILED_AUTH_CACHE_TTL_MS = 5000;
12586
12693
  var CLAUDE_SIGN_IN = {
12587
12694
  kind: "terminal",
12588
- hint: "Open a Claude terminal tab and run /login."
12695
+ hint: "Sign in to Claude from the sign-in card in this tab or from Settings → Claude."
12589
12696
  };
12590
12697
  var CLAUDE_SIGN_IN_REQUIRED_MESSAGE = "Claude isn't signed in. Sign in to Claude to keep working.";
12591
12698
  var authCache;
@@ -12593,6 +12700,10 @@ var authRead;
12593
12700
  var authReadGeneration;
12594
12701
  var authRefreshPending = false;
12595
12702
  var authGeneration = 0;
12703
+ var turnAuthenticationFailedUntil = 0;
12704
+ function withTurnFailure(value) {
12705
+ return Date.now() < turnAuthenticationFailedUntil && value.state === "signed-in" ? { ...value, state: "expired" } : value;
12706
+ }
12596
12707
  function presentSource(value) {
12597
12708
  return value && value !== "none" ? value : undefined;
12598
12709
  }
@@ -12629,8 +12740,9 @@ async function probeAccount() {
12629
12740
  }
12630
12741
  }
12631
12742
  function readClaudeAuthStatus() {
12632
- if (authCache && authCache.expiresAt > Date.now())
12633
- return Promise.resolve(authCache.value);
12743
+ if (authCache && authCache.expiresAt > Date.now()) {
12744
+ return Promise.resolve(withTurnFailure(authCache.value));
12745
+ }
12634
12746
  if (authRead)
12635
12747
  return authRead;
12636
12748
  const generation = authGeneration;
@@ -12645,7 +12757,7 @@ function readClaudeAuthStatus() {
12645
12757
  if (generation === authGeneration) {
12646
12758
  authCache = { expiresAt: Date.now() + ttl, value };
12647
12759
  }
12648
- return value;
12760
+ return withTurnFailure(value);
12649
12761
  }).finally(() => {
12650
12762
  if (authRead !== read)
12651
12763
  return;
@@ -12661,6 +12773,7 @@ function readClaudeAuthStatus() {
12661
12773
  return read;
12662
12774
  }
12663
12775
  function noteClaudeAuthenticationFailure() {
12776
+ turnAuthenticationFailedUntil = Date.now() + AUTH_CACHE_TTL_MS;
12664
12777
  authCache = undefined;
12665
12778
  if (authRead && authReadGeneration !== authGeneration)
12666
12779
  authRefreshPending = true;
@@ -12669,6 +12782,7 @@ function noteClaudeAuthenticationFailure() {
12669
12782
  function noteClaudeAuthenticationWorked() {
12670
12783
  authGeneration += 1;
12671
12784
  authRefreshPending = false;
12785
+ turnAuthenticationFailedUntil = 0;
12672
12786
  if (authCache?.value.state === "needs-auth")
12673
12787
  authCache = undefined;
12674
12788
  }
@@ -13845,9 +13959,86 @@ function backgroundCommandInstruction(env) {
13845
13959
  }
13846
13960
 
13847
13961
  // ../../bridges/claude-bridge/src/services/claude-execution-sandbox.ts
13848
- function claudeExecutionSandbox(policy, asksForApproval) {
13849
- if (policy && isCoordinatorReadOnlyPolicy(policy))
13850
- return claudeReadOnlySandbox(policy);
13962
+ import { realpathSync } from "node:fs";
13963
+
13964
+ // ../protocol/src/coordinator.ts
13965
+ var COORDINATOR_COMMAND_SANDBOX_ENV = "ORKESTRATOR_COORDINATOR_COMMAND_SANDBOX";
13966
+ function coordinatorCommandSandboxFromEnv(env) {
13967
+ return env[COORDINATOR_COMMAND_SANDBOX_ENV] === "unavailable" ? "unavailable" : "required";
13968
+ }
13969
+ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
13970
+ "ack_message",
13971
+ "add_ticket_comment",
13972
+ "address_multi_review",
13973
+ "adopt_workflow",
13974
+ "cancel_build_pipeline",
13975
+ "cancel_multi_review",
13976
+ "compare_project_files",
13977
+ "create_ticket",
13978
+ "get_build_pipeline",
13979
+ "get_environment",
13980
+ "get_launch_options",
13981
+ "get_message",
13982
+ "get_message_status",
13983
+ "get_multi_review",
13984
+ "get_repository_context",
13985
+ "get_tab_state",
13986
+ "get_tab_transcript",
13987
+ "get_ticket",
13988
+ "launch_environment",
13989
+ "launch_job",
13990
+ "launch_multi_review",
13991
+ "launch_node_environment",
13992
+ "list_agents",
13993
+ "list_environments",
13994
+ "list_mailboxes",
13995
+ "list_node_environments",
13996
+ "list_nodes",
13997
+ "list_projects",
13998
+ "list_tabs",
13999
+ "list_tickets",
14000
+ "list_workflows",
14001
+ "open_multi_review",
14002
+ "open_multi_review_fix",
14003
+ "pause_build_pipeline",
14004
+ "read_messages",
14005
+ "read_node_environment",
14006
+ "request_env_value_reveal",
14007
+ "request_environment_deletion",
14008
+ "request_project_file_update",
14009
+ "request_project_on_node",
14010
+ "resume_build_pipeline",
14011
+ "send_message",
14012
+ "send_node_message",
14013
+ "send_prompt_to_node_tab",
14014
+ "send_prompt_to_tab",
14015
+ "start_build_pipeline",
14016
+ "start_environment",
14017
+ "start_multi_review",
14018
+ "start_node_environment",
14019
+ "stop_environment",
14020
+ "stop_node_environment",
14021
+ "transfer_files",
14022
+ "update_ticket"
14023
+ ]);
14024
+
14025
+ // ../../bridges/claude-bridge/src/services/claude-execution-sandbox.ts
14026
+ function claudeReadOnlyWorkspaces(cwd) {
14027
+ let resolved;
14028
+ try {
14029
+ resolved = realpathSync(cwd);
14030
+ } catch {
14031
+ resolved = undefined;
14032
+ }
14033
+ return [...new Set([cwd, ...resolved ? [resolved] : []])];
14034
+ }
14035
+ function claudeExecutionSandbox(policy, asksForApproval, readOnly = {}) {
14036
+ if (policy && isCoordinatorReadOnlyPolicy(policy)) {
14037
+ return claudeReadOnlySandbox(policy, {
14038
+ workspaces: claudeReadOnlyWorkspaces(readOnly.cwd ?? claudeWorkspaceCwd()),
14039
+ commandSandbox: readOnly.commandSandbox ?? coordinatorCommandSandboxFromEnv(process.env)
14040
+ });
14041
+ }
13851
14042
  if (policy?.sandbox !== "provider")
13852
14043
  return;
13853
14044
  return {
@@ -14782,7 +14973,9 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
14782
14973
  forwardSubagentText: true,
14783
14974
  allowedTools,
14784
14975
  ...disallowedTools?.length ? { disallowedTools } : {},
14785
- ...policy?.sandbox === "provider" || coordinatorReadOnly ? { sandbox: claudeExecutionSandbox(policy, claudeAsksForApproval(policy, options)) } : {},
14976
+ ...policy?.sandbox === "provider" || coordinatorReadOnly ? {
14977
+ sandbox: claudeExecutionSandbox(policy, claudeAsksForApproval(policy, options), { cwd })
14978
+ } : {},
14786
14979
  abortController,
14787
14980
  ...session.sdkSessionId ? { resume: session.sdkSessionId } : {
14788
14981
  sessionId: sdkSessionIdFromBridgeId(session.id) ?? crypto.randomUUID()
@@ -14794,10 +14987,11 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
14794
14987
  systemPrompt: {
14795
14988
  type: "preset",
14796
14989
  preset: "claude_code",
14797
- append: [
14990
+ 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." : [
14798
14991
  "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.",
14799
14992
  backgroundCommandInstruction(queryEnvironment),
14800
- "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."
14993
+ "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.",
14994
+ "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."
14801
14995
  ].join(`
14802
14996
 
14803
14997
  `)
@@ -14858,6 +15052,34 @@ function observeClaudeMessage(diagnostics, message) {
14858
15052
  }
14859
15053
  }
14860
15054
 
15055
+ // ../../bridges/claude-bridge/src/services/session-auth-notice.ts
15056
+ var CLAUDE_LOGIN_REFUSED_MESSAGE = "Claude's login was refused. Sign in to Claude again to keep working.";
15057
+ var MAX_NOTICE_LENGTH = 500;
15058
+ function setSessionAuthNotice(session, sessionId, notice) {
15059
+ const previous = session.authNotice;
15060
+ if (!previous && !notice)
15061
+ return;
15062
+ if (previous && notice && previous.state === notice.state && previous.message === notice.message && previous.occurrenceId === notice.occurrenceId) {
15063
+ return;
15064
+ }
15065
+ session.authNotice = notice ? { ...notice, message: notice.message.slice(0, MAX_NOTICE_LENGTH) } : undefined;
15066
+ eventEmitter.emit({
15067
+ type: "session.updated",
15068
+ sessionId,
15069
+ data: { authNotice: session.authNotice ?? null }
15070
+ });
15071
+ }
15072
+ function noteSessionLoginRefused(session, sessionId, occurrenceId) {
15073
+ const current = session.authNotice;
15074
+ if (current?.state === "expired" && current.message === CLAUDE_LOGIN_REFUSED_MESSAGE)
15075
+ return;
15076
+ setSessionAuthNotice(session, sessionId, {
15077
+ state: "expired",
15078
+ message: CLAUDE_LOGIN_REFUSED_MESSAGE,
15079
+ occurrenceId
15080
+ });
15081
+ }
15082
+
14861
15083
  // ../../bridges/claude-bridge/src/services/session-manager-prompt-handlers/assistant.ts
14862
15084
  function applyClaudeUsageReport(session, report) {
14863
15085
  const previous = session.inProgressUsage ?? session.usage;
@@ -14916,10 +15138,13 @@ function handleAssistantMessage(turn, message) {
14916
15138
  const receivedAt = new Date().toISOString();
14917
15139
  const recordUnknownContentBlock = (blockType) => sessionHealth(session).recordUnknown(`block:${blockType}`);
14918
15140
  const assistantError = message.error;
14919
- if (assistantError === "authentication_failed")
15141
+ if (assistantError === "authentication_failed") {
14920
15142
  noteClaudeAuthenticationFailure();
14921
- else if (!assistantError)
15143
+ noteSessionLoginRefused(session, sessionId, typeof message.uuid === "string" ? message.uuid : crypto.randomUUID());
15144
+ } else if (!assistantError) {
14922
15145
  noteClaudeAuthenticationWorked();
15146
+ setSessionAuthNotice(session, sessionId, undefined);
15147
+ }
14923
15148
  const usageReport = message.usage_report;
14924
15149
  if (usageReport) {
14925
15150
  const usage = applyClaudeUsageReport(session, usageReport);
@@ -15204,6 +15429,16 @@ function handleAuthStatus(turn, message) {
15204
15429
  severity: auth.error ? "error" : "info",
15205
15430
  createdAt: new Date().toISOString()
15206
15431
  });
15432
+ const occurrenceId = typeof auth.uuid === "string" && auth.uuid ? auth.uuid : crypto.randomUUID();
15433
+ setSessionAuthNotice(session, sessionId, auth.error ? {
15434
+ state: "expired",
15435
+ message: `${content.slice(0, 300)}. Sign in to Claude again to keep working.`,
15436
+ occurrenceId
15437
+ } : auth.isAuthenticating ? {
15438
+ state: "needs-auth",
15439
+ message: "Claude is signing in again. Finish any sign-in it opened to continue.",
15440
+ occurrenceId
15441
+ } : undefined);
15207
15442
  }
15208
15443
  function handleStreamEvent(turn, message) {
15209
15444
  if (message.event?.type !== "ping") {
@@ -18006,7 +18241,8 @@ session.get("/:id", async (c) => {
18006
18241
  completionBlockedByBackgroundTasks: sessionData.completionBlockedByBackgroundTasks === true,
18007
18242
  rewindInProgress: sessionData.rewindInProgress === true,
18008
18243
  ...sessionData.commandInventoryState ? { commandRevision: sessionData.commandInventoryState.revision } : {},
18009
- ...readinessField()
18244
+ ...readinessField(),
18245
+ ...sessionData.authNotice ? { authNotice: sessionData.authNotice } : {}
18010
18246
  });
18011
18247
  });
18012
18248
  function readinessField() {
@@ -19295,156 +19531,6 @@ mcp.get("/servers", async (c) => {
19295
19531
  });
19296
19532
  var mcp_default = mcp;
19297
19533
 
19298
- // ../../bridges/claude-bridge/src/services/slash-commands.ts
19299
- import { open as open3, readdir as readdir2, stat as stat4 } from "node:fs/promises";
19300
- import { StringDecoder } from "node:string_decoder";
19301
- import { join as join11, basename, dirname as dirname2 } from "node:path";
19302
- var commandDescriptions = new Map;
19303
- var MAX_COMMAND_DESCRIPTION_CACHE_ENTRIES = 512;
19304
- function rememberCommandDescription(filePath, entry) {
19305
- commandDescriptions.delete(filePath);
19306
- commandDescriptions.set(filePath, entry);
19307
- while (commandDescriptions.size > MAX_COMMAND_DESCRIPTION_CACHE_ENTRIES) {
19308
- const oldest = commandDescriptions.keys().next();
19309
- if (oldest.done)
19310
- break;
19311
- commandDescriptions.delete(oldest.value);
19312
- }
19313
- }
19314
- var FRONTMATTER_READ_CHUNK_BYTES = 8 * 1024;
19315
- var MAX_FRONTMATTER_READ_BYTES = 256 * 1024;
19316
- function fingerprintOf2(stats) {
19317
- return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}`;
19318
- }
19319
- function hasCompleteFrontmatter(content) {
19320
- return /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/.test(content);
19321
- }
19322
- async function readFileFrontmatter(filePath) {
19323
- const handle = await open3(filePath, "r");
19324
- try {
19325
- const decoder = new StringDecoder("utf8");
19326
- let content = "";
19327
- let position = 0;
19328
- while (position < MAX_FRONTMATTER_READ_BYTES) {
19329
- const readLength = Math.min(FRONTMATTER_READ_CHUNK_BYTES, MAX_FRONTMATTER_READ_BYTES - position);
19330
- const buffer = Buffer.allocUnsafe(readLength);
19331
- const { bytesRead } = await handle.read(buffer, 0, readLength, position);
19332
- if (bytesRead === 0)
19333
- break;
19334
- position += bytesRead;
19335
- content += decoder.write(buffer.subarray(0, bytesRead));
19336
- if (position >= FRONTMATTER_READ_CHUNK_BYTES && !content.startsWith(`---
19337
- `) && !content.startsWith(`---\r
19338
- `) || hasCompleteFrontmatter(content)) {
19339
- break;
19340
- }
19341
- }
19342
- return content + decoder.end();
19343
- } finally {
19344
- await handle.close();
19345
- }
19346
- }
19347
- async function readCommandDescription(filePath) {
19348
- const fingerprint = fingerprintOf2(await stat4(filePath));
19349
- const cached = commandDescriptions.get(filePath);
19350
- if (cached && cached.fingerprint === fingerprint) {
19351
- return cached.description;
19352
- }
19353
- const description = parseDescription(await readFileFrontmatter(filePath));
19354
- rememberCommandDescription(filePath, { fingerprint, description });
19355
- return description;
19356
- }
19357
- function pruneCommandDescriptionCache(commandsDir, livePaths) {
19358
- for (const cachedPath of commandDescriptions.keys()) {
19359
- if (dirname2(cachedPath) === commandsDir && !livePaths.has(cachedPath)) {
19360
- commandDescriptions.delete(cachedPath);
19361
- }
19362
- }
19363
- }
19364
- var BUILTIN_COMMANDS = [
19365
- "/clear - Clear conversation history",
19366
- "/compact - Compact conversation to reduce tokens",
19367
- "/context - Show current context",
19368
- "/cost - Show token usage and cost",
19369
- "/doctor - Check system health",
19370
- "/goal - Set, view, or clear a completion goal",
19371
- "/help - Show available commands",
19372
- "/init - Re-initialize the session",
19373
- "/logout - Log out of Claude",
19374
- "/memory - Show memory usage",
19375
- "/model - Show or change model",
19376
- "/permissions - Manage permissions",
19377
- "/review - Review recent changes",
19378
- "/status - Show session status",
19379
- "/vim - Toggle vim mode"
19380
- ];
19381
- function parseDescription(content) {
19382
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
19383
- if (!match?.[1])
19384
- return;
19385
- const frontmatter = match[1];
19386
- const descMatch = frontmatter.match(/^description:\s*(?:"([^"]*?)"|'([^']*?)'|(.+?))\s*$/m);
19387
- if (!descMatch)
19388
- return;
19389
- return (descMatch[1] ?? descMatch[2] ?? descMatch[3])?.trim();
19390
- }
19391
- async function scanCommandsDir(commandsDir, prefix) {
19392
- let entries;
19393
- try {
19394
- entries = await readdir2(commandsDir);
19395
- } catch {
19396
- pruneCommandDescriptionCache(commandsDir, new Set);
19397
- return [];
19398
- }
19399
- const commands = [];
19400
- const livePaths = new Set;
19401
- for (const entry of entries) {
19402
- if (!entry.endsWith(".md"))
19403
- continue;
19404
- const name = basename(entry, ".md");
19405
- const fullName = prefix ? `/${prefix}${name}` : `/${name}`;
19406
- const commandPath = join11(commandsDir, entry);
19407
- livePaths.add(commandPath);
19408
- let description;
19409
- try {
19410
- description = await readCommandDescription(commandPath);
19411
- } catch {}
19412
- commands.push(description ? `${fullName} - ${description}` : fullName);
19413
- }
19414
- pruneCommandDescriptionCache(commandsDir, livePaths);
19415
- return commands;
19416
- }
19417
- async function discoverSlashCommands(cwd) {
19418
- const seen = new Set;
19419
- const result = [];
19420
- const addCommand = (cmd) => {
19421
- const name = cmd.split(" - ")[0].trim().toLowerCase();
19422
- if (!seen.has(name)) {
19423
- seen.add(name);
19424
- result.push(cmd);
19425
- }
19426
- };
19427
- const repoCommands = await scanCommandsDir(join11(cwd, ".claude", "commands"), "");
19428
- for (const cmd of repoCommands)
19429
- addCommand(cmd);
19430
- try {
19431
- const plugins = await getMergedPlugins(cwd);
19432
- for (const plugin of plugins) {
19433
- const manifest = await readPluginManifest(plugin.path);
19434
- const pluginName = manifest?.name || plugin.path.split("/").pop() || "unknown";
19435
- const commandsDir = join11(plugin.path, "commands");
19436
- const pluginCommands = await scanCommandsDir(commandsDir, `${pluginName}:`);
19437
- for (const cmd of pluginCommands)
19438
- addCommand(cmd);
19439
- }
19440
- } catch (error) {
19441
- console.warn("[slash-commands] Failed to scan plugin commands:", error);
19442
- }
19443
- for (const cmd of BUILTIN_COMMANDS)
19444
- addCommand(cmd);
19445
- return result.sort((a, b) => a.localeCompare(b));
19446
- }
19447
-
19448
19534
  // ../../bridges/claude-bridge/src/routes/plugins.ts
19449
19535
  var plugins = new Hono2;
19450
19536
  plugins.get("/", async (c) => {
@@ -19460,21 +19546,12 @@ plugins.get("/", async (c) => {
19460
19546
  return c.json({ error: error instanceof Error ? error.message : "Failed to get plugins" }, 500);
19461
19547
  }
19462
19548
  });
19463
- plugins.get("/commands", async (c) => {
19464
- try {
19465
- const cwd = process.env.CWD || process.cwd();
19466
- const commands = await discoverSlashCommands(cwd);
19467
- return c.json({ commands });
19468
- } catch (error) {
19469
- console.error("[plugins] Error discovering slash commands:", error);
19470
- return c.json({ error: error instanceof Error ? error.message : "Failed to discover slash commands" }, 500);
19471
- }
19472
- });
19473
19549
  var plugins_default = plugins;
19474
19550
 
19475
19551
  // ../protocol/src/parent-watchdog.ts
19476
19552
  var PARENT_PID_ENV = "ORKESTRATOR_PARENT_PID";
19477
- var DEFAULT_POLL_INTERVAL_MS = 15000;
19553
+ var PARENT_WATCH_INTERVAL_MS = 1000;
19554
+ var DEFAULT_POLL_INTERVAL_MS = PARENT_WATCH_INTERVAL_MS;
19478
19555
  function parseParentPid(value) {
19479
19556
  if (!value)
19480
19557
  return null;
@@ -21125,7 +21202,7 @@ compressTranscriptReads(app);
21125
21202
  app.get("/global/auth-check", (c) => c.json({ status: "ok" }));
21126
21203
  app.get("/global/auth", async (c) => c.json(await readClaudeAuthStatus()));
21127
21204
  app.get("/global/usage", async (c) => c.json({ account: await readClaudePlanUsage() }));
21128
- app.post("/global/auth/login", (c) => c.json({ error: "Open a Claude terminal tab and run /login." }, 405));
21205
+ app.post("/global/auth/login", (c) => c.json({ error: "Claude signs in from the sign-in card in its tab or from Settings → Claude." }, 405));
21129
21206
  app.post("/global/auth/logout", (c) => c.json({ error: "Claude sign-out is available from a terminal tab." }, 405));
21130
21207
  app.post("/global/refresh-catalog", async (c) => {
21131
21208
  await refreshClaudeCatalogs();