@pome-sh/cli 0.39.1 → 0.41.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "pome-sh",
3
- "version": "0.39.1",
4
- "git_sha": "ad57e84671dd2e6a2fb0dc3b66a78d97b95ed1c7",
5
- "build_time": "2026-08-30T05:08:05.900Z"
3
+ "version": "0.41.0",
4
+ "git_sha": "ea56e5f03b661023c9cea61aca95323fa0a9eada",
5
+ "build_time": "2026-08-30T06:26:23.470Z"
6
6
  }
@@ -1,4 +1,4 @@
1
- import { createHostedClient, perTwinReturnedByCloud, parseTaskFile, runAgentCommand, writeRunArtifactsCore, toTwinHttpEvent, redactJsonl, uploadRunBlobs, scoreFromFinalizeResponse, scoreStatus, evaluationCounts } from './chunk-BS47AE55.js';
1
+ import { createHostedClient, perTwinReturnedByCloud, parseTaskFile, runAgentCommand, writeRunArtifactsCore, toTwinHttpEvent, redactJsonl, uploadRunBlobs, scoreFromFinalizeResponse, scoreStatus, evaluationCounts } from './chunk-STMGQ7GU.js';
2
2
  import { readSeedFileText, parseSeedFileText, soleTwinOf, seedsForTwins } from './chunk-VPXIGCZW.js';
3
3
  import { readManifest, normalizeManifestTwins } from './chunk-DFOQGAKS.js';
4
4
  import { MOUNTED_TWINS, HostedAuthError, HostedDiscardRefusedError, HostedQuotaError, HostedOrchError, HostedTrialError, agentResponseSchema, isMultiTwinSeedEnvelope } from './chunk-NNXVR46L.js';
@@ -73,9 +73,10 @@ async function writeVerdictArtifact(runDir, verdict) {
73
73
  throw err;
74
74
  }
75
75
  }
