@pome-sh/cli 0.43.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +237 -117
  2. package/dist/build-info.json +3 -3
  3. package/dist/{checks-QYUPKBPT.js → checks-BOOQNOGJ.js} +4 -3
  4. package/dist/chunk-26UAPLHK.js +191 -0
  5. package/dist/{chunk-5A6HHA54.js → chunk-3T4FUF43.js} +2 -2
  6. package/dist/chunk-4LQ4IJOC.js +1 -0
  7. package/dist/chunk-7ULGZG7X.js +132 -0
  8. package/dist/{chunk-5KFDRR53.js → chunk-7VZBAHQ2.js} +2 -2
  9. package/dist/{chunk-OBFHOACQ.js → chunk-G3YRDMBE.js} +12 -71
  10. package/dist/{chunk-CS7O2ZXB.js → chunk-M7ATJ423.js} +5 -190
  11. package/dist/{chunk-ELIEDNF3.js → chunk-RYAQ2ZYA.js} +7 -7
  12. package/dist/{chunk-HRAD7MRX.js → chunk-TWURH7YM.js} +34 -10
  13. package/dist/{chunk-3YCX3KUL.js → chunk-ZVKPZHFQ.js} +1 -1
  14. package/dist/{runTrialGroup-4QL6S7QO.js → runTrialGroup-I3PC2AST.js} +8 -6
  15. package/dist/{server-KC57K5AC.js → server-GECKG2H6.js} +2 -2
  16. package/dist/src/cli/main.js +71 -140
  17. package/dist/{src-E7NTZL2F.js → src-252X4AEZ.js} +2 -2
  18. package/dist/{src-Z63IOSCJ.js → src-CV7QDV5E.js} +2 -2
  19. package/dist/{src-5F5OTVYT.js → src-D3PKLT54.js} +2 -2
  20. package/dist/{src-7X62AGW7.js → src-LPTWNBO5.js} +2 -2
  21. package/dist/{src-ESRY2MC7.js → src-P5MEPJY4.js} +2 -2
  22. package/dist/twinHarness-YDGIGB4P.js +6 -0
  23. package/dist/{twinSeed-4VQ7RRCL.js → twinSeed-ADWQ62CA.js} +1 -1
  24. package/dist/twinStart-E5R26SF4.js +422 -0
  25. package/dist/twinTape-3XNU3GRW.js +418 -0
  26. package/examples/agents/mcp-loop-agent.ts +0 -6
  27. package/package.json +1 -1
  28. package/dist/twinHarness-WJFDLEAO.js +0 -6
  29. package/dist/twinStart-QQ64EV3P.js +0 -183
@@ -445,6 +445,7 @@ function defineTwin(spec) {
445
445
  }
446
446
  var RESERVED_SESSION_PREFIXES = ["/_pome", "/mcp"];
447
447
  var CLIENT_IP_VAR = "pomeClientIp";
448
+ var ADMIN_NO_PEER_OPT_IN = "TWIN_ADMIN_ALLOW_NO_PEER";
448
449
  var nodeGetConnInfo;
