pullfrog 0.1.52 → 0.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -102232,9 +102232,9 @@ var providers = {
102232
102232
  },
102233
102233
  "gemini-flash": {
102234
102234
  displayName: "Gemini Flash",
102235
- resolve: "google/gemini-3.5-flash",
102235
+ resolve: "google/gemini-3.6-flash",
102236
102236
  effort: ["minimal", "low", "medium", "high"],
102237
- openRouterResolve: "openrouter/google/gemini-3.5-flash"
102237
+ openRouterResolve: "openrouter/google/gemini-3.6-flash"
102238
102238
  }
102239
102239
  }
102240
102240
  }),
@@ -102244,26 +102244,26 @@ var providers = {
102244
102244
  models: {
102245
102245
  grok: {
102246
102246
  displayName: "Grok",
102247
- resolve: "xai/grok-4.3",
102248
- effort: ["none", "low", "medium", "high"],
102249
- openRouterResolve: "openrouter/x-ai/grok-4.3",
102247
+ resolve: "xai/grok-4.5",
102248
+ effort: ["low", "medium", "high"],
102249
+ openRouterResolve: "openrouter/x-ai/grok-4.5",
102250
102250
  preferred: true
102251
102251
  },
102252
102252
  // legacy aliases — xAI retired the entire fast/code-fast line on
102253
102253
  // 2026-05-15 (https://docs.x.ai/developers/migration/may-15-deprecation)
102254
102254
  // and now redirects every deprecated text-model slug to grok-4.3 at
102255
102255
  // standard pricing. fall back to the live `xai/grok` so the alias
102256
- // chain resolves to grok-4.3 for both direct-key and OpenRouter users.
102256
+ // chain resolves to grok-4.5 for both direct-key and OpenRouter users.
102257
102257
  "grok-fast": {
102258
102258
  displayName: "Grok Fast",
102259
102259
  resolve: "xai/grok-4-1-fast",
102260
- openRouterResolve: "openrouter/x-ai/grok-4.3",
102260
+ openRouterResolve: "openrouter/x-ai/grok-4.5",
102261
102261
  fallback: "xai/grok"
102262
102262
  },
102263
102263
  "grok-code-fast": {
102264
102264
  displayName: "Grok Code Fast",
102265
102265
  resolve: "xai/grok-code-fast-1",
102266
- openRouterResolve: "openrouter/x-ai/grok-4.3",
102266
+ openRouterResolve: "openrouter/x-ai/grok-4.5",
102267
102267
  fallback: "xai/grok"
102268
102268
  }
102269
102269
  }
@@ -102280,20 +102280,25 @@ var providers = {
102280
102280
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro",
102281
102281
  preferred: true
102282
102282
  },
102283
- // DeepSeek upgraded the `deepseek-v4-flash` API model in place to the
102284
- // 0731 release, but OpenRouter forked it into a separate `-0731` id and
102285
- // left the April preview live at a HIGHER price — so the direct
102286
- // `resolve` needs no version and the OpenRouter route must be pinned.
102287
- // 0731 publishes a DIFFERENT OpenRouter ladder to both the April preview
102288
- // and Pro (`high, xhigh`) it gained a `low` rung and renamed the top to
102289
- // `max`. that makes `low`, not `high`, position 0 on this route, which is
102283
+ // DeepSeek upgrades the `deepseek-v4-flash` API model in place, so the
102284
+ // direct `resolve` needs no version. OpenRouter instead forked the 0731
102285
+ // release into its own id and left the April preview live under the
102286
+ // unversioned one at a HIGHER price ($0.14/$0.28 vs $0.09/$0.18) so the
102287
+ // OpenRouter route can never use the bare id. it tracks the rolling
102288
+ // `~deepseek/*-latest` pointer (rule 4), which resolves to 0731 at the
102289
+ // same price today and follows the next in-place upgrade on its own,
102290
+ // rather than pinning a dated snapshot that goes stale every release.
102291
+ // 0731 gained a `low` rung and renamed the top to `max`, unlike Pro
102292
+ // (`high, max` direct / `high, xhigh` via OpenRouter). that ladder now
102293
+ // reads the same on BOTH routes — the in-place direct upgrade caught up
102294
+ // to the fork — so `low`, not `high`, is position 0 either way, which is
102290
102295
  // why the OSS effort floor in `main.ts` pins the `high` rung by name.
102291
102296
  "deepseek-flash": {
102292
102297
  displayName: "DeepSeek Flash",
102293
102298
  resolve: "deepseek/deepseek-v4-flash",
102294
- effort: ["high", "max"],
102299
+ effort: ["low", "high", "max"],
102295
102300
  openRouterEffort: ["low", "high", "max"],
102296
- openRouterResolve: "openrouter/deepseek/deepseek-v4-flash-0731"
102301
+ openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
102297
102302
  },
102298
102303
  // legacy aliases — deepseek retires these on 2026-07-24; transparently
102299
102304
  // upgrade existing users to the v4 family via the fallback chain.
@@ -102439,21 +102444,19 @@ var providers = {
102439
102444
  },
102440
102445
  "gemini-flash": {
102441
102446
  displayName: "Gemini Flash",
102442
- resolve: "opencode/gemini-3.5-flash",
102447
+ resolve: "opencode/gemini-3.6-flash",
102443
102448
  effort: ["minimal", "low", "medium", "high"],
102444
- openRouterResolve: "openrouter/google/gemini-3.5-flash"
102449
+ openRouterResolve: "openrouter/google/gemini-3.6-flash"
102445
102450
  },
102446
102451
  "kimi-k2": {
102447
102452
  displayName: "Kimi K2",
102448
- // opencode Zen serves only up to k2.6; the OpenRouter fallback (used for
102449
- // router/oss proxy runs) takes the newer k2.7-code.
102450
- resolve: "opencode/kimi-k2.6",
102453
+ resolve: "opencode/kimi-k2.7-code",
102451
102454
  openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
102452
102455
  },
102453
- // Zen serves m2.5; the funded OpenRouter path tracks the current m2.7.
102456
+ // slug pins the m2 line for DB stability; resolve tracks the current m2.7.
102454
102457
  "minimax-m2.5": {
102455
102458
  displayName: "MiniMax M2",
102456
- resolve: "opencode/minimax-m2.5",
102459
+ resolve: "opencode/minimax-m2.7",
102457
102460
  openRouterResolve: "openrouter/minimax/minimax-m2.7"
102458
102461
  },
102459
102462
  "gpt-5-nano": {
@@ -102673,9 +102676,9 @@ var providers = {
102673
102676
  },
102674
102677
  grok: {
102675
102678
  displayName: "Grok",
102676
- resolve: "openrouter/x-ai/grok-4.3",
102677
- effort: ["none", "low", "medium", "high"],
102678
- openRouterResolve: "openrouter/x-ai/grok-4.3"
102679
+ resolve: "openrouter/x-ai/grok-4.5",
102680
+ effort: ["low", "medium", "high"],
102681
+ openRouterResolve: "openrouter/x-ai/grok-4.5"
102679
102682
  },
102680
102683
  "deepseek-pro": {
102681
102684
  displayName: "DeepSeek Pro",
@@ -102683,11 +102686,13 @@ var providers = {
102683
102686
  effort: ["high", "xhigh"],
102684
102687
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro"
102685
102688
  },
102689
+ // rolling pointer, not the bare id — see `deepseek/deepseek-flash` for why
102690
+ // OpenRouter's unversioned `deepseek-v4-flash` is the stale April preview.
102686
102691
  "deepseek-flash": {
102687
102692
  displayName: "DeepSeek Flash",
102688
- resolve: "openrouter/deepseek/deepseek-v4-flash-0731",
102693
+ resolve: "openrouter/~deepseek/deepseek-v4-flash-latest",
102689
102694
  effort: ["low", "high", "max"],
102690
- openRouterResolve: "openrouter/deepseek/deepseek-v4-flash-0731"
102695
+ openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
102691
102696
  },
102692
102697
  // legacy alias — deepseek retires this on 2026-07-24; transparently
102693
102698
  // upgrade existing users to the v4 family via the fallback chain.
@@ -103588,7 +103593,7 @@ var import_semver = __toESM(require_semver2(), 1);
103588
103593
  // package.json
103589
103594
  var package_default = {
103590
103595
  name: "pullfrog",
103591
- version: "0.1.52",
103596
+ version: "0.1.53",
103592
103597
  type: "module",
103593
103598
  bin: {
103594
103599
  pullfrog: "dist/cli.mjs",
@@ -164723,6 +164728,7 @@ var ShellParams = type({
164723
164728
  "background?": "boolean"
164724
164729
  });
164725
164730
  var detectedSandboxMethod;
164731
+ var usernsProbeError;
164726
164732
  function detectSandboxMethod() {
164727
164733
  if (detectedSandboxMethod !== void 0) {
164728
164734
  return detectedSandboxMethod;
@@ -164756,6 +164762,35 @@ function detectSandboxMethod() {
164756
164762
  }
164757
164763
  } catch {
164758
164764
  }
164765
+ try {
164766
+ const result = spawnSync6(
164767
+ "unshare",
164768
+ [
164769
+ "--user",
164770
+ "--map-root-user",
164771
+ "--pid",
164772
+ "--fork",
164773
+ "--mount",
164774
+ "setpriv",
164775
+ "--no-new-privs",
164776
+ "--bounding-set",
164777
+ "-all",
164778
+ "--inh-caps",
164779
+ "-all",
164780
+ "--ambient-caps",
164781
+ "-all",
164782
+ "true"
164783
+ ],
164784
+ { timeout: 5e3, stdio: ["ignore", "ignore", "pipe"] }
164785
+ );
164786
+ if (result.status === 0) {
164787
+ detectedSandboxMethod = "userns-unshare";
164788
+ log.debug("PID namespace isolation enabled (unprivileged userns unshare)");
164789
+ return "userns-unshare";
164790
+ }
164791
+ usernsProbeError = result.stderr?.toString().trim() || result.error?.message;
164792
+ } catch {
164793
+ }
164759
164794
  detectedSandboxMethod = "none";
164760
164795
  log.info("PID namespace isolation not available");
164761
164796
  return "none";
@@ -164803,7 +164838,7 @@ function spawnShell(params) {
164803
164838
  const ci = process.env.CI === "true";
164804
164839
  if (ci && sandboxMethod === "none") {
164805
164840
  throw new Error(
164806
- "pid namespace isolation is required in CI but unavailable (both unshare and sudo unshare failed)"
164841
+ "PID-namespace isolation is required in CI but unavailable: unprivileged unshare, sudo unshare, and userns-nested unshare all failed. " + (usernsProbeError ? `userns probe said: ${usernsProbeError}. ` : "") + "on a containerized / Kubernetes self-hosted runner the cause is usually one of: the pod's seccomp profile blocks user-namespace creation (Pod Security Standards baseline/restricted); the pod runs as root without CAP_SETFCAP, which the kernel requires to map uid 0; or the node kernel disallows unprivileged user namespaces (Ubuntu >= 23.10, Bottlerocket, Talos). elsewhere, check that `unshare` and `setpriv` (util-linux >= 2.31, which is what added --ambient-caps) are on PATH. see https://docs.pullfrog.com/security#self-hosted-runners"
164807
164842
  );
164808
164843
  }
164809
164844
  const repoRoot = resolveRepoRoot();
@@ -164822,6 +164857,23 @@ function spawnShell(params) {
164822
164857
  spawnOpts
164823
164858
  );
164824
164859
  }
164860
+ if (sandboxMethod === "userns-unshare") {
164861
+ const escaped = params.command.replace(/'/g, "'\\''");
164862
+ return spawn5(
164863
+ "unshare",
164864
+ [
164865
+ "--user",
164866
+ "--map-root-user",
164867
+ "--pid",
164868
+ "--fork",
164869
+ "--mount",
164870
+ "bash",
164871
+ "-c",
164872
+ `${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec setpriv --no-new-privs --bounding-set -all --inh-caps -all --ambient-caps -all bash -c '${escaped}'`
164873
+ ],
164874
+ spawnOpts
164875
+ );
164876
+ }
164825
164877
  if (sandboxMethod === "sudo-unshare") {
164826
164878
  const envArgs = [];
164827
164879
  for (const [k, v] of Object.entries(params.env)) {
@@ -166751,12 +166803,14 @@ var ShellPermissionInput = type.enumerated("disabled", "restricted", "enabled");
166751
166803
  var PushPermissionInput = type.enumerated("disabled", "restricted", "enabled");
166752
166804
  var StatusChecksInput = type.enumerated("disabled", "enabled");
166753
166805
  var ProgressCommentsInput = type.enumerated("disabled", "enabled");
166806
+ var DebugInput = type.enumerated("disabled", "enabled");
166754
166807
  var JsonPayload = type({
166755
166808
  "~pullfrog": "true",
166756
166809
  version: "string",
166757
166810
  "model?": "string | undefined",
166758
166811
  "modelExplicit?": "boolean | undefined",
166759
166812
  "effort?": "number | string | undefined",
166813
+ "debug?": "boolean | undefined",
166760
166814
  prompt: "string",
166761
166815
  "triggerer?": "string | undefined",
166762
166816
  "baseInstructions?": "string | undefined",
@@ -166791,6 +166845,7 @@ var Inputs = type({
166791
166845
  "prompt_file?": type.string.or("undefined"),
166792
166846
  "model?": type.string.or("undefined"),
166793
166847
  "effort?": type.string.or("undefined"),
166848
+ "debug?": DebugInput.or("undefined"),
166794
166849
  "timeout?": type.string.or("undefined"),
166795
166850
  "push?": PushPermissionInput.or("undefined"),
166796
166851
  "shell?": ShellPermissionInput.or("undefined"),
@@ -166854,6 +166909,7 @@ function resolveNonPromptInputs() {
166854
166909
  return Inputs.omit("prompt", "prompt_file").assert({
166855
166910
  model: core6.getInput("model") || void 0,
166856
166911
  effort: core6.getInput("effort") || void 0,
166912
+ debug: core6.getInput("debug") || void 0,
166857
166913
  timeout: core6.getInput("timeout") || void 0,
166858
166914
  cwd: core6.getInput("cwd") || void 0,
166859
166915
  push: core6.getInput("push") || void 0,
@@ -166870,6 +166926,8 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
166870
166926
  const model = jsonPayload?.model ?? inputs.model ?? repoSettings.model ?? void 0;
166871
166927
  const rawEffort = jsonPayload?.effort ?? inputs.effort ?? repoSettings.effort ?? void 0;
166872
166928
  const effort = rawEffort === void 0 ? void 0 : parseEffortPosition(String(rawEffort));
166929
+ const debug4 = jsonPayload?.debug ?? inputs.debug === "enabled";
166930
+ if (debug4) process.env.LOG_LEVEL = "debug";
166873
166931
  const isNonCollaborator = !isCollaborator(event);
166874
166932
  const repoShell = repoSettings.shell ?? "restricted";
166875
166933
  const inputShell = inputs.shell;
@@ -166890,6 +166948,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
166890
166948
  // the JSON payload). a GHA `model` input or the repo default is not explicit.
166891
166949
  modelExplicit: jsonPayload?.modelExplicit ?? false,
166892
166950
  effort,
166951
+ debug: debug4 || void 0,
166893
166952
  prompt,
166894
166953
  triggerer: jsonPayload?.triggerer ?? // it's not a common use case but GITHUB_ACTOR can be a user when the workflow is manually triggered by a user through GitHub Actions UI
166895
166954
  (!isPullfrog(process.env.GITHUB_ACTOR) ? process.env.GITHUB_ACTOR : void 0),
@@ -170053,7 +170112,7 @@ async function runCli4(input) {
170053
170112
  }
170054
170113
 
170055
170114
  // cli.ts
170056
- var VERSION10 = "0.1.52";
170115
+ var VERSION10 = "0.1.53";
170057
170116
  var bin = basename2(process.argv[1] || "");
170058
170117
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
170059
170118
  var rawArgs = process.argv.slice(2);
@@ -232,6 +232,14 @@ export interface WriteablePayload {
232
232
  modelExplicit?: boolean | undefined;
233
233
  /** reasoning-effort position on [0,1]; 0 is the model's cheapest rung, 1 its priciest */
234
234
  effort?: EffortPosition | undefined;
235
+ /**
236
+ * raise this one run's logging (`--debug`, or the `debug` action input): makes
237
+ * `log.debug` output visible and puts the opencode server at INFO. the only
238
+ * way to diagnose a run that fails with an empty log — see
239
+ * wiki/opencode-silent-stall.md. affects what we print, never what the agent
240
+ * may do, so it carries no access/permission consequence.
241
+ */
242
+ debug?: boolean | undefined;
235
243
  /** the user's actual request (body if @pullfrog tagged) */
236
244
  prompt: string;
237
245
  /** github username of the human who triggered this workflow run */
package/dist/index.js CHANGED
@@ -100297,9 +100297,9 @@ var providers = {
100297
100297
  },
100298
100298
  "gemini-flash": {
100299
100299
  displayName: "Gemini Flash",
100300
- resolve: "google/gemini-3.5-flash",
100300
+ resolve: "google/gemini-3.6-flash",
100301
100301
  effort: ["minimal", "low", "medium", "high"],
100302
- openRouterResolve: "openrouter/google/gemini-3.5-flash"
100302
+ openRouterResolve: "openrouter/google/gemini-3.6-flash"
100303
100303
  }
100304
100304
  }
100305
100305
  }),
@@ -100309,26 +100309,26 @@ var providers = {
100309
100309
  models: {
100310
100310
  grok: {
100311
100311
  displayName: "Grok",
100312
- resolve: "xai/grok-4.3",
100313
- effort: ["none", "low", "medium", "high"],
100314
- openRouterResolve: "openrouter/x-ai/grok-4.3",
100312
+ resolve: "xai/grok-4.5",
100313
+ effort: ["low", "medium", "high"],
100314
+ openRouterResolve: "openrouter/x-ai/grok-4.5",
100315
100315
  preferred: true
100316
100316
  },
100317
100317
  // legacy aliases — xAI retired the entire fast/code-fast line on
100318
100318
  // 2026-05-15 (https://docs.x.ai/developers/migration/may-15-deprecation)
100319
100319
  // and now redirects every deprecated text-model slug to grok-4.3 at
100320
100320
  // standard pricing. fall back to the live `xai/grok` so the alias
100321
- // chain resolves to grok-4.3 for both direct-key and OpenRouter users.
100321
+ // chain resolves to grok-4.5 for both direct-key and OpenRouter users.
100322
100322
  "grok-fast": {
100323
100323
  displayName: "Grok Fast",
100324
100324
  resolve: "xai/grok-4-1-fast",
100325
- openRouterResolve: "openrouter/x-ai/grok-4.3",
100325
+ openRouterResolve: "openrouter/x-ai/grok-4.5",
100326
100326
  fallback: "xai/grok"
100327
100327
  },
100328
100328
  "grok-code-fast": {
100329
100329
  displayName: "Grok Code Fast",
100330
100330
  resolve: "xai/grok-code-fast-1",
100331
- openRouterResolve: "openrouter/x-ai/grok-4.3",
100331
+ openRouterResolve: "openrouter/x-ai/grok-4.5",
100332
100332
  fallback: "xai/grok"
100333
100333
  }
100334
100334
  }
@@ -100345,20 +100345,25 @@ var providers = {
100345
100345
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro",
100346
100346
  preferred: true
100347
100347
  },
100348
- // DeepSeek upgraded the `deepseek-v4-flash` API model in place to the
100349
- // 0731 release, but OpenRouter forked it into a separate `-0731` id and
100350
- // left the April preview live at a HIGHER price — so the direct
100351
- // `resolve` needs no version and the OpenRouter route must be pinned.
100352
- // 0731 publishes a DIFFERENT OpenRouter ladder to both the April preview
100353
- // and Pro (`high, xhigh`) it gained a `low` rung and renamed the top to
100354
- // `max`. that makes `low`, not `high`, position 0 on this route, which is
100348
+ // DeepSeek upgrades the `deepseek-v4-flash` API model in place, so the
100349
+ // direct `resolve` needs no version. OpenRouter instead forked the 0731
100350
+ // release into its own id and left the April preview live under the
100351
+ // unversioned one at a HIGHER price ($0.14/$0.28 vs $0.09/$0.18) so the
100352
+ // OpenRouter route can never use the bare id. it tracks the rolling
100353
+ // `~deepseek/*-latest` pointer (rule 4), which resolves to 0731 at the
100354
+ // same price today and follows the next in-place upgrade on its own,
100355
+ // rather than pinning a dated snapshot that goes stale every release.
100356
+ // 0731 gained a `low` rung and renamed the top to `max`, unlike Pro
100357
+ // (`high, max` direct / `high, xhigh` via OpenRouter). that ladder now
100358
+ // reads the same on BOTH routes — the in-place direct upgrade caught up
100359
+ // to the fork — so `low`, not `high`, is position 0 either way, which is
100355
100360
  // why the OSS effort floor in `main.ts` pins the `high` rung by name.
100356
100361
  "deepseek-flash": {
100357
100362
  displayName: "DeepSeek Flash",
100358
100363
  resolve: "deepseek/deepseek-v4-flash",
100359
- effort: ["high", "max"],
100364
+ effort: ["low", "high", "max"],
100360
100365
  openRouterEffort: ["low", "high", "max"],
100361
- openRouterResolve: "openrouter/deepseek/deepseek-v4-flash-0731"
100366
+ openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
100362
100367
  },
100363
100368
  // legacy aliases — deepseek retires these on 2026-07-24; transparently
100364
100369
  // upgrade existing users to the v4 family via the fallback chain.
@@ -100504,21 +100509,19 @@ var providers = {
100504
100509
  },
100505
100510
  "gemini-flash": {
100506
100511
  displayName: "Gemini Flash",
100507
- resolve: "opencode/gemini-3.5-flash",
100512
+ resolve: "opencode/gemini-3.6-flash",
100508
100513
  effort: ["minimal", "low", "medium", "high"],
100509
- openRouterResolve: "openrouter/google/gemini-3.5-flash"
100514
+ openRouterResolve: "openrouter/google/gemini-3.6-flash"
100510
100515
  },
100511
100516
  "kimi-k2": {
100512
100517
  displayName: "Kimi K2",
100513
- // opencode Zen serves only up to k2.6; the OpenRouter fallback (used for
100514
- // router/oss proxy runs) takes the newer k2.7-code.
100515
- resolve: "opencode/kimi-k2.6",
100518
+ resolve: "opencode/kimi-k2.7-code",
100516
100519
  openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
100517
100520
  },
100518
- // Zen serves m2.5; the funded OpenRouter path tracks the current m2.7.
100521
+ // slug pins the m2 line for DB stability; resolve tracks the current m2.7.
100519
100522
  "minimax-m2.5": {
100520
100523
  displayName: "MiniMax M2",
100521
- resolve: "opencode/minimax-m2.5",
100524
+ resolve: "opencode/minimax-m2.7",
100522
100525
  openRouterResolve: "openrouter/minimax/minimax-m2.7"
100523
100526
  },
100524
100527
  "gpt-5-nano": {
@@ -100738,9 +100741,9 @@ var providers = {
100738
100741
  },
100739
100742
  grok: {
100740
100743
  displayName: "Grok",
100741
- resolve: "openrouter/x-ai/grok-4.3",
100742
- effort: ["none", "low", "medium", "high"],
100743
- openRouterResolve: "openrouter/x-ai/grok-4.3"
100744
+ resolve: "openrouter/x-ai/grok-4.5",
100745
+ effort: ["low", "medium", "high"],
100746
+ openRouterResolve: "openrouter/x-ai/grok-4.5"
100744
100747
  },
100745
100748
  "deepseek-pro": {
100746
100749
  displayName: "DeepSeek Pro",
@@ -100748,11 +100751,13 @@ var providers = {
100748
100751
  effort: ["high", "xhigh"],
100749
100752
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro"
100750
100753
  },
100754
+ // rolling pointer, not the bare id — see `deepseek/deepseek-flash` for why
100755
+ // OpenRouter's unversioned `deepseek-v4-flash` is the stale April preview.
100751
100756
  "deepseek-flash": {
100752
100757
  displayName: "DeepSeek Flash",
100753
- resolve: "openrouter/deepseek/deepseek-v4-flash-0731",
100758
+ resolve: "openrouter/~deepseek/deepseek-v4-flash-latest",
100754
100759
  effort: ["low", "high", "max"],
100755
- openRouterResolve: "openrouter/deepseek/deepseek-v4-flash-0731"
100760
+ openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
100756
100761
  },
100757
100762
  // legacy alias — deepseek retires this on 2026-07-24; transparently
100758
100763
  // upgrade existing users to the v4 family via the fallback chain.
@@ -101653,7 +101658,7 @@ var import_semver = __toESM(require_semver2(), 1);
101653
101658
  // package.json
101654
101659
  var package_default = {
101655
101660
  name: "pullfrog",
101656
- version: "0.1.52",
101661
+ version: "0.1.53",
101657
101662
  type: "module",
101658
101663
  bin: {
101659
101664
  pullfrog: "dist/cli.mjs",
@@ -162830,6 +162835,7 @@ var ShellParams = type({
162830
162835
  "background?": "boolean"
162831
162836
  });
162832
162837
  var detectedSandboxMethod;
162838
+ var usernsProbeError;
162833
162839
  function detectSandboxMethod() {
162834
162840
  if (detectedSandboxMethod !== void 0) {
162835
162841
  return detectedSandboxMethod;
@@ -162863,6 +162869,35 @@ function detectSandboxMethod() {
162863
162869
  }
162864
162870
  } catch {
162865
162871
  }
162872
+ try {
162873
+ const result = spawnSync6(
162874
+ "unshare",
162875
+ [
162876
+ "--user",
162877
+ "--map-root-user",
162878
+ "--pid",
162879
+ "--fork",
162880
+ "--mount",
162881
+ "setpriv",
162882
+ "--no-new-privs",
162883
+ "--bounding-set",
162884
+ "-all",
162885
+ "--inh-caps",
162886
+ "-all",
162887
+ "--ambient-caps",
162888
+ "-all",
162889
+ "true"
162890
+ ],
162891
+ { timeout: 5e3, stdio: ["ignore", "ignore", "pipe"] }
162892
+ );
162893
+ if (result.status === 0) {
162894
+ detectedSandboxMethod = "userns-unshare";
162895
+ log.debug("PID namespace isolation enabled (unprivileged userns unshare)");
162896
+ return "userns-unshare";
162897
+ }
162898
+ usernsProbeError = result.stderr?.toString().trim() || result.error?.message;
162899
+ } catch {
162900
+ }
162866
162901
  detectedSandboxMethod = "none";
162867
162902
  log.info("PID namespace isolation not available");
162868
162903
  return "none";
@@ -162910,7 +162945,7 @@ function spawnShell(params) {
162910
162945
  const ci = process.env.CI === "true";
162911
162946
  if (ci && sandboxMethod === "none") {
162912
162947
  throw new Error(
162913
- "pid namespace isolation is required in CI but unavailable (both unshare and sudo unshare failed)"
162948
+ "PID-namespace isolation is required in CI but unavailable: unprivileged unshare, sudo unshare, and userns-nested unshare all failed. " + (usernsProbeError ? `userns probe said: ${usernsProbeError}. ` : "") + "on a containerized / Kubernetes self-hosted runner the cause is usually one of: the pod's seccomp profile blocks user-namespace creation (Pod Security Standards baseline/restricted); the pod runs as root without CAP_SETFCAP, which the kernel requires to map uid 0; or the node kernel disallows unprivileged user namespaces (Ubuntu >= 23.10, Bottlerocket, Talos). elsewhere, check that `unshare` and `setpriv` (util-linux >= 2.31, which is what added --ambient-caps) are on PATH. see https://docs.pullfrog.com/security#self-hosted-runners"
162914
162949
  );
162915
162950
  }
162916
162951
  const repoRoot = resolveRepoRoot();
@@ -162929,6 +162964,23 @@ function spawnShell(params) {
162929
162964
  spawnOpts
162930
162965
  );
162931
162966
  }
162967
+ if (sandboxMethod === "userns-unshare") {
162968
+ const escaped = params.command.replace(/'/g, "'\\''");
162969
+ return spawn3(
162970
+ "unshare",
162971
+ [
162972
+ "--user",
162973
+ "--map-root-user",
162974
+ "--pid",
162975
+ "--fork",
162976
+ "--mount",
162977
+ "bash",
162978
+ "-c",
162979
+ `${PROC_CLEANUP} ${SOCKET_CLEANUP} ${fsMounts} exec setpriv --no-new-privs --bounding-set -all --inh-caps -all --ambient-caps -all bash -c '${escaped}'`
162980
+ ],
162981
+ spawnOpts
162982
+ );
162983
+ }
162932
162984
  if (sandboxMethod === "sudo-unshare") {
162933
162985
  const envArgs = [];
162934
162986
  for (const [k, v] of Object.entries(params.env)) {
@@ -164858,12 +164910,14 @@ var ShellPermissionInput = type.enumerated("disabled", "restricted", "enabled");
164858
164910
  var PushPermissionInput = type.enumerated("disabled", "restricted", "enabled");
164859
164911
  var StatusChecksInput = type.enumerated("disabled", "enabled");
164860
164912
  var ProgressCommentsInput = type.enumerated("disabled", "enabled");
164913
+ var DebugInput = type.enumerated("disabled", "enabled");
164861
164914
  var JsonPayload = type({
164862
164915
  "~pullfrog": "true",
164863
164916
  version: "string",
164864
164917
  "model?": "string | undefined",
164865
164918
  "modelExplicit?": "boolean | undefined",
164866
164919
  "effort?": "number | string | undefined",
164920
+ "debug?": "boolean | undefined",
164867
164921
  prompt: "string",
164868
164922
  "triggerer?": "string | undefined",
164869
164923
  "baseInstructions?": "string | undefined",
@@ -164898,6 +164952,7 @@ var Inputs = type({
164898
164952
  "prompt_file?": type.string.or("undefined"),
164899
164953
  "model?": type.string.or("undefined"),
164900
164954
  "effort?": type.string.or("undefined"),
164955
+ "debug?": DebugInput.or("undefined"),
164901
164956
  "timeout?": type.string.or("undefined"),
164902
164957
  "push?": PushPermissionInput.or("undefined"),
164903
164958
  "shell?": ShellPermissionInput.or("undefined"),
@@ -164961,6 +165016,7 @@ function resolveNonPromptInputs() {
164961
165016
  return Inputs.omit("prompt", "prompt_file").assert({
164962
165017
  model: core6.getInput("model") || void 0,
164963
165018
  effort: core6.getInput("effort") || void 0,
165019
+ debug: core6.getInput("debug") || void 0,
164964
165020
  timeout: core6.getInput("timeout") || void 0,
164965
165021
  cwd: core6.getInput("cwd") || void 0,
164966
165022
  push: core6.getInput("push") || void 0,
@@ -164977,6 +165033,8 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
164977
165033
  const model = jsonPayload?.model ?? inputs.model ?? repoSettings.model ?? void 0;
164978
165034
  const rawEffort = jsonPayload?.effort ?? inputs.effort ?? repoSettings.effort ?? void 0;
164979
165035
  const effort = rawEffort === void 0 ? void 0 : parseEffortPosition(String(rawEffort));
165036
+ const debug3 = jsonPayload?.debug ?? inputs.debug === "enabled";
165037
+ if (debug3) process.env.LOG_LEVEL = "debug";
164980
165038
  const isNonCollaborator = !isCollaborator(event);
164981
165039
  const repoShell = repoSettings.shell ?? "restricted";
164982
165040
  const inputShell = inputs.shell;
@@ -164997,6 +165055,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
164997
165055
  // the JSON payload). a GHA `model` input or the repo default is not explicit.
164998
165056
  modelExplicit: jsonPayload?.modelExplicit ?? false,
164999
165057
  effort,
165058
+ debug: debug3 || void 0,
165000
165059
  prompt,
165001
165060
  triggerer: jsonPayload?.triggerer ?? // it's not a common use case but GITHUB_ACTOR can be a user when the workflow is manually triggered by a user through GitHub Actions UI
165002
165061
  (!isPullfrog(process.env.GITHUB_ACTOR) ? process.env.GITHUB_ACTOR : void 0),
package/dist/internal.js CHANGED
@@ -207,9 +207,9 @@ var providers = {
207
207
  },
208
208
  "gemini-flash": {
209
209
  displayName: "Gemini Flash",
210
- resolve: "google/gemini-3.5-flash",
210
+ resolve: "google/gemini-3.6-flash",
211
211
  effort: ["minimal", "low", "medium", "high"],
212
- openRouterResolve: "openrouter/google/gemini-3.5-flash"
212
+ openRouterResolve: "openrouter/google/gemini-3.6-flash"
213
213
  }
214
214
  }
215
215
  }),
@@ -219,26 +219,26 @@ var providers = {
219
219
  models: {
220
220
  grok: {
221
221
  displayName: "Grok",
222
- resolve: "xai/grok-4.3",
223
- effort: ["none", "low", "medium", "high"],
224
- openRouterResolve: "openrouter/x-ai/grok-4.3",
222
+ resolve: "xai/grok-4.5",
223
+ effort: ["low", "medium", "high"],
224
+ openRouterResolve: "openrouter/x-ai/grok-4.5",
225
225
  preferred: true
226
226
  },
227
227
  // legacy aliases — xAI retired the entire fast/code-fast line on
228
228
  // 2026-05-15 (https://docs.x.ai/developers/migration/may-15-deprecation)
229
229
  // and now redirects every deprecated text-model slug to grok-4.3 at
230
230
  // standard pricing. fall back to the live `xai/grok` so the alias
231
- // chain resolves to grok-4.3 for both direct-key and OpenRouter users.
231
+ // chain resolves to grok-4.5 for both direct-key and OpenRouter users.
232
232
  "grok-fast": {
233
233
  displayName: "Grok Fast",
234
234
  resolve: "xai/grok-4-1-fast",
235
- openRouterResolve: "openrouter/x-ai/grok-4.3",
235
+ openRouterResolve: "openrouter/x-ai/grok-4.5",
236
236
  fallback: "xai/grok"
237
237
  },
238
238
  "grok-code-fast": {
239
239
  displayName: "Grok Code Fast",
240
240
  resolve: "xai/grok-code-fast-1",
241
- openRouterResolve: "openrouter/x-ai/grok-4.3",
241
+ openRouterResolve: "openrouter/x-ai/grok-4.5",
242
242
  fallback: "xai/grok"
243
243
  }
244
244
  }
@@ -255,20 +255,25 @@ var providers = {
255
255
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro",
256
256
  preferred: true
257
257
  },
258
- // DeepSeek upgraded the `deepseek-v4-flash` API model in place to the
259
- // 0731 release, but OpenRouter forked it into a separate `-0731` id and
260
- // left the April preview live at a HIGHER price — so the direct
261
- // `resolve` needs no version and the OpenRouter route must be pinned.
262
- // 0731 publishes a DIFFERENT OpenRouter ladder to both the April preview
263
- // and Pro (`high, xhigh`) it gained a `low` rung and renamed the top to
264
- // `max`. that makes `low`, not `high`, position 0 on this route, which is
258
+ // DeepSeek upgrades the `deepseek-v4-flash` API model in place, so the
259
+ // direct `resolve` needs no version. OpenRouter instead forked the 0731
260
+ // release into its own id and left the April preview live under the
261
+ // unversioned one at a HIGHER price ($0.14/$0.28 vs $0.09/$0.18) so the
262
+ // OpenRouter route can never use the bare id. it tracks the rolling
263
+ // `~deepseek/*-latest` pointer (rule 4), which resolves to 0731 at the
264
+ // same price today and follows the next in-place upgrade on its own,
265
+ // rather than pinning a dated snapshot that goes stale every release.
266
+ // 0731 gained a `low` rung and renamed the top to `max`, unlike Pro
267
+ // (`high, max` direct / `high, xhigh` via OpenRouter). that ladder now
268
+ // reads the same on BOTH routes — the in-place direct upgrade caught up
269
+ // to the fork — so `low`, not `high`, is position 0 either way, which is
265
270
  // why the OSS effort floor in `main.ts` pins the `high` rung by name.
266
271
  "deepseek-flash": {
267
272
  displayName: "DeepSeek Flash",
268
273
  resolve: "deepseek/deepseek-v4-flash",
269
- effort: ["high", "max"],
274
+ effort: ["low", "high", "max"],
270
275
  openRouterEffort: ["low", "high", "max"],
271
- openRouterResolve: "openrouter/deepseek/deepseek-v4-flash-0731"
276
+ openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
272
277
  },
273
278
  // legacy aliases — deepseek retires these on 2026-07-24; transparently
274
279
  // upgrade existing users to the v4 family via the fallback chain.
@@ -414,21 +419,19 @@ var providers = {
414
419
  },
415
420
  "gemini-flash": {
416
421
  displayName: "Gemini Flash",
417
- resolve: "opencode/gemini-3.5-flash",
422
+ resolve: "opencode/gemini-3.6-flash",
418
423
  effort: ["minimal", "low", "medium", "high"],
419
- openRouterResolve: "openrouter/google/gemini-3.5-flash"
424
+ openRouterResolve: "openrouter/google/gemini-3.6-flash"
420
425
  },
421
426
  "kimi-k2": {
422
427
  displayName: "Kimi K2",
423
- // opencode Zen serves only up to k2.6; the OpenRouter fallback (used for
424
- // router/oss proxy runs) takes the newer k2.7-code.
425
- resolve: "opencode/kimi-k2.6",
428
+ resolve: "opencode/kimi-k2.7-code",
426
429
  openRouterResolve: "openrouter/moonshotai/kimi-k2.7-code"
427
430
  },
428
- // Zen serves m2.5; the funded OpenRouter path tracks the current m2.7.
431
+ // slug pins the m2 line for DB stability; resolve tracks the current m2.7.
429
432
  "minimax-m2.5": {
430
433
  displayName: "MiniMax M2",
431
- resolve: "opencode/minimax-m2.5",
434
+ resolve: "opencode/minimax-m2.7",
432
435
  openRouterResolve: "openrouter/minimax/minimax-m2.7"
433
436
  },
434
437
  "gpt-5-nano": {
@@ -648,9 +651,9 @@ var providers = {
648
651
  },
649
652
  grok: {
650
653
  displayName: "Grok",
651
- resolve: "openrouter/x-ai/grok-4.3",
652
- effort: ["none", "low", "medium", "high"],
653
- openRouterResolve: "openrouter/x-ai/grok-4.3"
654
+ resolve: "openrouter/x-ai/grok-4.5",
655
+ effort: ["low", "medium", "high"],
656
+ openRouterResolve: "openrouter/x-ai/grok-4.5"
654
657
  },
655
658
  "deepseek-pro": {
656
659
  displayName: "DeepSeek Pro",
@@ -658,11 +661,13 @@ var providers = {
658
661
  effort: ["high", "xhigh"],
659
662
  openRouterResolve: "openrouter/deepseek/deepseek-v4-pro"
660
663
  },
664
+ // rolling pointer, not the bare id — see `deepseek/deepseek-flash` for why
665
+ // OpenRouter's unversioned `deepseek-v4-flash` is the stale April preview.
661
666
  "deepseek-flash": {
662
667
  displayName: "DeepSeek Flash",
663
- resolve: "openrouter/deepseek/deepseek-v4-flash-0731",
668
+ resolve: "openrouter/~deepseek/deepseek-v4-flash-latest",
664
669
  effort: ["low", "high", "max"],
665
- openRouterResolve: "openrouter/deepseek/deepseek-v4-flash-0731"
670
+ openRouterResolve: "openrouter/~deepseek/deepseek-v4-flash-latest"
666
671
  },
667
672
  // legacy alias — deepseek retires this on 2026-07-24; transparently
668
673
  // upgrade existing users to the v4 family via the fallback chain.
@@ -6,7 +6,7 @@ export declare const ShellParams: import("arktype/internal/variants/object.ts").
6
6
  working_directory?: string;
7
7
  background?: boolean;
8
8
  }, {}>;
9
- export type SandboxMethod = "unshare" | "sudo-unshare" | "none";
9
+ export type SandboxMethod = "unshare" | "sudo-unshare" | "userns-unshare" | "none";
10
10
  /** get the current sandbox method (for testing/diagnostics) */
11
11
  export declare function getSandboxMethod(): SandboxMethod;
12
12
  /** chars of shell output kept inline in the agent reply. anything past this
@@ -7,6 +7,7 @@ export declare const JsonPayload: import("arktype/internal/variants/object.ts").
7
7
  model?: string | undefined;
8
8
  modelExplicit?: boolean | undefined;
9
9
  effort?: string | number | undefined;
10
+ debug?: boolean | undefined;
10
11
  triggerer?: string | undefined;
11
12
  baseInstructions?: string | undefined;
12
13
  eventInstructions?: string;
@@ -33,6 +34,7 @@ export declare const Inputs: import("arktype/internal/variants/object.ts").Objec
33
34
  prompt_file?: string | undefined;
34
35
  model?: string | undefined;
35
36
  effort?: string | undefined;
37
+ debug?: "disabled" | "enabled" | undefined;
36
38
  timeout?: string | undefined;
37
39
  push?: "disabled" | "enabled" | "restricted" | undefined;
38
40
  shell?: "disabled" | "enabled" | "restricted" | undefined;
@@ -50,6 +52,7 @@ export declare function resolvePayload(resolvedPromptInput: ResolvedPromptInput,
50
52
  model: string | undefined;
51
53
  modelExplicit: boolean;
52
54
  effort: number | undefined;
55
+ debug: true | undefined;
53
56
  prompt: string;
54
57
  triggerer: string | undefined;
55
58
  baseInstructions: string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.52",
3
+ "version": "0.1.53",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "pullfrog": "dist/cli.mjs",