@whittlelabs/sifter 0.31.0 → 0.32.0

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 (3) hide show
  1. package/bin.js +263 -35
  2. package/bin.js.map +4 -4
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -10263,6 +10263,54 @@ var require_json_answer = __commonJS({
10263
10263
  }
10264
10264
  });
10265
10265
 
10266
+ // ../../packages/shuttle/dist/executors/models.js
10267
+ var require_models = __commonJS({
10268
+ "../../packages/shuttle/dist/executors/models.js"(exports2) {
10269
+ "use strict";
10270
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
10271
+ return mod && mod.__esModule ? mod : { "default": mod };
10272
+ };
10273
+ Object.defineProperty(exports2, "__esModule", { value: true });
10274
+ exports2.LIST_MODELS_TIMEOUT_MS = exports2.CLAUDE_CODE_EFFORTS = exports2.REASONING_EFFORTS = void 0;
10275
+ exports2.parseReasoningEffort = parseReasoningEffort;
10276
+ exports2.hintedEffort = hintedEffort;
10277
+ exports2.runListing = runListing;
10278
+ var os_1 = __importDefault(require("os"));
10279
+ var agent_process_1 = require_agent_process();
10280
+ exports2.REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
10281
+ exports2.CLAUDE_CODE_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
10282
+ function parseReasoningEffort(value, levels) {
10283
+ if (value === void 0)
10284
+ return void 0;
10285
+ if (!levels.includes(value)) {
10286
+ throw new Error(`"reasoningEffort" must be one of ${levels.join(", ")}`);
10287
+ }
10288
+ return value;
10289
+ }
10290
+ function hintedEffort(hints) {
10291
+ const effort = hints.effort;
10292
+ return typeof effort === "string" && /^[a-z]+$/.test(effort) ? effort : void 0;
10293
+ }
10294
+ exports2.LIST_MODELS_TIMEOUT_MS = 15e3;
10295
+ async function runListing(bin, args, signal, env) {
10296
+ try {
10297
+ const run = await (0, agent_process_1.runAgentProcess)({
10298
+ bin,
10299
+ args,
10300
+ cwd: os_1.default.tmpdir(),
10301
+ ...env ? { env } : {},
10302
+ idleTimeoutMs: exports2.LIST_MODELS_TIMEOUT_MS,
10303
+ toolName: bin,
10304
+ signal
10305
+ });
10306
+ return run.spawnError || run.code !== 0 ? null : run.stdout;
10307
+ } catch {
10308
+ return null;
10309
+ }
10310
+ }
10311
+ }
10312
+ });
10313
+
10266
10314
  // ../../packages/shuttle/dist/executors/workspace-telemetry.js
