pullfrog 0.1.60 → 0.1.61

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.
@@ -52,6 +52,16 @@ export declare function hasPostRunIssues(issues: PostRunIssues): boolean;
52
52
  * cache hit ratio at a glance. Dashboards that query `WorkflowRun.inputTokens`
53
53
  * directly are seeing the full total, not the log column.
54
54
  */
55
+ /**
56
+ * Which credential actually paid for a run.
57
+ *
58
+ * `subscription` is the one that changes what a cost figure MEANS: a Claude
59
+ * Pro/Max or ChatGPT subscription moves no per-token money, so a dollar figure
60
+ * on such a run is what it WOULD have cost on the API, not spend. Without this
61
+ * recorded, `WorkflowRun.costUsd` silently mixes the two and there is no way
62
+ * after the fact to separate them.
63
+ */
64
+ export type AgentCredential = "subscription" | "api_key" | "gateway" | "bedrock" | "vertex" | "foundry";
55
65
  export interface AgentUsage {
56
66
  agent: string;
57
67
  /** full billable input: non-cached + cache read + cache write */
package/dist/cli.mjs CHANGED
@@ -107355,7 +107355,7 @@ var import_semver = __toESM(require_semver2(), 1);
107355
107355
  // package.json
107356
107356
  var package_default = {
107357
107357
  name: "pullfrog",
107358
- version: "0.1.60",
107358
+ version: "0.1.61",
107359
107359
  type: "module",
107360
107360
  bin: {
107361
107361
  pullfrog: "dist/cli.mjs",
@@ -109481,6 +109481,19 @@ var claude = agent({
109481
109481
  delete env2.CLAUDE_CODE_OAUTH_TOKEN;
109482
109482
  }
109483
109483
  }
109484
+ if (env2.CLAUDE_CODE_USE_BEDROCK) {
109485
+ ctx.toolState.credential = "bedrock";
109486
+ } else if (env2.CLAUDE_CODE_USE_VERTEX) {
109487
+ ctx.toolState.credential = "vertex";
109488
+ } else if (env2.CLAUDE_CODE_USE_FOUNDRY) {
109489
+ ctx.toolState.credential = "foundry";
109490
+ } else if (env2.ANTHROPIC_AUTH_TOKEN) {
109491
+ ctx.toolState.credential = "gateway";
109492
+ } else if (env2.ANTHROPIC_API_KEY) {
109493
+ ctx.toolState.credential = "api_key";
109494
+ } else if (env2.CLAUDE_CODE_OAUTH_TOKEN) {
109495
+ ctx.toolState.credential = "subscription";
109496
+ }
109484
109497
  const effortEnvOverride = env2[CLAUDE_EFFORT_ENV]?.trim();
109485
109498
  if (effortEnvOverride) {
109486
109499
  log2.warning(
@@ -109943,6 +109956,18 @@ function toTodoWriteInput(item) {
109943
109956
  }))
109944
109957
  };
109945
109958
  }
109959
+ var CODEX_MODEL_PRICING = {
109960
+ "gpt-5.6-sol": { input: 5, cacheRead: 0.5, cacheWrite: 6.25, output: 30 },
109961
+ "gpt-5.6-luna": { input: 0.2, cacheRead: 0.02, cacheWrite: 0.25, output: 1.2 },
109962
+ "gpt-5.6-terra": { input: 2, cacheRead: 0.2, cacheWrite: 2.5, output: 12 }
109963
+ };
109964
+ function codexCostUsd(params) {
109965
+ const price = params.model ? CODEX_MODEL_PRICING[params.model] : void 0;
109966
+ if (!price) return void 0;
109967
+ const fresh = Math.max(0, params.input - params.cacheRead - params.cacheWrite);
109968
+ const usd = (fresh * price.input + params.cacheRead * price.cacheRead + params.cacheWrite * price.cacheWrite + params.output * price.output) / 1e6;
109969
+ return usd > 0 ? usd : void 0;
109970
+ }
109946
109971
  async function runCodex2(params) {
109947
109972
  const startTime = performance6.now();
109948
109973
  const thinkingTimer = new ThinkingTimer();
@@ -109954,14 +109979,15 @@ async function runCodex2(params) {
109954
109979
  let turnError = null;
109955
109980
  let lastProviderError = null;
109956
109981
  let stdoutBuffer = "";
109957
- const tokens = { input: 0, cacheRead: 0, output: 0 };
109982
+ const tokens = { input: 0, cacheRead: 0, cacheWrite: 0, output: 0 };
109958
109983
  function buildUsage2() {
109959
109984
  if (tokens.input === 0 && tokens.output === 0) return void 0;
109960
109985
  return {
109961
109986
  agent: "codex",
109962
109987
  inputTokens: tokens.input,
109963
109988
  outputTokens: tokens.output,
109964
- cacheReadTokens: tokens.cacheRead || void 0
109989
+ cacheReadTokens: tokens.cacheRead || void 0,
109990
+ costUsd: codexCostUsd({ model: params.model, ...tokens })
109965
109991
  };
109966
109992
  }
109967
109993
  function onItem(item, phase) {
@@ -110026,6 +110052,7 @@ async function runCodex2(params) {
110026
110052
  case "turn.completed":
110027
110053
  tokens.input += event.usage.input_tokens ?? 0;
110028
110054
  tokens.cacheRead += event.usage.cached_input_tokens ?? 0;
110055
+ tokens.cacheWrite += event.usage.cache_write_input_tokens ?? 0;
110029
110056
  tokens.output += event.usage.output_tokens ?? 0;
110030
110057
  return;
110031
110058
  case "turn.failed":
@@ -110186,6 +110213,11 @@ var codex = agent({
110186
110213
  } else if (process.env.OPENAI_API_KEY) {
110187
110214
  env2.CODEX_API_KEY = process.env.OPENAI_API_KEY;
110188
110215
  }
110216
+ if (codexHomeAuth) {
110217
+ ctx.toolState.credential = "subscription";
110218
+ } else if (env2.CODEX_API_KEY) {
110219
+ ctx.toolState.credential = "api_key";
110220
+ }
110189
110221
  const securityFlags = securityOverrideFlags({
110190
110222
  ctx,
110191
110223
  sandboxMode: ctx.payload.push === "disabled" ? "read-only" : "workspace-write",
@@ -110194,7 +110226,13 @@ var codex = agent({
110194
110226
  model: resolveCodexModel(ctx),
110195
110227
  effortRung: effort.rung && CODEX_EFFORTS.includes(effort.rung) ? effort.rung : void 0
110196
110228
  });
110197
- const runnerArgs = { cliPath, cwd: process.cwd(), env: env2, todoTracker: ctx.todoTracker };
110229
+ const runnerArgs = {
110230
+ cliPath,
110231
+ cwd: process.cwd(),
110232
+ env: env2,
110233
+ todoTracker: ctx.todoTracker,
110234
+ model: resolveCodexModel(ctx)
110235
+ };
110198
110236
  const initial = await runCodex2({
110199
110237
  ...runnerArgs,
110200
110238
  args: [...securityFlags, "exec", "--json", ctx.instructions.full],
@@ -189416,7 +189454,9 @@ var STRING_KEYS = [
189416
189454
  "reviewNodeId",
189417
189455
  "planCommentNodeId",
189418
189456
  "summarySnapshot",
189419
- "model"
189457
+ "model",
189458
+ "agent",
189459
+ "credential"
189420
189460
  ];
189421
189461
  var NUMBER_KEYS = [
189422
189462
  "inputTokens",
@@ -196195,7 +196235,10 @@ var JsonPayload = type({
196195
196235
  // optional so a payload from an older server build (pre-`checkRun`) still parses
196196
196236
  // against a newer action across a rolling deploy.
196197
196237
  "checkRun?": type({ id: "string" }).or("undefined"),
196198
- "generateSummary?": "boolean | undefined"
196238
+ "generateSummary?": "boolean | undefined",
196239
+ // optional so a payload from a pre-canary server build still parses against a
196240
+ // newer action across a rolling deploy.
196241
+ "codexArm?": "boolean | undefined"
196199
196242
  });
196200
196243
  var COLLABORATOR_PERMISSIONS = ["admin", "maintain", "write"];
196201
196244
  function isCollaborator(event) {
@@ -196324,6 +196367,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
196324
196367
  progressComment: jsonPayload?.progressComment,
196325
196368
  checkRun: jsonPayload?.checkRun,
196326
196369
  generateSummary: jsonPayload?.generateSummary,
196370
+ codexArm: jsonPayload?.codexArm,
196327
196371
  // permissions: inputs > repoSettings > fallbacks
196328
196372
  push: inputs.push ?? repoSettings.push ?? "restricted",
196329
196373
  shell: resolvedShell,
@@ -198268,8 +198312,12 @@ async function main() {
198268
198312
  const agent2 = resolveAgent({
198269
198313
  model: resolvedModel,
198270
198314
  proxyModel: payload.proxyModel,
198271
- codexAgent: runContext.repoSettings.codexAgent
198315
+ // the account opt-in and the canary arm are both admissions to codex, so
198316
+ // they OR: an account that opted in explicitly always gets it, and the
198317
+ // canary widens the pool without ever demoting a run that already had it.
198318
+ codexAgent: runContext.repoSettings.codexAgent || payload.codexArm === true
198272
198319
  });
198320
+ toolState.agent = agent2.name;
198273
198321
  const effectiveModel = payload.proxyModel ?? resolvedModel ?? payload.model;
198274
198322
  toolState.model = effectiveModel;
198275
198323
  if (!payload.proxyModel) {
@@ -198627,6 +198675,8 @@ ${instructions.user}` : null,
198627
198675
  if (toolContext) {
198628
198676
  const patch = aggregateUsage(toolState.usageEntries);
198629
198677
  if (toolState.model) patch.model = toolState.model;
198678
+ if (toolState.agent) patch.agent = toolState.agent;
198679
+ if (toolState.credential) patch.credential = toolState.credential;
198630
198680
  if (Object.keys(patch).length > 0) {
198631
198681
  await patchWorkflowRunFields(toolContext, patch);
198632
198682
  }
@@ -199184,7 +199234,7 @@ async function runCli4(input) {
199184
199234
  }
199185
199235
 
199186
199236
  // cli.ts
199187
- var VERSION10 = "0.1.60";
199237
+ var VERSION10 = "0.1.61";
199188
199238
  var bin = basename2(process.argv[1] || "");
199189
199239
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
199190
199240
  var rawArgs = process.argv.slice(2);
@@ -322,6 +322,13 @@ export interface WriteablePayload {
322
322
  } | undefined;
323
323
  /** when true, seed the PR summary tmpfile + persist edits at run end */
324
324
  generateSummary?: boolean | undefined;
325
+ /**
326
+ * the codex canary assigned this run to the EXPERIMENTAL codex harness. rolled
327
+ * once server-side at reservation and stamped on the run row, so the row and the
328
+ * run always agree on which arm this was. ORed with the account opt-in in
329
+ * `resolveAgent` — it widens who reaches codex, never narrows it.
330
+ */
331
+ codexArm?: boolean | undefined;
325
332
  }
326
333
  export type Payload = Readonly<WriteablePayload>;
327
334
  /**
package/dist/index.js CHANGED
@@ -105188,7 +105188,7 @@ var import_semver = __toESM(require_semver2(), 1);
105188
105188
  // package.json
105189
105189
  var package_default = {
105190
105190
  name: "pullfrog",
105191
- version: "0.1.60",
105191
+ version: "0.1.61",
105192
105192
  type: "module",
105193
105193
  bin: {
105194
105194
  pullfrog: "dist/cli.mjs",
@@ -107314,6 +107314,19 @@ var claude = agent({
107314
107314
  delete env2.CLAUDE_CODE_OAUTH_TOKEN;
107315
107315
  }
107316
107316
  }
107317
+ if (env2.CLAUDE_CODE_USE_BEDROCK) {
107318
+ ctx.toolState.credential = "bedrock";
107319
+ } else if (env2.CLAUDE_CODE_USE_VERTEX) {
107320
+ ctx.toolState.credential = "vertex";
107321
+ } else if (env2.CLAUDE_CODE_USE_FOUNDRY) {
107322
+ ctx.toolState.credential = "foundry";
107323
+ } else if (env2.ANTHROPIC_AUTH_TOKEN) {
107324
+ ctx.toolState.credential = "gateway";
107325
+ } else if (env2.ANTHROPIC_API_KEY) {
107326
+ ctx.toolState.credential = "api_key";
107327
+ } else if (env2.CLAUDE_CODE_OAUTH_TOKEN) {
107328
+ ctx.toolState.credential = "subscription";
107329
+ }
107317
107330
  const effortEnvOverride = env2[CLAUDE_EFFORT_ENV]?.trim();
107318
107331
  if (effortEnvOverride) {
107319
107332
  log.warning(
@@ -107823,6 +107836,18 @@ function toTodoWriteInput(item) {
107823
107836
  }))
107824
107837
  };
107825
107838
  }
107839
+ var CODEX_MODEL_PRICING = {
107840
+ "gpt-5.6-sol": { input: 5, cacheRead: 0.5, cacheWrite: 6.25, output: 30 },
107841
+ "gpt-5.6-luna": { input: 0.2, cacheRead: 0.02, cacheWrite: 0.25, output: 1.2 },
107842
+ "gpt-5.6-terra": { input: 2, cacheRead: 0.2, cacheWrite: 2.5, output: 12 }
107843
+ };
107844
+ function codexCostUsd(params) {
107845
+ const price = params.model ? CODEX_MODEL_PRICING[params.model] : void 0;
107846
+ if (!price) return void 0;
107847
+ const fresh = Math.max(0, params.input - params.cacheRead - params.cacheWrite);
107848
+ const usd = (fresh * price.input + params.cacheRead * price.cacheRead + params.cacheWrite * price.cacheWrite + params.output * price.output) / 1e6;
107849
+ return usd > 0 ? usd : void 0;
107850
+ }
107826
107851
  async function runCodex(params) {
107827
107852
  const startTime = performance6.now();
107828
107853
  const thinkingTimer = new ThinkingTimer();
@@ -107834,14 +107859,15 @@ async function runCodex(params) {
107834
107859
  let turnError = null;
107835
107860
  let lastProviderError = null;
107836
107861
  let stdoutBuffer = "";
107837
- const tokens = { input: 0, cacheRead: 0, output: 0 };
107862
+ const tokens = { input: 0, cacheRead: 0, cacheWrite: 0, output: 0 };
107838
107863
  function buildUsage2() {
107839
107864
  if (tokens.input === 0 && tokens.output === 0) return void 0;
107840
107865
  return {
107841
107866
  agent: "codex",
107842
107867
  inputTokens: tokens.input,
107843
107868
  outputTokens: tokens.output,
107844
- cacheReadTokens: tokens.cacheRead || void 0
107869
+ cacheReadTokens: tokens.cacheRead || void 0,
107870
+ costUsd: codexCostUsd({ model: params.model, ...tokens })
107845
107871
  };
107846
107872
  }
107847
107873
  function onItem(item, phase) {
@@ -107906,6 +107932,7 @@ async function runCodex(params) {
107906
107932
  case "turn.completed":
107907
107933
  tokens.input += event.usage.input_tokens ?? 0;
107908
107934
  tokens.cacheRead += event.usage.cached_input_tokens ?? 0;
107935
+ tokens.cacheWrite += event.usage.cache_write_input_tokens ?? 0;
107909
107936
  tokens.output += event.usage.output_tokens ?? 0;
107910
107937
  return;
107911
107938
  case "turn.failed":
@@ -108066,6 +108093,11 @@ var codex = agent({
108066
108093
  } else if (process.env.OPENAI_API_KEY) {
108067
108094
  env2.CODEX_API_KEY = process.env.OPENAI_API_KEY;
108068
108095
  }
108096
+ if (codexHomeAuth) {
108097
+ ctx.toolState.credential = "subscription";
108098
+ } else if (env2.CODEX_API_KEY) {
108099
+ ctx.toolState.credential = "api_key";
108100
+ }
108069
108101
  const securityFlags = securityOverrideFlags({
108070
108102
  ctx,
108071
108103
  sandboxMode: ctx.payload.push === "disabled" ? "read-only" : "workspace-write",
@@ -108074,7 +108106,13 @@ var codex = agent({
108074
108106
  model: resolveCodexModel(ctx),
108075
108107
  effortRung: effort.rung && CODEX_EFFORTS.includes(effort.rung) ? effort.rung : void 0
108076
108108
  });
108077
- const runnerArgs = { cliPath, cwd: process.cwd(), env: env2, todoTracker: ctx.todoTracker };
108109
+ const runnerArgs = {
108110
+ cliPath,
108111
+ cwd: process.cwd(),
108112
+ env: env2,
108113
+ todoTracker: ctx.todoTracker,
108114
+ model: resolveCodexModel(ctx)
108115
+ };
108078
108116
  const initial = await runCodex({
108079
108117
  ...runnerArgs,
108080
108118
  args: [...securityFlags, "exec", "--json", ctx.instructions.full],
@@ -187296,7 +187334,9 @@ var STRING_KEYS = [
187296
187334
  "reviewNodeId",
187297
187335
  "planCommentNodeId",
187298
187336
  "summarySnapshot",
187299
- "model"
187337
+ "model",
187338
+ "agent",
187339
+ "credential"
187300
187340
  ];
187301
187341
  var NUMBER_KEYS = [
187302
187342
  "inputTokens",
@@ -194075,7 +194115,10 @@ var JsonPayload = type({
194075
194115
  // optional so a payload from an older server build (pre-`checkRun`) still parses
194076
194116
  // against a newer action across a rolling deploy.
194077
194117
  "checkRun?": type({ id: "string" }).or("undefined"),
194078
- "generateSummary?": "boolean | undefined"
194118
+ "generateSummary?": "boolean | undefined",
194119
+ // optional so a payload from a pre-canary server build still parses against a
194120
+ // newer action across a rolling deploy.
194121
+ "codexArm?": "boolean | undefined"
194079
194122
  });
194080
194123
  var COLLABORATOR_PERMISSIONS = ["admin", "maintain", "write"];
194081
194124
  function isCollaborator(event) {
@@ -194204,6 +194247,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
194204
194247
  progressComment: jsonPayload?.progressComment,
194205
194248
  checkRun: jsonPayload?.checkRun,
194206
194249
  generateSummary: jsonPayload?.generateSummary,
194250
+ codexArm: jsonPayload?.codexArm,
194207
194251
  // permissions: inputs > repoSettings > fallbacks
194208
194252
  push: inputs.push ?? repoSettings.push ?? "restricted",
194209
194253
  shell: resolvedShell,
@@ -196148,8 +196192,12 @@ async function main() {
196148
196192
  const agent2 = resolveAgent({
196149
196193
  model: resolvedModel,
196150
196194
  proxyModel: payload.proxyModel,
196151
- codexAgent: runContext.repoSettings.codexAgent
196195
+ // the account opt-in and the canary arm are both admissions to codex, so
196196
+ // they OR: an account that opted in explicitly always gets it, and the
196197
+ // canary widens the pool without ever demoting a run that already had it.
196198
+ codexAgent: runContext.repoSettings.codexAgent || payload.codexArm === true
196152
196199
  });
196200
+ toolState.agent = agent2.name;
196153
196201
  const effectiveModel = payload.proxyModel ?? resolvedModel ?? payload.model;
196154
196202
  toolState.model = effectiveModel;
196155
196203
  if (!payload.proxyModel) {
@@ -196507,6 +196555,8 @@ ${instructions.user}` : null,
196507
196555
  if (toolContext) {
196508
196556
  const patch = aggregateUsage(toolState.usageEntries);
196509
196557
  if (toolState.model) patch.model = toolState.model;
196558
+ if (toolState.agent) patch.agent = toolState.agent;
196559
+ if (toolState.credential) patch.credential = toolState.credential;
196510
196560
  if (Object.keys(patch).length > 0) {
196511
196561
  await patchWorkflowRunFields(toolContext, patch);
196512
196562
  }
@@ -1,4 +1,4 @@
1
- import type { AgentUsage } from "./agents/shared.ts";
1
+ import type { AgentCredential, AgentUsage } from "./agents/shared.ts";
2
2
  import type { PrepResult } from "./prep/types.ts";
3
3
  import type { AgentDiagnostic } from "./utils/agentHangReport.ts";
4
4
  import type { DiffCoverageState } from "./utils/diffCoverage.ts";
@@ -136,6 +136,8 @@ export interface ToolState {
136
136
  output?: string | undefined;
137
137
  usageEntries: AgentUsage[];
138
138
  model?: string | undefined;
139
+ agent?: string | undefined;
140
+ credential?: AgentCredential | undefined;
139
141
  modelFallback?: {
140
142
  from: string;
141
143
  } | undefined;
@@ -10,7 +10,7 @@ import type { ToolContext } from "../mcp/server.ts";
10
10
  * don't parse the audit-only `payload`.
11
11
  * Keep in sync with `STRING_FIELDS` in `app/api/workflow-run/[runId]/route.ts`.
12
12
  */
13
- declare const STRING_KEYS: readonly ["prNodeId", "issueNodeId", "reviewNodeId", "planCommentNodeId", "summarySnapshot", "model"];
13
+ declare const STRING_KEYS: readonly ["prNodeId", "issueNodeId", "reviewNodeId", "planCommentNodeId", "summarySnapshot", "model", "agent", "credential"];
14
14
  /**
15
15
  * Number-valued usage fields — aggregated across all agent calls and PATCHed
16
16
  * once at end-of-run. Token counts are Int4 on the DB side (ample for any
@@ -28,6 +28,7 @@ export declare const JsonPayload: import("arktype/internal/variants/object.ts").
28
28
  id: string;
29
29
  } | undefined;
30
30
  generateSummary?: boolean | undefined;
31
+ codexArm?: boolean | undefined;
31
32
  }, {}>;
32
33
  export declare const Inputs: import("arktype/internal/variants/object.ts").ObjectType<{
33
34
  prompt?: string | undefined;
@@ -75,6 +76,7 @@ export declare function resolvePayload(resolvedPromptInput: ResolvedPromptInput,
75
76
  id: string;
76
77
  } | undefined;
77
78
  generateSummary: boolean | undefined;
79
+ codexArm: boolean | undefined;
78
80
  push: import("../external.ts").PushPermission;
79
81
  shell: import("../external.ts").ShellPermission;
80
82
  runStatusCheck: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.60",
3
+ "version": "0.1.61",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "pullfrog": "dist/cli.mjs",