orkestrator 2.29.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orkestrator",
3
- "version": "2.29.0-beta",
3
+ "version": "2.30.0-beta",
4
4
  "description": "Run the Orkestrator backend service without the desktop application",
5
5
  "homepage": "https://orkestrator.dev",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  "smoke:pack": "bun scripts/smoke-packed.ts",
32
32
  "test": "bun run build && bun test --preload ../../tests/setup-node.ts tests --parallel",
33
33
  "test:workspace": "bun test --preload ../../tests/setup-node.ts tests --only-failures ${ORKESTRATOR_TEST_TIMINGS_DIR:+--timings=$ORKESTRATOR_TEST_TIMINGS_DIR/cli.json --update-timings} --parallel=${ORKESTRATOR_TEST_WORKERS:-2}",
34
- "typecheck": "bunx tsc --noEmit -p tsconfig.json"
34
+ "typecheck": "bun check -p tsconfig.json"
35
35
  },
36
36
  "dependencies": {
37
37
  "@anthropic-ai/claude-agent-sdk": "0.3.309",
@@ -39,11 +39,11 @@
39
39
  "sharp": "0.35.5"
40
40
  },
41
41
  "devDependencies": {
42
- "@orkestrator/protocol": "2.29.0-beta",
43
- "@orkestrator/toolchain": "2.29.0-beta"
42
+ "@orkestrator/protocol": "2.30.0-beta",
43
+ "@orkestrator/toolchain": "2.30.0-beta"
44
44
  },
45
45
  "engines": {
46
- "bun": ">=1.4.3"
46
+ "bun": ">=1.4.4"
47
47
  },
48
48
  "//dependencies": "Only modules scripts/build.ts leaves unbundled belong here; everything else is inlined into dist/ and resources/. tests/cli.test.ts derives this list from the built artifacts and fails if it drifts."
49
49
  }
@@ -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;
@@ -13852,9 +13959,86 @@ function backgroundCommandInstruction(env) {
13852
13959
  }
13853
13960
 
13854
13961
  // ../../bridges/claude-bridge/src/services/claude-execution-sandbox.ts
13855
- function claudeExecutionSandbox(policy, asksForApproval) {
13856
- if (policy && isCoordinatorReadOnlyPolicy(policy))
13857
- 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
+ }
13858
14042
  if (policy?.sandbox !== "provider")
13859
14043
  return;
