pullfrog 0.1.23 → 0.1.25

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.
@@ -54,8 +54,17 @@ export declare const CLAUDE_PRETOOL_GATE_SOURCE: string;
54
54
  * literal JSON string per claude-code source `src/main.tsx` (`Path to a
55
55
  * settings JSON file or a JSON string`), but we use a path so the script
56
56
  * and its config sit side-by-side under `ctx.tmpdir`.
57
+ *
58
+ * `execToolDenyRules` are the native exec tools (Bash/Monitor/REPL/Workflow +
59
+ * their `Agent(...)` forms) to deny at a settings-source rule — the
60
+ * authoritative, bypass-immune layer. `--disallowedTools` alone (a `cliArg`
61
+ * deny) was observed to leak under `--dangerously-skip-permissions`, so the
62
+ * deny is carried here too. Both consumers use both returned fields: the flag
63
+ * `--settings` JSON (covers non-CI runs) writes the whole object, and
64
+ * `buildManagedSettings` (CI, /etc managed settings) spreads `hooks` and folds
65
+ * `permissions.deny` into its richer deny list.
57
66
  */
58
- export declare function buildClaudePretoolGateSettings(scriptAbsolutePath: string): {
67
+ export declare function buildClaudePretoolGateSettings(scriptAbsolutePath: string, execToolDenyRules: string[]): {
59
68
  hooks: {
60
69
  PreToolUse: Array<{
61
70
  matcher: string;
@@ -66,4 +75,7 @@ export declare function buildClaudePretoolGateSettings(scriptAbsolutePath: strin
66
75
  }>;
67
76
  }>;
68
77
  };
78
+ permissions: {
79
+ deny: string[];
80
+ };
69
81
  };
package/dist/cli.mjs CHANGED
@@ -100992,7 +100992,7 @@ var providers = {
100992
100992
  openRouterResolve: "openrouter/moonshotai/kimi-k2.6"
100993
100993
  },
100994
100994
  "minimax-m2.5": {
100995
- displayName: "MiniMax M2.5",
100995
+ displayName: "MiniMax M2",
100996
100996
  resolve: "opencode/minimax-m2.5",
100997
100997
  openRouterResolve: "openrouter/minimax/minimax-m2.5"
100998
100998
  },
@@ -101009,7 +101009,7 @@ var providers = {
101009
101009
  fallback: "opencode/big-pickle"
101010
101010
  },
101011
101011
  "minimax-m2.5-free": {
101012
- displayName: "MiniMax M2.5",
101012
+ displayName: "MiniMax M2",
101013
101013
  resolve: "opencode/minimax-m2.5-free",
101014
101014
  envVars: [],
101015
101015
  isFree: true,
@@ -101166,7 +101166,7 @@ var providers = {
101166
101166
  openRouterResolve: "openrouter/moonshotai/kimi-k2.6"
101167
101167
  },
101168
101168
  "minimax-m2.5": {
101169
- displayName: "MiniMax M2.5",
101169
+ displayName: "MiniMax M2",
101170
101170
  resolve: "openrouter/minimax/minimax-m2.5",
101171
101171
  openRouterResolve: "openrouter/minimax/minimax-m2.5"
101172
101172
  }
@@ -101867,7 +101867,7 @@ var import_semver = __toESM(require_semver2(), 1);
101867
101867
  // package.json
101868
101868
  var package_default = {
101869
101869
  name: "pullfrog",
101870
- version: "0.1.23",
101870
+ version: "0.1.25",
101871
101871
  type: "module",
101872
101872
  bin: {
101873
101873
  pullfrog: "dist/cli.mjs",
@@ -102520,7 +102520,7 @@ process.stdin.on("end", () => {
102520
102520
  process.exit(2);
102521
102521
  });
102522
102522
  `;
102523
- function buildClaudePretoolGateSettings(scriptAbsolutePath) {
102523
+ function buildClaudePretoolGateSettings(scriptAbsolutePath, execToolDenyRules) {
102524
102524
  return {
102525
102525
  hooks: {
102526
102526
  PreToolUse: [
@@ -102539,7 +102539,8 @@ function buildClaudePretoolGateSettings(scriptAbsolutePath) {
102539
102539
  ]
102540
102540
  }
102541
102541
  ]
102542
- }
102542
+ },
102543
+ permissions: { deny: execToolDenyRules }
102543
102544
  };
102544
102545
  }
102545
102546
 
@@ -103565,10 +103566,11 @@ async function installClaudeCli() {
103565
103566
  });
103566
103567
  }
103567
103568
  var CLAUDE_EXEC_TOOLS = ["Bash", "Monitor", "REPL", "Workflow"];
103568
- var CLAUDE_DISALLOWED_TOOLS = [
103569
+ var CLAUDE_EXEC_TOOL_DENY_RULES = [
103569
103570
  ...CLAUDE_EXEC_TOOLS,
103570
103571
  ...CLAUDE_EXEC_TOOLS.map((t2) => `Agent(${t2})`)
103571
- ].join(",");
103572
+ ];
103573
+ var CLAUDE_DISALLOWED_TOOLS = CLAUDE_EXEC_TOOL_DENY_RULES.join(",");
103572
103574
  function writeMcpConfig(ctx) {
103573
103575
  const configDir = join5(ctx.tmpdir, ".claude");
103574
103576
  mkdirSync4(configDir, { recursive: true });
@@ -103588,7 +103590,8 @@ function writePretoolGateAssets(ctx) {
103588
103590
  writeFileSync4(scriptPath, CLAUDE_PRETOOL_GATE_SOURCE);
103589
103591
  chmodSync2(scriptPath, 493);
103590
103592
  const settingsPath = join5(ctx.tmpdir, "pullfrog-claude-settings.json");
103591
- writeFileSync4(settingsPath, JSON.stringify(buildClaudePretoolGateSettings(scriptPath)));
103593
+ const settings = buildClaudePretoolGateSettings(scriptPath, CLAUDE_EXEC_TOOL_DENY_RULES);
103594
+ writeFileSync4(settingsPath, JSON.stringify(settings));
103592
103595
  return { scriptPath, settingsPath };
103593
103596
  }
103594
103597
  function buildAgentsJson() {
@@ -103990,11 +103993,20 @@ function buildManagedSettings(params) {
103990
103993
  `Glob(${path4}/**)`,
103991
103994
  `Glob(/${path4}/**)`
103992
103995
  ]);
103996
+ const gate = buildClaudePretoolGateSettings(
103997
+ params.pretoolGateScriptPath,
103998
+ CLAUDE_EXEC_TOOL_DENY_RULES
103999
+ );
103993
104000
  const base = {
103994
104001
  allowManagedPermissionRulesOnly: true,
103995
104002
  allowManagedHooksOnly: true,
103996
104003
  permissions: {
103997
104004
  deny: [
104005
+ // native exec tools — the authoritative, bypass-immune deny.
104006
+ // `--disallowedTools` (a cliArg-source deny) leaked under
104007
+ // `--dangerously-skip-permissions`; policySettings denies survive
104008
+ // bypassPermissions mode. covers top-level + Agent(...) subagent use.
104009
+ ...gate.permissions.deny,
103998
104010
  "Read(//proc/**)",
103999
104011
  "Read(//sys/**)",
104000
104012
  "Grep(//proc/**)",
@@ -104020,7 +104032,7 @@ function buildManagedSettings(params) {
104020
104032
  }
104021
104033
  };
104022
104034
  const hooks = {
104023
- ...buildClaudePretoolGateSettings(params.pretoolGateScriptPath).hooks
104035
+ ...gate.hooks
104024
104036
  };
104025
104037
  if (params.stopHookPath) {
104026
104038
  hooks.Stop = [
@@ -107629,6 +107641,7 @@ function installCodexAuth() {
107629
107641
  mkdirSync5(opencodeDir, { recursive: true });
107630
107642
  writeFileSync5(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
107631
107643
  `, { mode: 384 });
107644
+ process.env.XDG_DATA_HOME = xdgDataHome;
107632
107645
  log.info(`\xBB installed Codex auth at ${authPath}`);
107633
107646
  return {
107634
107647
  authPath,
@@ -117953,6 +117966,9 @@ var addTools = (ctx, server, tools) => {
117953
117966
  };
117954
117967
 
117955
117968
  // mcp/comment.ts
117969
+ function isNotFoundError(error49) {
117970
+ return error49 instanceof Error && error49.message.includes("Not Found");
117971
+ }
117956
117972
  function buildCommentFooter(ctx, customParts) {
117957
117973
  const runId = ctx.runId;
117958
117974
  return buildPullfrogFooter({
@@ -118109,7 +118125,30 @@ async function reportProgress(ctx, params) {
118109
118125
  const bodyWithoutFooter = stripExistingFooter(body);
118110
118126
  const footer = buildCommentFooter(ctx, customParts);
118111
118127
  const bodyWithFooter = `${bodyWithoutFooter}${footer}`;
118112
- const result = await updateProgressComment(apiCtx, existingComment, bodyWithFooter);
118128
+ let result;
118129
+ try {
118130
+ result = await updateProgressComment(apiCtx, existingComment, bodyWithFooter);
118131
+ } catch (error49) {
118132
+ if (params.liveProgress || existingComment.type !== "review" || !isNotFoundError(error49) || issueNumber === void 0) {
118133
+ throw error49;
118134
+ }
118135
+ log.warning(
118136
+ `progress review comment ${existingComment.id} is gone (404); posting a top-level comment on #${issueNumber} instead`
118137
+ );
118138
+ const created2 = await createLeapingProgressComment(
118139
+ apiCtx,
118140
+ { kind: "issue", issueNumber },
118141
+ bodyWithFooter
118142
+ );
118143
+ ctx.toolState.progressComment = created2.comment;
118144
+ if (!params.liveProgress) ctx.toolState.wasUpdated = true;
118145
+ return {
118146
+ commentId: created2.comment.id,
118147
+ url: created2.html_url,
118148
+ body: created2.body || "",
118149
+ action: "created"
118150
+ };
118151
+ }
118113
118152
  if (!params.liveProgress) ctx.toolState.wasUpdated = true;
118114
118153
  if (isPlanMode && result.node_id) {
118115
118154
  await patchWorkflowRunFields(ctx, { planCommentNodeId: result.node_id });
@@ -118212,10 +118251,7 @@ async function deleteProgressComment(ctx) {
118212
118251
  existing
118213
118252
  );
118214
118253
  } catch (error49) {
118215
- if (error49 instanceof Error && error49.message.includes("Not Found")) {
118216
- } else {
118217
- throw error49;
118218
- }
118254
+ if (!isNotFoundError(error49)) throw error49;
118219
118255
  }
118220
118256
  ctx.toolState.progressComment = null;
118221
118257
  return true;
@@ -151957,7 +151993,7 @@ function classifyPushError(msg) {
151957
151993
  if (TRANSIENT_PATTERNS.some((p2) => p2.test(msg))) return "transient";
151958
151994
  return "unknown";
151959
151995
  }
151960
- var TRANSIENT_RETRY_DELAYS_MS = [2e3, 5e3];
151996
+ var TRANSIENT_RETRY_DELAYS_MS = [2e3, 4e3, 8e3, 16e3, 3e4];
151961
151997
  async function pushWithRetry(args2, token) {
151962
151998
  let lastErr;
151963
151999
  for (let attempt = 0; attempt <= TRANSIENT_RETRY_DELAYS_MS.length; attempt++) {
@@ -155835,6 +155871,7 @@ function selectFallbackModelIfNeeded(input) {
155835
155871
  if (!input.resolvedModel) return { fallback: false };
155836
155872
  if (input.resolvedModel === FREE_FALLBACK_SLUG) return { fallback: false };
155837
155873
  if (!input.resolvedModel.includes("/")) return { fallback: false };
155874
+ if (input.agentName === "claude") return { fallback: false };
155838
155875
  if (input.authorized.has(input.resolvedModel)) return { fallback: false };
155839
155876
  return {
155840
155877
  fallback: true,
@@ -161815,7 +161852,8 @@ async function main() {
161815
161852
  const fallback = selectFallbackModelIfNeeded({
161816
161853
  resolvedModel: initialResolvedModel,
161817
161854
  proxyModel: payload.proxyModel,
161818
- authorized: authorized2
161855
+ authorized: authorized2,
161856
+ agentName: resolveAgent({ model: initialResolvedModel }).name
161819
161857
  });
161820
161858
  const effectiveSlug = fallback.fallback ? fallback.to : payload.model;
161821
161859
  const resolvedModel = fallback.fallback ? fallback.to : initialResolvedModel;
@@ -163004,7 +163042,7 @@ async function run2() {
163004
163042
  }
163005
163043
 
163006
163044
  // cli.ts
163007
- var VERSION10 = "0.1.23";
163045
+ var VERSION10 = "0.1.25";
163008
163046
  var bin = basename2(process.argv[1] || "");
163009
163047
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
163010
163048
  var rawArgs = process.argv.slice(2);
package/dist/index.js CHANGED
@@ -99192,7 +99192,7 @@ var providers = {
99192
99192
  openRouterResolve: "openrouter/moonshotai/kimi-k2.6"
99193
99193
  },
99194
99194
  "minimax-m2.5": {
99195
- displayName: "MiniMax M2.5",
99195
+ displayName: "MiniMax M2",
99196
99196
  resolve: "opencode/minimax-m2.5",
99197
99197
  openRouterResolve: "openrouter/minimax/minimax-m2.5"
99198
99198
  },
@@ -99209,7 +99209,7 @@ var providers = {
99209
99209
  fallback: "opencode/big-pickle"
99210
99210
  },
99211
99211
  "minimax-m2.5-free": {
99212
- displayName: "MiniMax M2.5",
99212
+ displayName: "MiniMax M2",
99213
99213
  resolve: "opencode/minimax-m2.5-free",
99214
99214
  envVars: [],
99215
99215
  isFree: true,
@@ -99366,7 +99366,7 @@ var providers = {
99366
99366
  openRouterResolve: "openrouter/moonshotai/kimi-k2.6"
99367
99367
  },
99368
99368
  "minimax-m2.5": {
99369
- displayName: "MiniMax M2.5",
99369
+ displayName: "MiniMax M2",
99370
99370
  resolve: "openrouter/minimax/minimax-m2.5",
99371
99371
  openRouterResolve: "openrouter/minimax/minimax-m2.5"
99372
99372
  }
@@ -100067,7 +100067,7 @@ var import_semver = __toESM(require_semver2(), 1);
100067
100067
  // package.json
100068
100068
  var package_default = {
100069
100069
  name: "pullfrog",
100070
- version: "0.1.23",
100070
+ version: "0.1.25",
100071
100071
  type: "module",
100072
100072
  bin: {
100073
100073
  pullfrog: "dist/cli.mjs",
@@ -100720,7 +100720,7 @@ process.stdin.on("end", () => {
100720
100720
  process.exit(2);
100721
100721
  });
100722
100722
  `;
100723
- function buildClaudePretoolGateSettings(scriptAbsolutePath) {
100723
+ function buildClaudePretoolGateSettings(scriptAbsolutePath, execToolDenyRules) {
100724
100724
  return {
100725
100725
  hooks: {
100726
100726
  PreToolUse: [
@@ -100739,7 +100739,8 @@ function buildClaudePretoolGateSettings(scriptAbsolutePath) {
100739
100739
  ]
100740
100740
  }
100741
100741
  ]
100742
- }
100742
+ },
100743
+ permissions: { deny: execToolDenyRules }
100743
100744
  };
100744
100745
  }
100745
100746
 
@@ -101765,10 +101766,11 @@ async function installClaudeCli() {
101765
101766
  });
101766
101767
  }
101767
101768
  var CLAUDE_EXEC_TOOLS = ["Bash", "Monitor", "REPL", "Workflow"];
101768
- var CLAUDE_DISALLOWED_TOOLS = [
101769
+ var CLAUDE_EXEC_TOOL_DENY_RULES = [
101769
101770
  ...CLAUDE_EXEC_TOOLS,
101770
101771
  ...CLAUDE_EXEC_TOOLS.map((t) => `Agent(${t})`)
101771
- ].join(",");
101772
+ ];
101773
+ var CLAUDE_DISALLOWED_TOOLS = CLAUDE_EXEC_TOOL_DENY_RULES.join(",");
101772
101774
  function writeMcpConfig(ctx) {
101773
101775
  const configDir = join4(ctx.tmpdir, ".claude");
101774
101776
  mkdirSync4(configDir, { recursive: true });
@@ -101788,7 +101790,8 @@ function writePretoolGateAssets(ctx) {
101788
101790
  writeFileSync3(scriptPath, CLAUDE_PRETOOL_GATE_SOURCE);
101789
101791
  chmodSync2(scriptPath, 493);
101790
101792
  const settingsPath = join4(ctx.tmpdir, "pullfrog-claude-settings.json");
101791
- writeFileSync3(settingsPath, JSON.stringify(buildClaudePretoolGateSettings(scriptPath)));
101793
+ const settings = buildClaudePretoolGateSettings(scriptPath, CLAUDE_EXEC_TOOL_DENY_RULES);
101794
+ writeFileSync3(settingsPath, JSON.stringify(settings));
101792
101795
  return { scriptPath, settingsPath };
101793
101796
  }
101794
101797
  function buildAgentsJson() {
@@ -102190,11 +102193,20 @@ function buildManagedSettings(params) {
102190
102193
  `Glob(${path4}/**)`,
102191
102194
  `Glob(/${path4}/**)`
102192
102195
  ]);
102196
+ const gate = buildClaudePretoolGateSettings(
102197
+ params.pretoolGateScriptPath,
102198
+ CLAUDE_EXEC_TOOL_DENY_RULES
102199
+ );
102193
102200
  const base = {
102194
102201
  allowManagedPermissionRulesOnly: true,
102195
102202
  allowManagedHooksOnly: true,
102196
102203
  permissions: {
102197
102204
  deny: [
102205
+ // native exec tools — the authoritative, bypass-immune deny.
102206
+ // `--disallowedTools` (a cliArg-source deny) leaked under
102207
+ // `--dangerously-skip-permissions`; policySettings denies survive
102208
+ // bypassPermissions mode. covers top-level + Agent(...) subagent use.
102209
+ ...gate.permissions.deny,
102198
102210
  "Read(//proc/**)",
102199
102211
  "Read(//sys/**)",
102200
102212
  "Grep(//proc/**)",
@@ -102220,7 +102232,7 @@ function buildManagedSettings(params) {
102220
102232
  }
102221
102233
  };
102222
102234
  const hooks = {
102223
- ...buildClaudePretoolGateSettings(params.pretoolGateScriptPath).hooks
102235
+ ...gate.hooks
102224
102236
  };
102225
102237
  if (params.stopHookPath) {
102226
102238
  hooks.Stop = [
@@ -105871,6 +105883,7 @@ function installCodexAuth() {
105871
105883
  mkdirSync5(opencodeDir, { recursive: true });
105872
105884
  writeFileSync4(authPath, `${JSON.stringify(opencodeAuth, null, 2)}
105873
105885
  `, { mode: 384 });
105886
+ process.env.XDG_DATA_HOME = xdgDataHome;
105874
105887
  log.info(`\xBB installed Codex auth at ${authPath}`);
105875
105888
  return {
105876
105889
  authPath,
@@ -116195,6 +116208,9 @@ var addTools = (ctx, server, tools) => {
116195
116208
  };
116196
116209
 
116197
116210
  // mcp/comment.ts
116211
+ function isNotFoundError(error49) {
116212
+ return error49 instanceof Error && error49.message.includes("Not Found");
116213
+ }
116198
116214
  function buildCommentFooter(ctx, customParts) {
116199
116215
  const runId = ctx.runId;
116200
116216
  return buildPullfrogFooter({
@@ -116351,7 +116367,30 @@ async function reportProgress(ctx, params) {
116351
116367
  const bodyWithoutFooter = stripExistingFooter(body);
116352
116368
  const footer = buildCommentFooter(ctx, customParts);
116353
116369
  const bodyWithFooter = `${bodyWithoutFooter}${footer}`;
116354
- const result = await updateProgressComment(apiCtx, existingComment, bodyWithFooter);
116370
+ let result;
116371
+ try {
116372
+ result = await updateProgressComment(apiCtx, existingComment, bodyWithFooter);
116373
+ } catch (error49) {
116374
+ if (params.liveProgress || existingComment.type !== "review" || !isNotFoundError(error49) || issueNumber === void 0) {
116375
+ throw error49;
116376
+ }
116377
+ log.warning(
116378
+ `progress review comment ${existingComment.id} is gone (404); posting a top-level comment on #${issueNumber} instead`
116379
+ );
116380
+ const created2 = await createLeapingProgressComment(
116381
+ apiCtx,
116382
+ { kind: "issue", issueNumber },
116383
+ bodyWithFooter
116384
+ );
116385
+ ctx.toolState.progressComment = created2.comment;
116386
+ if (!params.liveProgress) ctx.toolState.wasUpdated = true;
116387
+ return {
116388
+ commentId: created2.comment.id,
116389
+ url: created2.html_url,
116390
+ body: created2.body || "",
116391
+ action: "created"
116392
+ };
116393
+ }
116355
116394
  if (!params.liveProgress) ctx.toolState.wasUpdated = true;
116356
116395
  if (isPlanMode && result.node_id) {
116357
116396
  await patchWorkflowRunFields(ctx, { planCommentNodeId: result.node_id });
@@ -116454,10 +116493,7 @@ async function deleteProgressComment(ctx) {
116454
116493
  existing
116455
116494
  );
116456
116495
  } catch (error49) {
116457
- if (error49 instanceof Error && error49.message.includes("Not Found")) {
116458
- } else {
116459
- throw error49;
116460
- }
116496
+ if (!isNotFoundError(error49)) throw error49;
116461
116497
  }
116462
116498
  ctx.toolState.progressComment = null;
116463
116499
  return true;
@@ -150199,7 +150235,7 @@ function classifyPushError(msg) {
150199
150235
  if (TRANSIENT_PATTERNS.some((p) => p.test(msg))) return "transient";
150200
150236
  return "unknown";
150201
150237
  }
150202
- var TRANSIENT_RETRY_DELAYS_MS = [2e3, 5e3];
150238
+ var TRANSIENT_RETRY_DELAYS_MS = [2e3, 4e3, 8e3, 16e3, 3e4];
150203
150239
  async function pushWithRetry(args2, token) {
150204
150240
  let lastErr;
150205
150241
  for (let attempt = 0; attempt <= TRANSIENT_RETRY_DELAYS_MS.length; attempt++) {
@@ -154077,6 +154113,7 @@ function selectFallbackModelIfNeeded(input) {
154077
154113
  if (!input.resolvedModel) return { fallback: false };
154078
154114
  if (input.resolvedModel === FREE_FALLBACK_SLUG) return { fallback: false };
154079
154115
  if (!input.resolvedModel.includes("/")) return { fallback: false };
154116
+ if (input.agentName === "claude") return { fallback: false };
154080
154117
  if (input.authorized.has(input.resolvedModel)) return { fallback: false };
154081
154118
  return {
154082
154119
  fallback: true,
@@ -160057,7 +160094,8 @@ async function main() {
160057
160094
  const fallback = selectFallbackModelIfNeeded({
160058
160095
  resolvedModel: initialResolvedModel,
160059
160096
  proxyModel: payload.proxyModel,
160060
- authorized: authorized2
160097
+ authorized: authorized2,
160098
+ agentName: resolveAgent({ model: initialResolvedModel }).name
160061
160099
  });
160062
160100
  const effectiveSlug = fallback.fallback ? fallback.to : payload.model;
160063
160101
  const resolvedModel = fallback.fallback ? fallback.to : initialResolvedModel;
package/dist/internal.js CHANGED
@@ -258,7 +258,7 @@ var providers = {
258
258
  openRouterResolve: "openrouter/moonshotai/kimi-k2.6"
259
259
  },
260
260
  "minimax-m2.5": {
261
- displayName: "MiniMax M2.5",
261
+ displayName: "MiniMax M2",
262
262
  resolve: "opencode/minimax-m2.5",
263
263
  openRouterResolve: "openrouter/minimax/minimax-m2.5"
264
264
  },
@@ -275,7 +275,7 @@ var providers = {
275
275
  fallback: "opencode/big-pickle"
276
276
  },
277
277
  "minimax-m2.5-free": {
278
- displayName: "MiniMax M2.5",
278
+ displayName: "MiniMax M2",
279
279
  resolve: "opencode/minimax-m2.5-free",
280
280
  envVars: [],
281
281
  isFree: true,
@@ -432,7 +432,7 @@ var providers = {
432
432
  openRouterResolve: "openrouter/moonshotai/kimi-k2.6"
433
433
  },
434
434
  "minimax-m2.5": {
435
- displayName: "MiniMax M2.5",
435
+ displayName: "MiniMax M2",
436
436
  resolve: "openrouter/minimax/minimax-m2.5",
437
437
  openRouterResolve: "openrouter/minimax/minimax-m2.5"
438
438
  }
@@ -1,3 +1,4 @@
1
+ import type { AgentId } from "../external.ts";
1
2
  /**
2
3
  * Slug we fall back to when a BYOK-required model is configured but the
3
4
  * runner has no provider key in env. Picked because it's free, stable, and
@@ -30,9 +31,14 @@ export type FallbackDecision = {
30
31
  * - Resolved model is a raw Bedrock / Vertex ID (no `/`): the routing
31
32
  * validators (`validateBedrockSetup` / `validateVertexSetup`) cover
32
33
  * auth + region/location/model-id; `opencode models` does not.
34
+ * - The selected agent is `claude`: the Claude Code harness brings its own
35
+ * auth and `resolveAgent` only returns it when that auth is present.
36
+ * `opencode models` can't see `CLAUDE_CODE_OAUTH_TOKEN`, so without this
37
+ * an OAuth-subscription run on an Anthropic model would wrongly fall back.
33
38
  */
34
39
  export declare function selectFallbackModelIfNeeded(input: {
35
40
  resolvedModel: string | undefined;
36
41
  proxyModel: string | undefined;
37
42
  authorized: Set<string>;
43
+ agentName: AgentId;
38
44
  }): FallbackDecision;
@@ -23,6 +23,7 @@ export interface InstalledCodexAuth {
23
23
  * caller treats null as "no codex auth, fall through to API key flow".
24
24
  *
25
25
  * The env value is server-side guaranteed fresh by `maybeRotateCodexSecret`
26
- * in the run-context endpoint. We only parse + write it here; no refresh,
27
- * no DB interaction. */
26
+ * in the run-context endpoint. We parse + write it here and set
27
+ * `process.env.XDG_DATA_HOME` so every opencode subprocess discovers the
28
+ * auth.json; no refresh, no DB interaction. */
28
29
  export declare function installCodexAuth(): InstalledCodexAuth | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "pullfrog": "dist/cli.mjs",