orkestrator 2.34.5-beta → 2.35.1-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.
@@ -4812,6 +4812,7 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
4812
4812
  "stop_node_environment",
4813
4813
  "stop_worker_turn",
4814
4814
  "switch_provider_account",
4815
+ "set_account_auto_switch",
4815
4816
  "transfer_files",
4816
4817
  "update_schedule",
4817
4818
  "update_ticket"
@@ -8864,6 +8865,8 @@ function claudeLaunchConflict(runtime, request) {
8864
8865
  return "adaptive thinking";
8865
8866
  if (request.bypassCapable && !runtime.bypassCapable)
8866
8867
  return "bypass permission capability";
8868
+ if (request.repositoryInstructions !== undefined && runtime.repositoryInstructions !== request.repositoryInstructions)
8869
+ return "repository instructions";
8867
8870
  if (request.plugins !== undefined && runtime.plugins !== request.plugins)
8868
8871
  return "plugins";
8869
8872
  if (request.credentials !== undefined && runtime.credentials !== request.credentials)
@@ -13686,7 +13689,11 @@ function claudeUserMcpQueryOptions(policy) {
13686
13689
  async function loadScopedMcpServers(cwd, scope) {
13687
13690
  if (scope === "none") {
13688
13691
  const sources = { user: "excluded", project: "excluded" };
13689
- return { configs: {}, revision: { fingerprint: revisionFingerprint(sources), sources, scope } };
13692
+ return {
13693
+ configs: {},
13694
+ layers: new Map,
13695
+ revision: { fingerprint: revisionFingerprint(sources), sources, scope }
13696
+ };
13690
13697
  }
13691
13698
  const path = claudeJsonPath();
13692
13699
  const [global2, projectGlobal, projectLocal] = await Promise.all([
@@ -13699,11 +13706,22 @@ async function loadScopedMcpServers(cwd, scope) {
13699
13706
  ...projectGlobal?.value,
13700
13707
  ...projectLocal?.value
13701
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");
13702
13716
  const sources = {
13703
13717
  user: sourceDigest(global2.digest),
13704
13718
  project: projectLocal ? sourceDigest(projectLocal.digest) : "excluded"
13705
13719
  };
13706
- return { configs, revision: { fingerprint: revisionFingerprint(sources), sources, scope } };
13720
+ return {
13721
+ configs,
13722
+ layers,
13723
+ revision: { fingerprint: revisionFingerprint(sources), sources, scope }
13724
+ };
13707
13725
  }
13708
13726
  async function claudeAccountOnlyMcpServerNames(cwd, settingSources, declared, strictMcpConfig = false) {
13709
13727
  const path = claudeAccountJsonPath();
@@ -13723,21 +13741,28 @@ async function getMcpRuntimeConfig(cwd, env = process.env, connection, sources =
13723
13741
  const scoped = await loadScopedMcpServers(cwd, normalizeScope(sources));
13724
13742
  const { revision } = scoped;
13725
13743
  const configs = allowConfigured ? Object.fromEntries(Object.entries(scoped.configs).filter(([name]) => allowConfigured(name))) : scoped.configs;
13726
- const agentServer = getOrkestratorAgentMcpServerFromConnection(connection) ?? getOrkestratorAgentMcpServer(env);
13727
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);
13728
13747
  if (agentServer) {
13729
13748
  servers[AGENT_MCP_SERVER_NAME] = agentServer;
13730
- if (connection?.design)
13749
+ layers.set(AGENT_MCP_SERVER_NAME, "orkestrator");
13750
+ if (connection?.design) {
13731
13751
  servers["orkestrator-design"] = {
13732
13752
  ...agentServer,
13733
13753
  url: new URL("/design-mcp", agentServer.url).toString()
13734
13754
  };
13755
+ layers.set("orkestrator-design", "orkestrator");
13756
+ }
13735
13757
  }
13736
13758
  const artifactServer = getOrkestratorArtifactMcpServer(connection);
13737
- if (artifactServer)
13759
+ if (artifactServer) {
13738
13760
  servers[ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME] = artifactServer;
13739
- else
13761
+ layers.set(ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME, "orkestrator");
13762
+ } else {
13740
13763
  delete servers[ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME];
13764
+ layers.delete(ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME);
13765
+ }
13741
13766
  const research = normalizeScope(sources) === "none" ? coordinatorResearchMcpServersFromEnv(env) : [];
13742
13767
  for (const server of research)
13743
13768
  servers[server.name] = { type: "http", url: server.url };
@@ -13749,6 +13774,7 @@ async function getMcpRuntimeConfig(cwd, env = process.env, connection, sources =
13749
13774
  ...artifactServer ? [ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME] : [],
13750
13775
  ...research.map((server) => server.name)
13751
13776
  ]),
13777
+ layers,
13752
13778
  revision
13753
13779
  };
13754
13780
  }
@@ -13756,6 +13782,384 @@ async function getMcpRuntimeConfig(cwd, env = process.env, connection, sources =
13756
13782
  // ../../bridges/claude-bridge/src/services/session-manager-commands.ts
13757
13783
  import { randomUUID as randomUUID4 } from "node:crypto";
13758
13784
 
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
+ }
13801
+ }
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);
13813
+ }
13814
+ return runtimes.sort((left, right) => right.lastFrameAt - left.lastFrameAt).map((runtime) => runtime.control);
13815
+ }
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);
13838
+ }
13839
+ }
13840
+ return;
13841
+ }
13842
+ function liveInitializationResult(control) {
13843
+ if (typeof control.initializationResult !== "function")
13844
+ return;
13845
+ return control.initializationResult().then((result) => result && typeof result === "object" ? result : undefined);
13846
+ }
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;
13865
+ }
13866
+ function presentSource(value) {
13867
+ return value && value !== "none" ? value : undefined;
13868
+ }
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;
13874
+ return {
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
13884
+ };
13885
+ }
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;
13907
+ }
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
+ });
13920
+ }
13921
+ }
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
+
13759
14163
  // ../protocol/src/agent-command-catalogue.ts