13860
14044
  return {
@@ -14789,7 +14973,9 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
14789
14973
  forwardSubagentText: true,
14790
14974
  allowedTools,
14791
14975
  ...disallowedTools?.length ? { disallowedTools } : {},
14792
- ...policy?.sandbox === "provider" || coordinatorReadOnly ? { sandbox: claudeExecutionSandbox(policy, claudeAsksForApproval(policy, options)) } : {},
14976
+ ...policy?.sandbox === "provider" || coordinatorReadOnly ? {
14977
+ sandbox: claudeExecutionSandbox(policy, claudeAsksForApproval(policy, options), { cwd })
14978
+ } : {},
14793
14979
  abortController,
14794
14980
  ...session.sdkSessionId ? { resume: session.sdkSessionId } : {
14795
14981
  sessionId: sdkSessionIdFromBridgeId(session.id) ?? crypto.randomUUID()
@@ -14801,7 +14987,7 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
14801
14987
  systemPrompt: {
14802
14988
  type: "preset",
14803
14989
  preset: "claude_code",
14804
- 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." : [
14805
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.",
14806
14992
  backgroundCommandInstruction(queryEnvironment),
14807
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.",
@@ -14866,6 +15052,34 @@ function observeClaudeMessage(diagnostics, message) {
14866
15052
  }
14867
15053
  }
14868
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
+
14869
15083
  // ../../bridges/claude-bridge/src/services/session-manager-prompt-handlers/assistant.ts
14870
15084
  function applyClaudeUsageReport(session, report) {
14871
15085
  const previous = session.inProgressUsage ?? session.usage;
@@ -14924,10 +15138,13 @@ function handleAssistantMessage(turn, message) {
14924
15138
  const receivedAt = new Date().toISOString();
14925
15139
  const recordUnknownContentBlock = (blockType) => sessionHealth(session).recordUnknown(`block:${blockType}`);
14926
15140
  const assistantError = message.error;
14927
- if (assistantError === "authentication_failed")
15141
+ if (assistantError === "authentication_failed") {
14928
15142
  noteClaudeAuthenticationFailure();
14929
- else if (!assistantError)
15143
+ noteSessionLoginRefused(session, sessionId, typeof message.uuid === "string" ? message.uuid : crypto.randomUUID());
15144
+ } else if (!assistantError) {
14930
15145
  noteClaudeAuthenticationWorked();
15146
+ setSessionAuthNotice(session, sessionId, undefined);
15147
+ }
14931
15148
  const usageReport = message.usage_report;
14932
15149
  if (usageReport) {
14933
15150
  const usage = applyClaudeUsageReport(session, usageReport);
@@ -15212,6 +15429,16 @@ function handleAuthStatus(turn, message) {
15212
15429
  severity: auth.error ? "error" : "info",
15213
15430
  createdAt: new Date().toISOString()
15214
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);
15215
15442
  }
15216
15443
  function handleStreamEvent(turn, message) {
15217
15444
  if (message.event?.type !== "ping") {
@@ -18014,7 +18241,8 @@ session.get("/:id", async (c) => {
18014
18241
  completionBlockedByBackgroundTasks: sessionData.completionBlockedByBackgroundTasks === true,
18015
18242
  rewindInProgress: sessionData.rewindInProgress === true,
18016
18243
  ...sessionData.commandInventoryState ? { commandRevision: sessionData.commandInventoryState.revision } : {},
18017
- ...readinessField()
18244
+ ...readinessField(),
18245
+ ...sessionData.authNotice ? { authNotice: sessionData.authNotice } : {}
18018
18246
  });
18019
18247
  });
18020
18248
  function readinessField() {
@@ -19303,156 +19531,6 @@ mcp.get("/servers", async (c) => {
19303
19531
  });
19304
19532
  var mcp_default = mcp;
19305
19533
 
19306
- // ../../bridges/claude-bridge/src/services/slash-commands.ts
19307
- import { open as open3, readdir as readdir2, stat as stat4 } from "node:fs/promises";
19308
- import { StringDecoder } from "node:string_decoder";
19309
- import { join as join11, basename, dirname as dirname2 } from "node:path";
19310
- var commandDescriptions = new Map;
19311
- var MAX_COMMAND_DESCRIPTION_CACHE_ENTRIES = 512;
19312
- function rememberCommandDescription(filePath, entry) {
19313
- commandDescriptions.delete(filePath);
19314
- commandDescriptions.set(filePath, entry);
19315
- while (commandDescriptions.size > MAX_COMMAND_DESCRIPTION_CACHE_ENTRIES) {
19316
- const oldest = commandDescriptions.keys().next();
19317
- if (oldest.done)
19318
- break;
19319
- commandDescriptions.delete(oldest.value);
19320
- }
19321
- }
19322
- var FRONTMATTER_READ_CHUNK_BYTES = 8 * 1024;
19323
- var MAX_FRONTMATTER_READ_BYTES = 256 * 1024;
19324
- function fingerprintOf2(stats) {
19325
- return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}`;
19326
- }
19327
- function hasCompleteFrontmatter(content) {
19328
- return /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/.test(content);
19329
- }
19330
- async function readFileFrontmatter(filePath) {
19331
- const handle = await open3(filePath, "r");
19332
- try {
19333
- const decoder = new StringDecoder("utf8");
19334
- let content = "";
19335
- let position = 0;
19336
- while (position < MAX_FRONTMATTER_READ_BYTES) {
19337
- const readLength = Math.min(FRONTMATTER_READ_CHUNK_BYTES, MAX_FRONTMATTER_READ_BYTES - position);
19338
- const buffer = Buffer.allocUnsafe(readLength);
19339
- const { bytesRead } = await handle.read(buffer, 0, readLength, position);
19340
- if (bytesRead === 0)
19341
- break;
19342
- position += bytesRead;
19343
- content += decoder.write(buffer.subarray(0, bytesRead));
19344
- if (position >= FRONTMATTER_READ_CHUNK_BYTES && !content.startsWith(`---
19345
- `) && !content.startsWith(`---\r
19346
- `) || hasCompleteFrontmatter(content)) {
19347
- break;
19348
- }
19349
- }
19350
- return content + decoder.end();
19351
- } finally {
19352
- await handle.close();
19353
- }
19354
- }
19355
- async function readCommandDescription(filePath) {
19356
- const fingerprint = fingerprintOf2(await stat4(filePath));
19357
- const cached = commandDescriptions.get(filePath);
19358
- if (cached && cached.fingerprint === fingerprint) {
19359
- return cached.description;
19360
- }
19361
- const description = parseDescription(await readFileFrontmatter(filePath));
19362
- rememberCommandDescription(filePath, { fingerprint, description });
19363
- return description;
19364
- }
19365
- function pruneCommandDescriptionCache(commandsDir, livePaths) {
19366
- for (const cachedPath of commandDescriptions.keys()) {
19367
- if (dirname2(cachedPath) === commandsDir && !livePaths.has(cachedPath)) {
19368
- commandDescriptions.delete(cachedPath);
19369
- }
19370
- }
19371
- }
19372
- var BUILTIN_COMMANDS = [
19373
- "/clear - Clear conversation history",
19374
- "/compact - Compact conversation to reduce tokens",
19375
- "/context - Show current context",
19376
- "/cost - Show token usage and cost",
19377
- "/doctor - Check system health",
19378
- "/goal - Set, view, or clear a completion goal",
19379
- "/help - Show available commands",
19380
- "/init - Re-initialize the session",
19381
- "/logout - Log out of Claude",
19382
- "/memory - Show memory usage",
19383
- "/model - Show or change model",
19384
- "/permissions - Manage permissions",
19385
- "/review - Review recent changes",
19386
- "/status - Show session status",
19387
- "/vim - Toggle vim mode"
19388
- ];
19389
- function parseDescription(content) {
19390
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
19391
- if (!match?.[1])
19392
- return;
19393
- const frontmatter = match[1];
19394
- const descMatch = frontmatter.match(/^description:\s*(?:"([^"]*?)"|'([^']*?)'|(.+?))\s*$/m);
19395
- if (!descMatch)
19396
- return;
19397
- return (descMatch[1] ?? descMatch[2] ?? descMatch[3])?.trim();
19398
- }
19399
- async function scanCommandsDir(commandsDir, prefix) {
19400
- let entries;
19401
- try {
19402
- entries = await readdir2(commandsDir);
19403
- } catch {
19404
- pruneCommandDescriptionCache(commandsDir, new Set);
19405
- return [];
19406
- }
19407
- const commands = [];
19408
- const livePaths = new Set;
19409
- for (const entry of entries) {
19410
- if (!entry.endsWith(".md"))
19411
- continue;
19412
- const name = basename(entry, ".md");
19413
- const fullName = prefix ? `/${prefix}${name}` : `/${name}`;
19414
- const commandPath = join11(commandsDir, entry);
19415
- livePaths.add(commandPath);
19416
- let description;
19417
- try {
19418
- description = await readCommandDescription(commandPath);
19419
- } catch {}
19420
- commands.push(description ? `${fullName} - ${description}` : fullName);
19421
- }
19422
- pruneCommandDescriptionCache(commandsDir, livePaths);
19423
- return commands;
19424
- }
19425
- async function discoverSlashCommands(cwd) {
19426
- const seen = new Set;
19427
- const result = [];
19428
- const addCommand = (cmd) => {
19429
- const name = cmd.split(" - ")[0].trim().toLowerCase();
19430
- if (!seen.has(name)) {
19431
- seen.add(name);
19432
- result.push(cmd);
19433
- }
19434
- };
19435
- const repoCommands = await scanCommandsDir(join11(cwd, ".claude", "commands"), "");
19436
- for (const cmd of repoCommands)
19437
- addCommand(cmd);
19438
- try {
19439
- const plugins = await getMergedPlugins(cwd);
19440
- for (const plugin of plugins) {
19441
- const manifest = await readPluginManifest(plugin.path);
19442
- const pluginName = manifest?.name || plugin.path.split("/").pop() || "unknown";
19443
- const commandsDir = join11(plugin.path, "commands");
19444
- const pluginCommands = await scanCommandsDir(commandsDir, `${pluginName}:`);
19445
- for (const cmd of pluginCommands)
19446
- addCommand(cmd);
19447
- }
19448
- } catch (error) {
19449
- console.warn("[slash-commands] Failed to scan plugin commands:", error);
19450
- }
19451
- for (const cmd of BUILTIN_COMMANDS)
19452
- addCommand(cmd);
19453
- return result.sort((a, b) => a.localeCompare(b));
19454
- }
19455
-
19456
19534
  // ../../bridges/claude-bridge/src/routes/plugins.ts
19457
19535
  var plugins = new Hono2;
19458
19536
  plugins.get("/", async (c) => {
@@ -19468,21 +19546,12 @@ plugins.get("/", async (c) => {
19468
19546
  return c.json({ error: error instanceof Error ? error.message : "Failed to get plugins" }, 500);
19469
19547
  }
19470
19548
  });
19471
- plugins.get("/commands", async (c) => {
19472
- try {
19473
- const cwd = process.env.CWD || process.cwd();
19474
- const commands = await discoverSlashCommands(cwd);
19475
- return c.json({ commands });
19476
- } catch (error) {
19477
- console.error("[plugins] Error discovering slash commands:", error);
19478
- return c.json({ error: error instanceof Error ? error.message : "Failed to discover slash commands" }, 500);
19479
- }
19480
- });
19481
19549
  var plugins_default = plugins;
19482
19550
 
19483
19551
  // ../protocol/src/parent-watchdog.ts
19484
19552
  var PARENT_PID_ENV = "ORKESTRATOR_PARENT_PID";
19485
- var DEFAULT_POLL_INTERVAL_MS = 15000;
19553
+ var PARENT_WATCH_INTERVAL_MS = 1000;
19554
+ var DEFAULT_POLL_INTERVAL_MS = PARENT_WATCH_INTERVAL_MS;
19486
19555
  function parseParentPid(value) {
19487
19556
  if (!value)
19488
19557
  return null;
@@ -21133,7 +21202,7 @@ compressTranscriptReads(app);
21133
21202
  app.get("/global/auth-check", (c) => c.json({ status: "ok" }));
21134
21203
  app.get("/global/auth", async (c) => c.json(await readClaudeAuthStatus()));
21135
21204
  app.get("/global/usage", async (c) => c.json({ account: await readClaudePlanUsage() }));
21136
- 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));
21137
21206
  app.post("/global/auth/logout", (c) => c.json({ error: "Claude sign-out is available from a terminal tab." }, 405));
21138
21207
  app.post("/global/refresh-catalog", async (c) => {
21139
21208
  await refreshClaudeCatalogs();