@oxagen/cli 2.1.4 → 2.1.5-22

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.
Files changed (2) hide show
  1. package/oxagen.mjs +938 -191
  2. package/package.json +1 -1
package/oxagen.mjs CHANGED
@@ -5776,7 +5776,7 @@ var init_wire = __esm({
5776
5776
  harness: "stella",
5777
5777
  provider: "anthropic",
5778
5778
  prefix: "/stella/anthropic",
5779
- brokerable: false,
5779
+ brokerable: true,
5780
5780
  baseUrlFile: "$STELLA_HOME/stella.toml"
5781
5781
  }
5782
5782
  ];
@@ -7149,6 +7149,248 @@ var init_tools = __esm({
7149
7149
  }
7150
7150
  });
7151
7151
 
7152
+ // ../../packages/tacho/src/claude-code/github-text.ts
7153
+ function mentionsOxagenApp(text5) {
7154
+ return OXAGEN_APP_MENTION.test(text5);
7155
+ }
7156
+ function ghCommands(command2) {
7157
+ const commands = [];
7158
+ for (const piece of splitCommandList(command2)) {
7159
+ const words2 = leadingWords(piece);
7160
+ let i2 = 0;
7161
+ while (i2 < words2.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words2[i2] ?? ""))
7162
+ i2 += 1;
7163
+ if (isGh(words2[i2])) commands.push(words2.slice(i2));
7164
+ }
7165
+ return commands;
7166
+ }
7167
+ function leadingWords(piece) {
7168
+ const words2 = [];
7169
+ let current = "";
7170
+ let started = false;
7171
+ let quote;
7172
+ for (let i2 = 0; i2 < piece.length; i2 += 1) {
7173
+ const char = piece[i2];
7174
+ const substitution = quote !== "'" && (char === "`" || char === "$" && piece[i2 + 1] === "(");
7175
+ if (substitution) return words2;
7176
+ if (quote !== void 0) {
7177
+ if (char === quote) quote = void 0;
7178
+ else current += char;
7179
+ continue;
7180
+ }
7181
+ if (char === '"' || char === "'") {
7182
+ quote = char;
7183
+ started = true;
7184
+ continue;
7185
+ }
7186
+ if (char === "\\" && piece[i2 + 1] !== void 0) {
7187
+ current += piece[i2 + 1];
7188
+ started = true;
7189
+ i2 += 1;
7190
+ continue;
7191
+ }
7192
+ if ("|&()<>".includes(char)) break;
7193
+ if (/\s/.test(char)) {
7194
+ if (started) words2.push(current);
7195
+ current = "";
7196
+ started = false;
7197
+ continue;
7198
+ }
7199
+ current += char;
7200
+ started = true;
7201
+ }
7202
+ if (started && quote === void 0) words2.push(current);
7203
+ return words2;
7204
+ }
7205
+ function ghSubcommand(words2) {
7206
+ const found = [];
7207
+ for (let j = 1; j < words2.length && found.length < 2; j += 1) {
7208
+ const word = words2[j];
7209
+ if (GH_VALUE_FLAGS.has(word)) j += 1;
7210
+ else if (!word.startsWith("-")) found.push(word);
7211
+ }
7212
+ return [found[0], found[1]];
7213
+ }
7214
+ function ghApiCall(words2) {
7215
+ let method;
7216
+ let body2 = false;
7217
+ let path2;
7218
+ const start = words2.indexOf("api");
7219
+ for (let j = start + 1; j < words2.length; j += 1) {
7220
+ const word = words2[j];
7221
+ const eq = word.indexOf("=");
7222
+ const flag2 = word.startsWith("--") && eq > 0 ? word.slice(0, eq) : word;
7223
+ const inline = flag2 !== word ? word.slice(eq + 1) : void 0;
7224
+ if (/^-X[A-Za-z]+$/.test(word)) {
7225
+ method = word.slice(2);
7226
+ continue;
7227
+ }
7228
+ if (/^-[fF]./.test(word)) {
7229
+ body2 = true;
7230
+ continue;
7231
+ }
7232
+ if (API_VALUE_FLAGS.has(flag2)) {
7233
+ const value = inline ?? words2[j + 1];
7234
+ if (inline === void 0) j += 1;
7235
+ if (flag2 === "-X" || flag2 === "--method") method = value;
7236
+ if (API_BODY_FLAGS.has(flag2)) body2 = true;
7237
+ continue;
7238
+ }
7239
+ if (word.startsWith("-")) continue;
7240
+ path2 ??= word;
7241
+ }
7242
+ return {
7243
+ method: (method ?? (body2 ? "POST" : "GET")).toUpperCase(),
7244
+ ...path2 !== void 0 ? { path: path2 } : {}
7245
+ };
7246
+ }
7247
+ function hasBody(words2) {
7248
+ return words2.some(
7249
+ (word) => word === "-b" || word === "--body" || word === "-F" || word === "--body-file" || word.startsWith("--body=") || word.startsWith("--body-file=")
7250
+ );
7251
+ }
7252
+ function shellWritesComment(command2) {
7253
+ return ghCommands(command2).some((words2) => {
7254
+ const [noun, verb] = ghSubcommand(words2);
7255
+ if ((noun === "pr" || noun === "issue") && verb === "comment")
7256
+ return !words2.includes("--edit-last");
7257
+ if (noun === "pr" && verb === "review") return hasBody(words2);
7258
+ if (noun === "api") {
7259
+ const call2 = ghApiCall(words2);
7260
+ const path2 = call2.path?.split("?", 1)[0];
7261
+ return call2.method === "POST" && path2 !== void 0 && COMMENT_API_PATH.test(path2);
7262
+ }
7263
+ return false;
7264
+ });
7265
+ }
7266
+ function endsWithAny(tool, suffixes) {
7267
+ const name = nameWords(tool).join("_");
7268
+ return suffixes.some(
7269
+ (suffix) => name === suffix || name.endsWith(`_${suffix}`)
7270
+ );
7271
+ }
7272
+ function mcpWritesComment(tool, input) {
7273
+ if (endsWithAny(tool, [
7274
+ "add_issue_comment",
7275
+ "add_reply_to_pull_request_comment",
7276
+ "create_pull_request_review"
7277
+ ]))
7278
+ return true;
7279
+ if (endsWithAny(tool, ["pull_request_review_write"])) {
7280
+ const body2 = input["body"];
7281
+ return (input["method"] === "create" || input["method"] === "submit_pending") && typeof body2 === "string" && body2.trim().length > 0;
7282
+ }
7283
+ return false;
7284
+ }
7285
+ function commentAttrs(toolName3, input, response) {
7286
+ const fields = typeof input === "object" && input !== null ? input : {};
7287
+ let writes = false;
7288
+ if (SHELL_TOOLS.has(toolName3)) {
7289
+ const command2 = fields["command"];
7290
+ writes = typeof command2 === "string" && shellWritesComment(command2);
7291
+ } else {
7292
+ const tool = mcpToolName(toolName3)?.tool;
7293
+ writes = tool !== void 0 && mcpWritesComment(tool, fields);
7294
+ }
7295
+ if (!writes) return {};
7296
+ for (const text5 of responseTexts(response)) {
7297
+ const match = COMMENT_URL.exec(text5);
7298
+ if (match !== null)
7299
+ return { "comment.url": match[0], "comment.action": "created" };
7300
+ }
7301
+ return {};
7302
+ }
7303
+ function shellWritesGithubText(command2) {
7304
+ return ghCommands(command2).some((words2) => {
7305
+ const [noun, verb] = ghSubcommand(words2);
7306
+ if (noun === "api") return ghApiCall(words2).method !== "GET";
7307
+ if (noun === void 0 || verb === void 0) return false;
7308
+ return GH_TEXT_VERBS[noun]?.has(verb) === true;
7309
+ });
7310
+ }
7311
+ function mcpWritesGithubText(tool) {
7312
+ return nameWords(tool).some((word) => GITHUB_TEXT_WORDS.has(word)) && mcpToolMutates(tool);
7313
+ }
7314
+ function strings(value, out = [], depth = 0) {
7315
+ if (depth > 8) return out;
7316
+ if (typeof value === "string") out.push(value);
7317
+ else if (Array.isArray(value))
7318
+ for (const item of value) strings(item, out, depth + 1);
7319
+ else if (typeof value === "object" && value !== null)
7320
+ for (const item of Object.values(value)) strings(item, out, depth + 1);
7321
+ return out;
7322
+ }
7323
+ function oxagenMentionRefusal(toolName3, shellLine2, input) {
7324
+ if (shellLine2 !== void 0) {
7325
+ return shellWritesGithubText(shellLine2) && mentionsOxagenApp(shellLine2) ? OXAGEN_MENTION_REFUSAL : void 0;
7326
+ }
7327
+ const tool = mcpToolName(toolName3)?.tool;
7328
+ if (tool === void 0 || !mcpWritesGithubText(tool)) return void 0;
7329
+ return strings(input ?? {}).some(mentionsOxagenApp) ? OXAGEN_MENTION_REFUSAL : void 0;
7330
+ }
7331
+ var OXAGEN_APP_MENTION, OXAGEN_MENTION_REFUSAL, GH_VALUE_FLAGS, API_VALUE_FLAGS, API_BODY_FLAGS, COMMENT_API_PATH, SHELL_TOOLS, COMMENT_URL, GH_TEXT_VERBS, GITHUB_TEXT_WORDS;
7332
+ var init_github_text = __esm({
7333
+ "../../packages/tacho/src/claude-code/github-text.ts"() {
7334
+ "use strict";
7335
+ init_tools();
7336
+ OXAGEN_APP_MENTION = /(^|[^\w@/.-])@oxagen(-[a-z0-9-]+)?(\[bot\])?\b/i;
7337
+ OXAGEN_MENTION_REFUSAL = "This agent may not mention the Oxagen App in GitHub text, because a mention asks Oxagen to start or steer an agent. Remove the mention and run the call again.";
7338
+ GH_VALUE_FLAGS = /* @__PURE__ */ new Set(["-R", "--repo"]);
7339
+ API_VALUE_FLAGS = /* @__PURE__ */ new Set([
7340
+ "-X",
7341
+ "--method",
7342
+ "-H",
7343
+ "--header",
7344
+ "-f",
7345
+ "--raw-field",
7346
+ "-F",
7347
+ "--field",
7348
+ "--input",
7349
+ "-q",
7350
+ "--jq",
7351
+ "-t",
7352
+ "--template",
7353
+ "--hostname",
7354
+ "--cache",
7355
+ "-p",
7356
+ "--preview",
7357
+ "-R",
7358
+ "--repo"
7359
+ ]);
7360
+ API_BODY_FLAGS = /* @__PURE__ */ new Set([
7361
+ "-f",
7362
+ "--raw-field",
7363
+ "-F",
7364
+ "--field",
7365
+ "--input"
7366
+ ]);
7367
+ COMMENT_API_PATH = /\/(issues\/\d+\/comments|pulls\/\d+\/comments|pulls\/\d+\/comments\/\d+\/replies|pulls\/\d+\/reviews)\/?$/;
7368
+ SHELL_TOOLS = /* @__PURE__ */ new Set([
7369
+ "Bash",
7370
+ "Shell",
7371
+ "bash",
7372
+ "shell",
7373
+ "local_shell",
7374
+ "exec_command"
7375
+ ]);
7376
+ COMMENT_URL = /https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/(?:pull|issues)\/\d+#(?:issuecomment-\d+|discussion_r\d+|pullrequestreview-\d+)/;
7377
+ GH_TEXT_VERBS = {
7378
+ pr: /* @__PURE__ */ new Set(["create", "edit", "comment", "review", "close", "merge"]),
7379
+ issue: /* @__PURE__ */ new Set(["create", "edit", "comment", "close", "reopen"])
7380
+ };
7381
+ GITHUB_TEXT_WORDS = /* @__PURE__ */ new Set([
7382
+ "issue",
7383
+ "issues",
7384
+ "pull",
7385
+ "comment",
7386
+ "comments",
7387
+ "review",
7388
+ "reviews",
7389
+ "discussion"
7390
+ ]);
7391
+ }
7392
+ });
7393
+
7152
7394
  // ../../packages/tacho/src/policy/builtins.ts
7153
7395
  function isCedarHarness(harness) {
7154
7396
  return CEDAR_HARNESSES.includes(harness);
@@ -7877,7 +8119,7 @@ function pathMatches(spec, path2, context) {
7877
8119
  );
7878
8120
  }
7879
8121
  function isShellTool(toolName3) {
7880
- return SHELL_TOOLS.has(toolName3);
8122
+ return SHELL_TOOLS2.has(toolName3);
7881
8123
  }
7882
8124
  function compiledGlob(glob) {
7883
8125
  let regex = globCache.get(glob);
@@ -8046,6 +8288,12 @@ function evaluatePreToolUse(input) {
8046
8288
  { source: "human" }
8047
8289
  );
8048
8290
  }