13760
14164
  var NATIVE_AGENT_COMMAND_CATALOGUE_VERSION = 1;
13761
14165
  var COMMAND_CATALOGUE_LIMITS = Object.freeze({
@@ -14060,7 +14464,7 @@ function idleSteerPromptReply(prompt, agentLabel) {
14060
14464
 
14061
14465
  // ../../bridges/claude-bridge/src/services/claude-terminal-commands.ts
14062
14466
  import { mkdir as mkdir5, readFile as readFile6, rename as rename5, writeFile as writeFile5 } from "node:fs/promises";
14063
- import { join as join12 } from "node:path";
14467
+ import { join as join13 } from "node:path";
14064
14468
  var FILE_NAME = "terminal-slash-commands.json";
14065
14469
  var MAX_VERSIONS = 4;
14066
14470
  var MAX_NAMES = 512;
@@ -14071,7 +14475,7 @@ var dirty = false;
14071
14475
  var entries = [];
14072
14476
  var writes = Promise.resolve();
14073
14477
  function filePath() {
14074
- return join12(claudeOrkestratorDir(), FILE_NAME);
14478
+ return join13(claudeOrkestratorDir(), FILE_NAME);
14075
14479
  }
14076
14480
  function validVersion(value) {
14077
14481
  return typeof value === "string" && value.length > 0 && value.length <= MAX_VERSION_LENGTH;
@@ -14118,89 +14522,31 @@ function recordTerminalSlashCommands(version, commands) {
14118
14522
  const previous = entries.find((entry) => entry.version === version);
14119
14523
  if (previous && JSON.stringify(previous.commands) === JSON.stringify(names) && !dirty)
14120
14524
  return;
14121
- dirty = true;
14122
- entries = [
14123
- { version, commands: names },
14124
- ...entries.filter((entry) => entry.version !== version)
14125
- ].slice(0, MAX_VERSIONS);
14126
- writes = writes.then(async () => {
14127
- await load();
14128
- const directory = claudeOrkestratorDir();
14129
- await mkdir5(directory, { recursive: true });
14130
- const target = filePath();
14131
- const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
14132
- const snapshot = entries;
14133
- await writeFile5(temporary, `${JSON.stringify({ versions: snapshot })}
14134
- `, {
14135
- mode: 384
14136
- });
14137
- await rename5(temporary, target);
14138
- if (entries === snapshot)
14139
- dirty = false;
14140
- }).catch(() => {});
14141
- }
14142
-
14143
- // ../../bridges/claude-bridge/src/services/session-manager-catalog.ts
14144
- import { createHash as createHash10 } from "node:crypto";
14145
-
14146
- // ../../bridges/claude-bridge/src/services/live-claude-control.ts
14147
- var LIVE_CLAUDE_READ_TIMEOUT_MS = 5000;
14148
- function readsProbeSettings(settingSources) {
14149
- if (settingSources === undefined)
14150
- return false;
14151
- try {
14152
- const sources = JSON.parse(settingSources);
14153
- return Array.isArray(sources) && ["user", "project", "local"].every((source) => sources.includes(source));
14154
- } catch {
14155
- return false;
14156
- }
14157
- }
14158
- function liveClaudeControls() {
14159
- const runtimes = [];
14160
- for (const session of sessions.values()) {
14161
- const runtime = session.claudeRuntime;
14162
- if (runtime?.state !== "open" || runtime.abortController.signal.aborted)
14163
- continue;
14164
- if (session.queryControlDraining === runtime.control || session.deleting)
14165
- continue;
14166
- if (!readsProbeSettings(runtime.launch.settingSources))
14167
- continue;
14168
- runtimes.push(runtime);
14169
- }
14170
- return runtimes.sort((left, right) => right.lastFrameAt - left.lastFrameAt).map((runtime) => runtime.control);
14171
- }
14172
- async function readFromLiveClaude(read, timeoutMs = LIVE_CLAUDE_READ_TIMEOUT_MS) {
14173
- for (const control of liveClaudeControls()) {
14174
- let timer;
14175
- try {
14176
- const answer = read(control);
14177
- if (answer === undefined)
14178
- continue;
14179
- answer.catch(() => {
14180
- return;
14181
- });
14182
- const value = await Promise.race([
14183
- answer,
14184
- new Promise((resolve) => {
14185
- timer = setTimeout(() => resolve(undefined), timeoutMs);
14186
- })
14187
- ]);
14188
- if (value !== undefined)
14189
- return value;
14190
- } catch (error) {
14191
- debugLog("[live-claude-control] A running CLI could not answer:", error);
14192
- } finally {
14193
- clearTimeout(timer);
14194
- }
14195
- }
14196
- return;
14197
- }
14198
- function liveInitializationResult(control) {
14199
- if (typeof control.initializationResult !== "function")
14200
- return;
14201
- return control.initializationResult().then((result) => result && typeof result === "object" ? result : undefined);
14525
+ dirty = true;
14526
+ entries = [
14527
+ { version, commands: names },
14528
+ ...entries.filter((entry) => entry.version !== version)
14529
+ ].slice(0, MAX_VERSIONS);
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(() => {});
14202
14545
  }
14203
14546
 
14547
+ // ../../bridges/claude-bridge/src/services/session-manager-catalog.ts
14548
+ import { createHash as createHash10 } from "node:crypto";
14549
+
14204
14550
  // ../protocol/src/secret-redaction.ts
14205
14551
  var TEXT_RULES = [
14206
14552
  {
@@ -14237,7 +14583,11 @@ function redactSecrets(value) {
14237
14583
  }
14238
14584
 
14239
14585
  // ../../bridges/claude-bridge/src/services/mcp-runtime-status.ts
14240
- 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;
14241
14591
  if (name === "orkestrator")
14242
14592
  return "orkestrator";
14243
14593
  if (source?.includes("plugin") || scope?.includes("plugin"))
@@ -14259,145 +14609,16 @@ function mcpRuntimeState(status) {
14259
14609
  return "unknown";
14260
14610
  }
14261
14611
  }
14262
- function initMcpServerStatus(raw) {
14612
+ function initMcpServerStatus(raw, layers) {
14263
14613
  const source = typeof raw.source === "string" ? raw.source : undefined;
14264
14614
  return {
14265
14615
  name: raw.name,
14266
14616
  status: mcpRuntimeState(raw.status),
14267
- ...source ? { scope: mcpScope(source, raw.name) } : {},
14617
+ ...source || layers?.has(raw.name) ? { scope: mcpScope(source, raw.name, source, layers) } : {},
14268
14618
  ...typeof raw.error === "string" && raw.error ? { error: raw.error } : {},
14269
14619
  ...Array.isArray(raw.tools) ? { tools: raw.tools.filter((tool) => typeof tool === "string") } : {}
14270
14620
  };
14271
14621
  }
14272
-
14273
- // ../../bridges/claude-bridge/src/services/claude-sign-in.ts
14274
- var AUTH_CACHE_TTL_MS = 30000;
14275
- var FAILED_AUTH_CACHE_TTL_MS = 5000;
14276
- var CLAUDE_SIGN_IN = {
14277
- kind: "terminal",
14278
- hint: "Sign in to Claude from the sign-in card in this tab or from Settings → Claude."
14279
- };
14280
- var CLAUDE_SIGN_IN_REQUIRED_MESSAGE = "Claude isn't signed in. Sign in to Claude to keep working.";
14281
- var authCache;
14282
- var authRead;
14283
- var authReadGeneration;
14284
- var authRefreshPending = false;
14285
- var authGeneration = 0;
14286
- var turnAuthenticationFailedUntil = 0;
14287
- var reuseLiveAccounts = true;
14288
- function withTurnFailure(value) {
14289
- return Date.now() < turnAuthenticationFailedUntil && value.state === "signed-in" ? { ...value, state: "expired" } : value;
14290
- }
14291
- function presentSource(value) {
14292
- return value && value !== "none" ? value : undefined;
14293
- }
14294
- function normalizeClaudeAccount(account) {
14295
- const source = presentSource(account.apiKeySource) ?? presentSource(account.tokenSource);
14296
- const externalProvider = account.apiProvider && account.apiProvider !== "firstParty" ? account.apiProvider : undefined;
14297
- const signedIn = Boolean(source || account.subscriptionType || account.organization || externalProvider);
14298
- const label = account.organization || account.subscriptionType || externalProvider;
14299
- return {
14300
- state: signedIn ? "signed-in" : "needs-auth",
14301
- ...label ? {
14302
- account: {
14303
- label,
14304
- ...account.subscriptionType ? { plan: account.subscriptionType } : {}
14305
- }
14306
- } : {},
14307
- signIn: CLAUDE_SIGN_IN,
14308
- signOut: false
14309
- };
14310
- }
14311
- function liveSignedInAccount() {
14312
- return readFromLiveClaude((control) => liveInitializationResult(control)?.then((result) => {
14313
- const account = result?.account;
14314
- if (!account || typeof account !== "object")
14315
- return;
14316
- const status = normalizeClaudeAccount(account);
14317
- return status.state === "signed-in" ? status : undefined;
14318
- }));
14319
- }
14320
- async function probeAccount() {
14321
- if (reuseLiveAccounts) {
14322
- const live = await liveSignedInAccount();
14323
- if (live && reuseLiveAccounts)
14324
- return live;
14325
- }
14326
- let probe;
14327
- try {
14328
- probe = controlOnlyQuery({
14329
- cwd: process.env.CWD || process.cwd(),
14330
- env: { ...process.env },
14331
- ...claudeExecutableOptions()
14332
- });
14333
- return normalizeClaudeAccount(await probe.accountInfo());
14334
- } finally {
14335
- await Promise.resolve(probe?.close()).catch(() => {
14336
- return;
14337
- });
14338
- }
14339
- }
14340
- function readClaudeAuthStatus() {
14341
- if (authCache && authCache.expiresAt > Date.now()) {
14342
- return Promise.resolve(withTurnFailure(authCache.value));
14343
- }
14344
- if (authRead)
14345
- return authRead;
14346
- const generation = authGeneration;
14347
- const read = probeAccount().then((value) => ({ value, ttl: AUTH_CACHE_TTL_MS }), () => ({
14348
- value: {
14349
- state: "unknown",
14350
- signIn: CLAUDE_SIGN_IN,
14351
- signOut: false
14352
- },
14353
- ttl: FAILED_AUTH_CACHE_TTL_MS
14354
- })).then(({ value, ttl }) => {
14355
- if (generation === authGeneration) {
14356
- authCache = { expiresAt: Date.now() + ttl, value };
14357
- }
14358
- return withTurnFailure(value);
14359
- }).finally(() => {
14360
- if (authRead !== read)
14361
- return;
14362
- authRead = undefined;
14363
- authReadGeneration = undefined;
14364
- if (authRefreshPending) {
14365
- authRefreshPending = false;
14366
- readClaudeAuthStatus();
14367
- }
14368
- });
14369
- authReadGeneration = generation;
14370
- authRead = read;
14371
- return read;
14372
- }
14373
- function noteClaudeAuthenticationFailure() {
14374
- reuseLiveAccounts = false;
14375
- turnAuthenticationFailedUntil = Date.now() + AUTH_CACHE_TTL_MS;
14376
- authCache = undefined;
14377
- if (authRead && authReadGeneration !== authGeneration)
14378
- authRefreshPending = true;
14379
- readClaudeAuthStatus();
14380
- }
14381
- function noteClaudeAuthenticationWorked() {
14382
- reuseLiveAccounts = true;
14383
- authGeneration += 1;
14384
- authRefreshPending = false;
14385
- turnAuthenticationFailedUntil = 0;
14386
- if (authCache?.value.state === "needs-auth")
14387
- authCache = undefined;
14388
- }
14389
- function claudeReadiness() {
14390
- const cached = authCache;
14391
- if (cached?.value.state !== "needs-auth")
14392
- return;
14393
- if (cached.expiresAt <= Date.now())
14394
- readClaudeAuthStatus();
14395
- return {
14396
- state: "authentication-required",
14397
- message: CLAUDE_SIGN_IN_REQUIRED_MESSAGE,
14398
- signIn: CLAUDE_SIGN_IN
14399
- };
14400
- }
14401
14622
  // ../../bridges/claude-bridge/src/services/session-manager-catalog.ts
14402
14623
  function readableControl(session) {
14403
14624
  if (!session)
@@ -14548,7 +14769,7 @@ function mcpTransport(status) {
14548
14769
  function mcpError(error) {
14549
14770
  return redactSecrets(error).slice(0, 2000);
14550
14771
  }
14551
- function normalizeMcp(status) {
14772
+ function normalizeMcp(status, layers) {
14552
14773
  const mappedStatus = status.status === "pending" ? "connecting" : status.status;
14553
14774
  const tools = (status.tools ?? []).map((tool) => tool.name).filter(Boolean).slice(0, 256);
14554
14775
  const actions = status.status === "disabled" ? ["enable"] : status.status === "needs-auth" ? ["sign-in", "reconnect", "disable"] : ["reconnect", "disable"];
@@ -14557,7 +14778,7 @@ function normalizeMcp(status) {
14557
14778
  id: status.name,
14558
14779
  name: status.serverInfo?.name || status.name,
14559
14780
  status: mappedStatus,
14560
- scope: mcpScope(status.scope, status.name, status.source),
14781
+ scope: mcpScope(status.scope, status.name, status.source, layers),
14561
14782
  ...transport ? { transport } : {},
14562
14783
  toolCount: tools.length,
14563
14784
  ...tools.length ? { tools } : {},
@@ -14565,18 +14786,11 @@ function normalizeMcp(status) {
14565
14786
  actions
14566
14787
  };
14567
14788
  }
14568
- async function readSessionMcpServers(sessionId) {
14569
- const session = sessions.get(sessionId);
14570
- if (!session)
14571
- return [];
14572
- const control = readableControl(session);
14573
- if (control?.mcpServerStatus) {
14574
- try {
14575
- session.mcpInventory = (await control.mcpServerStatus()).slice(0, 128).map(normalizeMcp);
14576
- } catch (error) {
14577
- rethrowUnlessClosedTransport(error);
14578
- }
14579
- } 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) {
14580
14794
  session.mcpInventory = session.initData.mcpServers.slice(0, 128).map((server) => ({
14581
14795
  id: server.name,
14582
14796
  name: server.name,
@@ -14588,7 +14802,34 @@ async function readSessionMcpServers(sessionId) {
14588
14802
  ...server.error ? { error: mcpError(server.error) } : {}
14589
14803
  }));
14590
14804
  }
14591
- 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) ?? [];
14592
14833
  }
14593
14834
  async function performSessionMcpAction(sessionId, serverId, action) {
14594
14835
  const session = sessions.get(sessionId);
@@ -14611,7 +14852,11 @@ async function performSessionMcpAction(sessionId, serverId, action) {
14611
14852
  await control.reconnectMcpServer?.(serverId);
14612
14853
  }
14613
14854
  if (control.mcpServerStatus) {
14614
- 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);
14615
14860
  }
14616
14861
  return {};
14617
14862
  } finally {
@@ -15232,6 +15477,7 @@ async function refreshClaudeCommandCatalogue(sessionId) {
15232
15477
  }
15233
15478
  }
15234
15479
  async function refreshClaudeCatalogs() {
15480
+ invalidateClaudeModelCatalog();
15235
15481
  const sessionIds = Array.from(sessions.keys()).slice(0, 128);
15236
15482
  let next = 0;
15237
15483
  await Promise.allSettled(Array.from({ length: Math.min(8, sessionIds.length) }, async () => {
@@ -15332,7 +15578,6 @@ function createClaudeRuntimeMcp(servers, declared, accountOnly = []) {
15332
15578
  accountOnly: loadedOnlyByAccount
15333
15579
  };
15334
15580
  }
15335
- var FOREIGN_SOURCES = new Set(["plugin", "sdk", "claudeai"]);
15336
15581
  var MAX_REPORTED_STALE_SERVERS = 256;
15337
15582
  function conflict(message) {
15338
15583
  return sessionOperationError("conflict", message);
@@ -15387,7 +15632,7 @@ async function syncClaudeRuntimeMcp(runtime, next) {
15387
15632
  runtime.mcpServers = "";
15388
15633
  throw conflict("Claude required MCP server is not connected; retry the prompt");
15389
15634
  }
15390
- 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);
15391
15636
  for (const name of Array.from(mcp.stale)) {
15392
15637
  if (next.names.has(name) || !stillRuns(name)) {
15393
15638
  mcp.stale.delete(name);
@@ -15686,6 +15931,60 @@ function claudeArtifactServerTools(servers) {
15686
15931
  return ARTIFACT_TOOL_NAMES.map((name) => `mcp__${ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME}__${name}`);
15687
15932
  }
15688
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
+
15689
15988
  // ../protocol/src/claude-background-commands.ts
15690
15989
  var CLAUDE_BASH_MAX_TIMEOUT_ENV = "BASH_MAX_TIMEOUT_MS";
15691
15990
  var CLAUDE_BASH_DEFAULT_TIMEOUT_ENV = "BASH_DEFAULT_TIMEOUT_MS";
@@ -15734,6 +16033,17 @@ function backgroundCommandInstruction(env) {
15734
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.`;
15735
16034
  }
15736
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
+
15737
16047
  // ../protocol/src/agent-interaction-deadline.ts
15738
16048
  var SYSTEM_CLOCK = {
15739
16049
  now: () => Date.now(),
@@ -16076,61 +16386,6 @@ function getSessionInitData(sessionId) {
16076
16386
  const session = sessions.get(sessionId);
16077
16387
  return session?.initData;
16078
16388
  }
16079
- function catalogModels(models) {
16080
- return models.map((model) => ({
16081
- id: model.value,
16082
- resolvedModel: model.resolvedModel,
16083
- name: model.displayName,
16084
- description: model.description,
16085
- supportsFastMode: model.supportsFastMode,
16086
- supportsEffort: model.supportsEffort,
16087
- supportedEffortLevels: model.supportedEffortLevels,
16088
- supportsAdaptiveThinking: model.supportsAdaptiveThinking,
16089
- supportsAutoMode: model.supportsAutoMode
16090
- }));
16091
- }
16092
- function liveModels() {
16093
- return readFromLiveClaude((control) => liveInitializationResult(control)?.then((result) => Array.isArray(result?.models) && result.models.length > 0 ? result.models : undefined));
16094
- }
16095
- async function getAvailableModelCatalog() {
16096
- const live = await liveModels();
16097
- if (live) {
16098
- debugLog("[session-manager] Supported models read from a running CLI", {
16099
- count: live.length
16100
- });
16101
- return { source: "sdk", models: catalogModels(live) };
16102
- }
16103
- let q;
16104
- try {
16105
- const cwd = process.env.CWD || process.cwd();
16106
- debugLog("[session-manager] Fetching supported models", { cwd });
16107
- q = controlOnlyQuery({
16108
- cwd,
16109
- env: { ...process.env },
16110
- ...claudeExecutableOptions()
16111
- });
16112
- const models = await q.supportedModels();
16113
- debugLog("[session-manager] Supported models fetched", { count: models.length });
16114
- return { source: "sdk", models: catalogModels(models) };
16115
- } catch (error) {
16116
- console.error("[session-manager] Error fetching supported models:", error);
16117
- return {
16118
- source: "fallback",
16119
- models: CLAUDE_FALLBACK_MODEL_CATALOG.map((model) => ({
16120
- ...model,
16121
- ...model.supportedEffortLevels ? { supportedEffortLevels: [...model.supportedEffortLevels] } : {}
16122
- }))
16123
- };
16124
- } finally {
16125
- if (q?.return) {
16126
- try {
16127
- await q.return();
16128
- } catch (error) {
16129
- debugLog("[session-manager] Failed to clean up model query:", error);
16130
- }
16131
- }
16132
- }
16133
- }
16134
16389
  async function getClaudeRuntimeVersions() {
16135
16390
  let sdkVersion;
16136
16391
  let bundledCliVersion;
@@ -16568,9 +16823,15 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
16568
16823
  };
16569
16824
  const settingSources = claudeTurnSettingSources(session, options);
16570
16825
  session.commandDiscoveryInputs.includeLocalSettings = settingSources.includes("local");
16826
+ const repositoryInstructions = options?.readOnly || policy?.id === "coordinator-read-only" || settingSources.includes("project") ? undefined : await readRepositoryInstructions(cwd);
16571
16827
  const readOnlyCommandSandbox = isCoordinatorReadOnlyPolicy(policy) ? claudeReadOnlyCommandSandbox() : undefined;
16572
16828
  const [
16573
- { servers: mcpServers, names: mcpServerNames, revision: mcpConfigRevision },
16829
+ {
16830
+ servers: mcpServers,
16831
+ names: mcpServerNames,
16832
+ layers: mcpServerLayers,
16833
+ revision: mcpConfigRevision
16834
+ },
16574
16835
  { plugins, coordinatorSkills }
16575
16836
  ] = await Promise.all([
16576
16837
  getMcpRuntimeConfig(cwd, process.env, options?.agentMcp, mcpSourceScopeForPolicy(policy), userMcpServerFilter(policy)),
@@ -16645,11 +16906,13 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
16645
16906
  const mcpServersKey = createHash11("sha256").update(JSON.stringify(mcpServers)).digest("hex");
16646
16907
  return {
16647
16908
  settingSources,
16909
+ repositoryInstructions,
16648
16910
  cwd,
16649
16911
  policy,
16650
16912
  effortLevel,
16651
16913
  mcpServers,
16652
16914
  mcpServerNames,
16915
+ mcpServerLayers,
16653
16916
  accountOnlyMcpServers,
16654
16917
  mcpConfigRevision,
16655
16918
  plugins,
@@ -16671,6 +16934,7 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
16671
16934
  }
16672
16935
  function recordMcpConfigRevision(session, plan, mcp) {
16673
16936
  const { mcpConfigRevision } = plan;
16937
+ session.mcpServerLayers = plan.mcpServerLayers;
16674
16938
  if (!mcpConfigRevision)
16675
16939
  return;
16676
16940
  const staleServers = reportedStaleServers(mcp);
@@ -16753,11 +17017,9 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
16753
17017
  systemPrompt: {
16754
17018
  type: "preset",
16755
17019
  preset: "claude_code",
16756
- 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." : [
16757
- "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.",
16758
- backgroundCommandInstruction(queryEnvironment),
16759
- "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.",
16760
- "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] : []
16761
17023
  ].join(`
16762
17024
 
16763
17025
  `)
@@ -17092,7 +17354,7 @@ async function handleInitMessage(turn, message) {
17092
17354
  }
17093
17355
  const allMcpServers = initMsg.mcp_servers || [];
17094
17356
  const regularMcpServers = allMcpServers.filter((s) => !s.name?.startsWith("plugin:"));
17095
- const mcpServerStatuses = regularMcpServers.map(initMcpServerStatus);
17357
+ const mcpServerStatuses = regularMcpServers.map((server) => initMcpServerStatus(server, session.mcpServerLayers));
17096
17358
  const { statuses: pluginStatuses, errors: pluginErrors } = pluginStatusesFromInit(initMsg);
17097
17359
  const previousPluginErrors = new Set(session.pluginErrorSignatures);
17098
17360
  session.pluginErrorSignatures = pluginErrors.map(({ plugin, type, message, path, loaded }) => JSON.stringify([plugin, type, message, path, loaded]));
@@ -17279,6 +17541,28 @@ var LEAF_MESSAGE_HANDLERS = {
17279
17541
  conversation_reset: handleConversationReset
17280
17542
  };
17281
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
+
17282
17566
  // ../../bridges/claude-bridge/src/services/session-manager-prompt-handlers/result.ts
17283
17567
  function claudeStartupFailureMessage(reason) {
17284
17568
  switch (reason) {
@@ -17324,6 +17608,7 @@ function claudeStartupFailureMessage(reason) {
17324
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.";
17325
17609
  }
17326
17610
  }
17611
+ var MAX_USAGE_LIMIT_ERROR_CHARS = 500;
17327
17612
  var STOPPED_TERMINAL_REASONS = new Set([
17328
17613
  "aborted_streaming",
17329
17614
  "aborted_tools"
@@ -17456,8 +17741,9 @@ async function handleResultMessage(turn, message) {
17456
17741
  isError: resultIsError
17457
17742
  });
17458
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;
17459
17745
  const resultError = startupFailure || resultMsg.errors?.filter(Boolean).join(`
17460
- `) || (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}`);
17461
17747
  if (startupFailure) {
17462
17748
  appendTranscriptNotice(session, sessionId, (event) => eventEmitter.emit(event), {
17463
17749
  type: "status",
@@ -18389,7 +18675,8 @@ async function prepareJoin(turn, plan, running) {
18389
18675
  const required = mcpServers[AGENT_MCP_SERVER_NAME] ? [AGENT_MCP_SERVER_NAME, ...options?.agentMcp?.design ? ["orkestrator-design"] : []] : [];
18390
18676
  await assertClaudeRuntimeAccepts(session, options, {
18391
18677
  ...claudeLaunchResourceProfile(plugins, queryEnvironment),
18392
- settingSources: JSON.stringify(plan.settingSources)
18678
+ settingSources: JSON.stringify(plan.settingSources),
18679
+ repositoryInstructions: plan.repositoryInstructions ?? ""
18393
18680
  }, plan.readOnlyCommandSandbox);
18394
18681
  session.queryControl = running.control;
18395
18682
  turn.queryIteratorControl = running.control;
@@ -18449,6 +18736,7 @@ function launchClaudeRuntime(turn, plan) {
18449
18736
  launch: {
18450
18737
  ...claudeLaunchedProfile(session, options, plan.readOnlyCommandSandbox),
18451
18738
  settingSources: JSON.stringify(plan.settingSources),
18739
+ repositoryInstructions: plan.repositoryInstructions ?? "",
18452
18740
  ...claudeLaunchResourceProfile(plugins, queryEnvironment)
18453
18741
  },
18454
18742
  mcpServers: mcpServersKey,
@@ -18505,6 +18793,7 @@ function joinableRuntime(turn, plan) {
18505
18793
  const requested = {
18506
18794
  ...claudeLaunchProfile(turn.session, turn.options, plan.readOnlyCommandSandbox),
18507
18795
  settingSources: JSON.stringify(plan.settingSources),
18796
+ repositoryInstructions: plan.repositoryInstructions ?? "",
18508
18797
  ...claudeLaunchResourceProfile(plan.plugins, plan.queryEnvironment)
18509
18798
  };
18510
18799
  return claudeLaunchConflict(running.launch, requested) ? undefined : running;
@@ -18899,6 +19188,8 @@ async function settlesBy(work, deadline) {
18899
19188
  // ../../bridges/claude-bridge/src/routes/config.ts
18900
19189
  var config = new Hono2;
18901
19190
  config.get("/models", async (c) => {
19191
+ if (c.req.query("forceRefresh") === "true")
19192
+ invalidateClaudeModelCatalog();
18902
19193
  const startedAt = Date.now();
18903
19194
  console.log("[config] /models requested");
18904
19195
  const [{ models, source }, versions] = await Promise.all([