10267
10315
  var require_workspace_telemetry = __commonJS({
10268
10316
  "../../packages/shuttle/dist/executors/workspace-telemetry.js"(exports2) {
@@ -10499,6 +10547,7 @@ var require_claude_code = __commonJS({
10499
10547
  var progress_1 = require_progress();
10500
10548
  var agent_process_1 = require_agent_process();
10501
10549
  var json_answer_1 = require_json_answer();
10550
+ var models_1 = require_models();
10502
10551
  var workspace_telemetry_1 = require_workspace_telemetry();
10503
10552
  var apply_1 = require_apply();
10504
10553
  var DEFAULT_TIMEOUT = 6e5;
@@ -10544,6 +10593,12 @@ var require_claude_code = __commonJS({
10544
10593
  const subtype = meta.subtype && meta.subtype !== "success" ? `: ${meta.subtype}` : "";
10545
10594
  return `${fallback}${subtype}${status}`;
10546
10595
  }
10596
+ var CLAUDE_CODE_MODELS = [
10597
+ { id: "opus", label: "Claude Opus (latest)", efforts: [...models_1.CLAUDE_CODE_EFFORTS] },
10598
+ { id: "fable", label: "Claude Fable (latest)", efforts: [...models_1.CLAUDE_CODE_EFFORTS] },
10599
+ { id: "sonnet", label: "Claude Sonnet (latest)", efforts: [...models_1.CLAUDE_CODE_EFFORTS] },
10600
+ { id: "haiku", label: "Claude Haiku (latest)", efforts: [...models_1.CLAUDE_CODE_EFFORTS] }
10601
+ ];
10547
10602
  var READINESS_PROMPT = "Reply with the single word: ok";
10548
10603
  var READINESS_TIMEOUT_MS = 12e4;
10549
10604
  var INSTALL_REMEDY = "install Claude Code (npm install -g @anthropic-ai/claude-code) where this process can find it on PATH";
@@ -10624,6 +10679,7 @@ var require_claude_code = __commonJS({
10624
10679
  const childEnv = buildChildEnv(modelKey, configDir);
10625
10680
  const claudeHints = (0, loom_1.pickProviderHints)(spec, "claude-code");
10626
10681
  const hintedModel = typeof claudeHints.model === "string" && claudeHints.model.length > 0 ? claudeHints.model : void 0;
10682
+ const effort = (0, models_1.hintedEffort)(claudeHints);
10627
10683
  if (!workspace) {
10628
10684
  promptOnlyScratch = await fs_1.promises.mkdtemp(path_1.default.join(tmpBase, "shuttle-cc-run-"));
10629
10685
  }
@@ -10641,7 +10697,8 @@ var require_claude_code = __commonJS({
10641
10697
  // Local mode is the operator's own login: a failure of THAT is the
10642
10698
  // machine's, and the runner pauses on it. With a revealed key it is one
10643
10699
  // customer's, and only their job fails.
10644
- modelKey === null
10700
+ modelKey === null,
10701
+ effort
10645
10702
  );
10646
10703
  if (workspace) {
10647
10704
  response.outputs.push(...(0, workspace_telemetry_1.workspaceOutputs)(workspace, runCwd, readPaths, toolInputs));
@@ -10677,7 +10734,7 @@ var require_claude_code = __commonJS({
10677
10734
  outputLabel: handle.outputLabel,
10678
10735
  outputSchema: handle.outputSchema
10679
10736
  };
10680
- const { response } = await this.runClaude(followUp, spec, scratch, handle.childEnv, signal, handle.model, null, handle.sessionId, `${handle.sessionId}-repair`, handle.childEnv === void 0);
10737
+ const { response } = await this.runClaude(followUp, spec, scratch, handle.childEnv, signal, handle.model, null, handle.sessionId, `${handle.sessionId}-repair`, handle.childEnv === void 0, handle.effort);
10681
10738
  return response;
10682
10739
  } finally {
10683
10740
  await fs_1.promises.rm(scratch, { recursive: true, force: true }).catch(() => {
@@ -10723,9 +10780,14 @@ var require_claude_code = __commonJS({
10723
10780
  const said = meta?.detail ?? (0, agent_process_1.firstLine)(run.stderr) ?? (0, agent_process_1.firstLine)(run.stdout);
10724
10781
  return { ready: false, reason: said ?? `the claude CLI exited with code ${run.code}` };
10725
10782
  }
10726
- async runClaude(prompt, spec, cwd, childEnv, signal, modelOverride, reporter, resumeSessionId, transcriptName, local = false) {
10783
+ /** The CLI's aliases (see CLAUDE_CODE_MODELS); nothing to ask, so nothing can fail. */
10784
+ async listModels() {
10785
+ return CLAUDE_CODE_MODELS;
10786
+ }
10787
+ async runClaude(prompt, spec, cwd, childEnv, signal, modelOverride, reporter, resumeSessionId, transcriptName, local = false, effortOverride) {
10727
10788
  const outputLabel = spec.outputLabel;
10728
10789
  const model = modelOverride ?? this.config.model;
10790
+ const effort = effortOverride ?? this.config.reasoningEffort;
10729
10791
  const allowedTools = local ? this.config.allowedTools : checkoutScopedTools(this.config.allowedTools);
10730
10792
  if (!local && this.config.claudeSettings === "user") {
10731
10793
  throw new Error("claude-code: `claudeSettings: user` is for an installed Sifter whose login lives in Claude Code settings; a hosted run authenticates with the key it is handed and loads no settings");
@@ -10739,6 +10801,8 @@ var require_claude_code = __commonJS({
10739
10801
  }
10740
10802
  if (model)
10741
10803
  args.push("--model", model);
10804
+ if (effort)
10805
+ args.push("--effort", effort);
10742
10806
  if (this.config.maxTurns !== void 0)
10743
10807
  args.push("--max-turns", String(this.config.maxTurns));
10744
10808
  if (allowedTools && allowedTools.length > 0) {
@@ -10810,7 +10874,9 @@ var require_claude_code = __commonJS({
10810
10874
  parsed,
10811
10875
  usage: {
10812
10876
  aiProvider: "claude-code",
10813
- ...model ? { aiModel: model } : {},
10877
+ // The model that ran (an alias resolved), over the one asked for.
10878
+ ...stream.model ?? model ? { aiModel: stream.model ?? model } : {},
10879
+ ...effort ? { aiEffort: effort } : {},
10814
10880
  ...stream.usage ?? {}
10815
10881
  },
10816
10882
  // In-session repair handle (ADR 0017): present only when the run
@@ -10819,6 +10885,7 @@ var require_claude_code = __commonJS({
10819
10885
  continuation: {
10820
10886
  sessionId: stream.sessionId,
10821
10887
  model,
10888
+ effort,
10822
10889
  childEnv,
10823
10890
  outputLabel,
10824
10891
  outputSchema: spec.outputSchema
@@ -10843,6 +10910,9 @@ var require_claude_code = __commonJS({
10843
10910
  throw new Error('"model" must be a string');
10844
10911
  validated.model = config.model;
10845
10912
  }
10913
+ const effort = (0, models_1.parseReasoningEffort)(config.reasoningEffort, models_1.CLAUDE_CODE_EFFORTS);
10914
+ if (effort)
10915
+ validated.reasoningEffort = effort;
10846
10916
  if (config.maxTurns !== void 0) {
10847
10917
  if (typeof config.maxTurns !== "number" || !Number.isInteger(config.maxTurns) || config.maxTurns < 1) {
10848
10918
  throw new Error('"maxTurns" must be a positive integer');
@@ -10917,6 +10987,7 @@ var require_claude_code = __commonJS({
10917
10987
  let resultMeta = null;
10918
10988
  let structuredOutput = null;
10919
10989
  let sessionId = null;
10990
+ let model = null;
10920
10991
  const toolInputs = [];
10921
10992
  for (const line of stdout.split("\n")) {
10922
10993
  const trimmed = line.trim();
@@ -10930,7 +11001,9 @@ var require_claude_code = __commonJS({
10930
11001
  }
10931
11002
  if (typeof event.session_id === "string")
10932
11003
  sessionId = event.session_id;
10933
- if (event.type === "assistant") {
11004
+ if (event.type === "system" && event.subtype === "init" && typeof event.model === "string" && event.model) {
11005
+ model = event.model;
11006
+ } else if (event.type === "assistant") {
10934
11007
  const content = event.message?.content;
10935
11008
  if (!Array.isArray(content))
10936
11009
  continue;
@@ -10971,7 +11044,7 @@ var require_claude_code = __commonJS({
10971
11044
  };
10972
11045
  }
10973
11046
  }
10974
- return { finalText, readPaths, usage, resultMeta, structuredOutput, sessionId, toolInputs };
11047
+ return { finalText, readPaths, usage, resultMeta, structuredOutput, sessionId, model, toolInputs };
10975
11048
  }
10976
11049
  }
10977
11050
  });
@@ -11142,6 +11215,7 @@ var require_codex = __commonJS({
11142
11215
  exports2.resolveCodex = resolveCodex;
11143
11216
  exports2.codexReviewArgs = codexReviewArgs;
11144
11217
  exports2.parseCodexEvents = parseCodexEvents;
11218
+ exports2.parseCodexModels = parseCodexModels;
11145
11219
  exports2.createCodexExecutor = createCodexExecutor;
11146
11220
  var fs_1 = require("fs");
11147
11221
  var os_1 = __importDefault(require("os"));
@@ -11153,6 +11227,7 @@ var require_codex = __commonJS({
11153
11227
  var agent_process_1 = require_agent_process();
11154
11228
  var agent_common_1 = require_agent_common();
11155
11229
  var json_answer_1 = require_json_answer();
11230
+ var models_1 = require_models();
11156
11231
  var strict_schema_1 = require_strict_schema();
11157
11232
  var workspace_telemetry_1 = require_workspace_telemetry();
11158
11233
  var apply_1 = require_apply();
@@ -11226,7 +11301,6 @@ var require_codex = __commonJS({
11226
11301
  ...DISABLED_FEATURES.flatMap((f) => ["--disable", f])
11227
11302
  ];
11228
11303
  }
11229
- var REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
11230
11304
  var CODEX_FAULTS = {
11231
11305
  installRemedy: "install Codex (brew install codex, or npm install -g @openai/codex) where this process can find it on PATH",
11232
11306
  loginRemedy: "run `codex login` and sign in (or `codex login --with-api-key`)",
@@ -11260,6 +11334,7 @@ var require_codex = __commonJS({
11260
11334
  const workspace = ctx?.workspace ?? null;
11261
11335
  const hints = (0, loom_1.pickProviderHints)(spec, "codex");
11262
11336
  const model = typeof hints.model === "string" && hints.model.length > 0 ? hints.model : this.config.model;
11337
+ const effort = (0, models_1.hintedEffort)(hints) ?? this.config.reasoningEffort;
11263
11338
  const reporter = spec.progressChannel && ctx?.reportProgress ? new progress_1.ProgressReporter((frames) => ctx.reportProgress(spec.progressChannel, frames)) : null;
11264
11339
  const codex = requireCodex();
11265
11340
  const scratch = await fs_1.promises.mkdtemp(path_1.default.join(this.tmpBase(), "shuttle-codex-"));
@@ -11277,6 +11352,7 @@ var require_codex = __commonJS({
11277
11352
  cwd: runCwd,
11278
11353
  scratch,
11279
11354
  model,
11355
+ effort,
11280
11356
  signal,
11281
11357
  reporter,
11282
11358
  transcriptName: dispatch.job.id
@@ -11319,6 +11395,7 @@ var require_codex = __commonJS({
11319
11395
  cwd: scratch,
11320
11396
  scratch,
11321
11397
  model: handle.model,
11398
+ effort: handle.effort,
11322
11399
  signal,
11323
11400
  reporter: null,
11324
11401
  transcriptName: `${handle.threadId}-repair`
@@ -11373,11 +11450,24 @@ var require_codex = __commonJS({
11373
11450
  });
11374
11451
  }
11375
11452
  }
11453
+ /**
11454
+ * The catalog `codex debug models` prints: the models codex itself offers
11455
+ * in its picker, each with the reasoning levels it supports. A `debug`
11456
+ * subcommand, so not a stable contract; any failure or unknown shape
11457
+ * reports nothing.
11458
+ */
11459
+ async listModels(signal) {
11460
+ const codex = resolveCodex();
11461
+ if (!codex)
11462
+ return [];
11463
+ const stdout = await (0, models_1.runListing)(codex.bin, ["debug", "models"], signal);
11464
+ return stdout === null ? [] : parseCodexModels(stdout);
11465
+ }
11376
11466
  tmpBase() {
11377
11467
  return this.config.tmpDir ? (0, apply_1.expandHome)(this.config.tmpDir) : os_1.default.tmpdir();
11378
11468
  }
11379
11469
  async runCodex(opts) {
11380
- const { spec, model } = opts;
11470
+ const { spec, model, effort } = opts;
11381
11471
  const args = [...opts.args];
11382
11472
  const hasSchema = spec.outputSchema && typeof spec.outputSchema === "object" && Object.keys(spec.outputSchema).length > 0;
11383
11473
  if (hasSchema) {
@@ -11387,8 +11477,8 @@ var require_codex = __commonJS({
11387
11477
  }
11388
11478
  if (model)
11389
11479
  args.push("-m", model);
11390
- if (this.config.reasoningEffort)
11391
- args.push("-c", `model_reasoning_effort="${this.config.reasoningEffort}"`);
11480
+ if (effort)
11481
+ args.push("-c", `model_reasoning_effort="${effort}"`);
11392
11482
  args.push("-");
11393
11483
  const tap = opts.reporter ? codexProgressTap(opts.reporter) : null;
11394
11484
  const run = await (0, agent_process_1.runAgentProcess)({
@@ -11435,12 +11525,14 @@ var require_codex = __commonJS({
11435
11525
  usage: {
11436
11526
  aiProvider: "codex",
11437
11527
  ...model ? { aiModel: model } : {},
11528
+ ...effort ? { aiEffort: effort } : {},
11438
11529
  ...events.usage ?? {}
11439
11530
  },
11440
11531
  ...events.threadId ? {
11441
11532
  continuation: {
11442
11533
  threadId: events.threadId,
11443
11534
  model,
11535
+ effort,
11444
11536
  outputLabel: spec.outputLabel,
11445
11537
  outputSchema: spec.outputSchema
11446
11538
  }
@@ -11544,14 +11636,40 @@ var require_codex = __commonJS({
11544
11636
  failure: turnFailure ?? (finalText === null ? lastError : null)
11545
11637
  };
11546
11638
  }
11639
+ function parseCodexModels(stdout) {
11640
+ let body;
11641
+ try {
11642
+ body = JSON.parse(stdout);
11643
+ } catch {
11644
+ return [];
11645
+ }
11646
+ const entries = body?.models;
11647
+ if (!Array.isArray(entries))
11648
+ return [];
11649
+ const listed = entries.flatMap((entry) => {
11650
+ if (!entry || typeof entry !== "object")
11651
+ return [];
11652
+ const e = entry;
11653
+ if (typeof e.slug !== "string" || e.slug.length === 0 || e.visibility !== "list")
11654
+ return [];
11655
+ const levels = Array.isArray(e.supported_reasoning_levels) ? e.supported_reasoning_levels.flatMap((level) => {
11656
+ const effort = level?.effort;
11657
+ return typeof effort === "string" && /^[a-z]+$/.test(effort) ? [effort] : [];
11658
+ }) : [];
11659
+ const model = {
11660
+ id: e.slug,
11661
+ label: typeof e.display_name === "string" && e.display_name ? e.display_name : e.slug,
11662
+ ...levels.length > 0 ? { efforts: levels } : {}
11663
+ };
11664
+ return [{ model, priority: typeof e.priority === "number" ? e.priority : Number.MAX_SAFE_INTEGER }];
11665
+ });
11666
+ return listed.sort((a, b) => a.priority - b.priority).map((entry) => entry.model);
11667
+ }
11547
11668
  function createCodexExecutor(config, capabilityId = "codex") {
11548
11669
  const validated = (0, agent_common_1.parseAgentCommonConfig)(config, capabilityId);
11549
- if (config.reasoningEffort !== void 0) {
11550
- if (!REASONING_EFFORTS.includes(config.reasoningEffort)) {
11551
- throw new Error(`"reasoningEffort" must be one of ${REASONING_EFFORTS.join(", ")}`);
11552
- }
11553
- validated.reasoningEffort = config.reasoningEffort;
11554
- }
11670
+ const effort = (0, models_1.parseReasoningEffort)(config.reasoningEffort, models_1.REASONING_EFFORTS);
11671
+ if (effort)
11672
+ validated.reasoningEffort = effort;
11555
11673
  return new CodexExecutor(validated);
11556
11674
  }
11557
11675
  }
@@ -11569,6 +11687,7 @@ var require_copilot = __commonJS({
11569
11687
  exports2.withSchema = withSchema;
11570
11688
  exports2.usageOf = usageOf;
11571
11689
  exports2.parseCopilotEvents = parseCopilotEvents;
11690
+ exports2.parseCopilotModels = parseCopilotModels;
11572
11691
  exports2.createCopilotExecutor = createCopilotExecutor;
11573
11692
  var crypto_1 = require("crypto");
11574
11693
  var fs_1 = require("fs");
@@ -11581,6 +11700,7 @@ var require_copilot = __commonJS({
11581
11700
  var agent_process_1 = require_agent_process();
11582
11701
  var agent_common_1 = require_agent_common();
11583
11702
  var json_answer_1 = require_json_answer();
11703
+ var models_1 = require_models();
11584
11704
  var workspace_telemetry_1 = require_workspace_telemetry();
11585
11705
  var apply_1 = require_apply();
11586
11706
  var REVIEW_TOOLS = ["view", "grep", "glob"];
@@ -11601,7 +11721,6 @@ var require_copilot = __commonJS({
11601
11721
  ];
11602
11722
  }
11603
11723
  var RUN_SETTINGS = { disableAllHooks: true, autoUpdate: false };
11604
- var REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
11605
11724
  var COPILOT_FAULTS = {
11606
11725
  installRemedy: "install GitHub Copilot CLI (brew install --cask copilot-cli, or npm install -g @github/copilot) where this process can find it on PATH",
11607
11726
  loginRemedy: "run `copilot login` and sign in with a GitHub account that has a Copilot seat (a COPILOT_GITHUB_TOKEN, GH_TOKEN or GITHUB_TOKEN in the environment wins over that login)",
@@ -11639,6 +11758,7 @@ var require_copilot = __commonJS({
11639
11758
  const workspace = ctx?.workspace ?? null;
11640
11759
  const hints = (0, loom_1.pickProviderHints)(spec, "copilot");
11641
11760
  const model = typeof hints.model === "string" && hints.model.length > 0 ? hints.model : this.config.model;
11761
+ const effort = (0, models_1.hintedEffort)(hints) ?? this.config.reasoningEffort;
11642
11762
  const reporter = spec.progressChannel && ctx?.reportProgress ? new progress_1.ProgressReporter((frames) => ctx.reportProgress(spec.progressChannel, frames)) : null;
11643
11763
  const runs = path_1.default.join(this.tmpBase(), "shuttle-copilot-runs");
11644
11764
  await fs_1.promises.mkdir(runs, { recursive: true, mode: 448 });
@@ -11661,6 +11781,7 @@ var require_copilot = __commonJS({
11661
11781
  home,
11662
11782
  scratch,
11663
11783
  model,
11784
+ effort,
11664
11785
  signal,
11665
11786
  reporter,
11666
11787
  transcriptName: dispatch.job.id
@@ -11669,6 +11790,7 @@ var require_copilot = __commonJS({
11669
11790
  sessionId,
11670
11791
  home,
11671
11792
  model,
11793
+ effort,
11672
11794
  outputLabel: spec.outputLabel,
11673
11795
  outputSchema: spec.outputSchema
11674
11796
  };
@@ -11706,6 +11828,7 @@ var require_copilot = __commonJS({
11706
11828
  home: handle.home,
11707
11829
  scratch,
11708
11830
  model: handle.model,
11831
+ effort: handle.effort,
11709
11832
  signal,
11710
11833
  reporter: null,
11711
11834
  transcriptName: `${handle.sessionId}-repair`
@@ -11751,17 +11874,27 @@ var require_copilot = __commonJS({
11751
11874
  });
11752
11875
  }
11753
11876
  }
11877
+ /**
11878
+ * The `model` setting's values as `copilot help config` lists them: the
11879
+ * CLI's built-in set. It does not reflect an organization's model policy,
11880
+ * so a model listed here can still be refused for one account, and that
11881
+ * review fails in copilot's own words. Each takes the CLI-wide effort list.
11882
+ */
11883
+ async listModels(signal) {
11884
+ const stdout = await (0, models_1.runListing)("copilot", ["help", "config"], signal);
11885
+ return stdout === null ? [] : parseCopilotModels(stdout);
11886
+ }
11754
11887
  tmpBase() {
11755
11888
  return this.config.tmpDir ? (0, apply_1.expandHome)(this.config.tmpDir) : os_1.default.tmpdir();
11756
11889
  }
11757
11890
  async runCopilot(opts) {
11758
- const { spec, model } = opts;
11891
+ const { spec, model, effort } = opts;
11759
11892
  const usageFile = path_1.default.join(opts.scratch, `usage-${(0, crypto_1.randomUUID)()}.json`);
11760
11893
  const args = [...opts.args, `--usage-output-file=${usageFile}`, `--log-dir=${path_1.default.join(opts.home, "logs")}`];
11761
11894
  if (model)
11762
11895
  args.push(`--model=${model}`);
11763
- if (this.config.reasoningEffort)
11764
- args.push(`--reasoning-effort=${this.config.reasoningEffort}`);
11896
+ if (effort)
11897
+ args.push(`--reasoning-effort=${effort}`);
11765
11898
  const tap = opts.reporter ? copilotProgressTap(opts.reporter) : null;
11766
11899
  const run = await withOperatorLogin(opts.home, () => (0, agent_process_1.runAgentProcess)({
11767
11900
  bin: "copilot",
@@ -11807,7 +11940,12 @@ var require_copilot = __commonJS({
11807
11940
  rawText: answerText,
11808
11941
  parsed,
11809
11942
  // The model the session actually ran, from its usage file, over the one asked for.
11810
- usage: { aiProvider: "copilot", ...model ? { aiModel: model } : {}, ...usage ?? {} }
11943
+ usage: {
11944
+ aiProvider: "copilot",
11945
+ ...model ? { aiModel: model } : {},
11946
+ ...effort ? { aiEffort: effort } : {},
11947
+ ...usage ?? {}
11948
+ }
11811
11949
  },
11812
11950
  reads: events.readPaths,
11813
11951
  toolInputs: events.toolInputs
@@ -11995,14 +12133,25 @@ ${JSON.stringify(spec.outputSchema)}
11995
12133
  }
11996
12134
  return { finalText, readPaths, toolInputs, failure: finalText === null ? lastError : null };
11997
12135
  }
12136
+ function parseCopilotModels(helpText) {
12137
+ const lines = helpText.split("\n");
12138
+ const start = lines.findIndex((line) => /^\s*`model`:/.test(line));
12139
+ if (start === -1)
12140
+ return [];
12141
+ const models = [];
12142
+ for (const line of lines.slice(start + 1)) {
12143
+ const value = /^\s+-\s+"([^"]+)"\s*$/.exec(line);
12144
+ if (!value)
12145
+ break;
12146
+ models.push({ id: value[1], label: value[1], efforts: [...models_1.REASONING_EFFORTS] });
12147
+ }
12148
+ return models;
12149
+ }
11998
12150
  function createCopilotExecutor(config, capabilityId = "copilot") {
11999
12151
  const validated = (0, agent_common_1.parseAgentCommonConfig)(config, capabilityId);
12000
- if (config.reasoningEffort !== void 0) {
12001
- if (!REASONING_EFFORTS.includes(config.reasoningEffort)) {
12002
- throw new Error(`"reasoningEffort" must be one of ${REASONING_EFFORTS.join(", ")}`);
12003
- }
12004
- validated.reasoningEffort = config.reasoningEffort;
12005
- }
12152
+ const effort = (0, models_1.parseReasoningEffort)(config.reasoningEffort, models_1.REASONING_EFFORTS);
12153
+ if (effort)
12154
+ validated.reasoningEffort = effort;
12006
12155
  return new CopilotExecutor(validated);
12007
12156
  }
12008
12157
  }
@@ -12033,6 +12182,14 @@ var require_http_api = __commonJS({
12033
12182
  outputTokens: spec.costCapHint?.estimatedOutputTokens
12034
12183
  };
12035
12184
  }
12185
+ /**
12186
+ * The one model it runs. This executor takes no model hint (every call
12187
+ * names the configured model), so reporting the endpoint's catalog would
12188
+ * offer picks it would silently ignore.
12189
+ */
12190
+ async listModels() {
12191
+ return [{ id: this.config.model, label: this.config.model }];
12192
+ }
12036
12193
  async execute(dispatch, signal) {
12037
12194
  const { prompt, spec } = (0, loom_1.renderPromptExecution)(dispatch.inputs);
12038
12195
  const outputLabel = spec.outputLabel;
@@ -12973,10 +13130,12 @@ var require_anthropic_api = __commonJS({
12973
13130
  "use strict";
12974
13131
  Object.defineProperty(exports2, "__esModule", { value: true });
12975
13132
  exports2.createAnthropicApiExecutor = createAnthropicApiExecutor;
13133
+ exports2.parseAnthropicModels = parseAnthropicModels;
12976
13134
  var loom_1 = require_dist();
12977
13135
  var keep_1 = require_dist2();
12978
13136
  var env_ref_1 = require_env_ref();
12979
13137
  var validate_output_1 = require_validate_output();
13138
+ var models_1 = require_models();
12980
13139
  var DEFAULT_BASE_URL = "https://api.anthropic.com/v1/messages";
12981
13140
  var DEFAULT_MODEL = "claude-sonnet-4-6";
12982
13141
  var DEFAULT_MAX_TOKENS = 4096;
@@ -13032,6 +13191,37 @@ var require_anthropic_api = __commonJS({
13032
13191
  };
13033
13192
  return this.callApi(handle.apiKey, body, spec, handle.outputLabel, signal);
13034
13193
  }
13194
+ /**
13195
+ * The provider's own catalog, `/v1/models` beside the configured Messages
13196
+ * URL, asked with the operator's key. Dynamic mode reports nothing: its key
13197
+ * is a customer's, revealed per job, and there is none to ask with at start.
13198
+ */
13199
+ async listModels(signal) {
13200
+ const apiKey = this.instance.staticKey;
13201
+ if (!apiKey)
13202
+ return [];
13203
+ const url = modelsUrl(this.instance.baseUrl);
13204
+ if (!url)
13205
+ return [];
13206
+ const controller = new AbortController();
13207
+ const abort = () => controller.abort();
13208
+ const timer = setTimeout(abort, models_1.LIST_MODELS_TIMEOUT_MS);
13209
+ signal.addEventListener("abort", abort, { once: true });
13210
+ try {
13211
+ const response = await fetch(url, {
13212
+ headers: { "x-api-key": apiKey, "anthropic-version": ANTHROPIC_API_VERSION },
13213
+ signal: controller.signal
13214
+ });
13215
+ if (!response.ok)
13216
+ return [];
13217
+ return parseAnthropicModels(await response.json());
13218
+ } catch {
13219
+ return [];
13220
+ } finally {
13221
+ clearTimeout(timer);
13222
+ signal.removeEventListener("abort", abort);
13223
+ }
13224
+ }
13035
13225
  /** Shared Messages-API call + parse + validate, used by execute and continue. */
13036
13226
  async callApi(apiKey, body, spec, outputLabel, signal) {
13037
13227
  const timeoutController = new AbortController();
@@ -13073,7 +13263,8 @@ var require_anthropic_api = __commonJS({
13073
13263
  inputTokens: u.input_tokens,
13074
13264
  outputTokens: u.output_tokens,
13075
13265
  aiProvider: "anthropic",
13076
- aiModel: this.instance.model
13266
+ // The model this call ran, which a hint may have chosen.
13267
+ aiModel: body.model
13077
13268
  } : void 0;
13078
13269
  const parsed = safeJsonParse(rawText);
13079
13270
  (0, validate_output_1.assertOutputValid)(spec, parsed);
@@ -13105,7 +13296,8 @@ var require_anthropic_api = __commonJS({
13105
13296
  if (rawApiKeyFrom !== void 0 && rawApiKey !== void 0) {
13106
13297
  throw new Error('AnthropicApiExecutor accepts either "apiKey" (static) or "apiKeyFrom" (dynamic), not both');
13107
13298
  }
13108
- const resolveKey = rawApiKeyFrom !== void 0 ? buildDynamicResolver(config, deps) : buildStaticResolver(rawApiKey);
13299
+ const staticKey = rawApiKeyFrom === void 0 ? resolveStaticKey(rawApiKey) : null;
13300
+ const resolveKey = staticKey === null ? buildDynamicResolver(config, deps) : async () => staticKey;
13109
13301
  let baseUrl = DEFAULT_BASE_URL;
13110
13302
  if (config.baseUrl !== void 0) {
13111
13303
  if (typeof config.baseUrl !== "string" || config.baseUrl.length === 0) {
@@ -13136,6 +13328,7 @@ var require_anthropic_api = __commonJS({
13136
13328
  }
13137
13329
  return new AnthropicApiExecutor({
13138
13330
  resolveKey,
13331
+ staticKey,
13139
13332
  baseUrl,
13140
13333
  model,
13141
13334
  maxTokens,
@@ -13143,12 +13336,25 @@ var require_anthropic_api = __commonJS({
13143
13336
  capabilityId
13144
13337
  });
13145
13338
  }
13146
- function buildStaticResolver(rawApiKey) {
13339
+ function resolveStaticKey(rawApiKey) {
13147
13340
  if (typeof rawApiKey !== "string" || rawApiKey.length === 0) {
13148
13341
  throw new Error('AnthropicApiExecutor requires a non-empty "apiKey" string in config (or "apiKeyFrom: \\"job-metadata\\"" for dynamic key mode)');
13149
13342
  }
13150
- const resolved = (0, env_ref_1.resolveEnvRef)(rawApiKey, "apiKey");
13151
- return async () => resolved;
13343
+ return (0, env_ref_1.resolveEnvRef)(rawApiKey, "apiKey");
13344
+ }
13345
+ function modelsUrl(messagesUrl) {
13346
+ return /\/messages\/?$/.test(messagesUrl) ? messagesUrl.replace(/\/messages\/?$/, "/models") : null;
13347
+ }
13348
+ function parseAnthropicModels(body) {
13349
+ const data = body?.data;
13350
+ if (!Array.isArray(data))
13351
+ return [];
13352
+ return data.flatMap((entry) => {
13353
+ const { id, display_name: displayName } = entry ?? {};
13354
+ if (typeof id !== "string" || id.length === 0)
13355
+ return [];
13356
+ return [{ id, label: typeof displayName === "string" && displayName ? displayName : id }];
13357
+ });
13152
13358
  }
13153
13359
  function buildDynamicResolver(config, deps) {
13154
13360
  if (config.apiKeyFrom !== "job-metadata") {
@@ -22142,12 +22348,14 @@ var require_subscribe = __commonJS({
22142
22348
  if (id === STOPPED)
22143
22349
  return;
22144
22350
  resolvedPoolIds.push(id);
22351
+ const perPool = opts.subscriber.poolMetadata?.(ref);
22352
+ const metadata = opts.subscriber.metadata !== void 0 || perPool !== void 0 ? { ...opts.subscriber.metadata, ...perPool } : void 0;
22145
22353
  const registered = await untilAvailable(() => opts.jobsClient.registerMember(id, {
22146
22354
  identityType: opts.subscriber.identityType,
22147
22355
  identityId: opts.subscriber.identityId,
22148
22356
  intelligence: opts.subscriber.intelligence,
22149
22357
  ...opts.subscriber.displayName !== void 0 ? { displayName: opts.subscriber.displayName } : {},
22150
- ...opts.subscriber.metadata !== void 0 ? { metadata: opts.subscriber.metadata } : {}
22358
+ ...metadata !== void 0 ? { metadata } : {}
22151
22359
  }), mySession);
22152
22360
  if (registered === STOPPED)
22153
22361
  return;
@@ -27234,6 +27442,13 @@ var require_shuttle = __commonJS({
27234
27442
  });
27235
27443
  return { promise, resolve, reject };
27236
27444
  }
27445
+ async function listExecutorModels(executors, signal) {
27446
+ const listed = await Promise.all(executors.map(async (executor) => {
27447
+ const models = executor.listModels ? await executor.listModels(signal).catch(() => []) : [];
27448
+ return [executor.capability.id, models];
27449
+ }));
27450
+ return new Map(listed);
27451
+ }
27237
27452
  function isKeepUnavailable(err) {
27238
27453
  return err instanceof Error && err.name === "KeepUnavailableError";
27239
27454
  }
@@ -27382,6 +27597,10 @@ var require_shuttle = __commonJS({
27382
27597
  registerBuiltInExecutors(registry, this.brand.executorAllowlist);
27383
27598
  const executors = registry.build(mergeExecutorDefaults((0, environment_1.executorDefaultsFor)(this.brand, this.config.auth.jobsApiUrl), this.config.executors));
27384
27599
  const executorFor = (0, demux_1.createDemux)(executors, this.config.pools);
27600
+ const modelsByExecutor = await listExecutorModels(executors, this.stopping.signal);
27601
+ if (this.stopping.signal.aborted)
27602
+ return;
27603
+ logger.info("executor models listed", Object.fromEntries([...modelsByExecutor].map(([id, m]) => [id, m.length])));
27385
27604
  this.spendTracker = (0, spend_tracker_1.createSpendTrackerObserver)(this.brand, paths.spendStateFile);
27386
27605
  const observers = [
27387
27606
  (0, logger_2.createLoggerObserver)(),
@@ -27497,6 +27716,14 @@ var require_shuttle = __commonJS({
27497
27716
  return { status: "completed", outputs };
27498
27717
  };
27499
27718
  const pools = this.config.pools.map((p) => p.id ? { id: p.id } : { name: p.name });
27719
+ const poolExecutors = new Map(pools.map((ref, i) => [ref, this.config.pools[i].executor]));
27720
+ const poolMetadata = (ref) => {
27721
+ const executor = poolExecutors.get(ref);
27722
+ const models = executor ? modelsByExecutor.get(executor) : void 0;
27723
+ if (!executor || !models)
27724
+ return void 0;
27725
+ return { executor, ...models.length > 0 ? { models } : {} };
27726
+ };
27500
27727
  const attemptedUpgrades = /* @__PURE__ */ new Set();
27501
27728
  this.subscription = (0, jobs_1.subscribe)({
27502
27729
  jobsClient,
@@ -27513,7 +27740,8 @@ var require_shuttle = __commonJS({
27513
27740
  ...this.config.agent.metadata,
27514
27741
  client_version: this.brand.product.version,
27515
27742
  hostname: (0, os_1.hostname)()
27516
- }
27743
+ },
27744
+ poolMetadata
27517
27745
  },
27518
27746
  onJob,
27519
27747
  onDirectives: async (directives) => {
@@ -28375,7 +28603,7 @@ var import_path = require("path");
28375
28603
  var import_promises = require("fs/promises");
28376
28604
  var import_yaml = __toESM(require_dist3());
28377
28605
  var import_shuttle = __toESM(require_dist5());
28378
- var buildVersion = true ? "0.31.0" : pkg.version;
28606
+ var buildVersion = true ? "0.32.0" : pkg.version;
28379
28607
  var AGENT_EXECUTORS = ["claude-code", "codex", "copilot"];
28380
28608
  var sifterBrand = {
28381
28609
  product: {