8291
+ const mention = oxagenMentionRefusal(
8292
+ toolName3,
8293
+ isShellTool(toolName3) ? shellLineOf(toolInput ?? {}) : void 0,
8294
+ toolInput
8295
+ );
8296
+ if (mention !== void 0) return deny2("oxagen_app_mention", mention);
8049
8297
  if (!input.bundleVerified) {
8050
8298
  if (readOnly) {
8051
8299
  return {
@@ -8293,11 +8541,12 @@ function decideWithCedar(input) {
8293
8541
  };
8294
8542
  }
8295
8543
  }
8296
- var SHELL_OPERATORS, SHELL_LEADING_WORDS, ARGV_SHELLS, WINDOWS_PATH, SHELL_TOOLS, DECLARATION_SYNONYMS, GLOB_CACHE_LIMIT, globCache, parsedRuleCache, WRITE_EFFECTS;
8544
+ var SHELL_OPERATORS, SHELL_LEADING_WORDS, ARGV_SHELLS, WINDOWS_PATH, SHELL_TOOLS2, DECLARATION_SYNONYMS, GLOB_CACHE_LIMIT, globCache, parsedRuleCache, WRITE_EFFECTS;
8297
8545
  var init_bundle = __esm({
8298
8546
  "../../packages/tacho/src/host/bundle.ts"() {
8299
8547
  "use strict";
8300
8548
  init_digest();
8549
+ init_github_text();
8301
8550
  init_tools();
8302
8551
  init_builtins();
8303
8552
  init_hook();
@@ -8320,7 +8569,7 @@ var init_bundle = __esm({
8320
8569
  ]);
8321
8570
  ARGV_SHELLS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash"]);
8322
8571
  WINDOWS_PATH = /^(?:[A-Za-z]:[\\/]|\\\\)/;
8323
- SHELL_TOOLS = /* @__PURE__ */ new Set([
8572
+ SHELL_TOOLS2 = /* @__PURE__ */ new Set([
8324
8573
  "Bash",
8325
8574
  "Shell",
8326
8575
  "bash",
@@ -11907,7 +12156,8 @@ function normalizeHook(raw, env, options) {
11907
12156
  const tool = input.tool_name ?? "";
11908
12157
  const issued = {
11909
12158
  ...issueAttrs(tool, input.tool_input, input.tool_response),
11910
- ...releaseAttrs(tool, input.tool_input)
12159
+ ...releaseAttrs(tool, input.tool_input),
12160
+ ...commentAttrs(tool, input.tool_input, input.tool_response)
11911
12161
  };
11912
12162
  const named = {
11913
12163
  ...facts["effect_kind"] === "pr_open" ? pullRequestAttrs(input.tool_response) : {},
@@ -12203,6 +12453,7 @@ var init_hooks = __esm({
12203
12453
  init_wire();
12204
12454
  init_context();
12205
12455
  init_payload_members();
12456
+ init_github_text();
12206
12457
  init_tools();
12207
12458
  textMember = external_exports.preprocess((value) => {
12208
12459
  if (typeof value === "number" || typeof value === "boolean")
@@ -15458,7 +15709,7 @@ var init_tool_call_dedupe = __esm({
15458
15709
  * sighting and registers nothing. `owner` is the subagent whose chain the
15459
15710
  * row seals on.
15460
15711
  */
15461
- judge(toolUseId, source, hasBody, owner) {
15712
+ judge(toolUseId, source, hasBody2, owner) {
15462
15713
  if (toolUseId === void 0 || toolUseId.length === 0)
15463
15714
  return { verdict: { kind: "first" }, commit: () => {
15464
15715
  } };
@@ -15467,13 +15718,13 @@ var init_tool_call_dedupe = __esm({
15467
15718
  const commit = () => {
15468
15719
  const entry2 = seen ?? { sources: [], body: false };
15469
15720
  if (!entry2.sources.includes(source)) entry2.sources.push(source);
15470
- entry2.body = entry2.body || hasBody;
15721
+ entry2.body = entry2.body || hasBody2;
15471
15722
  if (owner !== void 0) entry2.owner ??= owner;
15472
15723
  this.entries.set(toolUseId, entry2);
15473
15724
  this.trim();
15474
15725
  };
15475
15726
  if (first === void 0) return { verdict: { kind: "first" }, commit };
15476
- if (hasBody && seen?.body !== true)
15727
+ if (hasBody2 && seen?.body !== true)
15477
15728
  return { verdict: { kind: "body", of: first }, commit };
15478
15729
  return { verdict: { kind: "repeat" }, commit };
15479
15730
  }
@@ -16305,7 +16556,7 @@ var init_otel = __esm({
16305
16556
  function rec2(value) {
16306
16557
  return isRecord2(value) ? value : void 0;
16307
16558
  }
16308
- function strings(value) {
16559
+ function strings2(value) {
16309
16560
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : void 0;
16310
16561
  }
16311
16562
  function inventoryFromInit(record) {
@@ -16317,15 +16568,15 @@ function inventoryFromInit(record) {
16317
16568
  const set = (key, value) => {
16318
16569
  if (value !== void 0) out[key] = value;
16319
16570
  };
16320
- set("tools_available", strings(init["tools"]));
16571
+ set("tools_available", strings2(init["tools"]));
16321
16572
  set("mcp_servers", init["mcp_servers"]);
16322
- set("agents_available", strings(init["agents"]));
16323
- set("skills_available", strings(init["skills"]));
16324
- set("slash_commands", strings(init["slash_commands"]));
16573
+ set("agents_available", strings2(init["agents"]));
16574
+ set("skills_available", strings2(init["skills"]));
16575
+ set("slash_commands", strings2(init["slash_commands"]));
16325
16576
  set("plugins", init["plugins"]);
16326
16577
  set("plugin_errors", init["plugin_errors"]);
16327
16578
  set("mcp_server_errors", init["mcp_server_errors"]);
16328
- set("harness_capabilities", strings(init["capabilities"]));
16579
+ set("harness_capabilities", strings2(init["capabilities"]));
16329
16580
  set("memory_paths", init["memory_paths"]);
16330
16581
  set("analytics_disabled", bool2(init["analytics_disabled"]));
16331
16582
  set("product_feedback_disabled", bool2(init["product_feedback_disabled"]));
@@ -17828,12 +18079,12 @@ var init_recorder = __esm({
17828
18079
  * sighting could not, and it is stamped so a reader counts one call. See
17829
18080
  * `tool-call-dedupe.ts` and ADR-140.
17830
18081
  */
17831
- toolCallSighting(body2, source, hasBody) {
18082
+ toolCallSighting(body2, source, hasBody2) {
17832
18083
  const toolUseId = typeof body2["tool_use_id"] === "string" ? body2["tool_use_id"] : void 0;
17833
18084
  const { verdict, commit } = this.toolCallLedger.judge(
17834
18085
  toolUseId,
17835
18086
  source,
17836
- hasBody,
18087
+ hasBody2,
17837
18088
  this.options.parent?.subagentId
17838
18089
  );
17839
18090
  if (verdict.kind === "repeat") return { attrs: void 0, commit };
@@ -22629,6 +22880,15 @@ import {
22629
22880
  function anthropicCredentialKind(secret, fallback) {
22630
22881
  return secret.trim().startsWith(CLAUDE_OAUTH_TOKEN_PREFIX) ? "oauth" : fallback;
22631
22882
  }
22883
+ function harnessOf(entry2) {
22884
+ return entry2.harness ?? defaultHarnessForProvider(entry2.provider);
22885
+ }
22886
+ function sealingContext(provider, harness) {
22887
+ return harness === defaultHarnessForProvider(provider) ? `${CREDENTIAL_STORE_SCHEMA}:${provider}` : `${CREDENTIAL_STORE_SCHEMA}:${provider}:${harness}`;
22888
+ }
22889
+ function matches(entry2, provider, harness) {
22890
+ return entry2.provider === provider && harnessOf(entry2) === harness;
22891
+ }
22632
22892
  function readKey(path2) {
22633
22893
  try {
22634
22894
  const hex = readFileSync7(path2, "utf8").trim();
@@ -22703,6 +22963,7 @@ function credentialPrefix(secret) {
22703
22963
  function custodyOf(entry2) {
22704
22964
  return {
22705
22965
  provider: entry2.provider,
22966
+ harness: harnessOf(entry2),
22706
22967
  kind: entry2.kind,
22707
22968
  source: entry2.source,
22708
22969
  taken_at: entry2.taken_at,
@@ -22768,49 +23029,56 @@ function openCredentialStore(paths) {
22768
23029
  `
22769
23030
  );
22770
23031
  }
23032
+ const ownerOf = (provider, harness) => harness ?? defaultHarnessForProvider(provider);
22771
23033
  return {
22772
- has: (provider) => readAll().some((e) => e.provider === provider),
22773
- read: (provider) => {
22774
- const entry2 = readAll().find((e) => e.provider === provider);
23034
+ has: (provider, harness) => {
23035
+ const owner = ownerOf(provider, harness);
23036
+ return readAll().some((e) => matches(e, provider, owner));
23037
+ },
23038
+ read: (provider, harness) => {
23039
+ const owner = ownerOf(provider, harness);
23040
+ const entry2 = readAll().find((e) => matches(e, provider, owner));
22775
23041
  if (entry2 === void 0) return void 0;
22776
23042
  return {
22777
23043
  kind: entry2.kind,
22778
23044
  secret: open(
22779
23045
  keyForRead(),
22780
23046
  entry2.sealed,
22781
- `${CREDENTIAL_STORE_SCHEMA}:${provider}`
23047
+ sealingContext(provider, owner)
22782
23048
  )
22783
23049
  };
22784
23050
  },
22785
- take: (provider, { kind, secret }, source, now) => {
23051
+ take: (provider, { kind, secret }, source, now, harness) => {
22786
23052
  if (secret.trim().length === 0)
22787
23053
  throw new Error("refusing to take an empty credential into custody");
23054
+ const owner = ownerOf(provider, harness);
22788
23055
  const entry2 = {
22789
23056
  provider,
23057
+ harness: owner,
22790
23058
  kind,
22791
23059
  source,
22792
23060
  taken_at: new Date(now).toISOString(),
22793
23061
  // A ChatGPT login is JSON, whose head says nothing; it is named.
22794
23062
  prefix: kind === "chatgpt" ? "login" : credentialPrefix(secret),
22795
- sealed: seal(
22796
- keyForWrite(),
22797
- secret,
22798
- `${CREDENTIAL_STORE_SCHEMA}:${provider}`
22799
- )
23063
+ sealed: seal(keyForWrite(), secret, sealingContext(provider, owner))
22800
23064
  };
22801
- writeAll2([...readAll().filter((e) => e.provider !== provider), entry2]);
23065
+ writeAll2([
23066
+ ...readAll().filter((e) => !matches(e, provider, owner)),
23067
+ entry2
23068
+ ]);
22802
23069
  return custodyOf(entry2);
22803
23070
  },
22804
- release: (provider) => {
23071
+ release: (provider, harness) => {
23072
+ const owner = ownerOf(provider, harness);
22805
23073
  const entries2 = readAll();
22806
- const entry2 = entries2.find((e) => e.provider === provider);
23074
+ const entry2 = entries2.find((e) => matches(e, provider, owner));
22807
23075
  if (entry2 === void 0) return void 0;
22808
23076
  const secret = open(
22809
23077
  keyForRead(),
22810
23078
  entry2.sealed,
22811
- `${CREDENTIAL_STORE_SCHEMA}:${provider}`
23079
+ sealingContext(provider, owner)
22812
23080
  );
22813
- writeAll2(entries2.filter((e) => e.provider !== provider));
23081
+ writeAll2(entries2.filter((e) => !matches(e, provider, owner)));
22814
23082
  return { kind: entry2.kind, secret };
22815
23083
  },
22816
23084
  status: () => readAll().map(custodyOf),
@@ -22837,6 +23105,7 @@ var init_credential_store = __esm({
22837
23105
  "../../packages/tacho/src/host/credential-store.ts"() {
22838
23106
  "use strict";
22839
23107
  init_zod();
23108
+ init_wire();
22840
23109
  init_fs();
22841
23110
  init_run_token();
22842
23111
  CREDENTIAL_STORE_SCHEMA = "tacho.credentials.v1";
@@ -22845,6 +23114,8 @@ var init_credential_store = __esm({
22845
23114
  "claude-code:settings.env",
22846
23115
  /** `OPENAI_API_KEY`, or the ChatGPT login's `tokens`, in Codex's `auth.json`. */
22847
23116
  "codex:auth.json",
23117
+ /** `providers.anthropic.api_key` in Stella's `stella.toml` or legacy `settings.json`. */
23118
+ "stella:settings",
22848
23119
  /** A ChatGPT login the gateway refreshed, replacing the one it took. */
22849
23120
  "chatgpt:refresh",
22850
23121
  /**
@@ -22879,6 +23150,12 @@ var init_credential_store = __esm({
22879
23150
  }).strict();
22880
23151
  entrySchema = external_exports.object({
22881
23152
  provider: external_exports.enum(RUN_TOKEN_PROVIDERS),
23153
+ /**
23154
+ * The harness the secret belongs to. Absent on a row written before
23155
+ * custody was keyed by harness; such a row is the provider's default
23156
+ * harness's (`harnessOf`).
23157
+ */
23158
+ harness: external_exports.enum(BROKERABLE_HARNESSES).optional(),
22882
23159
  kind: external_exports.enum(CREDENTIAL_KINDS),
22883
23160
  source: external_exports.enum(CREDENTIAL_SOURCES),
22884
23161
  taken_at: external_exports.string(),
@@ -23120,6 +23397,12 @@ function findTopLevelKey(lines, pattern) {
23120
23397
  return -1;
23121
23398
  }
23122
23399
  function findStellaBaseUrl(lines) {
23400
+ return findStellaKey(lines, BASE_URL_LINE);
23401
+ }
23402
+ function findStellaApiKey(lines) {
23403
+ return findStellaKey(lines, API_KEY_LINE);
23404
+ }
23405
+ function findStellaKey(lines, keyLine) {
23123
23406
  let header2 = -1;
23124
23407
  let key = -1;
23125
23408
  for (const line of tomlKeyLines(lines)) {
@@ -23140,11 +23423,11 @@ function findStellaBaseUrl(lines) {
23140
23423
  if (/^\s*["']?anthropic["']?\s*[.=]/.test(text5))
23141
23424
  return { kind: "conflict" };
23142
23425
  } else if (line.table === STELLA_TABLE && key < 0) {
23143
- if (BASE_URL_LINE.test(text5)) key = line.index;
23426
+ if (keyLine.test(text5)) key = line.index;
23144
23427
  }
23145
23428
  }
23146
23429
  if (header2 < 0) return { kind: "absent" };
23147
- const rest = key >= 0 ? BASE_URL_LINE.exec(lines[key]?.text ?? "")?.[1] ?? "" : "";
23430
+ const rest = key >= 0 ? keyLine.exec(lines[key]?.text ?? "")?.[1] ?? "" : "";
23148
23431
  return {
23149
23432
  kind: "table",
23150
23433
  header: header2,
@@ -23164,12 +23447,14 @@ function stellaTableIsEmpty(lines, header2) {
23164
23447
  }
23165
23448
  return true;
23166
23449
  }
23167
- var STELLA_TABLE, BASE_URL_LINE;
23450
+ var STELLA_TABLE, BASE_URL_LINE, STELLA_API_KEY_LINE, API_KEY_LINE;
23168
23451
  var init_model_base_url_toml = __esm({
23169
23452
  "../../packages/tacho/src/host/model-base-url-toml.ts"() {
23170
23453
  "use strict";
23171
23454
  STELLA_TABLE = "providers.anthropic";
23172
23455
  BASE_URL_LINE = /^\s*(?:base_url|"base_url"|'base_url')\s*=\s*(.*)$/;
23456
+ STELLA_API_KEY_LINE = /^(\s*(?:api_key|"api_key"|'api_key')\s*=\s*)("(?:[^"\\]|\\.)*"|'[^']*')(.*)$/;
23457
+ API_KEY_LINE = /^\s*(?:api_key|"api_key"|'api_key')\s*=\s*(.*)$/;
23173
23458
  }
23174
23459
  });
23175
23460
 
@@ -23865,6 +24150,11 @@ function helperCommandFor(tachoCommand) {
23865
24150
  return `${tachoCommand} credential issue --harness claude-code`;
23866
24151
  }
23867
24152
  function fileFor2(harness, homes) {
24153
+ if (harness === "stella")
24154
+ return modelBaseUrlFile("stella", {
24155
+ home: homes.home,
24156
+ ...homes.stellaHome !== void 0 ? { stellaHome: homes.stellaHome } : {}
24157
+ });
23868
24158
  return harness === "claude-code" ? join15(
23869
24159
  homes.claudeConfigDir ?? harnessConfigDirs(homes.home).claudeConfigDir,
23870
24160
  "settings.json"
@@ -23917,6 +24207,14 @@ function readSidecar2(path2, harness) {
23917
24207
  ) : []
23918
24208
  };
23919
24209
  if (harness === "codex") return { ...common, harness };
24210
+ if (harness === "stella") {
24211
+ const quote = parsed2["quote"];
24212
+ return {
24213
+ ...common,
24214
+ harness,
24215
+ quote: quote === "basic" || quote === "literal" ? quote : null
24216
+ };
24217
+ }
23920
24218
  const helper = parsed2["previous_helper"];
23921
24219
  return {
23922
24220
  ...common,
@@ -23994,6 +24292,23 @@ function describe4(harness, options, { changed, text: text5, reason: reason3, un
23994
24292
  );
23995
24293
  shadow = managed.shadow;
23996
24294
  if (managed.unreadable !== void 0) unreadable.push(managed.unreadable);
24295
+ } else if (harness === "stella") {
24296
+ let at2 = { kind: "absent" };
24297
+ try {
24298
+ at2 = stellaKeyIn(file, text5);
24299
+ } catch (error) {
24300
+ unreadable.push(error instanceof Error ? error.message : String(error));
24301
+ }
24302
+ if (at2.kind === "value") {
24303
+ brokered = looksLikeRunToken(at2.value);
24304
+ if (brokered) {
24305
+ const claims = peekRunTokenClaims(at2.value);
24306
+ if (claims !== void 0)
24307
+ tokenExpiresAt = new Date(claims.exp).toISOString();
24308
+ } else if (why === void 0 && anthropicCredentialKind(at2.value, "api_key") !== "api_key")
24309
+ why = "subscription_login";
24310
+ } else if (why === void 0)
24311
+ why = at2.kind === "unsupported" ? "unsupported_shape" : "no_key";
23997
24312
  } else {
23998
24313
  const auth = parseForReport(text5, file, unreadable);
23999
24314
  const key = codexTokenMember(auth);
@@ -24164,6 +24479,230 @@ function applyCodex(options, taken) {
24164
24479
  writeFileAtomicPreserving(file, next);
24165
24480
  return { changed: true, text: next };
24166
24481
  }
24482
+ function isJsonPath(file) {
24483
+ return file.endsWith(".json");
24484
+ }
24485
+ function objectMember(parent, key) {
24486
+ const value = parent?.[key];
24487
+ if (value === void 0) return void 0;
24488
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
24489
+ }
24490
+ function stellaKeyIn(file, text5) {
24491
+ if (isJsonPath(file)) {
24492
+ const settings = parseJsonObject(text5, file);
24493
+ const providers = objectMember(settings, "providers");
24494
+ if (providers === null) return { kind: "unsupported" };
24495
+ const anthropic = objectMember(providers, "anthropic");
24496
+ if (anthropic === null) return { kind: "unsupported" };
24497
+ const value2 = anthropic?.["api_key"];
24498
+ if (value2 === void 0 || value2 === null || value2 === "")
24499
+ return { kind: "absent" };
24500
+ if (typeof value2 !== "string") return { kind: "unsupported" };
24501
+ return { kind: "value", value: value2, quote: null };
24502
+ }
24503
+ const lines = splitLines(text5);
24504
+ const found = findStellaApiKey(lines);
24505
+ if (found.kind === "conflict") return { kind: "unsupported" };
24506
+ if (found.kind === "absent" || found.key < 0) return { kind: "absent" };
24507
+ const match = STELLA_API_KEY_LINE.exec(lines[found.key]?.text ?? "");
24508
+ const quoted3 = match?.[2];
24509
+ const value = quoted3 !== void 0 ? tomlStringValue(quoted3) : null;
24510
+ if (quoted3 === void 0 || value === null) return { kind: "unsupported" };
24511
+ if (value.length === 0) return { kind: "absent" };
24512
+ return {
24513
+ kind: "value",
24514
+ value,
24515
+ quote: quoted3.startsWith("'") ? "literal" : "basic"
24516
+ };
24517
+ }
24518
+ function tomlString(value, quote) {
24519
+ return quote === "literal" && !/['\r\n]/.test(value) ? `'${value}'` : JSON.stringify(value);
24520
+ }
24521
+ function withStellaKey(file, text5, value, quote) {
24522
+ if (isJsonPath(file)) {
24523
+ const settings = parseJsonObject(text5, file);
24524
+ const providers = objectMember(settings, "providers");
24525
+ const anthropic = objectMember(providers ?? void 0, "anthropic");
24526
+ if (providers === null || anthropic === null) return void 0;
24527
+ settings["providers"] = {
24528
+ ...providers ?? {},
24529
+ anthropic: { ...anthropic ?? {}, api_key: value }
24530
+ };
24531
+ return serializeJsonLike(text5, settings);
24532
+ }
24533
+ const lines = splitLines(text5 ?? "");
24534
+ const eol = (text5 ?? "").includes("\r\n") ? "\r\n" : "\n";
24535
+ const found = findStellaApiKey(lines);
24536
+ if (found.kind === "conflict") return void 0;
24537
+ const line = `api_key = ${tomlString(value, quote)}`;
24538
+ if (found.kind === "table" && found.key >= 0) {
24539
+ const at3 = lines[found.key];
24540
+ const match = STELLA_API_KEY_LINE.exec(at3.text);
24541
+ if (match === null) return void 0;
24542
+ at3.text = `${match[1] ?? ""}${tomlString(value, quote)}${match[3] ?? ""}`;
24543
+ return joinLines(lines);
24544
+ }
24545
+ if (found.kind === "table") {
24546
+ const header2 = lines[found.header];
24547
+ const after = header2.eol;
24548
+ if (header2.eol === "") header2.eol = eol;
24549
+ lines.splice(found.header + 1, 0, { text: line, eol: after });
24550
+ return joinLines(lines);
24551
+ }
24552
+ const marker = lines.findIndex((l) => STELLA_BLOCK_MARKER2.test(l.text));
24553
+ const at2 = marker >= 0 ? marker : lines.length;
24554
+ const before = lines[at2 - 1];
24555
+ const added = [];
24556
+ if (before !== void 0) {
24557
+ if (before.eol === "") before.eol = eol;
24558
+ if (before.text.trim().length > 0) added.push({ text: "", eol });
24559
+ }
24560
+ added.push({ text: "[providers.anthropic]", eol }, { text: line, eol });
24561
+ lines.splice(at2, 0, ...added);
24562
+ return joinLines(lines);
24563
+ }
24564
+ function withoutStellaKey(file, text5) {
24565
+ if (isJsonPath(file)) {
24566
+ const settings = parseJsonObject(text5, file);
24567
+ const anthropic = objectMember(
24568
+ objectMember(settings, "providers") ?? void 0,
24569
+ "anthropic"
24570
+ );
24571
+ if (anthropic === null || anthropic === void 0) return void 0;
24572
+ delete anthropic["api_key"];
24573
+ return serializeJsonLike(text5, settings);
24574
+ }
24575
+ const lines = splitLines(text5);
24576
+ const found = findStellaApiKey(lines);
24577
+ if (found.kind !== "table" || found.key < 0) return void 0;
24578
+ lines.splice(found.key, 1);
24579
+ return joinLines(lines);
24580
+ }
24581
+ function applyStella(options, taken) {
24582
+ const file = fileFor2("stella", options);
24583
+ const backup = sidecarFor2(file);
24584
+ const text5 = readTextIfExists(file);
24585
+ if (text5 === void 0) return { changed: false, text: text5, reason: "no_file" };
24586
+ const read2 = readSidecar2(backup, "stella");
24587
+ const existing = read2?.harness === "stella" ? read2 : void 0;
24588
+ const at2 = stellaKeyIn(file, text5);
24589
+ if (at2.kind === "unsupported")
24590
+ return { changed: false, text: text5, reason: "unsupported_shape" };
24591
+ if (at2.kind === "absent") return { changed: false, text: text5, reason: "no_key" };
24592
+ const current = at2.value;
24593
+ const ours = looksLikeRunToken(current);
24594
+ if (!ours) {
24595
+ if (anthropicCredentialKind(current, "api_key") !== "api_key")
24596
+ return { changed: false, text: text5, reason: "subscription_login" };
24597
+ taken.push({
24598
+ harness: "stella",
24599
+ provider: "anthropic",
24600
+ credential: { kind: "api_key", secret: current },
24601
+ member: STELLA_API_KEY_MEMBER
24602
+ });
24603
+ }
24604
+ const token = options.staticTokens?.stella;
24605
+ if (token === void 0) {
24606
+ if (!ours) taken.pop();
24607
+ return { changed: false, text: text5, reason: "no_token" };
24608
+ }
24609
+ if (current === token && existing !== void 0)
24610
+ return { changed: false, text: text5 };
24611
+ const next = withStellaKey(file, text5, token, at2.quote);
24612
+ if (next === void 0) {
24613
+ if (!ours) taken.pop();
24614
+ return { changed: false, text: text5, reason: "unsupported_shape" };
24615
+ }
24616
+ const took = ours ? [] : [{ member: STELLA_API_KEY_MEMBER, kind: "api_key" }];
24617
+ const sidecar = existing ? {
24618
+ ...existing,
24619
+ written_sha256: sha256Hex(next),
24620
+ // A key the person put back after enrolling sets the quote again.
24621
+ ...ours ? {} : { quote: at2.quote },
24622
+ taken: [
24623
+ ...existing.taken,
24624
+ ...took.filter(
24625
+ (t) => !existing.taken.some((e) => e.member === t.member)
24626
+ )
24627
+ ]
24628
+ } : {
24629
+ schema: SIDECAR_SCHEMA2,
24630
+ harness: "stella",
24631
+ existed: true,
24632
+ written_sha256: sha256Hex(next),
24633
+ quote: at2.quote,
24634
+ taken: took
24635
+ };
24636
+ writeSidecar(backup, sidecar);
24637
+ writeFileAtomicPreserving(file, next);
24638
+ return { changed: true, text: next };
24639
+ }
24640
+ function restoreStella(options, released, sidecar) {
24641
+ const file = fileFor2("stella", options);
24642
+ const backup = sidecarFor2(file);
24643
+ const text5 = readTextIfExists(file);
24644
+ const dropSidecar = () => {
24645
+ if (existsSync7(backup)) unlinkSync6(backup);
24646
+ };
24647
+ const quote = sidecar?.quote ?? null;
24648
+ if (text5 === void 0) {
24649
+ if (released !== void 0) {
24650
+ const written2 = withStellaKey(file, void 0, released.secret, quote);
24651
+ if (written2 !== void 0) {
24652
+ writeFileAtomicPreserving(file, written2);
24653
+ dropSidecar();
24654
+ return { changed: true, text: written2, secretRestored: true };
24655
+ }
24656
+ }
24657
+ dropSidecar();
24658
+ return { changed: false, text: text5, secretRestored: false };
24659
+ }
24660
+ const at2 = stellaKeyIn(file, text5);
24661
+ if (at2.kind === "value" && !looksLikeRunToken(at2.value)) {
24662
+ dropSidecar();
24663
+ return {
24664
+ changed: false,
24665
+ text: text5,
24666
+ secretRestored: false,
24667
+ reason: "foreign_key_present"
24668
+ };
24669
+ }
24670
+ if (at2.kind === "unsupported") {
24671
+ dropSidecar();
24672
+ return {
24673
+ changed: false,
24674
+ text: text5,
24675
+ secretRestored: false,
24676
+ reason: "unsupported_shape"
24677
+ };
24678
+ }
24679
+ const next = released !== void 0 ? withStellaKey(
24680
+ file,
24681
+ text5,
24682
+ released.secret,
24683
+ quote ?? (at2.kind === "value" ? at2.quote : null)
24684
+ ) : at2.kind === "value" ? withoutStellaKey(file, text5) : text5;
24685
+ if (next === void 0) {
24686
+ dropSidecar();
24687
+ return {
24688
+ changed: false,
24689
+ text: text5,
24690
+ secretRestored: false,
24691
+ reason: "unsupported_shape"
24692
+ };
24693
+ }
24694
+ if (next === text5) {
24695
+ dropSidecar();
24696
+ return { changed: false, text: text5, secretRestored: false };
24697
+ }
24698
+ writeFileAtomicPreserving(file, next);
24699
+ dropSidecar();
24700
+ return {
24701
+ changed: true,
24702
+ text: next,
24703
+ secretRestored: released !== void 0
24704
+ };
24705
+ }
24167
24706
  function restoreClaude(options, released, sidecar) {
24168
24707
  const file = fileFor2("claude-code", options);
24169
24708
  const backup = sidecarFor2(file);
@@ -24291,14 +24830,14 @@ async function applyModelCredentials(options, internals = {}) {
24291
24830
  outcomeOf(harness, options, "symlink"),
24292
24831
  internals
24293
24832
  );
24294
- const outcome = harness === "claude-code" ? applyClaude(options, taken) : applyCodex(options, taken);
24833
+ const outcome = harness === "claude-code" ? applyClaude(options, taken) : harness === "stella" ? applyStella(options, taken) : applyCodex(options, taken);
24295
24834
  return describe4(harness, options, outcome, internals);
24296
24835
  });
24297
24836
  return { harnesses, taken };
24298
24837
  }
24299
24838
  async function restoreModelCredentials(options, restore = {}, internals = {}) {
24300
24839
  const harnesses = uniqueHarnesses(options.harnesses).map((harness) => {
24301
- const released = restore.secrets?.[HARNESS_PROVIDER[harness]];
24840
+ const released = restore.secrets?.[harness];
24302
24841
  const { sidecar, unreadable } = sidecarOrWhy2(
24303
24842
  sidecarFor2(fileFor2(harness, options)),
24304
24843
  harness
@@ -24307,6 +24846,10 @@ async function restoreModelCredentials(options, restore = {}, internals = {}) {
24307
24846
  options,
24308
24847
  released,
24309
24848
  sidecar?.harness === "claude-code" ? sidecar : void 0
24849
+ ) : harness === "stella" ? restoreStella(
24850
+ options,
24851
+ released,
24852
+ sidecar?.harness === "stella" ? sidecar : void 0
24310
24853
  ) : restoreCodex(
24311
24854
  options,
24312
24855
  released,
@@ -24326,6 +24869,17 @@ async function peekModelCredentials(options) {
24326
24869
  const file = fileFor2(harness, options);
24327
24870
  const text5 = readTextIfExists(file);
24328
24871
  if (text5 === void 0) continue;
24872
+ if (harness === "stella") {
24873
+ const at2 = stellaKeyIn(file, text5);
24874
+ if (at2.kind === "value" && !looksLikeRunToken(at2.value) && anthropicCredentialKind(at2.value, "api_key") === "api_key")
24875
+ taken.push({
24876
+ harness,
24877
+ provider: "anthropic",
24878
+ credential: { kind: "api_key", secret: at2.value },
24879
+ member: STELLA_API_KEY_MEMBER
24880
+ });
24881
+ continue;
24882
+ }
24329
24883
  const document = parseJsonObject(text5, file);
24330
24884
  if (harness === "claude-code") {
24331
24885
  const env = envOf(document) ?? {};
@@ -24377,6 +24931,10 @@ function hasOrphanedModelCredential(harness, home, dirs = {}) {
24377
24931
  const text5 = readTextIfExists(file);
24378
24932
  if (text5 === void 0) return false;
24379
24933
  try {
24934
+ if (harness === "stella") {
24935
+ const at2 = stellaKeyIn(file, text5);
24936
+ return at2.kind === "value" && looksLikeRunToken(at2.value);
24937
+ }
24380
24938
  const document = parseJsonObject(text5, file);
24381
24939
  if (harness === "claude-code") return isTachoHelper(document[HELPER_KEY]);
24382
24940
  const key = codexTokenMember(document);
@@ -24390,14 +24948,14 @@ function staticTokenRenewWindowMs(host, now) {
24390
24948
  if (!Number.isFinite(notAfter)) return STATIC_TOKEN_RENEW_WINDOW_MS;
24391
24949
  return Math.min(STATIC_TOKEN_RENEW_WINDOW_MS, (notAfter - now) / 2);
24392
24950
  }
24393
- function staticTokenStillGood(token, host, keyPath2, now) {
24951
+ function staticTokenStillGood(token, host, keyPath2, now, provider = "openai") {
24394
24952
  if (token === void 0 || !looksLikeRunToken(token)) return false;
24395
24953
  const key = readRunTokenKey(keyPath2);
24396
24954
  if (key === void 0) return false;
24397
24955
  const verdict = verifyRunToken(token, {
24398
24956
  key,
24399
24957
  host: host.host_enrollment_id,
24400
- provider: "openai",
24958
+ provider,
24401
24959
  now
24402
24960
  });
24403
24961
  return verdict.ok && verdict.claims.exp - now > staticTokenRenewWindowMs(host, now);
@@ -24434,7 +24992,19 @@ function readCodexApiKeyMember(home, dirs = {}) {
24434
24992
  return void 0;
24435
24993
  }
24436
24994
  }
24437
- var HARNESS_PROVIDER, SIDECAR_SCHEMA2, HELPER_KEY, CLAUDE_API_KEY, CLAUDE_AUTH_TOKEN, CLAUDE_OAUTH_TOKEN, CLAUDE_MEMBERS, CODEX_KEY2, CODEX_TOKENS, CODEX_LAST_REFRESH, CHATGPT_REFRESH_HELD, HELPER_MARK, STATIC_TOKEN_RENEW_WINDOW_MS, CODEX_LAST_REFRESH_RESTATE_MS;
24995
+ function readStaticTokenMember(harness, home, dirs = {}) {
24996
+ if (harness === "codex") return readCodexApiKeyMember(home, dirs);
24997
+ const file = fileFor2("stella", { ...dirs, home });
24998
+ try {
24999
+ const text5 = readTextIfExists(file);
25000
+ if (text5 === void 0) return void 0;
25001
+ const at2 = stellaKeyIn(file, text5);
25002
+ return at2.kind === "value" ? at2.value : void 0;
25003
+ } catch {
25004
+ return void 0;
25005
+ }
25006
+ }
25007
+ var HARNESS_PROVIDER, STATIC_TOKEN_HARNESSES, SIDECAR_SCHEMA2, HELPER_KEY, CLAUDE_API_KEY, CLAUDE_AUTH_TOKEN, CLAUDE_OAUTH_TOKEN, CLAUDE_MEMBERS, CODEX_KEY2, CODEX_TOKENS, CODEX_LAST_REFRESH, CHATGPT_REFRESH_HELD, HELPER_MARK, STELLA_API_KEY_MEMBER, STELLA_BLOCK_MARKER2, STATIC_TOKEN_RENEW_WINDOW_MS, CODEX_LAST_REFRESH_RESTATE_MS;
24438
25008
  var init_model_credential = __esm({
24439
25009
  "../../packages/tacho/src/host/model-credential.ts"() {
24440
25010
  "use strict";
@@ -24443,9 +25013,13 @@ var init_model_credential = __esm({
24443
25013
  init_wire();
24444
25014
  init_credential_store();
24445
25015
  init_model_base_url();
25016
+ init_model_base_url_toml();
24446
25017
  init_paths();
24447
25018
  init_run_token();
24448
25019
  HARNESS_PROVIDER = BROKERABLE_HARNESS_PROVIDER;
25020
+ STATIC_TOKEN_HARNESSES = BROKERABLE_HARNESSES.filter(
25021
+ (harness) => harness !== "claude-code"
25022
+ );
24449
25023
  SIDECAR_SCHEMA2 = "oxagen.model-credential.v1";
24450
25024
  HELPER_KEY = "apiKeyHelper";
24451
25025
  CLAUDE_API_KEY = "ANTHROPIC_API_KEY";
@@ -24461,6 +25035,8 @@ var init_model_credential = __esm({
24461
25035
  CODEX_LAST_REFRESH = "last_refresh";
24462
25036
  CHATGPT_REFRESH_HELD = "held-by-oxagen-gateway";
24463
25037
  HELPER_MARK = /\bcredential issue --harness claude-code\s*$/;
25038
+ STELLA_API_KEY_MEMBER = "providers.anthropic.api_key";
25039
+ STELLA_BLOCK_MARKER2 = /^# >>> tacho enrollment /;
24464
25040
  STATIC_TOKEN_RENEW_WINDOW_MS = 7 * 24 * 60 * 6e4;
24465
25041
  CODEX_LAST_REFRESH_RESTATE_MS = 5 * 24 * 60 * 6e4;
24466
25042
  }
@@ -24472,7 +25048,8 @@ import { dirname as dirname8 } from "node:path";
24472
25048
  function harnessDirsOf(deps) {
24473
25049
  return {
24474
25050
  claudeConfigDir: dirname8(deps.paths.claudeSettings),
24475
- codexHome: dirname8(deps.paths.codexHooks)
25051
+ codexHome: dirname8(deps.paths.codexHooks),
25052
+ stellaHome: dirname8(deps.paths.stellaToml)
24476
25053
  };
24477
25054
  }
24478
25055
  function parseCredentialMode(value) {
@@ -24548,6 +25125,7 @@ async function credentialStatus(options, deps) {
24548
25125
  ].flatMap(
24549
25126
  (agent) => (agent.credentialStore?.status() ?? []).map((c) => ({
24550
25127
  provider: c.provider,
25128
+ harness: c.harness,
24551
25129
  kind: c.kind,
24552
25130
  source: c.source,
24553
25131
  taken_at: c.taken_at,
@@ -24561,6 +25139,7 @@ async function credentialStatus(options, deps) {
24561
25139
  const harnesses = [...new Set(enrolled)].filter(isCredentialHarness);
24562
25140
  const state = deps.modelCredentials !== void 0 && harnesses.length > 0 ? (await deps.modelCredentials.read({
24563
25141
  home: deps.home,
25142
+ stellaHome: dirname8(deps.paths.stellaToml),
24564
25143
  harnesses,
24565
25144
  helperCommand: deps.runtime.credentialHelperCommand
24566
25145
  })).harnesses : [];
@@ -24575,7 +25154,7 @@ async function credentialStatus(options, deps) {
24575
25154
  );
24576
25155
  for (const entry2 of custody)
24577
25156
  deps.out(
24578
- `Custody ${entry2.provider}: ${entry2.kind} ${entry2.prefix} from ${entry2.source}, taken ${entry2.taken_at}`
25157
+ `Custody ${entry2.provider}${entry2.harness === defaultHarnessForProvider(entry2.provider) ? "" : ` (${TACHO_HARNESS_LABELS[entry2.harness]})`}: ${entry2.kind} ${entry2.prefix} from ${entry2.source}, taken ${entry2.taken_at}`
24579
25158
  );
24580
25159
  for (const entry2 of state) deps.out(` ${describeHarness(entry2)}`);
24581
25160
  return report;
@@ -24585,6 +25164,22 @@ function describeHarness(entry2) {
24585
25164
  if (entry2.brokered) {
24586
25165
  return entry2.shadowedBy !== void 0 ? `${label2}: points at the gateway's run tokens, but ${entry2.shadowedBy.file} sets apiKeyHelper, and managed settings win, so it does not` : `${label2}: holds a run token; the gateway supplies the ${HARNESS_PROVIDER[entry2.harness]} credential`;
24587
25166
  }
25167
+ if (entry2.harness === "stella") {
25168
+ switch (entry2.reason) {
25169
+ case "no_key":
25170
+ return `${label2}: ${entry2.file} holds no providers.anthropic.api_key, so Stella sends the key it reads from ANTHROPIC_API_KEY or its credentials file, and that key crosses the proxy as its own. Put the key in ${entry2.file} and run \`oxagen agent enroll\` to broker it`;
25171
+ case "subscription_login":
25172
+ return `${label2}: ${entry2.file} holds a Claude subscription token in providers.anthropic.api_key, and Stella is brokered only on an API key; it crosses the proxy as its own`;
25173
+ case "unsupported_shape":
25174
+ return `${label2}: ${entry2.file} defines providers.anthropic in a shape a line edit cannot change safely, so it was left alone; its own key crosses the proxy`;
25175
+ case "symlink":
25176
+ return `${label2}: ${entry2.file} is a symbolic link, so it was left alone; its own key crosses the proxy`;
25177
+ case "no_file":
25178
+ return `${label2}: no ${entry2.file} yet; its own key crosses the proxy`;
25179
+ default:
25180
+ return `${label2}: holds its own key, which crosses the proxy (run \`oxagen agent enroll\` to broker it)`;
25181
+ }
25182
+ }
24588
25183
  switch (entry2.reason) {
24589
25184
  case "subscription_login":
24590
25185
  return `${label2}: signed in with a ChatGPT login that crosses the proxy as its own (run \`oxagen credential login --harness codex\` to put it in the gateway's custody)`;
@@ -24598,6 +25193,16 @@ function describeHarness(entry2) {
24598
25193
  return entry2.harness === "claude-code" ? `${label2}: holds its own credential, which crosses the proxy (run \`oxagen credential login\` to put your Claude subscription in the gateway's custody)` : `${label2}: holds its own credential, which crosses the proxy (run \`oxagen agent enroll\` to broker it)`;
24599
25194
  }
24600
25195
  }
25196
+ function stellaEnvKeyWarning(entries2, env) {
25197
+ const stella = entries2.find((entry2) => entry2.harness === "stella");
25198
+ if (stella?.brokered !== true) return void 0;
25199
+ if ((env[STELLA_ANTHROPIC_KEY_ENV]?.trim() ?? "").length === 0)
25200
+ return void 0;
25201
+ return `${STELLA_ANTHROPIC_KEY_ENV} is set in this environment. Stella reads it ahead of the run token in ${stella.file}, so an interactive Stella sends that key and the gateway refuses it as foreign_credential. Unset ${STELLA_ANTHROPIC_KEY_ENV} in your shell profile; the gateway supplies Stella's key. Runs Oxagen dispatches are not affected`;
25202
+ }
25203
+ function fileSourceOf(harness) {
25204
+ return harness === "claude-code" ? "claude-code:settings.env" : harness === "stella" ? "stella:settings" : "codex:auth.json";
25205
+ }
24601
25206
  async function brokerCredentials(host, harnesses, deps) {
24602
25207
  const warnings = [];
24603
25208
  const store = deps.credentialStore;
@@ -24607,21 +25212,26 @@ async function brokerCredentials(host, harnesses, deps) {
24607
25212
  return { harnesses: [], taken: [], warnings };
24608
25213
  const helperCommand = deps.runtime.credentialHelperCommand;
24609
25214
  const offered = /* @__PURE__ */ new Map();
25215
+ const stellaHome = dirname8(deps.paths.stellaToml);
24610
25216
  for (const taken of await contract.peek({
24611
25217
  home: deps.home,
25218
+ stellaHome,
24612
25219
  harnesses: targets,
24613
25220
  helperCommand
24614
25221
  })) {
24615
- offered.set(taken.provider, {
25222
+ offered.set(taken.harness, {
25223
+ provider: taken.provider,
24616
25224
  credential: taken.credential,
24617
- source: taken.harness === "claude-code" ? "claude-code:settings.env" : "codex:auth.json"
25225
+ source: fileSourceOf(taken.harness)
24618
25226
  });
24619
25227
  }
24620
25228
  for (const harness of targets) {
25229
+ if (harness === "stella") continue;
24621
25230
  const provider = HARNESS_PROVIDER[harness];
24622
25231
  const fromEnv = deps.env[brokerEnvVar(provider)];
24623
- if (!offered.has(provider) && typeof fromEnv === "string" && fromEnv.trim().length > 0)
24624
- offered.set(provider, {
25232
+ if (!offered.has(harness) && typeof fromEnv === "string" && fromEnv.trim().length > 0)
25233
+ offered.set(harness, {
25234
+ provider,
24625
25235
  credential: {
24626
25236
  kind: provider === "anthropic" ? anthropicCredentialKind(fromEnv, "api_key") : "bearer",
24627
25237
  secret: fromEnv.trim()
@@ -24629,8 +25239,9 @@ async function brokerCredentials(host, harnesses, deps) {
24629
25239
  source: "enroll:env"
24630
25240
  });
24631
25241
  const oauth = deps.env[CLAUDE_OAUTH_TOKEN_ENV]?.trim();
24632
- if (provider === "anthropic" && !offered.has(provider) && oauth !== void 0 && isSubscriptionToken(oauth))
24633
- offered.set(provider, {
25242
+ if (harness === "claude-code" && !offered.has(harness) && oauth !== void 0 && isSubscriptionToken(oauth))
25243
+ offered.set(harness, {
25244
+ provider,
24634
25245
  credential: { kind: "oauth", secret: oauth },
24635
25246
  source: "enroll:env"
24636
25247
  });
@@ -24639,52 +25250,70 @@ async function brokerCredentials(host, harnesses, deps) {
24639
25250
  const staticTokens = {};
24640
25251
  let state;
24641
25252
  try {
24642
- for (const [provider, offer] of offered) {
24643
- store.take(provider, offer.credential, offer.source, deps.now());
24644
- takenNow.push(provider);
25253
+ for (const [harness, offer] of offered) {
25254
+ store.take(
25255
+ offer.provider,
25256
+ offer.credential,
25257
+ offer.source,
25258
+ deps.now(),
25259
+ harness
25260
+ );
25261
+ takenNow.push({ provider: offer.provider, harness });
24645
25262
  }
24646
- if (targets.includes("codex") && store.has("openai")) {
24647
- {
24648
- const current = readCodexApiKeyMember(deps.home, harnessDirsOf(deps));
24649
- if (!staticTokenStillGood(
24650
- current,
24651
- host,
24652
- deps.paths.runTokenKey,
24653
- deps.now()
24654
- )) {
24655
- const issued = await issueStatic("codex", deps);
24656
- if (issued.token !== void 0) staticTokens.codex = issued.token;
24657
- else
24658
- warnings.push(`Codex keeps its own credential: ${issued.detail}`);
24659
- }
24660
- }
25263
+ for (const harness of STATIC_TOKEN_HARNESSES) {
25264
+ if (!targets.includes(harness)) continue;
25265
+ const provider = HARNESS_PROVIDER[harness];
25266
+ if (!store.has(provider, harness)) continue;
25267
+ const current = readStaticTokenMember(
25268
+ harness,
25269
+ deps.home,
25270
+ harnessDirsOf(deps)
25271
+ );
25272
+ if (staticTokenStillGood(
25273
+ current,
25274
+ host,
25275
+ deps.paths.runTokenKey,
25276
+ deps.now(),
25277
+ provider
25278
+ ))
25279
+ continue;
25280
+ const issued = await issueStatic(harness, deps);
25281
+ if (issued.token !== void 0) staticTokens[harness] = issued.token;
25282
+ else
25283
+ warnings.push(
25284
+ `${TACHO_HARNESS_LABELS[harness]} keeps its own credential: ${issued.detail}`
25285
+ );
24661
25286
  }
24662
25287
  state = await contract.apply({
24663
25288
  home: deps.home,
25289
+ stellaHome,
24664
25290
  harnesses: targets,
24665
25291
  helperCommand,
24666
25292
  staticTokens
24667
25293
  });
24668
25294
  } catch (error) {
24669
- for (const provider of takenNow) store.release(provider);
25295
+ for (const { provider, harness } of takenNow)
25296
+ store.release(provider, harness);
24670
25297
  throw error;
24671
25298
  }
24672
25299
  for (const taken of state.taken) {
24673
- if (offered.has(taken.provider)) continue;
25300
+ if (offered.has(taken.harness)) continue;
24674
25301
  store.take(
24675
25302
  taken.provider,
24676
25303
  taken.credential,
24677
- taken.harness === "claude-code" ? "claude-code:settings.env" : "codex:auth.json",
24678
- deps.now()
25304
+ fileSourceOf(taken.harness),
25305
+ deps.now(),
25306
+ taken.harness
24679
25307
  );
24680
- takenNow.push(taken.provider);
25308
+ takenNow.push({ provider: taken.provider, harness: taken.harness });
24681
25309
  }
24682
25310
  for (const entry2 of state.harnesses) {
24683
- const provider = HARNESS_PROVIDER[entry2.harness];
25311
+ const harness = entry2.harness;
25312
+ const provider = HARNESS_PROVIDER[harness];
24684
25313
  if (entry2.brokered) {
24685
- if (!store.has(provider)) {
25314
+ if (!store.has(provider, harness)) {
24686
25315
  await contract.restore(
24687
- { home: deps.home, harnesses: [entry2.harness], helperCommand },
25316
+ { home: deps.home, stellaHome, harnesses: [harness], helperCommand },
24688
25317
  {}
24689
25318
  );
24690
25319
  const index = state.harnesses.indexOf(entry2);
@@ -24695,19 +25324,19 @@ async function brokerCredentials(host, harnesses, deps) {
24695
25324
  reason: "no_token"
24696
25325
  };
24697
25326
  warnings.push(
24698
- provider === "anthropic" ? `${TACHO_HARNESS_LABELS[entry2.harness]} keeps its own login: the gateway holds no Anthropic credential yet. Run \`oxagen credential login\` to sign in with your Claude subscription, or put a key in ${entry2.file} and enroll again` : `${TACHO_HARNESS_LABELS[entry2.harness]} keeps its own login: no ${provider} key was found in ${entry2.file} or ${brokerEnvVar(provider)}, so there is nothing for the gateway to take into custody`
25327
+ harness === "claude-code" ? `${TACHO_HARNESS_LABELS[harness]} keeps its own login: the gateway holds no Anthropic credential yet. Run \`oxagen credential login\` to sign in with your Claude subscription, or put a key in ${entry2.file} and enroll again` : harness === "stella" ? `${TACHO_HARNESS_LABELS[harness]} keeps its own key: the gateway holds no Anthropic key of Stella's. Put the key in providers.anthropic.api_key in ${entry2.file} and enroll again` : `${TACHO_HARNESS_LABELS[harness]} keeps its own login: no ${provider} key was found in ${entry2.file} or ${brokerEnvVar(provider)}, so there is nothing for the gateway to take into custody`
24699
25328
  );
24700
25329
  }
24701
25330
  continue;
24702
25331
  }
24703
- if (store.has(provider) && takenNow.includes(provider)) {
24704
- const released = store.release(provider);
24705
- const index = takenNow.indexOf(provider);
24706
- if (index >= 0) takenNow.splice(index, 1);
24707
- const offer = offered.get(provider);
25332
+ const at2 = takenNow.findIndex((t) => t.harness === harness);
25333
+ if (at2 >= 0 && store.has(provider, harness)) {
25334
+ const released = store.release(provider, harness);
25335
+ takenNow.splice(at2, 1);
25336
+ const offer = offered.get(harness);
24708
25337
  if (offer?.source === "enroll:env" || released === void 0) continue;
24709
25338
  warnings.push(
24710
- `${TACHO_HARNESS_LABELS[entry2.harness]} keeps its own credential (${entry2.reason ?? "not brokered"}), so its ${provider} key was not taken into custody`
25339
+ `${TACHO_HARNESS_LABELS[harness]} keeps its own credential (${entry2.reason ?? "not brokered"}), so its ${provider} key was not taken into custody`
24711
25340
  );
24712
25341
  }
24713
25342
  }
@@ -24754,10 +25383,10 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
24754
25383
  let fromLogin = false;
24755
25384
  try {
24756
25385
  fromLogin = store?.status().some(
24757
- (c) => c.provider === provider && c.source === "claude:setup-token"
25386
+ (c) => c.provider === provider && c.harness === harness && c.source === "claude:setup-token"
24758
25387
  ) === true;
24759
- if (fromLogin) store?.release(provider);
24760
- else released = store?.read(provider);
25388
+ if (fromLogin) store?.release(provider, harness);
25389
+ else released = store?.read(provider, harness);
24761
25390
  } catch (error) {
24762
25391
  unreadable = error instanceof Error ? error.message : String(error);
24763
25392
  }
@@ -24774,18 +25403,23 @@ async function restoreCredentials(host, deps, mode = "unenroll", only) {
24774
25403
  );
24775
25404
  }
24776
25405
  const state = await contract.restore(
24777
- { home: deps.home, harnesses: [harness], helperCommand },
24778
- released !== void 0 ? { secrets: { [provider]: released } } : {}
25406
+ {
25407
+ home: deps.home,
25408
+ stellaHome: dirname8(deps.paths.stellaToml),
25409
+ harnesses: [harness],
25410
+ helperCommand
25411
+ },
25412
+ released !== void 0 ? { secrets: { [harness]: released } } : {}
24779
25413
  );
24780
25414
  for (const entry2 of state.harnesses) {
24781
25415
  if (entry2.changed) restored.push(entry2.file);
24782
25416
  if (released === void 0) continue;
24783
25417
  if (entry2.secretRestored === true) {
24784
- store?.release(provider);
25418
+ store?.release(provider, harness);
24785
25419
  continue;
24786
25420
  }
24787
25421
  if (mode === "unenroll") {
24788
- store?.release(provider);
25422
+ store?.release(provider, harness);
24789
25423
  warnings.push(
24790
25424
  `${entry2.file} already holds a ${provider} credential of its own, so the older one the gateway held was not written back and is discarded with the store`
24791
25425
  );
@@ -24989,7 +25623,7 @@ async function credentialLogin(options, deps) {
24989
25623
  detail: outcome.harnesses.some((h) => h.brokered) ? "Claude Code now holds a run token, and the gateway sends your subscription with every call" : "The subscription is in custody. Run `oxagen agent enroll` to route Claude Code through the gateway"
24990
25624
  };
24991
25625
  }
24992
- var MODEL_CREDENTIAL_HARNESSES, CREDENTIAL_MODES, CLAUDE_OAUTH_TOKEN_ENV;
25626
+ var MODEL_CREDENTIAL_HARNESSES, CREDENTIAL_MODES, STELLA_ANTHROPIC_KEY_ENV, CLAUDE_OAUTH_TOKEN_ENV;
24993
25627
  var init_credential = __esm({
24994
25628
  "../../packages/tacho/src/cli/credential.ts"() {
24995
25629
  "use strict";
@@ -25007,6 +25641,7 @@ var init_credential = __esm({
25007
25641
  "brokered",
25008
25642
  "passthrough"
25009
25643
  ];
25644
+ STELLA_ANTHROPIC_KEY_ENV = "ANTHROPIC_API_KEY";
25010
25645
  CLAUDE_OAUTH_TOKEN_ENV = "CLAUDE_CODE_OAUTH_TOKEN";
25011
25646
  }
25012
25647
  });
@@ -25777,7 +26412,7 @@ import { readFileSync as readFileSync10 } from "node:fs";
25777
26412
  import { dirname as dirname9, resolve as resolve3 } from "node:path";
25778
26413
  import { fileURLToPath } from "node:url";
25779
26414
  function readVersion() {
25780
- if (true) return "2.1.4";
26415
+ if (true) return "2.1.5-22";
25781
26416
  try {
25782
26417
  const here = dirname9(fileURLToPath(import.meta.url));
25783
26418
  const pkg = JSON.parse(
@@ -26168,7 +26803,8 @@ function defaultCliDeps(overrides = {}, managed = {}) {
26168
26803
  const harnessFiles = new HarnessFiles(paths.dir);
26169
26804
  const harnessDirs = {
26170
26805
  claudeConfigDir: dirname10(paths.claudeSettings),
26171
- codexHome: dirname10(paths.codexHooks)
26806
+ codexHome: dirname10(paths.codexHooks),
26807
+ stellaHome: dirname10(paths.stellaToml)
26172
26808
  };
26173
26809
  const daemonGet = async (path2, options) => {
26174
26810
  const host = readHostFile(paths.hostFile);
@@ -30163,6 +30799,7 @@ async function agentStatus(deps) {
30163
30799
  return void 0;
30164
30800
  const state = await deps.modelCredentials.read({
30165
30801
  home: deps.home,
30802
+ stellaHome: dirname11(deps.paths.stellaToml),
30166
30803
  harnesses: routedHarnesses,
30167
30804
  helperCommand: deps.runtime.credentialHelperCommand
30168
30805
  });
@@ -30224,6 +30861,7 @@ async function agentStatus(deps) {
30224
30861
  try {
30225
30862
  credentialCustody = deps.credentialStore.status().map((c) => ({
30226
30863
  provider: c.provider,
30864
+ harness: c.harness,
30227
30865
  kind: c.kind,
30228
30866
  source: c.source,
30229
30867
  taken_at: c.taken_at
@@ -30232,6 +30870,9 @@ async function agentStatus(deps) {
30232
30870
  problems.push(error instanceof Error ? error.message : String(error));
30233
30871
  }
30234
30872
  }
30873
+ const credentialWarnings = [];
30874
+ const envKey = stellaEnvKeyWarning(modelCredentials ?? [], deps.env);
30875
+ if (envKey !== void 0) credentialWarnings.push(envKey);
30235
30876
  const tiers = observedTiers(daemon);
30236
30877
  const retired = host.revoked_at !== null;
30237
30878
  const shipping = retired ? void 0 : shippingHealth(daemon, walStats, deps.now());
@@ -30242,6 +30883,7 @@ async function agentStatus(deps) {
30242
30883
  ...gateway !== void 0 ? { gateway } : {},
30243
30884
  ...modelBaseUrls !== void 0 ? { modelBaseUrls } : {},
30244
30885
  ...modelCredentials !== void 0 ? { modelCredentials } : {},
30886
+ ...credentialWarnings.length > 0 ? { credentialWarnings } : {},
30245
30887
  ...credentialCustody !== void 0 ? { credentialCustody } : {},
30246
30888
  ...Object.keys(tiers).length > 0 ? { tiers } : {},
30247
30889
  host: {
@@ -30391,6 +31033,8 @@ function printStatus(report, deps) {
30391
31033
  }
30392
31034
  for (const entry2 of modelCredentials ?? [])
30393
31035
  deps.out(`Credential ${describeHarness(entry2)}`);
31036
+ for (const warning of report.credentialWarnings ?? [])
31037
+ deps.out(`Warning ${warning}`);
30394
31038
  for (const harness of h.harnesses)
30395
31039
  deps.out(
30396
31040
  `Tier ${harness}: ${tiers[harness] ?? "no run since the collector started"}`
@@ -32248,7 +32892,6 @@ ${problems.map((problem) => ` ${problem}`).join("\n")}`
32248
32892
  if (unhooked.includes(harness)) return false;
32249
32893
  const file = modelBaseUrlFile(harness, {
32250
32894
  home: deps.home,
32251
- stellaHome,
32252
32895
  ...harnessDirsOf(deps)
32253
32896
  });
32254
32897
  let linked2 = false;
@@ -32308,9 +32951,9 @@ ${problems.map((problem) => ` ${problem}`).join("\n")}`
32308
32951
  const outcome = await brokerCredentials(host, reaching, deps);
32309
32952
  for (const entry2 of outcome.harnesses)
32310
32953
  deps.out(` ${describeHarness(entry2)}`);
32311
- for (const provider of outcome.taken)
32954
+ for (const { provider, harness } of outcome.taken)
32312
32955
  deps.out(
32313
- ` ${provider} credential taken into the gateway's custody (${deps.paths.credentials})`
32956
+ ` ${harness === "stella" ? "Stella's " : ""}${provider} credential taken into the gateway's custody (${deps.paths.credentials})`
32314
32957
  );
32315
32958
  warnings.push(...outcome.warnings);
32316
32959
  const unrouted = writable2.filter((h) => !reaching.includes(h));
@@ -34515,14 +35158,20 @@ function headlessArgs(harness, prompt) {
34515
35158
  return ["run", prompt];
34516
35159
  }
34517
35160
  }
34518
- function headlessEnv(harness) {
34519
- return harness === "stella" ? { STELLA_TRUST_PROJECT: "1" } : {};
35161
+ function headlessEnv(harness, stellaRunToken) {
35162
+ if (harness !== "stella") return {};
35163
+ return {
35164
+ STELLA_TRUST_PROJECT: "1",
35165
+ ...stellaRunToken !== void 0 ? { ANTHROPIC_API_KEY: stellaRunToken } : {}
35166
+ };
34520
35167
  }
35168
+ var DISPATCHED_STELLA_TOKEN_TTL_MS;
34521
35169
  var init_work_order_start = __esm({
34522
35170
  "../../packages/tacho/src/host/work-order-start.ts"() {
34523
35171
  "use strict";
34524
35172
  init_control_client();
34525
35173
  init_wire();
35174
+ DISPATCHED_STELLA_TOKEN_TTL_MS = 24 * 60 * 6e4;
34526
35175
  }
34527
35176
  });
34528
35177
 
@@ -34899,6 +35548,24 @@ async function workClaim(criterionId, options, deps) {
34899
35548
  );
34900
35549
  return 2;
34901
35550
  }
35551
+ if (options.workOrder === void 0 !== (options.item === void 0)) {
35552
+ deps.err(
35553
+ "Give --work-order and --item together, as the first prompt lists them for each item of a batch."
35554
+ );
35555
+ return 2;
35556
+ }
35557
+ if (options.workOrder !== void 0 && workOrderIdOf(options.workOrder) === void 0) {
35558
+ deps.err(
35559
+ `${JSON.stringify(options.workOrder)} is not a work order id. A work order id starts with wo_, as the first prompt lists it.`
35560
+ );
35561
+ return 2;
35562
+ }
35563
+ if (options.item !== void 0 && !/^wi_[0-9A-Za-z]+$/.test(options.item)) {
35564
+ deps.err(
35565
+ `${JSON.stringify(options.item)} is not a work item id. A work item id starts with wi_, as the first prompt lists it.`
35566
+ );
35567
+ return 2;
35568
+ }
34902
35569
  const workOrderId = workOrderIdOf(deps.env[WORK_ORDER_ENV]);
34903
35570
  if (workOrderId === void 0) {
34904
35571
  deps.err(
@@ -34925,7 +35592,9 @@ async function workClaim(criterionId, options, deps) {
34925
35592
  );
34926
35593
  return 1;
34927
35594
  }
34928
- const { agent, item } = found;
35595
+ const { agent } = found;
35596
+ const item = options.item ?? found.item;
35597
+ const claimOrder = options.workOrder ?? workOrderId;
34929
35598
  if (item === void 0) {
34930
35599
  deps.err(
34931
35600
  `The run of ${workOrderId} on this machine does not record its work item, because an older oxagen started it. Claims work in the next run that \`oxagen work start\` starts.`
@@ -34944,7 +35613,7 @@ async function workClaim(criterionId, options, deps) {
34944
35613
  try {
34945
35614
  answer = await client.claimWorkCriterion({
34946
35615
  item_id: item,
34947
- work_order_id: workOrderId,
35616
+ work_order_id: claimOrder,
34948
35617
  criterion_id: criterionId,
34949
35618
  head_sha: head2,
34950
35619
  text: text5
@@ -34962,7 +35631,7 @@ async function workClaim(criterionId, options, deps) {
34962
35631
  );
34963
35632
  return 1;
34964
35633
  }
34965
- deps.out(claimAnswerMessage(answer, criterionId, workOrderId, head2));
35634
+ deps.out(claimAnswerMessage(answer, criterionId, claimOrder, head2));
34966
35635
  return 0;
34967
35636
  }
34968
35637
  var NOT_ENROLLED;
@@ -34989,6 +35658,7 @@ __export(cli_exports, {
34989
35658
  CURSOR_COVERAGE_NOTE: () => CURSOR_COVERAGE_NOTE,
34990
35659
  MODEL_CREDENTIAL_HARNESSES: () => MODEL_CREDENTIAL_HARNESSES,
34991
35660
  SHIPPING_STALL_MS: () => SHIPPING_STALL_MS,
35661
+ STELLA_ANTHROPIC_KEY_ENV: () => STELLA_ANTHROPIC_KEY_ENV,
34992
35662
  acquireSubscriptionToken: () => acquireSubscriptionToken,
34993
35663
  addMissingClaudeCodeMcp: () => addMissingClaudeCodeMcp,
34994
35664
  agentNameFromCommand: () => agentNameFromCommand,
@@ -35052,6 +35722,7 @@ __export(cli_exports, {
35052
35722
  spawnAgent: () => spawnAgent,
35053
35723
  stableExecutablePath: () => stableExecutablePath,
35054
35724
  status: () => status,
35725
+ stellaEnvKeyWarning: () => stellaEnvKeyWarning,
35055
35726
  stopGateway: () => stopGateway,
35056
35727
  streamDaemon: () => streamDaemon,
35057
35728
  stripEnrollmentHooks: () => stripEnrollmentHooks,
@@ -40627,6 +41298,69 @@ var init_runner = __esm({
40627
41298
  }
40628
41299
  });
40629
41300
 
41301
+ // ../../packages/tacho/src/collector/static-token-renewal.ts
41302
+ async function renewStaticToken(harness, deps) {
41303
+ const provider = HARNESS_PROVIDER[harness];
41304
+ const label2 = TACHO_HARNESS_LABELS[harness];
41305
+ if (deps.host.host_status !== "active" || !deps.inCustody(provider, harness))
41306
+ return "skipped";
41307
+ const dirs = deps.stellaHome !== void 0 ? { stellaHome: deps.stellaHome } : {};
41308
+ let brokered = false;
41309
+ try {
41310
+ brokered = (await readModelCredentialState({
41311
+ home: deps.home,
41312
+ ...dirs,
41313
+ harnesses: [harness]
41314
+ })).harnesses[0]?.brokered === true;
41315
+ } catch (error) {
41316
+ deps.log(
41317
+ `static token renewal: cannot read ${label2}'s credential file: ${error instanceof Error ? error.message : String(error)}`
41318
+ );
41319
+ return "failed";
41320
+ }
41321
+ if (!brokered) return "skipped";
41322
+ const current = readStaticTokenMember(harness, deps.home, dirs);
41323
+ if (staticTokenStillGood(
41324
+ current,
41325
+ deps.host,
41326
+ deps.runTokenKeyPath,
41327
+ deps.now(),
41328
+ provider
41329
+ ))
41330
+ return "still_good";
41331
+ const issued = deps.issue?.({ harness, placement: "static" });
41332
+ if (issued === void 0 || issued.status !== 200) {
41333
+ deps.log(
41334
+ `static token renewal: not issued for ${label2} (${issued?.body.error ?? "no issuer"})`
41335
+ );
41336
+ return "not_issued";
41337
+ }
41338
+ try {
41339
+ await applyModelCredentials({
41340
+ home: deps.home,
41341
+ ...dirs,
41342
+ harnesses: [harness],
41343
+ staticTokens: { [harness]: issued.body.token }
41344
+ });
41345
+ deps.log(
41346
+ `renewed ${label2}'s static run token ${issued.body.token_id}, expires ${issued.body.expires_at}`
41347
+ );
41348
+ return "renewed";
41349
+ } catch (error) {
41350
+ deps.log(
41351
+ `static token renewal: could not write ${label2}'s credential file: ${error instanceof Error ? error.message : String(error)}`
41352
+ );
41353
+ return "failed";
41354
+ }
41355
+ }
41356
+ var init_static_token_renewal = __esm({
41357
+ "../../packages/tacho/src/collector/static-token-renewal.ts"() {
41358
+ "use strict";
41359
+ init_model_credential();
41360
+ init_wire();
41361
+ }
41362
+ });
41363
+
40630
41364
  // ../../packages/tacho/src/host/chatgpt-refresh.ts
40631
41365
  function jwtExpiryMs(token) {
40632
41366
  const payload = token.split(".")[1];
@@ -41532,6 +42266,20 @@ function createWorkOrderAutostart(deps) {
41532
42266
  }
41533
42267
  return searchPath;
41534
42268
  };
42269
+ const stellaRunToken = (id2) => {
42270
+ const answer = deps.issueRunToken?.({
42271
+ harness: "stella",
42272
+ placement: "static",
42273
+ ttl_ms: DISPATCHED_STELLA_TOKEN_TTL_MS
42274
+ });
42275
+ if (answer === void 0) return void 0;
42276
+ if (answer.status === 200) return answer.body.token;
42277
+ if (answer.body.code !== "credential_unavailable")
42278
+ log(
42279
+ `work order ${id2}: no run token for Stella (${answer.body.error}); it starts on its own key`
42280
+ );
42281
+ return void 0;
42282
+ };
41535
42283
  const lookup = deps.findCheckout ?? ((repository) => findCheckout(paths, repository, deps.host()));
41536
42284
  const retry = /* @__PURE__ */ new Map();
41537
42285
  const cleanups = /* @__PURE__ */ new Set();
@@ -41778,7 +42526,10 @@ function createWorkOrderAutostart(deps) {
41778
42526
  env: {
41779
42527
  ...deps.env,
41780
42528
  PATH: pathForHarness(),
41781
- ...headlessEnv(harness),
42529
+ ...headlessEnv(
42530
+ harness,
42531
+ harness === "stella" ? stellaRunToken(id2) : void 0
42532
+ ),
41782
42533
  [WORK_ORDER_ENV]: id2
41783
42534
  },
41784
42535
  logPath: workspace.log
@@ -46001,10 +46752,10 @@ function createGithubProxy(deps) {
46001
46752
  error: "GitHub custody is not configured for this repository and harness"
46002
46753
  }
46003
46754
  };
46004
- const matches = deps.registry.live().filter(
46755
+ const matches2 = deps.registry.live().filter(
46005
46756
  (s2) => !s2.pendingTerminal && s2.cwd && resolve13(s2.cwd) === resolve13(input.cwd) && deps.registry.agentOf(s2).harness === input.harness
46006
46757
  );
46007
- if (matches.length !== 1)
46758
+ if (matches2.length !== 1)
46008
46759
  return {
46009
46760
  status: 409,
46010
46761
  body: {
@@ -46021,7 +46772,7 @@ function createGithubProxy(deps) {
46021
46772
  return grant(
46022
46773
  {
46023
46774
  repository: input.repository,
46024
- session: matches[0].recorder.sessionUuid
46775
+ session: matches2[0].recorder.sessionUuid
46025
46776
  },
46026
46777
  host
46027
46778
  );
@@ -50028,11 +50779,11 @@ function providerForHarness(harness) {
50028
50779
  function issueRunToken(input, deps) {
50029
50780
  const harness = typeof input.harness === "string" ? input.harness : "";
50030
50781
  const provider = providerForHarness(harness);
50031
- if (provider === void 0)
50782
+ if (provider === void 0 || !isBrokerableHarness(harness))
50032
50783
  return {
50033
50784
  status: 400,
50034
50785
  body: {
50035
- error: "run tokens are issued for claude-code and codex, the harnesses whose model calls the gateway routes",
50786
+ error: `run tokens are issued for ${BROKERABLE_HARNESSES.join(", ")}, the harnesses whose model calls the gateway routes`,
50036
50787
  code: "harness_not_brokered"
50037
50788
  }
50038
50789
  };
@@ -50057,7 +50808,7 @@ function issueRunToken(input, deps) {
50057
50808
  };
50058
50809
  let held;
50059
50810
  try {
50060
- held = deps.store.read(provider);
50811
+ held = deps.store.read(provider, harness);
50061
50812
  } catch (error) {
50062
50813
  deps.log(
50063
50814
  `credential issuer: cannot read custody: ${error instanceof Error ? error.message : String(error)}`
@@ -50074,7 +50825,7 @@ function issueRunToken(input, deps) {
50074
50825
  return {
50075
50826
  status: 403,
50076
50827
  body: {
50077
- error: `the gateway holds no ${provider} credential in custody, so a run token would buy nothing; run \`oxagen agent enroll\` again`,
50828
+ error: `the gateway holds no ${provider} credential in custody${harness === "stella" ? " for Stella" : ""}, so a run token would buy nothing; run \`oxagen agent enroll\` again`,
50078
50829
  code: "credential_unavailable"
50079
50830
  }
50080
50831
  };
@@ -51503,6 +52254,9 @@ function createModelProxy(deps) {
51503
52254
  function harnessFor(provider) {
51504
52255
  return defaultHarnessForProvider(provider);
51505
52256
  }
52257
+ function custodyHarnessFor(route) {
52258
+ return route.harness !== void 0 && isBrokerableHarness(route.harness) ? route.harness : defaultHarnessForProvider(route.provider);
52259
+ }
51506
52260
  function correlate(req, route, body2) {
51507
52261
  const harness = route.harness ?? harnessFor(route.provider);
51508
52262
  const explicit = header(req, TACHO_MODEL_SESSION_HEADER);
@@ -51622,7 +52376,8 @@ function createModelProxy(deps) {
51622
52376
  function resolveCredential(req, route) {
51623
52377
  const broker = deps.credentials;
51624
52378
  const presented = presentedCredential(req);
51625
- const brokered = broker !== void 0 && route.harness !== "stella" && broker.brokered(route.provider) && route.upstream === "chatgpt" === (broker.kind?.(route.provider) === "chatgpt");
52379
+ const owner = custodyHarnessFor(route);
52380
+ const brokered = broker !== void 0 && broker.brokered(route.provider, owner) && route.upstream === "chatgpt" === (broker.kind?.(route.provider, owner) === "chatgpt");
51626
52381
  if (broker === void 0 || !brokered) {
51627
52382
  if (presented !== void 0 && looksLikeRunToken(presented.value)) {
51628
52383
  const claims = peekRunTokenClaims(presented.value);
@@ -51666,7 +52421,17 @@ function createModelProxy(deps) {
51666
52421
  }
51667
52422
  };
51668
52423
  }
51669
- const held2 = broker.custody(route.provider);
52424
+ if ((owner === "stella" || verdict.claims.harness === "stella") && verdict.claims.harness !== owner) {
52425
+ return {
52426
+ basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
52427
+ claims: verdict.claims,
52428
+ refusal: {
52429
+ code: "run_token_mismatch",
52430
+ message: CREDENTIAL_MESSAGES.run_token_mismatch
52431
+ }
52432
+ };
52433
+ }
52434
+ const held2 = broker.custody(route.provider, owner);
51670
52435
  if (held2 === void 0) {
51671
52436
  return {
51672
52437
  basis: TACHO_CREDENTIAL_GATEWAY_BROKERED,
@@ -53032,7 +53797,7 @@ var init_model_proxy = __esm({
53032
53797
  run_token_expired: "The run token this call carried has expired. The harness fetches a new one from the Oxagen gateway on its next attempt.",
53033
53798
  run_token_mismatch: "The run token this call carried was issued for another host or another model provider. Run `oxagen agent enroll` again on this machine so the harness fetches run tokens from this host's gateway.",
53034
53799
  run_token_required: "This machine brokers model credentials through the Oxagen gateway, and this call carried none. Run `oxagen agent enroll` again to point the harness at the gateway's run tokens.",
53035
- foreign_credential: "This machine brokers model credentials through the Oxagen gateway, and this call brought its own. Unset the provider's API key (ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN or OPENAI_API_KEY) in the shell and in the env block of any .claude/settings.json or .claude/settings.local.json the harness reads; the gateway supplies the credential.",
53800
+ foreign_credential: "This machine brokers model credentials through the Oxagen gateway, and this call brought its own. Unset the provider's API key (ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN or OPENAI_API_KEY) in the shell and in the env block of any .claude/settings.json or .claude/settings.local.json the harness reads; the gateway supplies the credential. Stella reads ANTHROPIC_API_KEY from the shell ahead of its stella.toml, so unset it there too.",
53036
53801
  credential_unavailable: "The run token is valid, but the Oxagen gateway holds no credential for this model provider. Run `oxagen agent enroll` again, or `oxagen credential status` to see what is in custody."
53037
53802
  };
53038
53803
  }
@@ -54720,10 +55485,11 @@ function cloudGatewayCredentialStore(disk, runToken) {
54720
55485
  kind: provider === "anthropic" ? "api_key" : "bearer",
54721
55486
  secret: runToken
54722
55487
  });
55488
+ const ours = (provider, harness) => harness === void 0 || harness === defaultHarnessForProvider(provider);
54723
55489
  return {
54724
55490
  ...disk,
54725
- has: () => true,
54726
- read: (provider) => held(provider),
55491
+ has: (provider, harness) => ours(provider, harness),
55492
+ read: (provider, harness) => ours(provider, harness) ? held(provider) : void 0,
54727
55493
  take: () => {
54728
55494
  throw new Error(
54729
55495
  "A sandbox's local gateway holds no model credential. The cloud gateway attaches the operator's key."
@@ -55091,9 +55857,9 @@ async function initializeDaemon(options, server, port, ready) {
55091
55857
  ) : diskCredentialStore;
55092
55858
  const runTokenKey = () => readRunTokenKey(paths.runTokenKey) ?? loadOrCreateRunTokenKey(paths.runTokenKey).key;
55093
55859
  runTokenKey();
55094
- function inCustody(provider) {
55860
+ function inCustody(provider, harness) {
55095
55861
  try {
55096
- return credentialStore.has(provider);
55862
+ return harness !== void 0 ? credentialStore.has(provider, harness) : credentialStore.status().some((c) => c.provider === provider) || credentialStore.has(provider);
55097
55863
  } catch (error) {
55098
55864
  log(
55099
55865
  `credential store unreadable: ${error instanceof Error ? error.message : String(error)}; check ${paths.credentials} and ${paths.credentialsKey}, then run \`oxagen credential status\``
@@ -55187,6 +55953,11 @@ async function initializeDaemon(options, server, port, ready) {
55187
55953
  env: options.workOrders?.env ?? process.env,
55188
55954
  now,
55189
55955
  log,
55956
+ // Bound late: `api` is built below, and a start runs only after a poll.
55957
+ issueRunToken: (input) => api.issueRunToken?.(input) ?? {
55958
+ status: 403,
55959
+ body: { error: "no issuer", code: "credential_unavailable" }
55960
+ },
55190
55961
  ...options.workOrders?.spawn !== void 0 ? { spawn: options.workOrders.spawn } : {},
55191
55962
  ...options.workOrders?.isAlive !== void 0 ? { isAlive: options.workOrders.isAlive } : {},
55192
55963
  ...options.workOrders?.findCheckout !== void 0 ? { findCheckout: options.workOrders.findCheckout } : {}
@@ -56958,17 +57729,19 @@ async function initializeDaemon(options, server, port, ready) {
56958
57729
  ...options.beforeForward !== void 0 ? { beforeForward: options.beforeForward } : {},
56959
57730
  ...options.pauseHold,
56960
57731
  credentials: {
56961
- brokered: (provider) => inCustody(provider),
56962
- kind: (provider) => {
57732
+ brokered: (provider, harness) => inCustody(provider, harness),
57733
+ kind: (provider, harness) => {
56963
57734
  try {
56964
- return credentialStore.status().find((entry2) => entry2.provider === provider)?.kind;
57735
+ return credentialStore.status().find(
57736
+ (entry2) => entry2.provider === provider && entry2.harness === harness
57737
+ )?.kind;
56965
57738
  } catch {
56966
57739
  return void 0;
56967
57740
  }
56968
57741
  },
56969
- custody: (provider) => {
57742
+ custody: (provider, harness) => {
56970
57743
  try {
56971
- return credentialStore.read(provider);
57744
+ return credentialStore.read(provider, harness);
56972
57745
  } catch (error) {
56973
57746
  log(
56974
57747
  `credential store unreadable: ${error instanceof Error ? error.message : String(error)}; check ${paths.credentials} and ${paths.credentialsKey}, then run \`oxagen credential status\``
@@ -57669,8 +58442,11 @@ async function initializeDaemon(options, server, port, ready) {
57669
58442
  let lastStaticRenewalAt = 0;
57670
58443
  let lastLoginRefreshAt = 0;
57671
58444
  async function renewStaticTokens() {
57672
- if (!host.harnesses.includes("codex")) return;
57673
- if (now() - lastLoginRefreshAt >= CHATGPT_LOGIN_CHECK_MS) {
58445
+ const harnesses = STATIC_TOKEN_HARNESSES.filter(
58446
+ (h) => host.harnesses.includes(h)
58447
+ );
58448
+ if (harnesses.length === 0) return;
58449
+ if (harnesses.includes("codex") && now() - lastLoginRefreshAt >= CHATGPT_LOGIN_CHECK_MS) {
57674
58450
  lastLoginRefreshAt = now();
57675
58451
  try {
57676
58452
  const refreshed = await refreshChatGptCustody(
@@ -57692,44 +58468,17 @@ async function initializeDaemon(options, server, port, ready) {
57692
58468
  }
57693
58469
  if (now() - lastStaticRenewalAt < STATIC_TOKEN_RENEWAL_CHECK_MS) return;
57694
58470
  lastStaticRenewalAt = now();
57695
- if (host.host_status !== "active" || !inCustody("openai")) return;
57696
- const home = options.home ?? homedir7();
57697
- let state;
57698
- try {
57699
- state = (await readModelCredentialState({ home, harnesses: ["codex"] })).harnesses[0];
57700
- } catch (error) {
57701
- log(
57702
- `static token renewal: cannot read Codex's auth file: ${error instanceof Error ? error.message : String(error)}`
57703
- );
57704
- return;
57705
- }
57706
- if (state === void 0 || !state.brokered) return;
57707
- const current = readCodexApiKeyMember(home);
57708
- if (staticTokenStillGood(current, host, paths.runTokenKey, now())) return;
57709
- const issued = api.issueRunToken?.({
57710
- harness: "codex",
57711
- placement: "static"
57712
- });
57713
- if (issued === void 0 || issued.status !== 200) {
57714
- log(
57715
- `static token renewal: not issued (${issued?.body.error ?? "no issuer"})`
57716
- );
57717
- return;
57718
- }
57719
- try {
57720
- await applyModelCredentials({
57721
- home,
57722
- harnesses: ["codex"],
57723
- staticTokens: { codex: issued.body.token }
58471
+ for (const harness of harnesses)
58472
+ await renewStaticToken(harness, {
58473
+ home: options.home ?? homedir7(),
58474
+ stellaHome: dirname24(paths.stellaToml),
58475
+ host,
58476
+ runTokenKeyPath: paths.runTokenKey,
58477
+ now,
58478
+ inCustody,
58479
+ issue: api.issueRunToken,
58480
+ log
57724
58481
  });
57725
- log(
57726
- `renewed Codex's static run token ${issued.body.token_id}, expires ${issued.body.expires_at}`
57727
- );
57728
- } catch (error) {
57729
- log(
57730
- `static token renewal: could not write Codex's auth file: ${error instanceof Error ? error.message : String(error)}`
57731
- );
57732
- }
57733
58482
  }
57734
58483
  async function tick() {
57735
58484
  await controlTick();
@@ -57929,6 +58678,7 @@ var init_daemon = __esm({
57929
58678
  init_claude_code_mcp_writer();
57930
58679
  init_model_base_url();
57931
58680
  init_model_credential();
58681
+ init_static_token_renewal();
57932
58682
  init_chatgpt_refresh();
57933
58683
  init_run_token();
57934
58684
  init_process_scan();
@@ -60294,7 +61044,7 @@ var init_package = __esm({
60294
61044
  "package.json"() {
60295
61045
  package_default = {
60296
61046
  name: "@oxagen/cli",
60297
- version: "2.1.4",
61047
+ version: "2.1.5-22",
60298
61048
  description: "Oxagen governance CLI \u2014 spend, lineage, grounding, credentials, and audit (agentic coding lives in the stella CLI)",
60299
61049
  type: "module",
60300
61050
  main: "./dist/index.js",
@@ -66533,13 +67283,13 @@ function ruleIssues(rule, value) {
66533
67283
  }
66534
67284
  }
66535
67285
  function testJson(test) {
66536
- const matches = {
67286
+ const matches2 = {
66537
67287
  properties: {
66538
67288
  [test.field]: { const: "is" in test ? test.is : test.isNot }
66539
67289
  },
66540
67290
  required: [test.field]
66541
67291
  };
66542
- return "is" in test ? matches : { not: matches };
67292
+ return "is" in test ? matches2 : { not: matches2 };
66543
67293
  }
66544
67294
  function nested(path2, leaf) {
66545
67295
  return path2.reduceRight(
@@ -77639,10 +78389,10 @@ function verifyRunExport(files2) {
77639
78389
  const shipped = parseJson2(redactionsText, "redactions", checks);
77640
78390
  if (shipped !== null) {
77641
78391
  const recomputed = summarizeRunExportRedactions(frames);
77642
- const matches = digestJcs(recomputed) === digestJcs(shipped);
78392
+ const matches2 = digestJcs(recomputed) === digestJcs(shipped);
77643
78393
  hold(
77644
78394
  "redactions",
77645
- matches,
78395
+ matches2,
77646
78396
  `${recomputed.redacted_total} redaction(s) summarised`,
77647
78397
  "redactions.json does not match what the frames record"
77648
78398
  );
@@ -79282,7 +80032,7 @@ var init_run_shared = __esm({
79282
80032
  });
79283
80033
 
79284
80034
  // ../../packages/oxagen/src/contracts/run.list.ts
79285
- var runPublicIdSchema, frameSeqSchema, runSourceSchema, runStatusSchema, runOutcomeSchema, runCostSchema, replayGradeSchema, generatedRunSummarySchema, editedRunSummarySchema, runSummarySchema, operatorKindSchema, RUN_OPERATOR_ROLES, runOperatorRoleSchema, runModelSchema, runMachineSnapshotSchema, runMachineSchema, COMMAND_BLOCKS, STEER_BLOCKS, HOST_POLL_WINDOW_MS, RUN_PULL_REQUEST_MAX, runPullRequestSchema, runDiffSchema, runAttributionSchema, RUN_PULL_REQUEST_FILTERS, RUN_PAUSE_STATES, runPauseSchema, RUN_REPLAY_FILTERS, RUN_SORT_KEYS, RUN_LIST_TOTAL_BOUND, RUN_LABEL_MAX, runItemSchema, runList;
80035
+ var runPublicIdSchema, frameSeqSchema, runSourceSchema, runStatusSchema, runOutcomeSchema, runCostSchema, replayGradeSchema, generatedRunSummarySchema, editedRunSummarySchema, runSummarySchema, operatorKindSchema, RUN_OPERATOR_ROLES, runOperatorRoleSchema, runModelSchema, runMachineSnapshotSchema, runMachineSchema, COMMAND_BLOCKS, HOST_POLL_WINDOW_MS, RUN_PULL_REQUEST_MAX, runPullRequestSchema, runDiffSchema, runAttributionSchema, RUN_PULL_REQUEST_FILTERS, RUN_PAUSE_STATES, runPauseSchema, RUN_REPLAY_FILTERS, RUN_SORT_KEYS, RUN_LIST_TOTAL_BOUND, RUN_LABEL_MAX, runItemSchema, runList;
79286
80036
  var init_run_list = __esm({
79287
80037
  "../../packages/oxagen/src/contracts/run.list.ts"() {
79288
80038
  "use strict";
@@ -79368,7 +80118,6 @@ var init_run_list = __esm({
79368
80118
  "host_offline",
79369
80119
  "daemon_chain"
79370
80120
  ];
79371
- STEER_BLOCKS = ["no_prompt_carrier"];
79372
80121
  HOST_POLL_WINDOW_MS = 5 * 6e4;
79373
80122
  RUN_PULL_REQUEST_MAX = 10;
79374
80123
  runPullRequestSchema = external_exports.object({
@@ -79628,13 +80377,6 @@ var init_run_list = __esm({
79628
80377
  * resume, steer or cancel on it. Absent reads as false.
79629
80378
  */
79630
80379
  inApp: external_exports.boolean().optional(),
79631
- /**
79632
- * Why a steer or message cannot reach this run when the other commands
79633
- * can, or null when it can. `dispatch_command` refuses on the same rule
79634
- * (`steerBlockOf`). Null for a ledger run. Optional, read like
79635
- * `commandBlock`: absent means not known.
79636
- */
79637
- steerBlock: external_exports.enum(STEER_BLOCKS).nullable().optional(),
79638
80380
  /**
79639
80381
  * The model the run ended on, falling back to the one it started on; null
79640
80382
  * when the store recorded no model, which is every ledger run.
@@ -81095,7 +81837,7 @@ function opposite(a, b3) {
81095
81837
  }
81096
81838
  function similarStatements(entries2) {
81097
81839
  const bySize = entries2.map(compared).sort((a, b3) => a.set.size - b3.set.size);
81098
- const matches = [];
81840
+ const matches2 = [];
81099
81841
  for (let i2 = 0; i2 < bySize.length; i2 += 1) {
81100
81842
  const a = bySize[i2];
81101
81843
  for (let j = i2 + 1; j < bySize.length; j += 1) {
@@ -81103,14 +81845,14 @@ function similarStatements(entries2) {
81103
81845
  if (a.normalized !== b3.normalized && a.set.size < b3.set.size * NEAR_DUPLICATE_SIMILARITY)
81104
81846
  break;
81105
81847
  if (!alike(a, b3)) continue;
81106
- matches.push({
81848
+ matches2.push({
81107
81849
  a: a.entry,
81108
81850
  b: b3.entry,
81109
81851
  opposite: opposite(a.entry, b3.entry)
81110
81852
  });
81111
81853
  }
81112
81854
  }
81113
- return matches;
81855
+ return matches2;
81114
81856
  }
81115
81857
  function active(lineage, kind, effect, file, statement) {
81116
81858
  return { lineage, kind, effect, file, statement };
@@ -89802,17 +90544,17 @@ function registryLaunch({
89802
90544
  ]
89803
90545
  };
89804
90546
  }
89805
- const matches = (entry2.server.packages ?? []).filter(
90547
+ const matches2 = (entry2.server.packages ?? []).filter(
89806
90548
  (candidate) => candidate.registryType === type
89807
90549
  );
89808
- const [pkg] = matches;
89809
- if (pkg === void 0 || matches.length > 1) {
90550
+ const [pkg] = matches2;
90551
+ if (pkg === void 0 || matches2.length > 1) {
89810
90552
  return {
89811
90553
  ok: false,
89812
90554
  problems: [
89813
90555
  {
89814
90556
  field: "source.registry_type",
89815
- message: pkg === void 0 ? `the entry lists no ${type} package` : `the entry lists ${matches.length} ${type} packages, so source.registry_type cannot pick one`
90557
+ message: pkg === void 0 ? `the entry lists no ${type} package` : `the entry lists ${matches2.length} ${type} packages, so source.registry_type cannot pick one`
89816
90558
  }
89817
90559
  ]
89818
90560
  };
@@ -95833,13 +96575,13 @@ function timestampFromJson(timestamp2, json2) {
95833
96575
  if (typeof json2 !== "string") {
95834
96576
  throw new Error(`cannot decode message ${timestamp2.$typeName} from JSON: ${formatVal(json2)}`);
95835
96577
  }
95836
- const matches = json2.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.([0-9]{1,9}))?(?:Z|([+-][0-9][0-9]:[0-9][0-9]))$/);
95837
- if (!matches) {
96578
+ const matches2 = json2.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.([0-9]{1,9}))?(?:Z|([+-][0-9][0-9]:[0-9][0-9]))$/);
96579
+ if (!matches2) {
95838
96580
  throw new Error(`cannot decode message ${timestamp2.$typeName} from JSON: invalid RFC 3339 string`);
95839
96581
  }
95840
96582
  const ms = Date.parse(
95841
96583
  // biome-ignore format: want this to read well
95842
- matches[1] + "-" + matches[2] + "-" + matches[3] + "T" + matches[4] + ":" + matches[5] + ":" + matches[6] + (matches[8] ? matches[8] : "Z")
96584
+ matches2[1] + "-" + matches2[2] + "-" + matches2[3] + "T" + matches2[4] + ":" + matches2[5] + ":" + matches2[6] + (matches2[8] ? matches2[8] : "Z")
95843
96585
  );
95844
96586
  if (Number.isNaN(ms)) {
95845
96587
  throw new Error(`cannot decode message ${timestamp2.$typeName} from JSON: invalid RFC 3339 string`);
@@ -95849,8 +96591,8 @@ function timestampFromJson(timestamp2, json2) {
95849
96591
  }
95850
96592
  timestamp2.seconds = protoInt64.parse(ms / 1e3);
95851
96593
  timestamp2.nanos = 0;
95852
- if (matches[7]) {
95853
- timestamp2.nanos = parseInt("1" + matches[7] + "0".repeat(9 - matches[7].length)) - 1e9;
96594
+ if (matches2[7]) {
96595
+ timestamp2.nanos = parseInt("1" + matches2[7] + "0".repeat(9 - matches2[7].length)) - 1e9;
95854
96596
  }
95855
96597
  }
95856
96598
  function durationFromJson(duration, json2) {
@@ -106166,18 +106908,18 @@ function lintRegistry(folder, report) {
106166
106908
  function lintPackage(source, entry2, type, report) {
106167
106909
  const other = otherChoice(source, entry2, type);
106168
106910
  const packages = entry2.server.packages ?? [];
106169
- const matches = packages.filter(
106911
+ const matches2 = packages.filter(
106170
106912
  (candidate) => candidate.registryType === type
106171
106913
  );
106172
- const [pkg] = matches;
106173
- if (pkg === void 0 || matches.length > 1) {
106914
+ const [pkg] = matches2;
106915
+ if (pkg === void 0 || matches2.length > 1) {
106174
106916
  const bundle2 = packages.some(
106175
106917
  (candidate) => candidate.registryType === "mcpb"
106176
106918
  );
106177
106919
  report("package_cannot_run", {
106178
106920
  tool: void 0,
106179
106921
  field: "source.registry_type",
106180
- message: pkg === void 0 ? `The catalog entry lists no ${type} package${bundle2 ? ", and the local gateway cannot run its mcpb bundle" : ""}, so nothing could start the server.` : `The catalog entry lists ${matches.length} ${type} packages, so source.registry_type cannot pick one.`,
106922
+ message: pkg === void 0 ? `The catalog entry lists no ${type} package${bundle2 ? ", and the local gateway cannot run its mcpb bundle" : ""}, so nothing could start the server.` : `The catalog entry lists ${matches2.length} ${type} packages, so source.registry_type cannot pick one.`,
106181
106923
  fix: other
106182
106924
  });
106183
106925
  return;
@@ -138560,10 +139302,15 @@ function buildProgram() {
138560
139302
  });
138561
139303
  work.command("claim").description(
138562
139304
  "From inside a work order's run, claim that the pushed head commit meets one criterion of the brief. A person still decides."
138563
- ).argument("<criterion>", "The criterion's id from the brief, such as c1").requiredOption("--text <text>", "How the commit meets the criterion").action(async (criterionId, opts) => {
138564
- const { handleWorkClaim: handleWorkClaim2 } = await Promise.resolve().then(() => (init_tacho(), tacho_exports));
138565
- process.exitCode = await handleWorkClaim2(criterionId, opts);
138566
- });
139305
+ ).argument("<criterion>", "The criterion's id from the brief, such as c1").requiredOption("--text <text>", "How the commit meets the criterion").option(
139306
+ "--work-order <id>",
139307
+ "The work order of a batch item, as the first prompt lists it"
139308
+ ).option("--item <id>", "The work item of that work order").action(
139309
+ async (criterionId, opts) => {
139310
+ const { handleWorkClaim: handleWorkClaim2 } = await Promise.resolve().then(() => (init_tacho(), tacho_exports));
139311
+ process.exitCode = await handleWorkClaim2(criterionId, opts);
139312
+ }
139313
+ );
138567
139314
  const env = program2.command("env").description("Manage workspace environments");
138568
139315
  env.command("list").description("List environments in the active workspace").option("--json", "Output JSON").action(async (opts) => {
138569
139316
  const { handleEnvList: handleEnvList2 } = await Promise.resolve().then(() => (init_env2(), env_exports));