76
- function looksLikeVerdictArtifactBase(parsed) {
76
+ function isVerdictArtifact(parsed) {
77
77
  if (typeof parsed !== "object" || parsed === null) return false;
78
78
  const v = parsed;
79
+ if (v.version !== VERDICT_ARTIFACT_VERSION) return false;
79
80
  if (v.source !== "cloud-finalize") return false;
80
81
  if (typeof v.session_id !== "string") return false;
81
82
  if (typeof v.task_name !== "string") return false;
@@ -84,6 +85,11 @@ function looksLikeVerdictArtifactBase(parsed) {
84
85
  if (typeof v.finalized_at !== "string") return false;
85
86
  if (typeof v.passed !== "boolean") return false;
86
87
  if (typeof v.score !== "number") return false;
88
+ if (typeof v.state !== "string" || !VALID_STATES.has(v.state)) return false;
89
+ if (typeof v.evaluated !== "number") return false;
90
+ if (typeof v.not_evaluated !== "number") return false;
91
+ if (typeof v.pre_satisfied !== "number") return false;
92
+ if (typeof v.total !== "number") return false;
87
93
  if (!Array.isArray(v.criteria_results)) return false;
88
94
  return v.criteria_results.every((r) => {
89
95
  if (typeof r !== "object" || r === null) return false;
@@ -92,18 +98,6 @@ function looksLikeVerdictArtifactBase(parsed) {
92
98
  return typeof criterion === "object" && criterion !== null && typeof criterion.text === "string" && typeof result.reason === "string" && typeof result.passed === "boolean" && typeof result.skipped === "boolean";
93
99
  });
94
100
  }
95
- function isVerdictArtifact(parsed) {
96
- if (!looksLikeVerdictArtifactBase(parsed)) return false;
97
- const v = parsed;
98
- if (v.version !== VERDICT_ARTIFACT_VERSION) return false;
99
- if (typeof v.task_path !== "string") return false;
100
- if (typeof v.state !== "string" || !VALID_STATES.has(v.state)) return false;
101
- if (typeof v.evaluated !== "number") return false;
102
- if (typeof v.not_evaluated !== "number") return false;
103
- if (typeof v.pre_satisfied !== "number") return false;
104
- if (typeof v.total !== "number") return false;
105
- return true;
106
- }
107
101
  var MISSING_ERROR_CODES = /* @__PURE__ */ new Set([
108
102
  "ENOENT",
109
103
  "ENOTDIR",
@@ -128,21 +122,17 @@ async function readVerdictArtifactDetailed(runDir) {
128
122
  } catch {
129
123
  return { status: "unreadable" };
130
124
  }
131
- if (!looksLikeVerdictArtifactBase(parsed)) return { status: "unreadable" };
132
- const version = typeof parsed.version === "number" ? parsed.version : null;
133
- if (version !== VERDICT_ARTIFACT_VERSION) return { status: "stale-version", version };
134
125
  if (!isVerdictArtifact(parsed)) return { status: "unreadable" };
135
126
  return { status: "ok", trial: { runDir, verdict: parsed } };
136
127
  }
137
128
  async function scanVerdictArtifactsDetailed(artifactsRoot) {
138
129
  const trials = [];
139
- const staleVersionDirs = [];
140
130
  const unreadableDirs = [];
141
131
  let slugs;
142
132
  try {
143
133
  slugs = await readdir(artifactsRoot);
144
134
  } catch {
145
- return { trials, staleVersionDirs, unreadableDirs };
135
+ return { trials, unreadableDirs };
146
136
  }
147
137
  for (const slug of slugs) {
148
138
  const slugDir = join(artifactsRoot, slug);
@@ -156,33 +146,20 @@ async function scanVerdictArtifactsDetailed(artifactsRoot) {
156
146
  const runDir = join(slugDir, runId);
157
147
  const result = await readVerdictArtifactDetailed(runDir);
158
148
  if (result.status === "ok") trials.push(result.trial);
159
- else if (result.status === "stale-version") staleVersionDirs.push(runDir);
160
149
  else if (result.status === "unreadable") unreadableDirs.push(runDir);
161
150
  }
162
151
  }
163
152
  unreadableDirs.sort();
164
- return { trials, staleVersionDirs, unreadableDirs };
153
+ return { trials, unreadableDirs };
165
154
  }
166
155
  async function discoverRunSet(target) {
167
156
  const anchorResult = await readVerdictArtifactDetailed(target);
168
- if (anchorResult.status === "stale-version") {
169
- return {
170
- kind: "trial-dir",
171
- set: null,
172
- incompleteSet: null,
173
- totalSets: 0,
174
- staleVersionCount: 1,
175
- unreadableCount: 0,
176
- unreadablePaths: []
177
- };
178
- }
179
157
  if (anchorResult.status === "unreadable") {
180
158
  return {
181
159
  kind: "trial-dir",
182
160
  set: null,
183
161
  incompleteSet: null,
184
162
  totalSets: 0,
185
- staleVersionCount: 0,
186
163
  unreadableCount: 1,
187
164
  unreadablePaths: [target]
188
165
  };
@@ -190,7 +167,7 @@ async function discoverRunSet(target) {
190
167
  if (anchorResult.status === "ok") {
191
168
  const anchor = anchorResult.trial;
192
169
  const root = join(target, "..", "..");
193
- const { trials: trials2, staleVersionDirs: staleVersionDirs2, unreadableDirs: unreadableDirs2 } = await scanVerdictArtifactsDetailed(root);
170
+ const { trials: trials2, unreadableDirs: unreadableDirs2 } = await scanVerdictArtifactsDetailed(root);
194
171
  const sets2 = groupRunSets(trials2);
195
172
  const own = sets2.find(
196
173
  (s) => anchor.verdict.group_id !== null && s.groupId === anchor.verdict.group_id || anchor.verdict.group_id === null && s.trials.length === 1 && s.trials[0].verdict.session_id === anchor.verdict.session_id
@@ -200,12 +177,11 @@ async function discoverRunSet(target) {
200
177
  set: own,
201
178
  incompleteSet: null,
202
179
  totalSets: Math.max(sets2.length, 1),
203
- staleVersionCount: staleVersionDirs2.length,
204
180
  unreadableCount: unreadableDirs2.length,
205
181
  unreadablePaths: unreadableDirs2
206
182
  };
207
183
  }
208
- const { trials, staleVersionDirs, unreadableDirs } = await scanVerdictArtifactsDetailed(target);
184
+ const { trials, unreadableDirs } = await scanVerdictArtifactsDetailed(target);
209
185
  const sets = groupRunSets(trials);
210
186
  const failedSet = latestFailedRunSet(sets);
211
187
  return {
@@ -213,7 +189,6 @@ async function discoverRunSet(target) {
213
189
  set: failedSet,
214
190
  incompleteSet: failedSet ? null : latestIncompleteRunSet(sets),
215
191
  totalSets: sets.length,
216
- staleVersionCount: staleVersionDirs.length,
217
192
  unreadableCount: unreadableDirs.length,
218
193
  unreadablePaths: unreadableDirs
219
194
  };
@@ -440,7 +415,7 @@ function mapHttpError(status, json) {
440
415
  }
441
416
  if (status === 404) {
442
417
  return new HostedOrchError(
443
- err?.message ?? "POST /v1/agents is not available at this API URL. Upgrade pome-cloud or check that --api-url/POME_API_URL points at a version that supports agent registration."
418
+ err?.message ?? "POST /v1/agents returned 404 \u2014 check that --api-url/POME_API_URL points at a Pome control plane."
444
419
  );
445
420
  }
446
421
  return new HostedOrchError(err?.message ?? `POST /v1/agents \u2192 HTTP ${status}`);
@@ -1255,4 +1230,4 @@ async function abandonBestEffort(client, sessionId, errorCode) {
1255
1230
  }
1256
1231
  }
1257
1232
 
1258
- export { DEFAULT_CONTROL_PLANE_URL, DEFAULT_DASHBOARD_URL, DEFAULT_DOCS_SITE_ORIGIN, SESSION_TWIN_NAMES, VERDICT_ARTIFACT_VERSION, clearLocalCredentials, discoverRunSet, ensurePomeGitignored, friendlyHostedError, persistCredentialsAfterLogin, postAgentResolver, readLinkCache, resolveCachedAgentId, resolveCredentials, resolveRunAgentIdentity, resolveSeams, runSessionCreate, runSessionList, runSessionStop, runTaskHosted, writeLinkCache };
1233
+ export { DEFAULT_CONTROL_PLANE_URL, DEFAULT_DASHBOARD_URL, DEFAULT_DOCS_SITE_ORIGIN, SESSION_TWIN_NAMES, clearLocalCredentials, discoverRunSet, ensurePomeGitignored, friendlyHostedError, persistCredentialsAfterLogin, postAgentResolver, readLinkCache, resolveCachedAgentId, resolveCredentials, resolveRunAgentIdentity, resolveSeams, runSessionCreate, runSessionList, runSessionStop, runTaskHosted, writeLinkCache };
@@ -1525,7 +1525,7 @@ function scoreFromFinalizeResponse(finalized) {
1525
1525
  }
1526
1526
  function redactJsonl(body) {
1527
1527
  const lines = body.split("\n");
1528
- const redacted = lines.filter((line) => line.trim().length > 0).map((line) => {
1528
+ const redacted = lines.map((line) => line.trim()).filter((line) => line.length > 0).map((line) => {
1529
1529
  try {
1530
1530
  return JSON.stringify(redactSecrets(JSON.parse(line)));
1531
1531
  } catch {
@@ -1,4 +1,4 @@
1
- import { parseTaskFile, seedStateForTwin, runAgentCommand, writeRunArtifactsCore } from './chunk-BS47AE55.js';
1
+ import { parseTaskFile, seedStateForTwin, runAgentCommand, writeRunArtifactsCore } from './chunk-STMGQ7GU.js';
2
2
  import { createRecorder, bootTwin } from './chunk-5NPGY73F.js';
3
3
  import { getAvailablePort } from './chunk-XDU6TD4O.js';
4
4
  import { buildEgressAllowlist, readBlockedEgress } from './chunk-CBFKZZBR.js';
@@ -1,7 +1,7 @@
1
1
  import { newGroupId } from './chunk-JNXZBK3O.js';
2
2
  import { DemoCapacityError, capacityLabel, parseCapacityMarker, capacityKindFrom } from './chunk-ZX4WNSZ5.js';
3
- import { runTask, demoTaskPath, DEMO_TASK_NAME, DEMO_REPO } from './chunk-AEFXTGW3.js';
4
- import { createHostedClient, parseTaskFile, uploadRunBlobs, scoreFromFinalizeResponse, scoreStatus, isNarrated, outcomeOf, criterionPhrase, narratorReadingLines } from './chunk-BS47AE55.js';
3
+ import { runTask, demoTaskPath, DEMO_TASK_NAME, DEMO_REPO } from './chunk-WERG4AUH.js';
4
+ import { createHostedClient, parseTaskFile, uploadRunBlobs, scoreFromFinalizeResponse, scoreStatus, isNarrated, outcomeOf, criterionPhrase, narratorReadingLines } from './chunk-STMGQ7GU.js';
5
5
  import './chunk-NW7HGA2K.js';
6
6
  import './chunk-PASFBRK4.js';
7
7
  import './chunk-3FZY376K.js';
@@ -1,6 +1,6 @@
1
1
  import { newGroupId } from './chunk-JNXZBK3O.js';
2
- import { runTaskHosted, resolveRunAgentIdentity } from './chunk-3NQUFSIX.js';
3
- import { createHostedClient, parseTaskFile, outcomeOf, isNarrated, criterionPhrase, narratorReadingLines } from './chunk-BS47AE55.js';
2
+ import { runTaskHosted, resolveRunAgentIdentity } from './chunk-KD6OUKCT.js';
3
+ import { createHostedClient, parseTaskFile, outcomeOf, isNarrated, criterionPhrase, narratorReadingLines } from './chunk-STMGQ7GU.js';
4
4
  import './chunk-VPXIGCZW.js';
5
5
  import './chunk-NW7HGA2K.js';
6
6
  import './chunk-PASFBRK4.js';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { DEFAULT_CONTROL_PLANE_URL, DEFAULT_DASHBOARD_URL, clearLocalCredentials, friendlyHostedError, SESSION_TWIN_NAMES, runSessionCreate, runSessionList, runSessionStop, resolveCredentials, runTaskHosted, discoverRunSet, VERDICT_ARTIFACT_VERSION, persistCredentialsAfterLogin, DEFAULT_DOCS_SITE_ORIGIN, resolveSeams, resolveCachedAgentId, readLinkCache, postAgentResolver, writeLinkCache, ensurePomeGitignored } from '../../chunk-3NQUFSIX.js';
3
- import { assetPath, runTask, resolvePackageRoot, DEMO_TASK_NAME, demoTaskPath } from '../../chunk-AEFXTGW3.js';
4
- import { parseTaskFile, scoreStatus, runScoreLine, narratorReadingLines, readLatestRun, readMetaSummary, outcomeOf, readConfigTwins, scoreCountsSummary, criterionRowLine, readCodeCriteria, createHostedClient, toTwinHttpEvent, redactJsonl, scoreFromFinalizeResponse, parseGitHubSeedState, uploadRunBlobs, isPreSatisfied } from '../../chunk-BS47AE55.js';
2
+ import { DEFAULT_CONTROL_PLANE_URL, DEFAULT_DASHBOARD_URL, clearLocalCredentials, friendlyHostedError, SESSION_TWIN_NAMES, runSessionCreate, runSessionList, runSessionStop, resolveCredentials, runTaskHosted, discoverRunSet, persistCredentialsAfterLogin, DEFAULT_DOCS_SITE_ORIGIN, resolveSeams, resolveCachedAgentId, readLinkCache, postAgentResolver, writeLinkCache, ensurePomeGitignored } from '../../chunk-KD6OUKCT.js';
3
+ import { assetPath, runTask, resolvePackageRoot, DEMO_TASK_NAME, demoTaskPath } from '../../chunk-WERG4AUH.js';
4
+ import { parseTaskFile, scoreStatus, runScoreLine, narratorReadingLines, readLatestRun, readMetaSummary, outcomeOf, readConfigTwins, scoreCountsSummary, criterionRowLine, readCodeCriteria, createHostedClient, redactJsonl, scoreFromFinalizeResponse, parseGitHubSeedState, uploadRunBlobs, isPreSatisfied } from '../../chunk-STMGQ7GU.js';
5
5
  import '../../chunk-VPXIGCZW.js';
6
6
  import '../../chunk-NW7HGA2K.js';
7
7
  import { GMAIL_CHECKS } from '../../chunk-JM6VS62R.js';
@@ -21,7 +21,7 @@ import { oneOf, defineCheck, repoRef, VACUITY_SENTINEL_NUMBER, childStatePath, V
21
21
  import '../../chunk-YBWG5JK2.js';
22
22
  import '../../chunk-HRAD7MRX.js';
23
23
  import { eventSchema, isLegacyEventRow } from '../../chunk-6KJC4BTO.js';
24
- import { redactEvent, redactSecrets } from '../../chunk-SG6ZTIMT.js';
24
+ import { redactSecrets, redactEvent } from '../../chunk-SG6ZTIMT.js';
25
25
  import '../../chunk-2K6BJ3PI.js';
26
26
  import '../../chunk-FBSA5L36.js';
27
27
  import { Command } from 'commander';
@@ -38,17 +38,37 @@ import Anthropic from '@anthropic-ai/sdk';
38
38
  import { zodOutputFormat } from '@anthropic-ai/sdk/helpers/zod';
39
39
 
40
40
  async function readEventsJsonl(runDir) {
41
+ const file = resolve(runDir, "events.jsonl");
41
42
  let raw;
42
43
  try {
43
- raw = await readFile(join(runDir, "events.jsonl"), "utf8");
44
+ raw = await readFile(file, "utf8");
44
45
  } catch (err) {
45
46
  if (err.code === "ENOENT") {
46
47
  return { kind: "missing" };
47
48
  }
48
49
  throw err;
49
50
  }
50
- const rows = raw.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => JSON.parse(line));
51
- const events = rows.map((row) => eventSchema.parse(row));
51
+ const events = [];
52
+ const lines = raw.split("\n");
53
+ for (let i = 0; i < lines.length; i += 1) {
54
+ const line = lines[i].trim();
55
+ if (line.length === 0) continue;
56
+ let parsed;
57
+ try {
58
+ parsed = JSON.parse(line);
59
+ } catch (err) {
60
+ throw new Error(
61
+ `pome inspect: ${file} line ${i + 1} is not valid JSON \u2014 ${err instanceof Error ? err.message : String(err)}. Re-run the task to record the trace again.`
62
+ );
63
+ }
64
+ const result = eventSchema.safeParse(parsed);
65
+ if (!result.success) {
66
+ throw new Error(
67
+ `pome inspect: ${file} line ${i + 1} is not a recorded event \u2014 ${result.error.issues[0]?.message ?? "unrecognized shape"}. Re-run the task to record the trace again.`
68
+ );
69
+ }
70
+ events.push(result.data);
71
+ }
52
72
  return { kind: "events", events };
53
73
  }
54
74
  function computeTraceHealth(input) {
@@ -290,8 +310,8 @@ async function startCallbackServer(expectedState) {
290
310
  let rejectCode = null;
291
311
  let settled = false;
292
312
  let deliveredCode = false;
293
- const codePromise = new Promise((resolve5, reject) => {
294
- resolveCode = resolve5;
313
+ const codePromise = new Promise((resolve6, reject) => {
314
+ resolveCode = resolve6;
295
315
  rejectCode = reject;
296
316
  });
297
317
  const fail = (err) => {
@@ -335,9 +355,9 @@ async function startCallbackServer(expectedState) {
335
355
  succeed(code);
336
356
  void closeServer(server);
337
357
  });
338
- await new Promise((resolve5, reject) => {
358
+ await new Promise((resolve6, reject) => {
339
359
  server?.once("error", reject);
340
- server?.listen(0, "127.0.0.1", resolve5);
360
+ server?.listen(0, "127.0.0.1", resolve6);
341
361
  });
342
362
  const address = server.address();
343
363
  if (!address || typeof address === "string") {
@@ -374,21 +394,21 @@ async function startCallbackServer(expectedState) {
374
394
  };
375
395
  }
376
396
  function closeServer(server) {
377
- return new Promise((resolve5) => {
378
- server?.close(() => resolve5());
397
+ return new Promise((resolve6) => {
398
+ server?.close(() => resolve6());
379
399
  });
380
400
  }
381
401
  async function openBrowser(url) {
382
402
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "powershell.exe" : "xdg-open";
383
403
  const args = process.platform === "win32" ? ["-NoProfile", "-Command", "Start-Process", url] : [url];
384
- await new Promise((resolve5) => {
404
+ await new Promise((resolve6) => {
385
405
  execFile(command, args, (error) => {
386
406
  if (error) {
387
407
  console.error(
388
408
  "Could not open a browser automatically \u2014 copy the URL above into a browser."
389
409
  );
390
410
  }
391
- resolve5();
411
+ resolve6();
392
412
  });
393
413
  });
394
414
  }
@@ -591,7 +611,7 @@ function topicMatchesFilter(t, filter) {
591
611
  return t.id.includes(f) || t.title.toLowerCase().includes(f) || t.keywords.some((k) => k.toLowerCase().includes(f)) || t.path.toLowerCase().includes(f);
592
612
  }
593
613
  async function promptLine(rl, q) {
594
- return await new Promise((resolve5) => rl.question(q, resolve5));
614
+ return await new Promise((resolve6) => rl.question(q, resolve6));
595
615
  }
596
616
  function printTopicUrl(topic, site) {
597
617
  console.log(`${site}${topic.path}`);
@@ -3605,13 +3625,19 @@ function validateJsonl(name, raw) {
3605
3625
  const line = lines[i].trim();
3606
3626
  if (line.length === 0) continue;
3607
3627
  nonEmpty += 1;
3628
+ let parsed;
3608
3629
  try {
3609
- JSON.parse(line);
3630
+ parsed = JSON.parse(line);
3610
3631
  } catch {
3611
3632
  throw new HostedUsageError(
3612
3633
  `pome eval: ${name} is corrupt \u2014 line ${i + 1} is not valid JSON.`
3613
3634
  );
3614
3635
  }
3636
+ if (name === "events.jsonl" && isLegacyEventRow(parsed)) {
3637
+ throw new HostedUsageError(
3638
+ `pome eval: events.jsonl line ${i + 1} has no "kind" \u2014 it was recorded before the unified event shape, and the control plane refuses it. Re-run the task to record the trace again.`
3639
+ );
3640
+ }
3615
3641
  }
3616
3642
  if (name === "events.jsonl" && nonEmpty === 0) {
3617
3643
  throw new HostedUsageError(
@@ -3744,11 +3770,7 @@ async function runEval(options) {
3744
3770
  baseUrl: options.hosted.baseUrl,
3745
3771
  apiKey: options.hosted.apiKey
3746
3772
  });
3747
- const eventsJsonl = artifacts.eventsJsonl.split("\n").filter((line) => line.trim().length > 0).map((line) => {
3748
- const parsed = JSON.parse(line);
3749
- const event2 = isLegacyEventRow(parsed) ? toTwinHttpEvent(parsed) : parsed;
3750
- return JSON.stringify(redactEvent(event2));
3751
- }).join("\n") + "\n";
3773
+ const eventsJsonl = redactJsonl(artifacts.eventsJsonl);
3752
3774
  const blobs = {
3753
3775
  eventsJsonl,
3754
3776
  stateInitialJson: JSON.stringify(
@@ -4575,7 +4597,7 @@ function firstSentence(description) {
4575
4597
  function resolveExampleRef(env = process.env) {
4576
4598
  const override = env.POME_EXAMPLE_REF?.trim();
4577
4599
  if (override) return override;
4578
- const baked = "ad57e84671dd2e6a2fb0dc3b66a78d97b95ed1c7".trim() ;
4600
+ const baked = "ea56e5f03b661023c9cea61aca95323fa0a9eada".trim() ;
4579
4601
  return FULL_SHA.test(baked) ? baked : "main";
4580
4602
  }
4581
4603
  function rawUrlFor(example, file, ref) {
@@ -4929,7 +4951,7 @@ var DEFAULT_AGENT_COMMAND = `node ${DEFAULT_AGENT_FILE}`;
4929
4951
  var MANIFEST_SCHEMA_URL = "https://pome.sh/schemas/v1/pome.json";
4930
4952
  var MAX_UNREADABLE_PATHS_SHOWN = 5;
4931
4953
  function readPackageVersion() {
4932
- if ("0.39.1".length > 0) return "0.39.1";
4954
+ if ("0.41.0".length > 0) return "0.41.0";
4933
4955
  try {
4934
4956
  const here = dirname(fileURLToPath(import.meta.url));
4935
4957
  const candidates = [
@@ -4947,8 +4969,16 @@ function readPackageVersion() {
4947
4969
  }
4948
4970
  return "0.0.0";
4949
4971
  }
4972
+ function globals(cmd) {
4973
+ return cmd.optsWithGlobals();
4974
+ }
4950
4975
  function createProgram() {
4951
4976
  const program = new Command();
4977
+ program.configureHelp({ showGlobalOptions: true }).option(
4978
+ "--api-url <url>",
4979
+ "Control-plane base URL.",
4980
+ process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
4981
+ ).option("--artifacts-dir <dir>", "Directory for run artifacts", "runs");
4952
4982
  program.name("pome").description(
4953
4983
  "Test AI agents against digital twins of real SaaS APIs. Runs are recorded to app.pome.sh. Start with `pome demo`."
4954
4984
  ).version(PACKAGE_VERSION).showHelpAfterError("(add --help for usage)");
@@ -5060,10 +5090,6 @@ function createProgram() {
5060
5090
  console.error(postInitMessage);
5061
5091
  });
5062
5092
  program.command("login").summary("Sign in and save an API key").description("Sign in with Clerk and store a hosted team API key (macOS Keychain or ~/.pome/credentials.json)").option(
5063
- "--api-url <url>",
5064
- "Control-plane base URL.",
5065
- process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
5066
- ).option(
5067
5093
  "--dashboard-url <url>",
5068
5094
  "App URL for Clerk sign-in (must serve /cli/login).",
5069
5095
  process.env.POME_DASHBOARD_URL ?? DEFAULT_DASHBOARD_URL
@@ -5072,8 +5098,8 @@ function createProgram() {
5072
5098
  "Label for the API key minted by this login.",
5073
5099
  "pome login"
5074
5100
  ).action(
5075
- async (options) => {
5076
- await loginWithClerk(options);
5101
+ async (options, cmd) => {
5102
+ await loginWithClerk({ ...options, apiUrl: globals(cmd).apiUrl });
5077
5103
  }
5078
5104
  );
5079
5105
  program.command("logout").summary("Delete the saved API key").description("Remove locally stored hosted credentials (Keychain entry and/or ~/.pome/credentials.json)").action(async () => {
@@ -5130,17 +5156,13 @@ function createProgram() {
5130
5156
  "One per declared parameter. Repeat the flag.",
5131
5157
  (value, previous = []) => [...previous, value],
5132
5158
  []
5133
- ).option(
5134
- "--api-url <url>",
5135
- "Control-plane URL",
5136
- process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
5137
5159
  ).description(
5138
5160
  "Add one [code] criterion by picking a declared check \u2014 pome writes the English"
5139
- ).action(async (file, opts) => {
5161
+ ).action(async (file, opts, cmd) => {
5140
5162
  await runChecksAddCommand(file, {
5141
5163
  check: opts.check,
5142
5164
  arg: opts.arg,
5143
- apiBaseUrl: opts.apiUrl
5165
+ apiBaseUrl: globals(cmd).apiUrl
5144
5166
  });
5145
5167
  });
5146
5168
  checks.command("lint").argument("<file...>", "Task markdown file(s) \u2014 shell globs work: tasks/*.md").description("Report [code] criteria that bind no declared check, so are never graded").action(async (files) => {
@@ -5156,10 +5178,6 @@ function createProgram() {
5156
5178
  "Register a cloud entity (agent, ...) and link this project to it"
5157
5179
  );
5158
5180
  register.command("agent").argument("<name>", 'Human-readable agent name (e.g. "triage-bot")').option(
5159
- "--api-url <url>",
5160
- "Control-plane URL",
5161
- process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
5162
- ).option(
5163
5181
  "--force",
5164
5182
  "Re-resolve the agent even when .pome/link.json already links one",
5165
5183
  false
@@ -5169,10 +5187,10 @@ function createProgram() {
5169
5187
  ).description(
5170
5188
  "Create a cloud agent under the current team and write agent.slug to pome.json"
5171
5189
  ).action(
5172
- async (name, opts) => {
5190
+ async (name, opts, cmd) => {
5173
5191
  try {
5174
5192
  await runRegisterAgent({
5175
- apiBaseUrl: opts.apiUrl,
5193
+ apiBaseUrl: globals(cmd).apiUrl,
5176
5194
  dashboardBaseUrl: process.env.POME_DASHBOARD_URL ?? DEFAULT_DASHBOARD_URL,
5177
5195
  name,
5178
5196
  force: opts.force,
@@ -5206,18 +5224,14 @@ function createProgram() {
5206
5224
  ).option(
5207
5225
  "--seed <path>",
5208
5226
  "Start the sandbox from a JSON or YAML seed file instead of each twin's default. A seed REPLACES the default; it does not merge. Same file `pome twin start --seed` takes; write one with `pome twin seed <name>`."
5209
- ).option(
5210
- "--api-url <url>",
5211
- "Control-plane URL",
5212
- process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
5213
5227
  ).option(
5214
5228
  "--secrets-file <path>",
5215
5229
  "Write shell exports containing session secrets to a local file with mode 0600"
5216
5230
  ).option("--json", "Print the sandbox as JSON instead of the human summary.", false).action(
5217
- async (opts) => {
5231
+ async (opts, cmd) => {
5218
5232
  try {
5219
5233
  await runSessionCreate({
5220
- apiBaseUrl: opts.apiUrl,
5234
+ apiBaseUrl: globals(cmd).apiUrl,
5221
5235
  twins: opts.twin ?? [],
5222
5236
  json: opts.json,
5223
5237
  secretsFile: opts.secretsFile,
@@ -5229,16 +5243,12 @@ function createProgram() {
5229
5243
  }
5230
5244
  }
5231
5245
  );
5232
- session.command("list").description("List hosted sandboxes (defaults to --state running, like the dashboard)").option(
5233
- "--api-url <url>",
5234
- "Control-plane URL",
5235
- process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
5236
- ).option("--limit <n>", "Max rows", "20").option(
5246
+ session.command("list").description("List hosted sandboxes (defaults to --state running, like the dashboard)").option("--limit <n>", "Max rows", "20").option(
5237
5247
  "--state <state>",
5238
5248
  "Filter by sandbox state: running, ready, done, expired, or all. `running` also matches the server-side `ready` state, the way the dashboard shows them in one column.",
5239
5249
  "running"
5240
5250
  ).option("--json", "Print the sandboxes as JSON.", false).action(
5241
- async (opts) => {
5251
+ async (opts, cmd) => {
5242
5252
  const validStates = [
5243
5253
  "running",
5244
5254
  "ready",
@@ -5255,7 +5265,7 @@ function createProgram() {
5255
5265
  }
5256
5266
  try {
5257
5267
  await runSessionList({
5258
- apiBaseUrl: opts.apiUrl,
5268
+ apiBaseUrl: globals(cmd).apiUrl,
5259
5269
  limit: Number.parseInt(opts.limit, 10) || 20,
5260
5270
  state: opts.state,
5261
5271
  json: opts.json
@@ -5267,18 +5277,14 @@ function createProgram() {
5267
5277
  }
5268
5278
  );
5269
5279
  session.command("stop").description("Stop a hosted sandbox").argument("<session-id>", "Sandbox id (ses_\u2026)").option(
5270
- "--api-url <url>",
5271
- "Control-plane URL",
5272
- process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
5273
- ).option(
5274
5280
  "--discard",
5275
5281
  "Confirm destroying a session whose run has not been graded",
5276
5282
  false
5277
5283
  ).action(
5278
- async (sessionId, opts) => {
5284
+ async (sessionId, opts, cmd) => {
5279
5285
  try {
5280
5286
  await runSessionStop({
5281
- apiBaseUrl: opts.apiUrl,
5287
+ apiBaseUrl: globals(cmd).apiUrl,
5282
5288
  sessionId,
5283
5289
  discard: opts.discard === true
5284
5290
  });
@@ -5295,10 +5301,6 @@ function createProgram() {
5295
5301
  ).option("--agent <command>", "Command that starts your agent.").option(
5296
5302
  "-n, --trials <count>",
5297
5303
  "Number of trials to run as one group, 1 to 20. Hosted only; defaults to the task's `runs` field."
5298
- ).option("--artifacts-dir <dir>", "Directory for run artifacts", "runs").option(
5299
- "--api-url <url>",
5300
- "Control-plane base URL.",
5301
- process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
5302
5304
  ).option("--agent-model <name>", "Informational; model name recorded on the run.", "unknown").option(
5303
5305
  "--agent-version <version>",
5304
5306
  "Override the manifest's agent.version."
@@ -5311,7 +5313,8 @@ function createProgram() {
5311
5313
  ).description(
5312
5314
  "Run a task, or every task in a directory, and print the score. With no path, runs the demo task Pome copies into your project on first use. Refuses to start when `pome doctor` fails, and there is no --force. See `pome docs cli-run` for trial groups and exit codes."
5313
5315
  ).action(
5314
- async (target, options) => {
5316
+ async (target, options, cmd) => {
5317
+ const { apiUrl, artifactsDir } = globals(cmd);
5315
5318
  let trialsFlag;
5316
5319
  if (options.trials !== void 0) {
5317
5320
  try {
@@ -5400,7 +5403,7 @@ function createProgram() {
5400
5403
  }
5401
5404
  }
5402
5405
  try {
5403
- hostedCreds = useLocal ? null : await resolveCredentials({ apiBaseUrl: options.apiUrl });
5406
+ hostedCreds = useLocal ? null : await resolveCredentials({ apiBaseUrl: apiUrl });
5404
5407
  } catch (err) {
5405
5408
  const code = exitCodeFor(err);
5406
5409
  console.error(err instanceof Error ? err.message : String(err));
@@ -5424,7 +5427,7 @@ function createProgram() {
5424
5427
  taskForRuns.config.runs
5425
5428
  );
5426
5429
  if (k > 1) {
5427
- const { runTrialGroup } = await import('../../runTrialGroup-YACZ2WTV.js');
5430
+ const { runTrialGroup } = await import('../../runTrialGroup-OGTLUB2X.js');
5428
5431
  const fileForRerun = relative(process.cwd(), file);
5429
5432
  const rerunCommand = defaultTask ? options.trials !== void 0 ? `pome run -n ${k}` : "pome run" : `pome run ${fileForRerun && !fileForRerun.startsWith("..") ? fileForRerun : file} -n ${k}`;
5430
5433
  const groupResult = await runTrialGroup({
@@ -5432,7 +5435,7 @@ function createProgram() {
5432
5435
  agentCommand,
5433
5436
  agentCommandSource: options.agent ? "--agent" : configCommand ? "pome.json" : "built-in default",
5434
5437
  trials: k,
5435
- artifactsDir: options.artifactsDir,
5438
+ artifactsDir,
5436
5439
  hosted: {
5437
5440
  baseUrl: hostedCreds.apiBaseUrl,
5438
5441
  apiKey: hostedCreds.apiKey
@@ -5450,7 +5453,7 @@ function createProgram() {
5450
5453
  const result = await runTaskHosted({
5451
5454
  taskPath: file,
5452
5455
  agentCommand,
5453
- artifactsDir: options.artifactsDir,
5456
+ artifactsDir,
5454
5457
  hosted: { baseUrl: hostedCreds.apiBaseUrl, apiKey: hostedCreds.apiKey },
5455
5458
  agentModel: options.agentModel,
5456
5459
  agentVersion: options.agentVersion
@@ -5478,7 +5481,7 @@ function createProgram() {
5478
5481
  const result = await runTask({
5479
5482
  taskPath: file,
5480
5483
  agentCommand,
5481
- artifactsDir: options.artifactsDir,
5484
+ artifactsDir,
5482
5485
  // Commander negates --no-* flags: `--no-capture` → `capture: false`.
5483
5486
  noCapture: options.capture === false
5484
5487
  });
@@ -5505,28 +5508,24 @@ function createProgram() {
5505
5508
  );
5506
5509
  program.command("demo").summary("Run a sample task, no account needed").description(
5507
5510
  "Zero-auth first-run demo: boots a local GitHub twin, runs the bundled demo agent for 5 isolated trials (model calls via pome's anonymous demo gateway), and prints per-trial verdicts evaluated in Pome cloud. No signup, no API keys; ends with a no-login preview link."
5508
- ).option(
5509
- "--api-url <url>",
5510
- "Control-plane base URL.",
5511
- process.env.POME_API_BASE ?? process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
5512
5511
  ).option(
5513
5512
  "--trials <n>",
5514
5513
  "Number of trials to run, 1 to 10",
5515
5514
  "5"
5516
- ).option("--artifacts-dir <dir>", "Directory for run artifacts", "runs").action(
5517
- async (opts) => {
5515
+ ).action(
5516
+ async (opts, cmd) => {
5518
5517
  const trials = Number.parseInt(opts.trials, 10);
5519
5518
  if (!Number.isInteger(trials) || trials < 1 || trials > 10) {
5520
5519
  console.error(`Invalid --trials "${opts.trials}" (expected 1-10).`);
5521
5520
  process.exitCode = 5;
5522
5521
  return;
5523
5522
  }
5524
- const { runDemo } = await import('../../runDemo-B2SXM4OC.js');
5523
+ const { runDemo } = await import('../../runDemo-DRDMCG25.js');
5525
5524
  const result = await runDemo({
5526
- apiBase: opts.apiUrl.replace(/\/$/, ""),
5525
+ apiBase: globals(cmd).apiUrl.replace(/\/$/, ""),
5527
5526
  dashboardBase: process.env.POME_DASHBOARD_URL ?? DEFAULT_DASHBOARD_URL,
5528
5527
  trials,
5529
- artifactsDir: opts.artifactsDir
5528
+ artifactsDir: globals(cmd).artifactsDir
5530
5529
  });
5531
5530
  process.exitCode = result.exitCode;
5532
5531
  }
@@ -5548,29 +5547,21 @@ function createProgram() {
5548
5547
  program.command("eval").summary("Score a trace recorded earlier").argument(
5549
5548
  "[run-dir]",
5550
5549
  "Existing run directory (runs/<task>/<run-id>). Omit to use <artifacts-dir>/latest.json."
5551
- ).option(
5552
- "--artifacts-dir <dir>",
5553
- "Directory whose latest.json picks the run when no run dir is given.",
5554
- "runs"
5555
5550
  ).option(
5556
5551
  "--agent <slug>",
5557
5552
  "Agent identity for the eval session. Defaults to agent.slug from pome.json."
5558
5553
  ).option(
5559
5554
  "--task <name>",
5560
5555
  "Task name recorded on the eval session. Defaults to meta.json's `scenario` slug (a legacy key name; then title)."
5561
- ).option(
5562
- "--api-url <url>",
5563
- "Control-plane base URL.",
5564
- process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL
5565
5556
  ).description(
5566
5557
  "Upload an existing raw trace directory to Pome cloud for evaluation and print the authoritative score (capture/eval split \u2014 no local scoring; requires a control plane with POST /v1/eval-sessions)"
5567
5558
  ).action(
5568
- async (runDir, opts) => {
5569
- await runEvalCommand(runDir, opts);
5559
+ async (runDir, opts, cmd) => {
5560
+ await runEvalCommand(runDir, { ...opts, ...globals(cmd) });
5570
5561
  }
5571
5562
  );
5572
- program.command("inspect").argument("<run>", "Run id, run directory, or latest").option("--artifacts-dir <dir>", "Directory for run artifacts", "runs").description("Print a human-readable run report").action(async (run, options) => {
5573
- const latest = run === "latest" ? await readLatestRun(options.artifactsDir) : void 0;
5563
+ program.command("inspect").argument("<run>", "Run id, run directory, or latest").description("Print a human-readable run report").action(async (run, _options, cmd) => {
5564
+ const latest = run === "latest" ? await readLatestRun(globals(cmd).artifactsDir) : void 0;
5574
5565
  const runDir = latest?.run_dir ?? resolve(run);
5575
5566
  const eventsResult = await readEventsJsonl(runDir);
5576
5567
  const meta = await readMetaSummary(runDir);
@@ -5621,14 +5612,9 @@ function createProgram() {
5621
5612
  }
5622
5613
  const root = target ?? "runs";
5623
5614
  const discovery = await discoverRunSet(resolve(root));
5624
- if (discovery.staleVersionCount > 0) {
5625
- console.error(
5626
- `${discovery.staleVersionCount} verdict.json file(s) under ${root} are not artifact version ${VERDICT_ARTIFACT_VERSION} (the only version this CLI reads) and were skipped \u2014 re-run \`pome run\` to record those trials again.`
5627
- );
5628
- }
5629
5615
  if (discovery.unreadableCount > 0) {
5630
5616
  console.error(
5631
- `${discovery.unreadableCount} verdict.json file(s) under ${root} could not be read (truncated, hand-edited, or not a verdict artifact) and were skipped:`
5617
+ `${discovery.unreadableCount} verdict.json file(s) under ${root} could not be read (truncated, hand-edited, written by an older CLI, or not a verdict artifact) and were skipped \u2014 re-run \`pome run\` to record those trials again:`
5632
5618
  );
5633
5619
  for (const path of discovery.unreadablePaths.slice(0, MAX_UNREADABLE_PATHS_SHOWN)) {
5634
5620
  console.error(` - ${path}`);
@@ -5639,7 +5625,7 @@ function createProgram() {
5639
5625
  }
5640
5626
  }
5641
5627
  if (discovery.totalSets === 0) {
5642
- if (discovery.staleVersionCount === 0 && discovery.unreadableCount === 0) {
5628
+ if (discovery.unreadableCount === 0) {
5643
5629
  console.error(
5644
5630
  `No finalized run sets under ${root} \u2014 hosted \`pome run\` records a verdict.json per trial; run one first (or point fix-prompt at your artifacts dir).`
5645
5631
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pome-sh/cli",
3
- "version": "0.39.1",
3
+ "version": "0.41.0",
4
4
  "description": "Test AI agents against digital twins of real SaaS APIs. Records tool-call traces and scores them on pome.sh.",
5
5
  "keywords": [
6
6
  "ai",
@@ -1,3 +0,0 @@
1
- {"ts":"2026-05-11T22:21:54.256Z","run_id":"m0-1-refund-retry","twin":"stripe","request_id":"req_f2e53238-8783-4cfd-b5de-efd60f442be8","correlation_id":"req_f2e53238-8783-4cfd-b5de-efd60f442be8","scenario_step_id":null,"step_id":null,"tool_call_id":null,"method":"POST","path":"/s/default/v1/refunds","request_body":"{\"charge\":\"ch_q9dVWLmN4flQFRzqYRe7iAu1\",\"amount\":7500}","status":402,"response_body":{"error":{"type":"card_error","code":"card_declined","message":"Simulated lost-response failure: refund persisted server-side, but response delivery to the client failed."}},"latency_ms":0,"fidelity":"semantic","state_mutation":true,"state_delta":{"before":null,"after":{"id":"re_LOW1fo91HP1TYIe31f04Kpcn","account_id":"acct_default","charge_id":"ch_q9dVWLmN4flQFRzqYRe7iAu1","payment_intent_id":"pi_pKYzqMO9yYaY2jesB8VcRRxc","amount":7500,"currency":"usd","status":"succeeded","reason":null,"idempotency_key":null,"created":1778538114}},"error":"Simulated lost-response failure: refund persisted server-side, but response delivery to the client failed."}
2
- {"ts":"2026-05-11T22:21:54.257Z","run_id":"m0-1-refund-retry","twin":"stripe","request_id":"req_c0774431-a954-458c-9b38-8f416cfde198","correlation_id":"req_c0774431-a954-458c-9b38-8f416cfde198","scenario_step_id":null,"step_id":null,"tool_call_id":null,"method":"POST","path":"/s/default/v1/refunds","request_body":"{\"charge\":\"ch_q9dVWLmN4flQFRzqYRe7iAu1\",\"amount\":7500}","status":200,"response_body":{"id":"re_L5jCYOtsrPiLpbCCiQZtnOmv","object":"refund","amount":7500,"balance_transaction":null,"charge":"ch_q9dVWLmN4flQFRzqYRe7iAu1","created":1778538114,"currency":"usd","metadata":{},"payment_intent":"pi_pKYzqMO9yYaY2jesB8VcRRxc","reason":null,"receipt_number":null,"source_transfer_reversal":null,"status":"succeeded","transfer_reversal":null},"latency_ms":1,"fidelity":"semantic","state_mutation":true,"state_delta":{"before":null,"after":{"id":"re_L5jCYOtsrPiLpbCCiQZtnOmv","account_id":"acct_default","charge_id":"ch_q9dVWLmN4flQFRzqYRe7iAu1","payment_intent_id":"pi_pKYzqMO9yYaY2jesB8VcRRxc","amount":7500,"currency":"usd","status":"succeeded","reason":null,"idempotency_key":null,"created":1778538114}},"error":null}
3
- {"ts":"2026-05-11T22:21:54.257Z","run_id":"m0-1-refund-retry","twin":"stripe","request_id":"req_37c6074d-7b44-4c06-8b69-86567aa2ae8a","correlation_id":"req_37c6074d-7b44-4c06-8b69-86567aa2ae8a","scenario_step_id":null,"step_id":null,"tool_call_id":null,"method":"GET","path":"/s/default/v1/charges/ch_q9dVWLmN4flQFRzqYRe7iAu1","request_body":null,"status":200,"response_body":{"id":"ch_q9dVWLmN4flQFRzqYRe7iAu1","object":"charge","amount":20000,"amount_captured":20000,"amount_refunded":15000,"application":null,"application_fee":null,"application_fee_amount":null,"balance_transaction":"txn_nuda9v4Uks4931U8xOShQZsV","billing_details":{"address":{"city":null,"country":null,"line1":null,"line2":null,"postal_code":null,"state":null},"email":null,"name":null,"phone":null},"calculated_statement_descriptor":null,"captured":true,"created":1778538114,"currency":"usd","customer":null,"description":null,"disputed":false,"failure_balance_transaction":null,"failure_code":null,"failure_message":null,"fraud_details":{},"invoice":null,"livemode":false,"metadata":{},"on_behalf_of":null,"outcome":{"network_status":"approved_by_network","reason":null,"risk_level":"normal","seller_message":"Payment complete.","type":"authorized"},"paid":true,"payment_intent":"pi_pKYzqMO9yYaY2jesB8VcRRxc","payment_method":null,"payment_method_details":{"type":"crypto","crypto":{"buyer_address":null,"network":"base","token_currency":"usdc","transaction_hash":null}},"receipt_email":null,"receipt_number":null,"receipt_url":null,"refunded":false,"review":null,"shipping":null,"source_transfer":null,"statement_descriptor":null,"statement_descriptor_suffix":null,"status":"succeeded","transfer_data":null,"transfer_group":null},"latency_ms":0,"fidelity":"semantic","state_mutation":false,"state_delta":null,"error":null}