449
450
  function loadNodeGetConnInfo() {
450
451
  nodeGetConnInfo ??= import('@hono/node-server/conninfo').then((mod) => mod.getConnInfo, () => void 0);
@@ -490,10 +491,11 @@ function createAdminGate(options = {}) {
490
491
  }
491
492
  const remote = await getClientIp(c);
492
493
  if (!remote) {
493
- if (process.env.NODE_ENV === "production")
494
- return forbidden();
495
- await next();
496
- return;
494
+ if (process.env[ADMIN_NO_PEER_OPT_IN] === "1") {
495
+ await next();
496
+ return;
497
+ }
498
+ return forbidden();
497
499
  }
498
500
  if (!isLoopbackAddress(remote))
499
501
  return forbidden();
@@ -503,13 +505,22 @@ function createAdminGate(options = {}) {
503
505
 
504
506
  // ../packages/sdk/dist/auth.js
505
507
  var PROVIDER_SHAPED_TEAM_ID = "provider-shaped";
508
+ var DEV_ONLY_INSECURE_SECRET = "dev-only-insecure-secret";
509
+ var DEV_SECRETS_OPT_IN = "POME_ALLOW_DEV_SECRETS";
506
510
  function resolveAuthSecret() {
507
511
  const secret = process.env.TWIN_AUTH_SECRET;
508
- if (!secret && process.env.NODE_ENV === "production") {
509
- throw new Error("TWIN_AUTH_SECRET required in production");
510
- }
511
- return secret ?? "dev-only-insecure-secret";
512
+ if (secret)
513
+ return secret;
514
+ if (process.env[DEV_SECRETS_OPT_IN] === "1")
515
+ return DEV_ONLY_INSECURE_SECRET;
516
+ throw new MissingAuthSecretError();
512
517
  }
518
+ var MissingAuthSecretError = class extends Error {
519
+ constructor() {
520
+ super(`TWIN_AUTH_SECRET is not set. Set it, or set ${DEV_SECRETS_OPT_IN}=1 to serve the public dev secret on a twin nothing but this machine can reach.`);
521
+ this.name = "MissingAuthSecretError";
522
+ }
523
+ };
513
524
  var SIG_LENGTH = 22;
514
525
  function mintProviderToken(spec, options) {
515
526
  const prefix = options.prefix ?? spec.prefixes[0];
@@ -674,7 +685,14 @@ function bearerAuth(options = {}) {
674
685
  }
675
686
  }
676
687
  if (options.providerToken) {
677
- const providerSid = verifyProviderToken(options.providerToken, token);
688
+ let providerSid;
689
+ try {
690
+ providerSid = verifyProviderToken(options.providerToken, token);
691
+ } catch (err) {
692
+ if (err instanceof MissingAuthSecretError)
693
+ return respond(unauthorized("invalid", { token }));
694
+ throw err;
695
+ }
678
696
  if (providerSid) {
679
697
  const mismatch2 = checkSid(providerSid);
680
698
  if (mismatch2)
@@ -1281,8 +1299,14 @@ function isLoopbackHost(value) {
1281
1299
  function ensureTwinAuthSecret(twin, host) {
1282
1300
  if (process.env.TWIN_AUTH_SECRET)
1283
1301
  return;
1284
- if (isLoopbackHost(host))
1302
+ if (isLoopbackHost(host)) {
1303
+ if (process.env[DEV_SECRETS_OPT_IN] === "1")
1304
+ return;
1305
+ const secret = randomBytes(32).toString("hex");
1306
+ process.env.TWIN_AUTH_SECRET = secret;
1307
+ console.log(`[twin-${twin}] TWIN_AUTH_SECRET not set \u2014 generated ${secret} for this loopback boot (not persisted; set TWIN_AUTH_SECRET to choose one, or ${DEV_SECRETS_OPT_IN}=1 for the public dev secret)`);
1285
1308
  return;
1309
+ }
1286
1310
  const dataDir = process.env.POME_TWIN_DATA_DIR || join(".pome-data", twin);
1287
1311
  const secretPath = join(dataDir, "secret");
1288
1312
  try {
@@ -1,4 +1,4 @@
1
- import { isTwinName, TWIN_REGISTRY, TWIN_NAMES } from './chunk-ELIEDNF3.js';
1
+ import { isTwinName, TWIN_REGISTRY, TWIN_NAMES } from './chunk-RYAQ2ZYA.js';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { parse } from 'yaml';
4
4
 
@@ -1,14 +1,16 @@
1
- import { runTaskHosted, createHostedClient, parseTaskFile, resolveRunAgentIdentity, outcomeOf, isNarrated, criterionPhrase, narratorReadingLines } from './chunk-OBFHOACQ.js';
2
- import './chunk-3YCX3KUL.js';
1
+ import { runTaskHosted, createHostedClient, parseTaskFile, resolveRunAgentIdentity, outcomeOf, isNarrated, criterionPhrase, narratorReadingLines } from './chunk-G3YRDMBE.js';
2
+ import './chunk-ZVKPZHFQ.js';
3
+ import './chunk-4LQ4IJOC.js';
3
4
  import './chunk-NW7HGA2K.js';
4
5
  import './chunk-PASFBRK4.js';
5
6
  import './chunk-3FZY376K.js';
6
- import './chunk-ELIEDNF3.js';
7
- import { HostedQuotaError, HostedTrialError } from './chunk-CS7O2ZXB.js';
7
+ import './chunk-RYAQ2ZYA.js';
8
+ import { HostedQuotaError, HostedTrialError } from './chunk-26UAPLHK.js';
9
+ import './chunk-M7ATJ423.js';
8
10
  import './chunk-NBOQN5VX.js';
9
11
  import './chunk-YBWG5JK2.js';
10
- import './chunk-HRAD7MRX.js';
11
- import './chunk-5KFDRR53.js';
12
+ import './chunk-TWURH7YM.js';
13
+ import './chunk-7VZBAHQ2.js';
12
14
  import './chunk-SG6ZTIMT.js';
13
15
  import './chunk-2K6BJ3PI.js';
14
16
  import './chunk-FBSA5L36.js';
@@ -1,4 +1,4 @@
1
- export { POME_RECORDER_EVENTS_PATH, PROVIDER_SHAPED_TEAM_ID, TwinBootError, TwinError, UnknownToolError, bearerAuth, createAdminGate, createApp, createFileBackedRecorderStore, createRecorderHandle, createRecorderStore, created, ensureTwinAuthSecret, failureInjectionMiddleware, formTokenResolver, isLoopbackHost, mintProviderToken, ok, queryTokenResolver, recordedRequestHeaders, requireAdminAuth, resolveAuthSecret, resolveRecorderStore, serve, setClientIp, setRecordedTool, toTwinHttpEventRow, twinBuildInfo, verifyProviderToken } from './chunk-HRAD7MRX.js';
2
- import './chunk-5KFDRR53.js';
1
+ export { POME_RECORDER_EVENTS_PATH, PROVIDER_SHAPED_TEAM_ID, TwinBootError, TwinError, UnknownToolError, bearerAuth, createAdminGate, createApp, createFileBackedRecorderStore, createRecorderHandle, createRecorderStore, created, ensureTwinAuthSecret, failureInjectionMiddleware, formTokenResolver, isLoopbackHost, mintProviderToken, ok, queryTokenResolver, recordedRequestHeaders, requireAdminAuth, resolveAuthSecret, resolveRecorderStore, serve, setClientIp, setRecordedTool, toTwinHttpEventRow, twinBuildInfo, verifyProviderToken } from './chunk-TWURH7YM.js';
2
+ import './chunk-7VZBAHQ2.js';
3
3
  export { redactEvent, redactSecrets } from './chunk-SG6ZTIMT.js';
4
4
  export { FAILURE_INJECTION_OVERRIDE_KEY, createFailureInjectionStore, failureInjectionRuleSchema } from './chunk-FBSA5L36.js';
@@ -1,30 +1,32 @@
1
1
  #!/usr/bin/env node
2
- import { DEFAULT_CONTROL_PLANE_URL, DEFAULT_DASHBOARD_URL, clearLocalCredentials, friendlyHostedError, SESSION_TWIN_NAMES, runSessionCreate, runSessionList, runSessionStop, resolveCredentials, parseTaskFile, runTaskHosted, scoreStatus, runScoreLine, narratorReadingLines, readLatestRun, readMetaSummary, discoverRunSet, outcomeOf, persistCredentialsAfterLogin, DEFAULT_DOCS_SITE_ORIGIN, resolveSeams, readConfigTwins, resolveCachedAgentId, readLinkCache, seedStateForTwin, runAgentCommand, scoreCountsSummary, criterionRowLine, readCodeCriteria, postAgentResolver, writeLinkCache, ensurePomeGitignored, writeRunArtifactsCore, createHostedClient, redactJsonl, scoreFromFinalizeResponse, parseGitHubSeedState, uploadRunBlobs, isPreSatisfied } from '../../chunk-OBFHOACQ.js';
3
- import '../../chunk-3YCX3KUL.js';
2
+ import { DEFAULT_CONTROL_PLANE_URL, DEFAULT_DASHBOARD_URL, clearLocalCredentials, friendlyHostedError, SESSION_TWIN_NAMES, runSessionCreate, runSessionList, runSessionStop, resolveCredentials, parseTaskFile, runTaskHosted, scoreStatus, runScoreLine, narratorReadingLines, readLatestRun, readMetaSummary, discoverRunSet, outcomeOf, persistCredentialsAfterLogin, DEFAULT_DOCS_SITE_ORIGIN, resolveSeams, readConfigTwins, resolveCachedAgentId, readLinkCache, seedStateForTwin, runAgentCommand, scoreCountsSummary, criterionRowLine, readCodeCriteria, postAgentResolver, writeLinkCache, ensurePomeGitignored, writeRunArtifactsCore, createHostedClient, redactJsonl, scoreFromFinalizeResponse, parseGitHubSeedState, uploadRunBlobs, isPreSatisfied } from '../../chunk-G3YRDMBE.js';
3
+ import '../../chunk-ZVKPZHFQ.js';
4
+ import '../../chunk-4LQ4IJOC.js';
4
5
  import '../../chunk-NW7HGA2K.js';
5
6
  import { GMAIL_CHECKS } from '../../chunk-JM6VS62R.js';
6
7
  import '../../chunk-PASFBRK4.js';
7
8
  import { LINEAR_CHECKS } from '../../chunk-TSOSDKXP.js';
8
9
  import '../../chunk-3FZY376K.js';
9
- import { createRecorder, bootTwin } from '../../chunk-5A6HHA54.js';
10
- import { TWIN_NAME_LIST, TWIN_REGISTRY, createGitHubSmokeApp } from '../../chunk-ELIEDNF3.js';
10
+ import { createRecorder, bootTwin } from '../../chunk-3T4FUF43.js';
11
+ import { TWIN_NAME_LIST, TWIN_REGISTRY, createGitHubSmokeApp } from '../../chunk-RYAQ2ZYA.js';
11
12
  import { getAvailablePort } from '../../chunk-XDU6TD4O.js';
12
- import { MOUNTED_TWINS, readManifest, deriveAgentSlug, writeManifest, MANIFEST_JSON, exitCodeFor, readRequiredManifest, HostedUsageError, HostedOrchError, normalizeManifestTwins } from '../../chunk-CS7O2ZXB.js';
13
+ import { readManifest, writeManifest, MANIFEST_JSON, exitCodeFor, readRequiredManifest, HostedUsageError, HostedOrchError, normalizeManifestTwins } from '../../chunk-26UAPLHK.js';
14
+ import { MOUNTED_TWINS, deriveAgentSlug } from '../../chunk-M7ATJ423.js';
13
15
  import { buildEgressAllowlist, readBlockedEgress } from '../../chunk-CBFKZZBR.js';
14
16
  import { TAPE_ASSERTABLE_TOOLS } from '../../chunk-C4BVTUA3.js';
15
17
  import { seedSchema } from '../../chunk-NBOQN5VX.js';
16
18
  import { SLACK_CHECKS } from '../../chunk-2ZGVDTJC.js';
17
19
  import { oneOf, defineCheck, repoRef, VACUITY_SENTINEL_NUMBER, childStatePath, VACUITY_SENTINEL, statePath, templateSlots, renderCheck, checksDigest, checkPattern, checkNearMissPattern } from '../../chunk-JWJYNAWI.js';
18
20
  import '../../chunk-YBWG5JK2.js';
19
- import '../../chunk-HRAD7MRX.js';
20
- import { eventSchema, isLegacyEventRow } from '../../chunk-5KFDRR53.js';
21
+ import '../../chunk-TWURH7YM.js';
22
+ import { eventSchema, isLegacyEventRow } from '../../chunk-7VZBAHQ2.js';
21
23
  import { redactSecrets, redactEvent } from '../../chunk-SG6ZTIMT.js';
22
24
  import '../../chunk-2K6BJ3PI.js';
23
25
  import '../../chunk-FBSA5L36.js';
24
26
  import { Command } from 'commander';
25
27
  import { existsSync, readFileSync, realpathSync, statSync, constants } from 'node:fs';
26
28
  import { mkdir, readFile, writeFile, readdir, copyFile, stat, cp } from 'node:fs/promises';
27
- import { dirname, resolve, join, basename, relative, sep, extname } from 'node:path';
29
+ import { dirname, resolve, join, basename, relative, extname } from 'node:path';
28
30
  import { fileURLToPath } from 'node:url';
29
31
  import { serve } from '@hono/node-server';
30
32
  import { randomBytes, randomUUID, createHash } from 'node:crypto';
@@ -322,78 +324,6 @@ function makeShutdown(child, graceMs) {
322
324
  return done;
323
325
  };
324
326
  }
325
- function resolveTwinHttpParents(rows) {
326
- const eventIdByToolUseId = /* @__PURE__ */ new Map();
327
- for (const row of rows) {
328
- if (row.kind === "ToolUseEvent") eventIdByToolUseId.set(row.tool_use_id, row.event_id);
329
- }
330
- if (eventIdByToolUseId.size === 0) return rows;
331
- return rows.map((row) => {
332
- if (row.kind !== "TwinHttpEvent") return row;
333
- if (row.parent_event_id != null) return row;
334
- const causingToolUseId = row.tool_call_id ?? row.correlation_id ?? null;
335
- if (causingToolUseId === null) return row;
336
- const parentEventId = eventIdByToolUseId.get(causingToolUseId);
337
- return parentEventId === void 0 ? row : { ...row, parent_event_id: parentEventId };
338
- });
339
- }
340
- async function mergeAdapterSignalsIntoEvents(signalsPath, eventsJsonlPath) {
341
- let rawSignals;
342
- try {
343
- rawSignals = await readFile(signalsPath, "utf8");
344
- } catch {
345
- return { appended: 0, dropped: 0 };
346
- }
347
- let dropped = 0;
348
- const signalRows = [];
349
- for (const line of rawSignals.split("\n")) {
350
- if (line.length === 0) continue;
351
- let parsed;
352
- try {
353
- parsed = JSON.parse(line);
354
- } catch {
355
- dropped += 1;
356
- continue;
357
- }
358
- const result = eventSchema.safeParse(parsed);
359
- if (!result.success) {
360
- dropped += 1;
361
- continue;
362
- }
363
- signalRows.push(redactEvent(result.data));
364
- }
365
- if (signalRows.length === 0) return { appended: 0, dropped };
366
- let rawEvents;
367
- try {
368
- rawEvents = await readFile(eventsJsonlPath, "utf8");
369
- } catch {
370
- rawEvents = "";
371
- }
372
- const eventRows = [];
373
- const unparseablePassthrough = [];
374
- for (const line of rawEvents.split("\n")) {
375
- if (line.length === 0) continue;
376
- let parsed;
377
- try {
378
- parsed = JSON.parse(line);
379
- } catch {
380
- unparseablePassthrough.push(line);
381
- continue;
382
- }
383
- const result = eventSchema.safeParse(parsed);
384
- if (result.success) {
385
- eventRows.push(redactEvent(result.data));
386
- } else {
387
- unparseablePassthrough.push(line);
388
- }
389
- }
390
- const merged = resolveTwinHttpParents(eventRows.concat(signalRows));
391
- merged.sort((a, b) => a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0);
392
- const sortedJsonl = merged.map((r) => JSON.stringify(r)).join("\n");
393
- const head = unparseablePassthrough.length > 0 ? unparseablePassthrough.join("\n") + "\n" : "";
394
- await writeFile(eventsJsonlPath, head + sortedJsonl + "\n");
395
- return { appended: signalRows.length, dropped };
396
- }
397
327
 
398
328
  // src/runner/runTaskCore.ts
399
329
  async function writeRunNoScore(input) {
@@ -428,9 +358,6 @@ async function runTask(options) {
428
358
  const writeRun = writeRunNoScore;
429
359
  const runDir = join(artifactsDir, scenario.slug, runId);
430
360
  await mkdir(runDir, { recursive: true });
431
- const signalsPath = join(runDir, "signals.jsonl");
432
- await writeFile(signalsPath, "");
433
- const signalsPathForEnv = sep === "\\" ? signalsPath.replace(/\\/g, "/") : signalsPath;
434
361
  const eventsJsonlPath = join(runDir, "events.jsonl");
435
362
  await writeFile(eventsJsonlPath, "");
436
363
  const egressAllowHosts = buildEgressAllowlist(process.env, {
@@ -525,7 +452,6 @@ async function runTask(options) {
525
452
  POME_AUTH_TOKEN: token,
526
453
  POME_RUN_ID: runId,
527
454
  POME_ARTIFACTS_DIR: runDir,
528
- POME_ADAPTER_SIGNALS_PATH: signalsPathForEnv,
529
455
  ...options.extraAgentEnv ?? {},
530
456
  ...proxyEnv
531
457
  };
@@ -575,7 +501,6 @@ async function runTask(options) {
575
501
  stateInitial,
576
502
  stateFinal: stateFinal2
577
503
  });
578
- await mergeAdapterSignalsIntoEvents(signalsPath, eventsJsonlPath);
579
504
  void exitCode2;
580
505
  const blockedEgress2 = await collectBlockedEgress();
581
506
  return { scenario, runId, artifacts: artifacts2, agent: preflight, exitCode: 3, blockedEgress: blockedEgress2 };
@@ -601,7 +526,6 @@ async function runTask(options) {
601
526
  stateInitial,
602
527
  stateFinal
603
528
  });
604
- await mergeAdapterSignalsIntoEvents(signalsPath, eventsJsonlPath);
605
529
  const blockedEgress = await collectBlockedEgress();
606
530
  return { scenario, runId, artifacts, agent, exitCode, blockedEgress };
607
531
  } finally {
@@ -1187,7 +1111,7 @@ function extractJsonPayload(text) {
1187
1111
 
1188
1112
  // src/task/seed-verifier.ts
1189
1113
  async function verifySeedWithTwin(seed) {
1190
- const { GitHubDomain, openGitHubCloneDatabase } = await import('../../src-E7NTZL2F.js');
1114
+ const { GitHubDomain, openGitHubCloneDatabase } = await import('../../src-252X4AEZ.js');
1191
1115
  const db = openGitHubCloneDatabase(":memory:");
1192
1116
  try {
1193
1117
  new GitHubDomain(db).seed(seed);
@@ -3395,7 +3319,7 @@ function localDigest(twin) {
3395
3319
  }
3396
3320
  function bakedVersions() {
3397
3321
  try {
3398
- return JSON.parse('{"@pome-sh/sdk":"0.11.6","@pome-sh/wire":"0.4.1","@pome-sh/twin-github":"0.12.0","@pome-sh/twin-gmail":"0.4.0","@pome-sh/twin-linear":"0.4.1","@pome-sh/twin-slack":"0.4.1","@pome-sh/twin-stripe":"0.4.7"}');
3322
+ return JSON.parse('{"@pome-sh/sdk":"0.11.6","@pome-sh/wire":"0.4.2","@pome-sh/twin-github":"0.12.0","@pome-sh/twin-gmail":"0.4.0","@pome-sh/twin-linear":"0.4.1","@pome-sh/twin-slack":"0.4.1","@pome-sh/twin-stripe":"0.4.7"}');
3399
3323
  } catch {
3400
3324
  return {};
3401
3325
  }
@@ -4093,13 +4017,13 @@ function validateJsonl(name, raw) {
4093
4017
  `pome eval: ${name} is corrupt \u2014 line ${i + 1} is not valid JSON.`
4094
4018
  );
4095
4019
  }
4096
- if (name === "events.jsonl" && isLegacyEventRow(parsed)) {
4020
+ if (isLegacyEventRow(parsed)) {
4097
4021
  throw new HostedUsageError(
4098
4022
  `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.`
4099
4023
  );
4100
4024
  }
4101
4025
  }
4102
- if (name === "events.jsonl" && nonEmpty === 0) {
4026
+ if (nonEmpty === 0) {
4103
4027
  throw new HostedUsageError(
4104
4028
  "pome eval: events.jsonl is empty \u2014 the run captured no trace to evaluate."
4105
4029
  );
@@ -4120,24 +4044,12 @@ async function readRunDirArtifacts(runDir) {
4120
4044
  parseJsonFile("state_initial.json", stateInitialJson);
4121
4045
  const stateFinalJson = await readRequiredFile(runDir, "state_final.json");
4122
4046
  parseJsonFile("state_final.json", stateFinalJson);
4123
- let signalsJsonl = null;
4124
- try {
4125
- signalsJsonl = await readFile(join(runDir, "signals.jsonl"), "utf8");
4126
- } catch (err) {
4127
- if (err.code !== "ENOENT") {
4128
- throw new HostedUsageError(
4129
- `pome eval: signals.jsonl could not be read: ${err instanceof Error ? err.message : String(err)}`
4130
- );
4131
- }
4132
- }
4133
- if (signalsJsonl !== null) validateJsonl("signals.jsonl", signalsJsonl);
4134
4047
  return {
4135
4048
  runDir,
4136
4049
  meta,
4137
4050
  eventsJsonl,
4138
4051
  stateInitialJson,
4139
4052
  stateFinalJson,
4140
- signalsJsonl,
4141
4053
  metaJson: metaRaw
4142
4054
  };
4143
4055
  }
@@ -4239,7 +4151,6 @@ async function runEval(options) {
4239
4151
  stateFinalJson: JSON.stringify(
4240
4152
  redactSecrets(JSON.parse(artifacts.stateFinalJson))
4241
4153
  ),
4242
- signalsJsonl: redactJsonl(artifacts.signalsJsonl ?? ""),
4243
4154
  // D18.1 — already validated as parseable JSON in readRunDirArtifacts.
4244
4155
  // Re-serialize with the SAME formatting writeRunArtifactsCore's writeJson
4245
4156
  // uses (2-space indent + trailing newline) so the bytes `pome eval`
@@ -4284,8 +4195,7 @@ async function runEval(options) {
4284
4195
  expectedBehavior: "",
4285
4196
  traceStorageKey: keys.eventsKey ?? void 0,
4286
4197
  stateInitialStorageKey: keys.stateInitialKey ?? void 0,
4287
- stateFinalStorageKey: keys.stateFinalKey ?? void 0,
4288
- signalsStorageKey: keys.signalsKey ?? void 0
4198
+ stateFinalStorageKey: keys.stateFinalKey ?? void 0
4289
4199
  });
4290
4200
  }
4291
4201
  let finalized;
@@ -4961,7 +4871,7 @@ function firstSentence(description) {
4961
4871
  function resolveExampleRef(env = process.env) {
4962
4872
  const override = env.POME_EXAMPLE_REF?.trim();
4963
4873
  if (override) return override;
4964
- const baked = "589885b59adab9a5fded977620a6adc25125da85".trim() ;
4874
+ const baked = "a80259dcddd5ec6f1a16dba3a6f252539996ebb7".trim() ;
4965
4875
  return FULL_SHA.test(baked) ? baked : "main";
4966
4876
  }
4967
4877
  function rawUrlFor(example, file, ref) {
@@ -5324,7 +5234,7 @@ var DEFAULT_AGENT_COMMAND = `node ${DEFAULT_AGENT_FILE}`;
5324
5234
  var MANIFEST_SCHEMA_URL = "https://pome.sh/schemas/v1/pome.json";
5325
5235
  var MAX_UNREADABLE_PATHS_SHOWN = 5;
5326
5236
  function readPackageVersion() {
5327
- if ("0.43.0".length > 0) return "0.43.0";
5237
+ if ("0.44.0".length > 0) return "0.44.0";
5328
5238
  try {
5329
5239
  const here = dirname(fileURLToPath(import.meta.url));
5330
5240
  const candidates = [
@@ -5728,7 +5638,7 @@ function createProgram() {
5728
5638
  return;
5729
5639
  }
5730
5640
  {
5731
- const { runDoctorChecks } = await import('../../checks-QYUPKBPT.js');
5641
+ const { runDoctorChecks } = await import('../../checks-BOOQNOGJ.js');
5732
5642
  const { renderDoctorReport } = await import('../../render-ZQQ4UMNO.js');
5733
5643
  const doctorReport = await runDoctorChecks({ mode: useLocal ? "full" : "hosted" });
5734
5644
  if (!doctorReport.ok) {
@@ -5766,7 +5676,7 @@ function createProgram() {
5766
5676
  taskForRuns.config.runs
5767
5677
  );
5768
5678
  if (k > 1) {
5769
- const { runTrialGroup } = await import('../../runTrialGroup-4QL6S7QO.js');
5679
+ const { runTrialGroup } = await import('../../runTrialGroup-I3PC2AST.js');
5770
5680
  const fileForRerun = relative(process.cwd(), file);
5771
5681
  const rerunCommand = defaultTask ? options.trials !== void 0 ? `pome run -n ${k}` : "pome run" : `pome run ${fileForRerun && !fileForRerun.startsWith("..") ? fileForRerun : file} -n ${k}`;
5772
5682
  const groupResult = await runTrialGroup({
@@ -5848,7 +5758,7 @@ function createProgram() {
5848
5758
  program.command("doctor").summary("Check the agent and twin wiring").description(
5849
5759
  "Check the agent\u2194twin wiring: pome.json (or pome.yaml) present + valid, the local twin boots + serves, requests routed to the twin (not a hardcoded production host), egress floor active. On failure prints one named cause (file:line where knowable) + one concrete fix and exits non-zero."
5850
5760
  ).action(async () => {
5851
- const { runDoctorChecks } = await import('../../checks-QYUPKBPT.js');
5761
+ const { runDoctorChecks } = await import('../../checks-BOOQNOGJ.js');
5852
5762
  const { renderDoctorReport } = await import('../../render-ZQQ4UMNO.js');
5853
5763
  const report = await runDoctorChecks();
5854
5764
  for (const line of renderDoctorReport(report, { passNote: true })) console.error(line);
@@ -5999,64 +5909,85 @@ function createProgram() {
5999
5909
  })
6000
5910
  );
6001
5911
  });
6002
- const twin = program.command("twin").summary("Run a twin on this machine").description("Start a twin on this machine, print its status or a starter seed file");
5912
+ const twin = program.command("twin").summary("Run a twin on this machine").description(
5913
+ "Start a twin on this machine, print its status, show its tape, or write a starter seed file"
5914
+ );
6003
5915
  twin.command("start").argument(
6004
- "[name]",
6005
- `Twin name (${TWIN_NAME_LIST.join(" | ")}). Optional when --seed names exactly one twin.`
5916
+ "[names...]",
5917
+ `Twin names (${TWIN_NAME_LIST.join(" | ")}). One, or several to boot together (each on its own port). Optional when --seed names exactly one twin.`
6006
5918
  ).option(
6007
5919
  "--port <port>",
6008
5920
  // Built from the registry rather than restated: the per-twin overrides
6009
5921
  // are the registry's to add, and this text went stale when linear's did.
6010
5922
  `Port to bind (default: $PORT, else ${TWIN_NAME_LIST.filter(
6011
5923
  (twin2) => TWIN_REGISTRY[twin2].portEnvName
6012
- ).map((twin2) => `${TWIN_REGISTRY[twin2].portEnvName}/${TWIN_REGISTRY[twin2].defaultPort} for ${twin2}`).join(", ")}, otherwise 3333)`
5924
+ ).map((twin2) => `${TWIN_REGISTRY[twin2].portEnvName}/${TWIN_REGISTRY[twin2].defaultPort} for ${twin2}`).join(", ")}, otherwise 3333). With several twins: the first twin's port; the rest take the next free ports above it. Without it, each twin takes its default port, else the next free one.`
6013
5925
  ).option(
6014
5926
  "--seed <path>",
6015
- "Boot this twin from a JSON or YAML seed file instead of its default. A seed REPLACES the default; it does not merge. Takes the per-twin envelope { <twin>: { \u2026 } } or one twin's flat seed. Overrides POME_SEED_JSON."
5927
+ "Boot from a JSON or YAML seed file instead of the default. A seed REPLACES the default; it does not merge. Takes the per-twin envelope { <twin>: { \u2026 } } or one twin's flat seed (several twins need the envelope, one entry per named twin). Overrides POME_SEED_JSON."
6016
5928
  ).description(
6017
- "Start a standalone twin as a long-lived foreground server (Ctrl-C to stop)"
6018
- ).action(async (name, options) => {
6019
- const { runTwinStartCommand } = await import('../../twinStart-QQ64EV3P.js');
6020
- await runTwinStartCommand(name, options);
5929
+ "Start one or more standalone twins as a long-lived foreground server (Ctrl-C to stop)"
5930
+ ).action(async (names, options) => {
5931
+ const { runTwinStartCommand } = await import('../../twinStart-E5R26SF4.js');
5932
+ await runTwinStartCommand(names, options);
6021
5933
  });
6022
5934
  twin.command("new-seed").argument("<name...>", `Twin name (${TWIN_NAME_LIST.join(" | ")}). Repeat for one file covering several.`).option("--out <path>", "Write to this file instead of stdout. Refuses to overwrite.").summary("Print a new starter seed file for a twin").description(
6023
5935
  "Print a new starter seed file for a twin, generated from the twin's own starting state. One twin is flat, several are the per-twin envelope. Boot it with `twin start <twin> --seed`, seed a sandbox with `sandbox create --twin <twin> --seed`, or drop it beside a task as <task>.seed.json"
6024
5936
  ).action(async (names, options) => {
6025
- const { runTwinSeedCommand } = await import('../../twinSeed-4VQ7RRCL.js');
5937
+ const { runTwinSeedCommand } = await import('../../twinSeed-ADWQ62CA.js');
6026
5938
  await runTwinSeedCommand(names, options);
6027
5939
  });
6028
- twin.command("status").summary("Say whether the local twin is running").description(
6029
- "Say whether the twin `pome twin start` last booted here is still running, and print its paste-able env lines"
5940
+ twin.command("status").summary("Say whether the local twins are running").description(
5941
+ "Say whether each twin `pome twin start` booted here is still running, and print its paste-able env lines"
6030
5942
  ).action(async () => {
6031
5943
  const statusPath = ".pome/twin-status.json";
6032
5944
  if (!existsSync(statusPath)) {
6033
5945
  console.log("No standalone twin status found.");
6034
5946
  return;
6035
5947
  }
6036
- let status;
6037
- let origin;
5948
+ const { standaloneStatusEntries } = await import('../../twinStart-E5R26SF4.js');
5949
+ let entries;
6038
5950
  try {
6039
- status = JSON.parse(await readFile(statusPath, "utf8"));
6040
- if (typeof status.name !== "string" || status.name === "") throw new Error("no name");
6041
- origin = new URL(status.rest_url).origin;
5951
+ entries = standaloneStatusEntries(JSON.parse(await readFile(statusPath, "utf8"))).map(
5952
+ (status) => ({ ...status, origin: new URL(status.rest_url).origin })
5953
+ );
5954
+ if (entries.length === 0 || entries.some((status) => status.name === "")) {
5955
+ throw new Error("no twins");
5956
+ }
6042
5957
  } catch {
6043
5958
  throw new Error(
6044
5959
  `pome twin status: ${statusPath} is unreadable \u2014 start a twin with \`pome twin start <${TWIN_NAME_LIST.join("|")}>\`.`
6045
5960
  );
6046
5961
  }
6047
- const running = await fetch(`${origin}/healthz`, {
6048
- signal: AbortSignal.timeout(1e3)
6049
- }).then(
6050
- async (res) => res.ok && (await res.json().catch(() => ({}))).twin === status.name,
6051
- () => false
6052
- );
6053
- console.log(
6054
- running ? `${status.name} twin \u2014 running` : `${status.name} twin \u2014 not running (stale ${statusPath})`
6055
- );
6056
- const envName = TWIN_REGISTRY[status.name]?.envName ?? status.name.toUpperCase();
6057
- console.log(`POME_${envName}_REST_URL=${status.rest_url}`);
6058
- console.log(`POME_${envName}_MCP_URL=${status.mcp_url}`);
6059
- console.log(`POME_AUTH_TOKEN=${status.auth_token}`);
5962
+ for (const [index, status] of entries.entries()) {
5963
+ const running = await fetch(`${status.origin}/healthz`, {
5964
+ signal: AbortSignal.timeout(1e3)
5965
+ }).then(
5966
+ async (res) => res.ok && (await res.json().catch(() => ({}))).twin === status.name,
5967
+ () => false
5968
+ );
5969
+ if (index > 0) console.log("");
5970
+ console.log(
5971
+ running ? `${status.name} twin \u2014 running` : `${status.name} twin \u2014 not running (stale ${statusPath})`
5972
+ );
5973
+ const envName = TWIN_REGISTRY[status.name]?.envName ?? status.name.toUpperCase();
5974
+ console.log(`POME_${envName}_REST_URL=${status.rest_url}`);
5975
+ console.log(`POME_${envName}_MCP_URL=${status.mcp_url}`);
5976
+ console.log(`POME_AUTH_TOKEN=${status.auth_token}`);
5977
+ }
5978
+ });
5979
+ twin.command("tape").argument(
5980
+ "[name]",
5981
+ `Twin name (${TWIN_NAME_LIST.join(" | ")}). Optional when one twin is recorded in .pome/twin-status.json.`
5982
+ ).option(
5983
+ "--diff",
5984
+ "Also print the state diff since the twin booted \u2014 its seed, default or --seed \u2014 per collection: added, changed, removed.",
5985
+ false
5986
+ ).option("--json", "Print the tape (and the diff, with --diff) as one JSON envelope.", false).summary("Show what the agent did on a local twin").description(
5987
+ "Print the running twin's tape: one line per request with status, fidelity and whether state changed, so a call that claimed success but landed nothing stands out. Reads the twin's address and token from .pome/twin-status.json; no account, no hosted call."
5988
+ ).action(async (name, options) => {
5989
+ const { runTwinTapeCommand } = await import('../../twinTape-3XNU3GRW.js');
5990
+ await runTwinTapeCommand(name, options);
6060
5991
  });
6061
5992
  program.command("capture-server").summary("Record the agent's model calls").description(
6062
5993
  "Boot an HTTP CONNECT-tunnel proxy that appends one LlmCallEvent per tunnel to events.jsonl. Spawned by `pome run`; agent traffic flows via HTTPS_PROXY."
@@ -2,8 +2,8 @@ import './chunk-C4BVTUA3.js';
2
2
  import { defaultSeedState, parseSeed } from './chunk-NBOQN5VX.js';
3
3
  export { defaultSeedState, parseSeed, seedSchema } from './chunk-NBOQN5VX.js';
4
4
  import { routeInputDeclarer, integerInput, mountDeclaredRoute, UndeclaredInputError, MalformedBodyError } from './chunk-IZFM7W2V.js';
5
- import { loadMcpToolFixture, defineTwin, twinBuildInfo, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-HRAD7MRX.js';
6
- import './chunk-5KFDRR53.js';
5
+ import { loadMcpToolFixture, defineTwin, twinBuildInfo, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-TWURH7YM.js';
6
+ import './chunk-7VZBAHQ2.js';
7
7
  import './chunk-SG6ZTIMT.js';
8
8
  import './chunk-FBSA5L36.js';
9
9
  import { z, ZodError } from 'zod';
@@ -3,8 +3,8 @@ import { gmailErrorEnvelope, gmailSeedSchema, parseSeed, defaultSeedState, notFo
3
3
  export { DEFAULT_GMAIL_AGENT_EMAIL, GmailError, SEARCH_MAILBOX_MESSAGE_BUDGET, agentPathInboxMailbox, defaultSeedState, gmailErrorEnvelope, gmailSeedSchema, loadSeedFromEnv, parseSearchQuery, parseSeed, validateSearchQuery } from './chunk-PASFBRK4.js';
4
4
  import './chunk-JWJYNAWI.js';
5
5
  import { routeInputDeclarer, integerInput, booleanInput, repeatedInput, mountDeclaredRoute, UndeclaredInputError, MalformedBodyError } from './chunk-IZFM7W2V.js';
6
- import { loadMcpToolFixture, deriveMcpToolTable, defineTwin, openTwinDatabase, createApp } from './chunk-HRAD7MRX.js';
7
- import './chunk-5KFDRR53.js';
6
+ import { loadMcpToolFixture, deriveMcpToolTable, defineTwin, openTwinDatabase, createApp } from './chunk-TWURH7YM.js';
7
+ import './chunk-7VZBAHQ2.js';
8
8
  import './chunk-SG6ZTIMT.js';
9
9
  import './chunk-FBSA5L36.js';
10
10
  import { createHash, createHmac, timingSafeEqual, randomBytes } from 'node:crypto';
@@ -3,8 +3,8 @@ import { MCP_PAGE_MAX, defaultSeedState, linearSeedSchema, notFound, badUserInpu
3
3
  export { DEFAULT_LINEAR_CLOCK, DEFAULT_LINEAR_EMAIL, DEFAULT_LINEAR_PORT, DEFAULT_LINEAR_SID, DEFAULT_LINEAR_TOKEN, LINEAR_PROVIDER_TOKEN_PREFIX, LinearTwinError, STATE_EXPORT_CAP, assertWebhookUrl, defaultSeedState, linearErrorEnvelope, linearSeedSchema, loadSeedFromEnv, parseSeed, unauthorizedEnvelope, unsupportedEnvelope, webhookUrlError } from './chunk-3FZY376K.js';
4
4
  import './chunk-JWJYNAWI.js';
5
5
  import { routeInputDeclarer, mountDeclaredRoute, UndeclaredInputError } from './chunk-IZFM7W2V.js';
6
- import { loadMcpToolFixture, deriveMcpToolTable, openTwinDatabase, defineTwin, createApp } from './chunk-HRAD7MRX.js';
7
- import './chunk-5KFDRR53.js';
6
+ import { loadMcpToolFixture, deriveMcpToolTable, openTwinDatabase, defineTwin, createApp } from './chunk-TWURH7YM.js';
7
+ import './chunk-7VZBAHQ2.js';
8
8
  import './chunk-SG6ZTIMT.js';
9
9
  import './chunk-FBSA5L36.js';
10
10
  import { createHmac, timingSafeEqual, createHash, randomBytes } from 'node:crypto';
@@ -3,8 +3,8 @@ import './chunk-JWJYNAWI.js';
3
3
  import { defaultSeedState, parseSeed } from './chunk-YBWG5JK2.js';
4
4
  export { defaultSeedState, loadSeedFromEnv, parseSeed, seedSchema } from './chunk-YBWG5JK2.js';
5
5
  import { routeInputDeclarer, booleanInput, integerInput, mountDeclaredRoute, UndeclaredInputError } from './chunk-IZFM7W2V.js';
6
- import { loadMcpToolFixture, defineTwin, queryTokenResolver, formTokenResolver, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-HRAD7MRX.js';
7
- import './chunk-5KFDRR53.js';
6
+ import { loadMcpToolFixture, defineTwin, queryTokenResolver, formTokenResolver, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp, typeDisagreements } from './chunk-TWURH7YM.js';
7
+ import './chunk-7VZBAHQ2.js';
8
8
  import './chunk-SG6ZTIMT.js';
9
9
  import './chunk-FBSA5L36.js';
10
10
  import { z, ZodError } from 'zod';
@@ -1,6 +1,6 @@
1
1
  import { routeInputDeclarer, bracketedQuery, integerInput, booleanInput, mountDeclaredRoute, UndeclaredInputError } from './chunk-IZFM7W2V.js';
2
- import { loadMcpToolFixture, openTwinDatabase, defineTwin, twinBuildInfo, deriveMcpToolTable, failureInjectionMiddleware, createApp, recordedRequestHeaders, UnknownToolError } from './chunk-HRAD7MRX.js';
3
- import './chunk-5KFDRR53.js';
2
+ import { loadMcpToolFixture, openTwinDatabase, defineTwin, twinBuildInfo, deriveMcpToolTable, failureInjectionMiddleware, createApp, recordedRequestHeaders, UnknownToolError } from './chunk-TWURH7YM.js';
3
+ import './chunk-7VZBAHQ2.js';
4
4
  import './chunk-SG6ZTIMT.js';
5
5
  import { defaultSeed, seedSchema } from './chunk-2K6BJ3PI.js';
6
6
  export { DEFAULT_API_KEY, DEFAULT_SID, defaultSeed, loadSeedFromEnv, parseSeed, seedSchema } from './chunk-2K6BJ3PI.js';
@@ -0,0 +1,6 @@
1
+ export { UnsupportedTwinError, bootTwin } from './chunk-3T4FUF43.js';
2
+ export { STRIPE_LOCAL_ACCOUNT_ID } from './chunk-RYAQ2ZYA.js';
3
+ import './chunk-TWURH7YM.js';
4
+ import './chunk-7VZBAHQ2.js';
5
+ import './chunk-SG6ZTIMT.js';
6
+ import './chunk-FBSA5L36.js';
@@ -1,4 +1,4 @@
1
- import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-ELIEDNF3.js';
1
+ import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-RYAQ2ZYA.js';
2
2
  import { writeFile } from 'node:fs/promises';
3
3
 
4
4
  async function generateSeedFile(twins) {