@kky42/pievo-server 2.4.9 → 2.4.10

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 (65) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{MachinesModal-wxHeRJsP.js → MachinesModal-ByHejech.js} +1 -1
  3. package/.output/public/assets/app-IrlFuBXa.css +2 -0
  4. package/.output/public/assets/{index-DXJGEV9d.js → index-ia26HsP1.js} +3 -3
  5. package/.output/public/assets/{invite._token-DgbAZ8tR.js → invite._token-tDfe3p0Z.js} +1 -1
  6. package/.output/public/assets/{loops._loopId-hqovx5nj.js → loops._loopId-CuYq_XgT.js} +3 -3
  7. package/.output/public/assets/{loops._loopId_.runs._runId-B233CSov.js → loops._loopId_.runs._runId-mOaF51Qu.js} +1 -1
  8. package/.output/public/assets/{routes-BcmIcHul.js → routes-Dbl_3lt_.js} +1 -1
  9. package/.output/public/assets/{t._teamId-CJOyBon9.js → t._teamId-BVsl0EGm.js} +1 -1
  10. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  11. package/.output/server/_ssr/{DashboardView-Dj-FImnr.mjs → DashboardView-BMHMrOfC.mjs} +6 -6
  12. package/.output/server/_ssr/{MachinesModal-C8UdXFwP.mjs → MachinesModal-BhRw7oOx.mjs} +8 -15
  13. package/.output/server/_ssr/{SignIn-kgyyAjxa.mjs → SignIn-BhoZIOUp.mjs} +3 -3
  14. package/.output/server/_ssr/{SocialLinks-ZYIMrv4W.mjs → SocialLinks-DVCLmql0.mjs} +2 -11
  15. package/.output/server/_ssr/{actionUi-DCNmrAjw.mjs → actionUi-Cf6DuEC2.mjs} +2 -2
  16. package/.output/server/_ssr/{artifactFiles-Dg7Csxw-.mjs → artifactFiles-ChuJ4PzC.mjs} +3 -4
  17. package/.output/server/_ssr/{artifactKind-Bex7n005.mjs → artifactKind-DXumOv7c.mjs} +3 -7
  18. package/.output/server/_ssr/{artifacts-CGJQi12P.mjs → artifacts-DOLHJHcd.mjs} +1 -1
  19. package/.output/server/_ssr/{auth-pONvN2Mo.mjs → auth-LwPp_iim.mjs} +9 -13
  20. package/.output/server/_ssr/{boot-DoRoAQuI.mjs → boot-BIxtZHU-.mjs} +61 -209
  21. package/.output/server/_ssr/{boot-DF8p07PC.mjs → boot-D8cj4uZj.mjs} +1 -1
  22. package/.output/server/_ssr/{bun-sqlite-dialect-BW9W1_Ps-fxdbbtvy.mjs → bun-sqlite-dialect-BW9W1_Ps-CHMDkKqA.mjs} +2 -2
  23. package/.output/server/_ssr/{d1-sqlite-dialect-BLC8LXE6-CcOEyONf.mjs → d1-sqlite-dialect-BLC8LXE6-DYum6qky.mjs} +2 -2
  24. package/.output/server/_ssr/db-BphSJs66.mjs +2 -0
  25. package/.output/server/_ssr/{dist-ghpS-jzd.mjs → dist-bWqs5sTy.mjs} +2 -2
  26. package/.output/server/_ssr/{factory-D7WCUKnW.mjs → factory-pBwrjVTI.mjs} +2 -2
  27. package/.output/server/_ssr/{invite._token-hkOJXnJU.mjs → invite._token-CPSvdDox.mjs} +4 -4
  28. package/.output/server/_ssr/{invite._token-YSls7Te8.mjs → invite._token-CRBsTSTZ.mjs} +6 -6
  29. package/.output/server/_ssr/{invite._token-CGzJGIfm.mjs → invite._token-CaTLt92a.mjs} +2 -2
  30. package/.output/server/_ssr/{kysely-adapter-DD_FuS-x.mjs → kysely-adapter-DMf5pFTf.mjs} +1 -1
  31. package/.output/server/_ssr/{lifecycleUi-DOr6Af13.mjs → lifecycleUi-DauKavgx.mjs} +1 -2
  32. package/.output/server/_ssr/logger-EinA2J6y.mjs +2 -0
  33. package/.output/server/_ssr/{loopApi-Vb7RrshX.mjs → loopApi-Im6JSK0p.mjs} +10 -11
  34. package/.output/server/_ssr/{loopProjection-DfpYkHDU.mjs → loopProjection-DIpGR6rp.mjs} +2 -4
  35. package/.output/server/_ssr/{loops._loopId-BKUT85ip.mjs → loops._loopId-B5iPseDG.mjs} +13 -27
  36. package/.output/server/_ssr/{loops._loopId_.runs._runId-CBXFBNrN.mjs → loops._loopId_.runs._runId-BA-P6sGA.mjs} +6 -44
  37. package/.output/server/_ssr/{loops._loopId_.runs._runId-Bha29x1u.mjs → loops._loopId_.runs._runId-DVtG0xYb.mjs} +3 -3
  38. package/.output/server/_ssr/{machineFns-BFBPpfxd.mjs → machineFns-Bb9KNXun.mjs} +3 -5
  39. package/.output/server/_ssr/{node-sqlite-dialect-BDYK0mnk.mjs → node-sqlite-dialect-Dho9YjXb.mjs} +2 -2
  40. package/.output/server/_ssr/{router-DfegU72d.mjs → router-DPz3uoUr.mjs} +22 -30
  41. package/.output/server/_ssr/{routes-BwTvON37.mjs → routes-B9zD2h4Y.mjs} +5 -7
  42. package/.output/server/_ssr/{routes-Bx3C3K_H.mjs → routes-BuILEmHs.mjs} +5 -5
  43. package/.output/server/_ssr/{routes-D5GHah06.mjs → routes-FO0MFfYK.mjs} +2 -2
  44. package/.output/server/_ssr/runDiff-BLdot9mi.mjs +2 -0
  45. package/.output/server/_ssr/ssr.mjs +39 -39
  46. package/.output/server/_ssr/store-CFCp_ggC.mjs +2 -0
  47. package/.output/server/_ssr/{t._teamId-CpPl9_XU.mjs → t._teamId-1t5wOXrV.mjs} +5 -5
  48. package/.output/server/_ssr/{t._teamId-BFs1MfBR.mjs → t._teamId-A37pQ8Vp.mjs} +5 -5
  49. package/.output/server/_ssr/{t._teamId-CrZ7EkUL.mjs → t._teamId-D1BTwhWf.mjs} +2 -2
  50. package/.output/server/_ssr/{teamFns-BVjiPvv8.mjs → teamFns-DfqtuBW2.mjs} +3 -7
  51. package/.output/server/_ssr/{teamFns-3lItAtfc.mjs → teamFns-cI_tsiCv.mjs} +2 -3
  52. package/.output/server/_ssr/{tokens-DUsrVE9g.mjs → tokens-CUz4LDLm.mjs} +1 -1
  53. package/.output/server/_ssr/{ui-BEPYdOmJ.mjs → ui-DGz-q9MT.mjs} +1 -22
  54. package/.output/server/{_tanstack-start-manifest_v-qnHwyto-.mjs → _tanstack-start-manifest_v-CojB4Veh.mjs} +11 -11
  55. package/.output/server/drizzle/0001_add_pi_agent.sql +9 -0
  56. package/.output/server/drizzle/meta/_journal.json +7 -0
  57. package/.output/server/index.mjs +65 -65
  58. package/drizzle/0001_add_pi_agent.sql +9 -0
  59. package/drizzle/meta/_journal.json +7 -0
  60. package/package.json +1 -1
  61. package/.output/public/assets/app-CuZ0I3Mb.css +0 -2
  62. package/.output/server/_ssr/db-DS0gckHd.mjs +0 -2
  63. package/.output/server/_ssr/logger-BuIBg8Jv.mjs +0 -2
  64. package/.output/server/_ssr/runDiff-CcG2PgCn.mjs +0 -2
  65. package/.output/server/_ssr/store-yOJEt4It.mjs +0 -2
@@ -11,7 +11,7 @@ import { fileURLToPath } from "node:url";
11
11
  import path from "node:path";
12
12
  import os from "node:os";
13
13
  import fs$1 from "node:fs/promises";
14
- //#region node_modules/.nitro/vite/services/ssr/assets/boot-DoRoAQuI.js
14
+ //#region node_modules/.nitro/vite/services/ssr/assets/boot-BIxtZHU-.js
15
15
  var import_pino = /* @__PURE__ */ __toESM(require_pino());
16
16
  var import_pino_pretty = /* @__PURE__ */ __toESM(require_pino_pretty());
17
17
  function defineErrorCodes(codes) {
@@ -1268,7 +1268,6 @@ function r2Config() {
1268
1268
  * a deploy can tune them without a rebuild. Each has a safe, generous default —
1269
1269
  * the bias is "keep storage bounded without ever surprising a healthy loop".
1270
1270
  */
1271
- /** A positive env integer, or `fallback` when unset / unparseable / non-positive. */
1272
1271
  function posIntEnv(name, fallback) {
1273
1272
  const raw = process.env[name]?.trim();
1274
1273
  if (!raw) return fallback;
@@ -1321,11 +1320,9 @@ function dbWatchdogEnabled() {
1321
1320
  if (process.env.VITEST || false) return false;
1322
1321
  return Boolean(databaseUrl());
1323
1322
  }
1324
- /** How often the DB watchdog pings `select 1`. Default 20s. */
1325
1323
  function dbWatchdogIntervalMs() {
1326
1324
  return posIntEnv("PIEVO_DB_WATCHDOG_INTERVAL_MS", 2e4);
1327
1325
  }
1328
- /** Hard per-ping deadline; a ping that exceeds it counts as a failure. Default 5s. */
1329
1326
  function dbWatchdogTimeoutMs() {
1330
1327
  return posIntEnv("PIEVO_DB_WATCHDOG_TIMEOUT_MS", 5e3);
1331
1328
  }
@@ -1338,8 +1335,6 @@ function dbWatchdogFailureThreshold() {
1338
1335
  return posIntEnv("PIEVO_DB_WATCHDOG_FAILURES", 3);
1339
1336
  }
1340
1337
  /**
1341
- * Central logger (pino + pino-pretty).
1342
- *
1343
1338
  * Uses pino-pretty as a *synchronous* destination stream (not a worker-thread
1344
1339
  * transport) so logs flush before process.exit() in the one-shot CLIs. Writes to
1345
1340
  * stderr (fd 2) to keep stdout clean for QR codes and human CLI output.
@@ -1405,7 +1400,7 @@ function poolOptionsFor(url, transactionPooler = isTransactionPooler(url)) {
1405
1400
  };
1406
1401
  }
1407
1402
  /** The coding agent a loop is bound to AND executed with (BYOA on the owner's
1408
- * machine): `claude-code` → Claude Code, `codex` → `codex exec`.
1403
+ * machine): `claude-code` → Claude Code, `codex` → `codex exec`, `pi` → Pi.
1409
1404
  * Non-Claude agents may still have thinner daemon telemetry until stream
1410
1405
  * integration lands; execution itself is real for every value.
1411
1406
  *
@@ -1415,9 +1410,11 @@ function poolOptionsFor(url, transactionPooler = isTransactionPooler(url)) {
1415
1410
  * web agent `<select>` (LoopForm). So widening the set is a one-line edit HERE with
1416
1411
  * no other server change (the daemon's own enum in `packages/daemon/src/create.ts`
1417
1412
  * is a separate package, widened alongside). */
1418
- var CODING_AGENTS = ["claude-code", "codex"];
1419
- /** Coerce an unknown value to a known `CodingAgent`, or null when unrecognized.
1420
- * Canonical loop validation and the web agent select share this enum source. */
1413
+ var CODING_AGENTS = [
1414
+ "claude-code",
1415
+ "codex",
1416
+ "pi"
1417
+ ];
1421
1418
  function coerceCodingAgent(value) {
1422
1419
  return typeof value === "string" && CODING_AGENTS.includes(value) ? value : null;
1423
1420
  }
@@ -1434,23 +1431,16 @@ function coerceCodingAgent(value) {
1434
1431
  * columns use `jsonb().$type<>()`; booleans use native `boolean()`.
1435
1432
  */
1436
1433
  var machines = pgTable("machines", {
1437
- /** m-sha256(deviceToken)[:16] */
1438
1434
  id: text("id").primaryKey(),
1439
- /** Owning user (Better Auth user.id) — creator attribution. */
1440
1435
  userId: text("user_id").notNull(),
1441
- /** Owning team — the scope machines and loops are listed by. */
1442
1436
  teamId: text("team_id").notNull(),
1443
- /** Friendly name (set AFTER the daemon connects; empty string = pending/unnamed). */
1444
1437
  name: text("name").notNull(),
1445
- /** Daemon-reported machine identity (captured on first connect). */
1446
1438
  hostname: text("hostname"),
1447
1439
  platform: text("platform"),
1448
1440
  arch: text("arch"),
1449
- /** Daemon package version reported on poll. Drives the web upgrade hint. */
1450
1441
  daemonVersion: text("daemon_version"),
1451
1442
  /** Breaking machine protocol last observed on poll. */
1452
1443
  daemonProtocol: integer("daemon_protocol"),
1453
- /** Hash of the device token (machine identity derives from the token). */
1454
1444
  tokenHash: text("token_hash").notNull(),
1455
1445
  /**
1456
1446
  * Plaintext device token. Stored so the UI can re-show the connect command
@@ -1458,67 +1448,51 @@ var machines = pgTable("machines", {
1458
1448
  * for a self-hosted team tool where the DB is already the trust root).
1459
1449
  */
1460
1450
  token: text("token"),
1461
- /** Workdir allowlist the daemon enforces as cwd jail; null/[] = unrestricted. */
1462
1451
  roots: jsonb("roots").$type(),
1463
- /** Last WS contact (ISO). */
1464
1452
  lastSeen: text("last_seen"),
1465
- /** Live WS connection state. */
1466
1453
  online: boolean("online").notNull().default(false),
1467
1454
  createdAt: text("created_at").notNull()
1468
1455
  }, (t) => [index("machines_user_idx").on(t.userId), index("machines_team_idx").on(t.teamId)]);
1469
1456
  var loops = pgTable("loops", {
1470
1457
  id: text("id").primaryKey(),
1471
- /** Owning user (creator attribution). */
1472
1458
  userId: text("user_id").notNull(),
1473
- /** Owning team — the scope loops are listed and authorized by. */
1474
1459
  teamId: text("team_id").notNull(),
1475
1460
  /** Execution machine (set at creation; no cross-machine fallback). */
1476
1461
  machineId: text("machine_id").notNull(),
1477
1462
  name: text("name").notNull(),
1478
- /** The user's complete runtime prompt. Canonical writes always provide it. */
1479
1463
  prompt: text("prompt").notNull(),
1480
- /** User-authored meanings appended to every delivered prompt. */
1481
1464
  statusKeep: text("status_keep").notNull(),
1482
1465
  statusNoChange: text("status_no_change").notNull(),
1483
1466
  statusBlock: text("status_block").notNull(),
1484
1467
  /** Exact workdir-relative artifact paths collected by the daemon after provider exit. */
1485
1468
  artifacts: jsonb("artifacts").$type().notNull().default([]),
1486
1469
  cron: text("cron").notNull(),
1487
- /** Cadence facts stay denormalized internally; external writes use LoopSchedule. */
1488
1470
  scheduleMode: text("schedule_mode", { enum: ["cron", "continuous"] }).notNull(),
1489
1471
  /** Cron-only overlap behavior. Continuous runs never overlap. */
1490
1472
  cronOverlap: text("cron_overlap", { enum: ["skip", "queue-one"] }).notNull(),
1491
- /** Delay used when a done/error terminal restores continuous cadence. */
1492
1473
  continuousDelayMinutes: integer("continuous_delay_minutes").notNull(),
1493
- /** IANA tz for cron rows; null for continuous rows. */
1494
1474
  timezone: text("timezone"),
1495
- /** Absolute project dir ON THE MACHINE the agent runs in (cwd). */
1496
1475
  workdir: text("workdir").notNull(),
1497
1476
  /** Durable Stop-before-delete marker. A deleting loop is never claimable. */
1498
1477
  deleteRequestedAt: timestamp("delete_requested_at", {
1499
1478
  withTimezone: true,
1500
1479
  mode: "string"
1501
1480
  }),
1502
- /** Diagnostic annotation for a paused loop. */
1503
1481
  pauseCause: jsonb("pause_cause").$type(),
1504
- /** Optional provider model id. Null delegates model selection to the coding-agent CLI. */
1505
1482
  model: text("model"),
1506
- /** Optional provider reasoning-effort value. Null delegates to the coding-agent CLI. */
1507
1483
  reasoningEffort: text("reasoning_effort"),
1508
1484
  /** Coding agent this loop is BOUND TO and EXECUTED with: the daemon's
1509
1485
  * `buildAgentSpawn` branches on this value (`claude-code` → claude,
1510
- * `codex` → `codex exec`). Measured from the creating CLI's env when
1486
+ * `codex` → `codex exec`, `pi` → Pi print mode). Measured from the creating CLI's env when
1511
1487
  * detectable, else the declared/selected value. The text column carries
1512
1488
  * both a TypeScript enum and a database CHECK constraint. */
1513
1489
  agent: text("agent", { enum: CODING_AGENTS }).notNull(),
1514
1490
  enabled: boolean("enabled").notNull().default(true),
1515
- /** One-shot override, independent of the recurring cadence (ISO). */
1516
1491
  nextRunAt: text("next_run_at"),
1517
1492
  /** The next recurring cadence occurrence not yet materialized as a run. Cron
1518
1493
  * advances this fact after each due occurrence; continuous clears it on due
1519
1494
  * and restores it from a successful/error terminal. */
1520
1495
  nextCadenceAt: text("next_cadence_at"),
1521
- /** Last history number assigned under this loop's row lock. */
1522
1496
  lastRunIndex: integer("last_run_index").notNull().default(0),
1523
1497
  createdAt: text("created_at").notNull(),
1524
1498
  updatedAt: text("updated_at").notNull()
@@ -1527,7 +1501,7 @@ var loops = pgTable("loops", {
1527
1501
  index("loops_team_idx").on(t.teamId),
1528
1502
  index("loops_machine_idx").on(t.machineId),
1529
1503
  index("delete_requested_loops").on(t.deleteRequestedAt).where(sql`${t.deleteRequestedAt} IS NOT NULL`),
1530
- check("loops_agent_check", sql`${t.agent} IN ('claude-code', 'codex')`),
1504
+ check("loops_agent_check", sql`${t.agent} IN ('claude-code', 'codex', 'pi')`),
1531
1505
  check("loops_schedule_mode_check", sql`${t.scheduleMode} IN ('cron', 'continuous')`),
1532
1506
  check("loops_cron_overlap_check", sql`${t.cronOverlap} IN ('skip', 'queue-one')`)
1533
1507
  ]);
@@ -1535,11 +1509,9 @@ var runs = pgTable("runs", {
1535
1509
  id: text("id").primaryKey(),
1536
1510
  loopId: text("loop_id").notNull(),
1537
1511
  machineId: text("machine_id").notNull(),
1538
- /** Execution profile captured atomically at claim. Pending/unclaimed rows are null. */
1539
1512
  agent: text("agent", { enum: CODING_AGENTS }),
1540
1513
  model: text("model"),
1541
1514
  reasoningEffort: text("reasoning_effort"),
1542
- /** 1-based execution/terminal order, assigned at claim or unclaimed terminalization. */
1543
1515
  runIndex: integer("run_index"),
1544
1516
  phase: text("phase", { enum: [
1545
1517
  "pending",
@@ -1551,12 +1523,9 @@ var runs = pgTable("runs", {
1551
1523
  /** Durable queue authority. It only promotes system→owner; diagnostic trigger
1552
1524
  * reasons, if added later, must never drive lifecycle state. */
1553
1525
  requestedBy: text("requested_by", { enum: ["owner", "system"] }).notNull().default("system"),
1554
- /** Current phase/event timestamp. */
1555
1526
  ts: text("ts").notNull(),
1556
- /** Immutable queue/history age anchor plus mutation timestamp. */
1557
1527
  createdAt: text("created_at").notNull().default(sql`CURRENT_TIMESTAMP::text`),
1558
1528
  updatedAt: text("updated_at").notNull().default(sql`CURRENT_TIMESTAMP::text`),
1559
- /** Canonical report status. */
1560
1529
  status: text("status", { enum: [
1561
1530
  "keep",
1562
1531
  "no-change",
@@ -1567,18 +1536,14 @@ var runs = pgTable("runs", {
1567
1536
  exitCode: integer("exit_code"),
1568
1537
  finalText: text("final_text"),
1569
1538
  error: text("error"),
1570
- /** Provider session id on the machine, retained for owner-side continuation. */
1571
1539
  sessionId: text("session_id"),
1572
- /** Provider-neutral token usage. Dollar cost is deliberately not stored. */
1573
1540
  usage: jsonb("usage").$type(),
1574
1541
  /** Monotonic cancellation intent. Running becomes canceled only on daemon report. */
1575
1542
  cancelRequestedAt: timestamp("cancel_requested_at", {
1576
1543
  withTimezone: true,
1577
1544
  mode: "string"
1578
1545
  }),
1579
- /** Last poll where this machine declared the run active. Timeout authority. */
1580
1546
  heartbeatAt: text("heartbeat_at"),
1581
- /** Latest rejected terminal-report diagnosis, if any. */
1582
1547
  reportIncident: jsonb("report_incident").$type()
1583
1548
  }, (t) => [
1584
1549
  index("runs_loop_idx").on(t.loopId),
@@ -1589,13 +1554,12 @@ var runs = pgTable("runs", {
1589
1554
  index("runs_loop_terminal_history_idx").on(t.loopId, t.runIndex).where(sql`${t.runIndex} IS NOT NULL AND ${t.phase} IN ('done', 'error', 'canceled')`),
1590
1555
  uniqueIndex("runs_loop_pending_idx").on(t.loopId).where(sql`${t.phase} = 'pending'`),
1591
1556
  uniqueIndex("one_running_run_per_loop").on(t.loopId).where(sql`${t.phase} = 'running'`),
1592
- check("runs_agent_check", sql`${t.agent} IS NULL OR ${t.agent} IN ('claude-code', 'codex')`),
1557
+ check("runs_agent_check", sql`${t.agent} IS NULL OR ${t.agent} IN ('claude-code', 'codex', 'pi')`),
1593
1558
  check("runs_phase_check", sql`${t.phase} IN ('pending', 'running', 'done', 'error', 'canceled')`),
1594
1559
  check("runs_requested_by_check", sql`${t.requestedBy} IN ('owner', 'system')`),
1595
1560
  check("runs_status_check", sql`${t.status} IS NULL OR ${t.status} IN ('keep', 'no-change', 'block')`)
1596
1561
  ]);
1597
1562
  var runLeases = pgTable("run_leases", {
1598
- /** sha256 hex of the full `rk_…` wire token. */
1599
1563
  tokenHash: text("token_hash").primaryKey(),
1600
1564
  runId: text("run_id").notNull(),
1601
1565
  loopId: text("loop_id").notNull(),
@@ -1645,11 +1609,8 @@ var terminalReportIncidents = pgTable("terminal_report_incidents", {
1645
1609
  check("terminal_report_incidents_disposition_check", sql`${t.disposition} IN ('run-error', 'telemetry-rejected')`)
1646
1610
  ]);
1647
1611
  var connectKeys = pgTable("connect_keys", {
1648
- /** m-sha256(connectKey)[:16] — the machine id this key self-registers as. */
1649
1612
  machineId: text("machine_id").primaryKey(),
1650
- /** The user who minted the key (the authenticated dashboard session). */
1651
1613
  userId: text("user_id").notNull(),
1652
- /** The validated active team the key was minted under. */
1653
1614
  teamId: text("team_id").notNull(),
1654
1615
  /** Mint time (ISO) — drives the TTL. */
1655
1616
  mintedAt: text("minted_at").notNull()
@@ -1657,7 +1618,6 @@ var connectKeys = pgTable("connect_keys", {
1657
1618
  var teams = pgTable("teams", {
1658
1619
  id: text("id").primaryKey(),
1659
1620
  name: text("name").notNull(),
1660
- /** The user whose personal team this is (null for the open-mode shared team). */
1661
1621
  ownerUserId: text("owner_user_id"),
1662
1622
  createdAt: text("created_at").notNull()
1663
1623
  });
@@ -1673,18 +1633,13 @@ var teamMembers = pgTable("team_members", {
1673
1633
  check("team_members_role_check", sql`${t.role} IN ('owner', 'member')`)
1674
1634
  ]);
1675
1635
  var teamInvites = pgTable("team_invites", {
1676
- /** The wire token the recipient presents (`/invite/<token>`). */
1677
1636
  token: text("token").primaryKey(),
1678
1637
  teamId: text("team_id").notNull(),
1679
- /** Role granted on redeem (capped at the inviter's role at mint time). */
1680
1638
  role: text("role", { enum: ["owner", "member"] }).notNull().default("member"),
1681
- /** The owner who minted the invite (attribution). */
1682
1639
  invitedByUserId: text("invited_by_user_id").notNull(),
1683
- /** When the invite lapses (ISO). A redeem past this is refused. */
1684
1640
  expiresAt: text("expires_at").notNull(),
1685
1641
  /** Single-use stamp: set on redeem so the same link can't be reused. */
1686
1642
  redeemedAt: text("redeemed_at"),
1687
- /** The user who redeemed it (attribution). */
1688
1643
  redeemedByUserId: text("redeemed_by_user_id"),
1689
1644
  createdAt: text("created_at").notNull()
1690
1645
  }, (t) => [index("team_invites_team_idx").on(t.teamId), check("team_invites_role_check", sql`${t.role} IN ('owner', 'member')`)]);
@@ -1693,7 +1648,6 @@ var teamInvites = pgTable("team_invites", {
1693
1648
  * the byte store under the hash; this row records verified byte presence and size.
1694
1649
  */
1695
1650
  var blobs = pgTable("blobs", {
1696
- /** sha256 hex of the bytes (the byte-store key). */
1697
1651
  hash: text("hash").primaryKey(),
1698
1652
  size: integer("size").notNull(),
1699
1653
  createdAt: text("created_at").notNull()
@@ -1709,14 +1663,12 @@ var artifactFiles = pgTable("artifact_files", {
1709
1663
  loopId: text("loop_id").notNull(),
1710
1664
  /** Normalized workdir-relative path (never absolute or escaping the workdir). */
1711
1665
  path: text("path").notNull(),
1712
- /** → blobs.hash. Null when deleted or oversize (no bytes stored). */
1713
1666
  hash: text("hash"),
1714
1667
  size: integer("size"),
1715
1668
  /** Bytes contain a NUL (set even for oversize metadata-only files). */
1716
1669
  binary: boolean("binary").notNull().default(false),
1717
1670
  /** File exceeds the per-file byte cap → metadata-only (path + size), no blob. */
1718
1671
  oversize: boolean("oversize").notNull().default(false),
1719
- /** Tombstone: the file vanished from the loop's manifest (kept for future diffs). */
1720
1672
  deleted: boolean("deleted").notNull().default(false),
1721
1673
  updatedAt: text("updated_at").notNull()
1722
1674
  }, (t) => [
@@ -1943,7 +1895,6 @@ function newLoopId() {
1943
1895
  function nowIso$1() {
1944
1896
  return (/* @__PURE__ */ new Date()).toISOString();
1945
1897
  }
1946
- /** First cron occurrence strictly after `after`, interpreted in the loop's zone. */
1947
1898
  function nextCronAt(cron, timezone, after) {
1948
1899
  if (!timezone) throw new Error("invariant: cron schedule has no timezone");
1949
1900
  const probe = new E(cron, {
@@ -1969,8 +1920,6 @@ async function listLoops(teamId) {
1969
1920
  const q = db.select().from(loops);
1970
1921
  return teamId ? await q.where(eq(loops.teamId, teamId)) : await q;
1971
1922
  }
1972
- /** Count a team's loops without materializing their (large) rows — the
1973
- * delete-block guard + settings-detail badge only need the tally. */
1974
1923
  async function countLoopsForTeam(teamId) {
1975
1924
  const r = (await db.select({ n: sql`count(*)` }).from(loops).where(eq(loops.teamId, teamId)))[0];
1976
1925
  return Number(r?.n ?? 0);
@@ -1978,7 +1927,6 @@ async function countLoopsForTeam(teamId) {
1978
1927
  async function getLoop(id) {
1979
1928
  return (await db.select().from(loops).where(eq(loops.id, id)))[0];
1980
1929
  }
1981
- /** Loops bound to a machine — used by machine cascade authorization and UI counts. */
1982
1930
  async function loopsForMachine(machineId) {
1983
1931
  return db.select().from(loops).where(eq(loops.machineId, machineId));
1984
1932
  }
@@ -2080,7 +2028,6 @@ async function updateLoopTx(tx, current, patch, at) {
2080
2028
  }
2081
2029
  return loop;
2082
2030
  }
2083
- /** Partial owner/system update under the loop lifecycle lock. */
2084
2031
  async function updateLoop(id, patch) {
2085
2032
  return db.transaction(async (tx) => {
2086
2033
  const current = (await tx.select().from(loops).where(eq(loops.id, id)).for("update"))[0];
@@ -2112,11 +2059,9 @@ async function pauseLoopState(id) {
2112
2059
  };
2113
2060
  });
2114
2061
  }
2115
- /** Pause a loop and return its updated row. */
2116
2062
  async function pauseLoop(id) {
2117
2063
  return (await pauseLoopState(id))?.loop;
2118
2064
  }
2119
- /** Start a paused loop unless deletion has begun. */
2120
2065
  async function startLoop(id) {
2121
2066
  return db.transaction(async (tx) => {
2122
2067
  const current = (await tx.select().from(loops).where(eq(loops.id, id)).for("update"))[0];
@@ -2146,14 +2091,12 @@ async function stopLoopTx(tx, current, at) {
2146
2091
  }).where(and(eq(runs.id, running.id), eq(runs.phase, "running"), isNull(runs.cancelRequestedAt))).returning())[0] ?? running
2147
2092
  };
2148
2093
  }
2149
- /** Stop is one loop-locked write: Pause + cancel all queue + mark current run. */
2150
2094
  async function stopLoop(id) {
2151
2095
  return db.transaction(async (tx) => {
2152
2096
  const current = (await tx.select().from(loops).where(eq(loops.id, id)).for("update"))[0];
2153
2097
  return current ? stopLoopTx(tx, current, nowIso$1()) : void 0;
2154
2098
  });
2155
2099
  }
2156
- /** Stop-run preserves loop lifecycle; running cancellation waits for daemon proof. */
2157
2100
  async function requestRunCancel(loopId, runId) {
2158
2101
  return db.transaction(async (tx) => {
2159
2102
  if (!(await tx.select({ id: loops.id }).from(loops).where(eq(loops.id, loopId)).for("update"))[0]) return void 0;
@@ -2338,7 +2281,6 @@ async function runningRunForLoop(loopId) {
2338
2281
  async function getReportReceipt(reportId) {
2339
2282
  return (await db.select().from(runReportReceipts).where(eq(runReportReceipts.reportId, reportId)))[0];
2340
2283
  }
2341
- /** Deterministic primary key for exact rejected-payload replay evidence. */
2342
2284
  function terminalIncidentReceiptId(reportId, payloadDigest) {
2343
2285
  return createHash("sha256").update(reportId + payloadDigest).digest("hex");
2344
2286
  }
@@ -2407,7 +2349,6 @@ async function refreshRunHeartbeats(machineId, runIds, at, staleBefore) {
2407
2349
  updatedAt: at
2408
2350
  }).where(and(eq(runs.machineId, machineId), eq(runs.phase, "running"), inArray(runs.id, runIds), or(isNull(runs.heartbeatAt), lt(runs.heartbeatAt, staleBefore)))).returning({ id: runs.id })).length;
2409
2351
  }
2410
- /** Validate the report-only authority for one currently running run. */
2411
2352
  async function activeRunForReportTx(tx, loopId, runId, leaseTokenHash) {
2412
2353
  const lease = (await tx.select().from(runLeases).where(and(eq(runLeases.tokenHash, leaseTokenHash), eq(runLeases.runId, runId), eq(runLeases.loopId, loopId), eq(runLeases.state, "active"))).limit(1).for("update"))[0];
2413
2354
  if (!lease) return { state: "invalid-lease" };
@@ -2792,11 +2733,9 @@ async function claimReadyRunForMachine(machineId, at = nowIso$1(), excludeRunIds
2792
2733
  };
2793
2734
  });
2794
2735
  }
2795
- /** Newest-last run history for a loop (chronological), capped. */
2796
2736
  async function listRuns(loopId, limit = 30) {
2797
2737
  return (await db.select().from(runs).where(eq(runs.loopId, loopId)).orderBy(desc(runs.ts)).limit(limit)).reverse();
2798
2738
  }
2799
- /** Live reconciliation projection for dashboard/run-detail reads. */
2800
2739
  async function reconciliationStatesForRuns(loopId, runIds, at = nowIso$1()) {
2801
2740
  if (!runIds.length) return /* @__PURE__ */ new Map();
2802
2741
  const rows = await db.select({
@@ -2818,7 +2757,6 @@ async function countRuns(loopId) {
2818
2757
  const r = (await db.select({ n: sql`count(*)` }).from(runs).where(eq(runs.loopId, loopId)))[0];
2819
2758
  return Number(r?.n ?? 0);
2820
2759
  }
2821
- /** Run count and reported input + output tokens in a bounded recent window. */
2822
2760
  async function recentLoopUsage(loopId, since) {
2823
2761
  const row = (await db.select({
2824
2762
  runCount: sql`count(*)`,
@@ -2832,7 +2770,6 @@ async function recentLoopUsage(loopId, since) {
2832
2770
  tokenCount: Number(row?.tokenCount ?? 0)
2833
2771
  };
2834
2772
  }
2835
- /** Open runs (pending/running) — used by the timeout-reclaim sweep. */
2836
2773
  async function openRuns() {
2837
2774
  return db.select().from(runs).where(inArray(runs.phase, ["pending", "running"]));
2838
2775
  }
@@ -2914,7 +2851,6 @@ async function setMachineOnline(id, online) {
2914
2851
  lastSeen: nowIso$1()
2915
2852
  }).where(eq(machines.id, id));
2916
2853
  }
2917
- /** Deterministic personal-team id for a user (open mode ⇒ the shared "team-shared"). */
2918
2854
  function teamIdForUser(userId) {
2919
2855
  return `team-${userId ?? "shared"}`;
2920
2856
  }
@@ -2948,7 +2884,6 @@ async function ensureTeam(id, name, ownerUserId) {
2948
2884
  function newTeamId() {
2949
2885
  return `team-${randomUUID().slice(0, 12)}`;
2950
2886
  }
2951
- /** Whether this is the user's renamable but undeletable personal team. */
2952
2887
  function isPersonalTeam(team) {
2953
2888
  return !!team.ownerUserId && team.id === teamIdForUser(team.ownerUserId);
2954
2889
  }
@@ -2974,16 +2909,12 @@ async function createTeam(name, ownerUserId) {
2974
2909
  return team;
2975
2910
  });
2976
2911
  }
2977
- /** Rename a team (no name-sync fights this — see ensureTeam). */
2978
2912
  async function renameTeam(teamId, name) {
2979
2913
  await db.update(teams).set({ name }).where(eq(teams.id, teamId));
2980
2914
  }
2981
- /** A user's membership row in a team (undefined ⇒ not a member). */
2982
2915
  async function getTeamMember(teamId, userId) {
2983
2916
  return (await db.select().from(teamMembers).where(and(eq(teamMembers.teamId, teamId), eq(teamMembers.userId, userId))))[0];
2984
2917
  }
2985
- /** A team's members joined with their user email/name (owners first, then by
2986
- * join order). The member-list surface in the team settings UI. */
2987
2918
  async function listTeamMembers(teamId) {
2988
2919
  return (await db.select({
2989
2920
  id: teamMembers.id,
@@ -2999,7 +2930,6 @@ async function listTeamMembers(teamId) {
2999
2930
  displayName: r.displayName ?? null
3000
2931
  })).sort((a, b) => a.role === b.role ? a.createdAt < b.createdAt ? -1 : 1 : a.role === "owner" ? -1 : 1);
3001
2932
  }
3002
- /** Resolve a user by email for direct membership; undefined means no account yet. */
3003
2933
  async function userByEmail(email) {
3004
2934
  const r = (await db.select({
3005
2935
  id: user.id,
@@ -3078,7 +3008,6 @@ async function deleteTeamCascade(teamId) {
3078
3008
  if (outcome === "ok") ensuredTeams.delete(teamId);
3079
3009
  return outcome;
3080
3010
  }
3081
- /** Mint an invite for a team. `token` is the caller-supplied wire token. */
3082
3011
  async function createInvite(input) {
3083
3012
  return (await db.insert(teamInvites).values({
3084
3013
  ...input,
@@ -3088,7 +3017,6 @@ async function createInvite(input) {
3088
3017
  async function getInvite(token) {
3089
3018
  return (await db.select().from(teamInvites).where(eq(teamInvites.token, token)))[0];
3090
3019
  }
3091
- /** A team's still-live invites (unredeemed, unexpired), newest first. */
3092
3020
  async function listPendingInvites(teamId) {
3093
3021
  const now = nowIso$1();
3094
3022
  return db.select().from(teamInvites).where(and(eq(teamInvites.teamId, teamId), isNull(teamInvites.redeemedAt), gt(teamInvites.expiresAt, now))).orderBy(desc(teamInvites.createdAt));
@@ -3120,23 +3048,18 @@ async function redeemInviteAtomic(token, userId, grant) {
3120
3048
  return true;
3121
3049
  });
3122
3050
  }
3123
- /** Revoke a pending invite (owner action). */
3124
3051
  async function deleteInvite(token) {
3125
3052
  await db.delete(teamInvites).where(eq(teamInvites.token, token));
3126
3053
  }
3127
3054
  async function getTeam(id) {
3128
3055
  return (await db.select().from(teams).where(eq(teams.id, id)))[0];
3129
3056
  }
3130
- /** Teams the user belongs to (membership join), newest first. Drives the team
3131
- * switcher — a regular user has just their personal team (no dropdown). */
3132
3057
  async function listTeamsForUser(userId) {
3133
3058
  return (await db.select({ t: teams }).from(teamMembers).innerJoin(teams, eq(teamMembers.teamId, teams.id)).where(eq(teamMembers.userId, userId)).orderBy(desc(teams.createdAt))).map((r) => r.t);
3134
3059
  }
3135
- /** Whether the user is a member of the team (authorizes a team-switch request). */
3136
3060
  async function isTeamMember(teamId, userId) {
3137
3061
  return !!(await db.select({ id: teamMembers.id }).from(teamMembers).where(and(eq(teamMembers.teamId, teamId), eq(teamMembers.userId, userId))))[0];
3138
3062
  }
3139
- /** Does the server already have metadata for this blob hash? (drives needHashes). */
3140
3063
  async function blobExists(hash) {
3141
3064
  return !!(await db.select({ hash: blobs.hash }).from(blobs).where(eq(blobs.hash, hash)))[0];
3142
3065
  }
@@ -3150,7 +3073,6 @@ async function blobSizes(hashes) {
3150
3073
  }).from(blobs).where(inArray(blobs.hash, [...new Set(hashes)]));
3151
3074
  return new Map(rows.map((row) => [row.hash, row.size]));
3152
3075
  }
3153
- /** Record verified blob metadata idempotently. */
3154
3076
  async function recordBlob(hash, size) {
3155
3077
  await db.insert(blobs).values({
3156
3078
  hash,
@@ -3219,16 +3141,12 @@ async function reconcileConfiguredArtifacts(loopId, configuredAtStart, keepPaths
3219
3141
  return stale.length;
3220
3142
  });
3221
3143
  }
3222
- /** The loop's current (non-deleted) file set, path-sorted. */
3223
3144
  async function listArtifacts(loopId) {
3224
3145
  return db.select().from(artifactFiles).where(and(eq(artifactFiles.loopId, loopId), eq(artifactFiles.deleted, false))).orderBy(artifactFiles.path);
3225
3146
  }
3226
- /** One file row by loop + path (live or tombstoned). */
3227
3147
  async function getArtifactFile(loopId, path) {
3228
3148
  return (await db.select().from(artifactFiles).where(and(eq(artifactFiles.loopId, loopId), eq(artifactFiles.path, path))))[0];
3229
3149
  }
3230
- /** The loop's CURRENT live file set as a snapshot manifest (path → metadata) —
3231
- * what report() captures as the terminal run's artifact state. */
3232
3150
  async function buildLoopManifest(loopId) {
3233
3151
  const manifest = {};
3234
3152
  for (const f of await listArtifacts(loopId)) manifest[f.path] = {
@@ -3256,7 +3174,6 @@ async function putRunSnapshot(runId, loopId, manifest) {
3256
3174
  }
3257
3175
  });
3258
3176
  }
3259
- /** A run's captured snapshot, or undefined when the run predates the feature. */
3260
3177
  async function getRunSnapshot(runId) {
3261
3178
  return (await db.select().from(runSnapshots).where(eq(runSnapshots.runId, runId)))[0];
3262
3179
  }
@@ -3277,7 +3194,6 @@ async function pruneRunSnapshots(loopId, keep) {
3277
3194
  const pred = survivors.length ? and(eq(runSnapshots.loopId, loopId), notInArray(runSnapshots.runId, survivors)) : eq(runSnapshots.loopId, loopId);
3278
3195
  return (await db.delete(runSnapshots).where(pred).returning({ id: runSnapshots.runId })).length;
3279
3196
  }
3280
- /** Distinct loop ids that currently have at least one run snapshot. */
3281
3197
  async function loopIdsWithSnapshots() {
3282
3198
  return (await db.selectDistinct({ loopId: runSnapshots.loopId }).from(runSnapshots)).map((r) => r.loopId);
3283
3199
  }
@@ -3328,7 +3244,6 @@ async function countRunSnapshots() {
3328
3244
  const r = (await db.select({ n: sql`count(*)` }).from(runSnapshots))[0];
3329
3245
  return Number(r?.n ?? 0);
3330
3246
  }
3331
- /** Server-side artifact-sync helpers: path safety, byte caps, and hashing. */
3332
3247
  /** Per-file byte cap. At or under ⇒ bytes sync; over ⇒ metadata-only (no bytes). */
3333
3248
  var BLOB_CAP = 10 * 1024 * 1024;
3334
3249
  /** Hard ceiling on the exact-manifest sync POST body. */
@@ -3352,7 +3267,6 @@ function exactRecord(value, fields) {
3352
3267
  function sha256Buf(bytes) {
3353
3268
  return createHash("sha256").update(bytes).digest("hex");
3354
3269
  }
3355
- /** A 64-char lowercase hex sha256. */
3356
3270
  function isValidHash(hash) {
3357
3271
  return typeof hash === "string" && /^[0-9a-f]{64}$/.test(hash);
3358
3272
  }
@@ -3423,11 +3337,9 @@ function parseArtifactSyncBody(value) {
3423
3337
  function sha256(s) {
3424
3338
  return createHash("sha256").update(s).digest("hex");
3425
3339
  }
3426
- /** Mint a fresh device token (`dk_…`) — the one wire format `machineIdFromToken` consumes. */
3427
3340
  function mintDeviceToken() {
3428
3341
  return `dk_${randomBytes(15).toString("hex")}`;
3429
3342
  }
3430
- /** Derive the stable machine id from its device token. */
3431
3343
  function machineIdFromToken(token) {
3432
3344
  return `m-${sha256(token).slice(0, 16)}`;
3433
3345
  }
@@ -3460,7 +3372,6 @@ async function rememberConnectKey(connectKey, intent) {
3460
3372
  set: row
3461
3373
  });
3462
3374
  }
3463
- /** Peek (NON-evicting) the team/minter a connect-key was minted under, if still live. */
3464
3375
  async function readClaimIntent(connectKey, now = Date.now()) {
3465
3376
  if (!connectKey) return void 0;
3466
3377
  const row = (await db.select().from(connectKeys).where(eq(connectKeys.machineId, machineIdFromToken(connectKey))))[0];
@@ -3470,7 +3381,6 @@ async function readClaimIntent(connectKey, now = Date.now()) {
3470
3381
  teamId: row.teamId
3471
3382
  };
3472
3383
  }
3473
- /** The remembered owner of a self-registering machine, if any (still-live key). */
3474
3384
  async function getDeviceOwner(machineId, now = Date.now()) {
3475
3385
  const row = (await db.select().from(connectKeys).where(eq(connectKeys.machineId, machineId)))[0];
3476
3386
  if (!row || !connectKeyFresh(row.mintedAt, now)) return void 0;
@@ -3533,7 +3443,6 @@ var newIdempotency = /* @__PURE__ */ new Map();
3533
3443
  function pruneNewIdempotency(now) {
3534
3444
  for (const [key, rec] of newIdempotency) if (now - rec.at > 9e5) newIdempotency.delete(key);
3535
3445
  }
3536
- /** Remember that `key` (from THIS machine) created `loopId`. Pruned on write. */
3537
3446
  function recordNewIdempotency(key, machineId, loopId, now = Date.now()) {
3538
3447
  pruneNewIdempotency(now);
3539
3448
  newIdempotency.set(key, {
@@ -3595,8 +3504,6 @@ function validIdentifiers(value, rejectNumericLeadingZero) {
3595
3504
  return !rejectNumericLeadingZero || !/^\d+$/.test(identifier) || identifier === "0" || !identifier.startsWith("0");
3596
3505
  });
3597
3506
  }
3598
- /** Parse the leading numeric core `x.y.z` of a version, ignoring any pre-release
3599
- * / build suffix. Returns null when it isn't a recognizable version. */
3600
3507
  function core(v) {
3601
3508
  const m = /^\s*v?(\d+)\.(\d+)\.(\d+)/.exec(v);
3602
3509
  if (!m) return null;
@@ -3606,7 +3513,6 @@ function core(v) {
3606
3513
  Number(m[3])
3607
3514
  ];
3608
3515
  }
3609
- /** Does `v` carry a pre-release suffix (e.g. "1.2.3-rc.1")? */
3610
3516
  function isPre(v) {
3611
3517
  return /^\s*v?\d+\.\d+\.\d+-/.test(v);
3612
3518
  }
@@ -3706,7 +3612,6 @@ async function readJsonBody(request, maxBytes) {
3706
3612
  return { kind: "invalid" };
3707
3613
  }
3708
3614
  }
3709
- /** Shared clipping cap for large prompt and terminal telemetry strings. */
3710
3615
  var WIRE_TEXT_CAP = 512 * 1024;
3711
3616
  /** Validate an optional poll string without coercion, clipping, or NUL removal. */
3712
3617
  function validOptionalPollString(value, cap) {
@@ -3723,7 +3628,6 @@ function nowIso() {
3723
3628
  function stripNul(s) {
3724
3629
  return s.replace(/\u0000/g, "");
3725
3630
  }
3726
- /** Clip a free-text wire field and strip NUL before persistence. */
3727
3631
  function clipText(s, cap) {
3728
3632
  return stripNul(s.slice(0, cap));
3729
3633
  }
@@ -3880,7 +3784,6 @@ var LocalBlobStore = class {
3880
3784
  await this.syncDir(path.dirname(target));
3881
3785
  }
3882
3786
  };
3883
- /** In-memory blob store — injectable test adapter. */
3884
3787
  var MemoryBlobStore = class {
3885
3788
  map = /* @__PURE__ */ new Map();
3886
3789
  async has(hash) {
@@ -3896,11 +3799,9 @@ var MemoryBlobStore = class {
3896
3799
  this.map.delete(hash);
3897
3800
  }
3898
3801
  };
3899
- /** Object key for a blob hash. Flat namespace under a prefix — hashes are unique. */
3900
3802
  function blobKey(hash) {
3901
3803
  return `blobs/${hash}`;
3902
3804
  }
3903
- /** Cloudflare R2 blob store over the S3-compatible API (AWS SDK v3). */
3904
3805
  var R2BlobStore = class {
3905
3806
  cfg;
3906
3807
  client;
@@ -3967,7 +3868,6 @@ var R2BlobStore = class {
3967
3868
  }));
3968
3869
  }
3969
3870
  };
3970
- /** S3 "object absent" maps to a 404 / NoSuchKey / NotFound error shape. */
3971
3871
  function isNotFound(err) {
3972
3872
  const e = err;
3973
3873
  return e?.name === "NotFound" || e?.name === "NoSuchKey" || e?.$metadata?.httpStatusCode === 404;
@@ -4158,35 +4058,21 @@ async function authenticateDeviceToken(deviceToken, options = {}) {
4158
4058
  machine
4159
4059
  };
4160
4060
  }
4161
- /** Whether a token needs quoting inside a key/value line or comma-delimited row. */
4162
4061
  function needsQuote(s) {
4163
4062
  return s === "" || /[\s,:"]/.test(s);
4164
4063
  }
4165
- /** Quote and escape a string so it remains on one line. */
4166
4064
  function quote(s) {
4167
4065
  return `"${s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\r?\n/g, "\\n")}"`;
4168
4066
  }
4169
- /** Render one scalar the TOON way: a finite number / boolean bare, null|undefined as
4170
- * the em-dash placeholder, a string bare unless it needs quoting. */
4171
4067
  function scalar(v) {
4172
4068
  if (v === null || v === void 0) return "—";
4173
4069
  if (typeof v === "number") return Number.isFinite(v) ? String(v) : "—";
4174
4070
  if (typeof v === "boolean") return v ? "true" : "false";
4175
4071
  return needsQuote(v) ? quote(v) : v;
4176
4072
  }
4177
- /** A single top-level `key: value` line (value scalar-rendered). */
4178
4073
  function kvLine(key, v) {
4179
4074
  return `${key}: ${scalar(v)}`;
4180
4075
  }
4181
- /**
4182
- * A detail block: a top key, then each field indented two spaces as `key: value`.
4183
- * The value may be a pre-rendered raw string (pass a `{ raw }`) so composite values
4184
- * (e.g. a size hint or an already-quoted body) render verbatim.
4185
- *
4186
- * topKey:
4187
- * name: "Docs Sweep"
4188
- * cron: "0 6 * * 1"
4189
- */
4190
4076
  function detailBlock(topKey, rows) {
4191
4077
  const lines = [`${topKey}:`];
4192
4078
  for (const [k, v] of rows) {
@@ -4195,44 +4081,25 @@ function detailBlock(topKey, rows) {
4195
4081
  }
4196
4082
  return lines.join("\n");
4197
4083
  }
4198
- /** Render a count, optionally with total or displayed-window context. */
4199
4084
  function countLine(count, opts = {}) {
4200
4085
  if (opts.total !== void 0) return `count: ${count} of ${opts.total} total`;
4201
4086
  if (opts.showing !== void 0) return `count: ${count} (showing first ${opts.showing})`;
4202
4087
  return `count: ${count}`;
4203
4088
  }
4204
- /**
4205
- * A typed list block: the header row `name[N]{f1,f2}:` then each record as an
4206
- * indented comma row (cells scalar-rendered, quoted only when needed). Does NOT emit
4207
- * the `count:` line — compose it with `countLine` so the caller controls the
4208
- * aggregate flavor.
4209
- *
4210
- * loops[2]{id,name,cron,enabled}:
4211
- * loop-abc,"Docs Sweep","0 6 * * 1",on
4212
- */
4213
4089
  function listBlock(name, fields, rows) {
4214
4090
  const lines = [`${name}[${rows.length}]{${fields.join(",")}}:`];
4215
4091
  for (const row of rows) lines.push(` ${row.map(scalar).join(",")}`);
4216
4092
  return lines.join("\n");
4217
4093
  }
4218
- /** Render an empty named collection. */
4219
4094
  function emptyList(name) {
4220
4095
  return `${name}: []`;
4221
4096
  }
4222
- /** Render command templates as an indented help block. */
4223
4097
  function helpBlock(lines) {
4224
4098
  return [`help[${lines.length}]:`, ...lines.map((l) => ` ${l}`)].join("\n");
4225
4099
  }
4226
- /**
4227
- * Render a quoted error plus a machine-readable code.
4228
- *
4229
- * error: "status must be keep|no-change|block (got \"wibble\")"
4230
- * code: VALIDATION_ERROR
4231
- */
4232
4100
  function errorBlock(message, code) {
4233
4101
  return `error: ${quote(message)}\ncode: ${code}`;
4234
4102
  }
4235
- /** Map an HTTP status to its CLI error code. */
4236
4103
  function codeForStatus(status) {
4237
4104
  switch (status) {
4238
4105
  case 400: return "VALIDATION_ERROR";
@@ -4244,7 +4111,6 @@ function codeForStatus(status) {
4244
4111
  default: return "ERROR";
4245
4112
  }
4246
4113
  }
4247
- /** Clip content at `cap` and append its original size plus recovery hint. */
4248
4114
  function truncate(text, cap, tail = "use --full to see complete body") {
4249
4115
  if (text.length <= cap) return {
4250
4116
  value: text,
@@ -4255,8 +4121,6 @@ function truncate(text, cap, tail = "use --full to see complete body") {
4255
4121
  truncated: true
4256
4122
  };
4257
4123
  }
4258
- /** Join document sections with a single newline, dropping empty/blank sections so a
4259
- * missing optional block (e.g. no rejections) leaves no stray blank line. */
4260
4124
  function doc(...sections) {
4261
4125
  return sections.filter((s) => typeof s === "string" && s.length > 0).join("\n");
4262
4126
  }
@@ -4323,6 +4187,24 @@ function optionalProvider(value, field) {
4323
4187
  value: normalizeProviderSetting(value)
4324
4188
  };
4325
4189
  }
4190
+ var PI_THINKING = new Set([
4191
+ "off",
4192
+ "minimal",
4193
+ "low",
4194
+ "medium",
4195
+ "high",
4196
+ "xhigh",
4197
+ "max"
4198
+ ]);
4199
+ function validateReasoningEffort(value, agent) {
4200
+ const result = optionalProvider(value, "reasoningEffort");
4201
+ if (!result.ok) return result;
4202
+ if (agent === "pi" && result.value !== null && !PI_THINKING.has(result.value)) return {
4203
+ ok: false,
4204
+ detail: "reasoningEffort for pi must be one of off, minimal, low, medium, high, xhigh, max, or null"
4205
+ };
4206
+ return result;
4207
+ }
4326
4208
  function validTimezone(timezone) {
4327
4209
  try {
4328
4210
  new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format();
@@ -4534,7 +4416,6 @@ function statusDefinitionsFromLoop(loop) {
4534
4416
  block: loop.statusBlock
4535
4417
  };
4536
4418
  }
4537
- /** The single owner-facing structured loop shape used by show/list JSON output. */
4538
4419
  function canonicalLoopEnvelope(loop) {
4539
4420
  return {
4540
4421
  id: loop.id,
@@ -4575,13 +4456,13 @@ function validateLoopCreate(value) {
4575
4456
  if (!artifacts.ok) return artifacts;
4576
4457
  const model = optionalProvider(raw.model, "model");
4577
4458
  if (!model.ok) return model;
4578
- const reasoningEffort = optionalProvider(raw.reasoningEffort, "reasoningEffort");
4579
- if (!reasoningEffort.ok) return reasoningEffort;
4580
4459
  const agent = coerceCodingAgent(raw.agent);
4581
4460
  if (!agent) return {
4582
4461
  ok: false,
4583
4462
  detail: `agent is required and must be one of ${CODING_AGENTS.join(", ")}`
4584
4463
  };
4464
+ const reasoningEffort = validateReasoningEffort(raw.reasoningEffort, agent);
4465
+ if (!reasoningEffort.ok) return reasoningEffort;
4585
4466
  if (raw.enabled !== void 0 && typeof raw.enabled !== "boolean") return {
4586
4467
  ok: false,
4587
4468
  detail: "enabled must be boolean"
@@ -4668,19 +4549,21 @@ function validateLoopEdit(loop, value) {
4668
4549
  if (!result.ok) return result;
4669
4550
  update.model = result.value;
4670
4551
  }
4671
- if (raw.reasoningEffort !== void 0) {
4672
- const result = optionalProvider(raw.reasoningEffort, "reasoningEffort");
4673
- if (!result.ok) return result;
4674
- update.reasoningEffort = result.value;
4675
- }
4552
+ let effectiveAgent = loop.agent;
4676
4553
  if (raw.agent !== void 0) {
4677
4554
  const agent = coerceCodingAgent(raw.agent);
4678
4555
  if (!agent) return {
4679
4556
  ok: false,
4680
4557
  detail: `agent must be one of ${CODING_AGENTS.join(", ")}`
4681
4558
  };
4559
+ effectiveAgent = agent;
4682
4560
  update.agent = agent;
4683
4561
  }
4562
+ if (raw.reasoningEffort !== void 0 || raw.agent !== void 0) {
4563
+ const result = validateReasoningEffort(raw.reasoningEffort !== void 0 ? raw.reasoningEffort : loop.reasoningEffort, effectiveAgent);
4564
+ if (!result.ok) return result;
4565
+ if (raw.reasoningEffort !== void 0) update.reasoningEffort = result.value;
4566
+ }
4684
4567
  if (raw.enabled !== void 0) {
4685
4568
  if (typeof raw.enabled !== "boolean") return {
4686
4569
  ok: false,
@@ -4747,7 +4630,6 @@ var ONLINE_TTL_MS = 3e4;
4747
4630
  /** Auto-pause after this many consecutive errors. Canceled rows are transparent;
4748
4631
  * zero disables the breaker. */
4749
4632
  var AUTOPAUSE_STREAK = Math.max(0, Number(process.env.PIEVO_FAILURE_AUTOPAUSE_STREAK ?? 3));
4750
- /** How long pending system work on an offline machine stays claimable. */
4751
4633
  var DEFERRED_MAX_MS = 7 * 864e5;
4752
4634
  /** A claimed run that never reports within this window is reclaimed as timed out. */
4753
4635
  var configuredRunTimeoutMs = Number(process.env.PIEVO_RUN_TIMEOUT_MS || 20 * 6e4);
@@ -4762,9 +4644,9 @@ var EDITABLE_LOOP_FIELDS = LOOP_EDIT_FIELDS;
4762
4644
  * it never satisfies the successful-run reporting protocol. Run errors share
4763
4645
  * this cap. Exported for `cli.ts` so the report verb uses the same budget. */
4764
4646
  var MESSAGE_CAP = 2e3;
4765
- /** A claude-code session id is a UUID-ish token — anything longer is garbage. */
4647
+ /** Provider session IDs are opaque short tokens; this rejects malformed payloads
4648
+ * without constraining known provider formats. */
4766
4649
  var SESSION_ID_CAP = 200;
4767
- /** Keep heartbeat throttling safely inside custom short timeout windows. */
4768
4650
  function heartbeatRefreshMs(runTimeoutMs) {
4769
4651
  if (!Number.isFinite(runTimeoutMs) || runTimeoutMs <= 0) return 1;
4770
4652
  return Math.max(1, Math.min(6e4, runTimeoutMs / 3));
@@ -4970,7 +4852,6 @@ var MachineGateway = class {
4970
4852
  * process-local; unlike durable run leases, a deploy drops them and the daemon
4971
4853
  * simply re-polls. */
4972
4854
  pollWaiters = /* @__PURE__ */ new Map();
4973
- /** Resolve (and disarm) a machine's parked long-poll waiter, if any. */
4974
4855
  wakeMachine(machineId) {
4975
4856
  this.pollWaiters.get(machineId)?.(true);
4976
4857
  }
@@ -5138,7 +5019,6 @@ var MachineGateway = class {
5138
5019
  body: { delivery }
5139
5020
  };
5140
5021
  }
5141
- /** Protocol-v4 poll: plural local state, one new delivery per poll. */
5142
5022
  async pollV4(deviceToken, request) {
5143
5023
  const rawRequest = request;
5144
5024
  if (request === null || typeof request !== "object" || Array.isArray(request) || Object.keys(rawRequest).some((key) => !POLL_V4_REQUEST_FIELDS.has(key)) || !validPollInfo(rawRequest?.info) || rawRequest.protocolVersion !== void 0 && (typeof rawRequest.protocolVersion !== "number" || !Number.isInteger(rawRequest.protocolVersion)) || rawRequest.currentRuns !== void 0 && (!Array.isArray(rawRequest.currentRuns) || rawRequest.currentRuns.some((run) => !validPollCurrentRun(run))) || rawRequest.daemonInstanceId !== void 0 && !validPollWireId(rawRequest.daemonInstanceId) || rawRequest.recoveryComplete !== void 0 && typeof rawRequest.recoveryComplete !== "boolean") return {
@@ -5208,7 +5088,6 @@ var MachineGateway = class {
5208
5088
  }
5209
5089
  };
5210
5090
  }
5211
- /** Idle v4 polls long-poll; active/reporting polls return immediately. */
5212
5091
  async pollV4Wait(deviceToken, request, waitMs = LONG_POLL_WAIT_MS) {
5213
5092
  if (request.currentRuns?.length || request.protocolVersion !== 4) return this.pollV4(deviceToken, request);
5214
5093
  const auth = await authenticateDeviceToken(deviceToken, { allowUnknown: true });
@@ -5267,8 +5146,6 @@ var MachineGateway = class {
5267
5146
  }
5268
5147
  };
5269
5148
  }
5270
- /** Create a validated loop through device authority, bind it to that machine,
5271
- * and schedule it immediately. */
5272
5149
  async createLoop(deviceToken, body) {
5273
5150
  const auth = await authenticateDeviceToken(deviceToken);
5274
5151
  if (!auth.ok) return auth.response;
@@ -5361,9 +5238,6 @@ var MachineGateway = class {
5361
5238
  };
5362
5239
  }
5363
5240
  }
5364
- /** List loops bound to this machine. `--fields` extends the minimal default
5365
- * columns and rejects unknown names. `--json` returns full structured records
5366
- * (first byte `[`), mirroring `show --json` — the daemon prints `text` either way. */
5367
5241
  async listLoops(deviceToken, fieldsFlag, json) {
5368
5242
  const auth = await authenticateDeviceToken(deviceToken);
5369
5243
  if (!auth.ok) return auth.response;
@@ -5409,7 +5283,6 @@ var MachineGateway = class {
5409
5283
  }
5410
5284
  };
5411
5285
  }
5412
- /** Edit a loop through owner authority, scoped to loops bound to this machine. */
5413
5286
  async editLoop(deviceToken, id, patch, dryRun = false) {
5414
5287
  const auth = await authenticateDeviceToken(deviceToken);
5415
5288
  if (!auth.ok) return auth.response;
@@ -5431,20 +5304,30 @@ var MachineGateway = class {
5431
5304
  };
5432
5305
  const update = validated.value;
5433
5306
  const applied = Object.keys(patch ?? {});
5434
- if (dryRun) return {
5435
- status: 200,
5436
- body: {
5437
- ok: true,
5438
- dryRun: true,
5307
+ if (dryRun) {
5308
+ const before = canonicalLoopEnvelope(loop);
5309
+ const after = canonicalLoopEnvelope({
5310
+ ...loop,
5311
+ ...update
5312
+ });
5313
+ const { id: _readOnlyId, ...config } = after;
5314
+ const preview = {
5439
5315
  id: loop.id,
5440
5316
  applied,
5441
- config: { ...patch },
5442
- text: JSON.stringify({
5443
- id: loop.id,
5444
- applied
5445
- }, null, 2)
5446
- }
5447
- };
5317
+ before,
5318
+ after
5319
+ };
5320
+ return {
5321
+ status: 200,
5322
+ body: {
5323
+ ok: true,
5324
+ dryRun: true,
5325
+ ...preview,
5326
+ config,
5327
+ text: JSON.stringify(preview, null, 2)
5328
+ }
5329
+ };
5330
+ }
5448
5331
  if (Object.keys(update).length === 0) return {
5449
5332
  status: 200,
5450
5333
  body: {
@@ -5850,7 +5733,6 @@ function fmtTimeZoned(iso, timezone, opts = {}) {
5850
5733
  return fmtTime(iso);
5851
5734
  }
5852
5735
  }
5853
- /** Minimal default columns for `pievo loops`. */
5854
5736
  var LIST_DEFAULT_FIELDS = [
5855
5737
  "id",
5856
5738
  "name",
@@ -5858,8 +5740,6 @@ var LIST_DEFAULT_FIELDS = [
5858
5740
  "enabled",
5859
5741
  "nextFire"
5860
5742
  ];
5861
- /** The optional columns `--fields` may add (the "available" set an unknown field is
5862
- * measured against). `runs`/`lastResult` are derived per loop. */
5863
5743
  var LIST_OPTIONAL_FIELDS = [
5864
5744
  "timezone",
5865
5745
  "model",
@@ -5867,7 +5747,6 @@ var LIST_OPTIONAL_FIELDS = [
5867
5747
  "runs",
5868
5748
  "lastResult"
5869
5749
  ];
5870
- /** One `loops` cell for a named column (scalar-rendered by `listBlock`). */
5871
5750
  function loopCell(rec, field) {
5872
5751
  switch (field) {
5873
5752
  case "id": return rec.id;
@@ -5883,13 +5762,10 @@ function loopCell(rec, field) {
5883
5762
  default: return null;
5884
5763
  }
5885
5764
  }
5886
- /** Render the typed loop list. Columns are the default set
5887
- * plus any `--fields` extras (validated + resolved by `listLoops`). */
5888
5765
  function renderLoopsText(loops, fields) {
5889
5766
  if (!loops.length) return doc(countLine(0), emptyList("loops"), helpBlock(["Run `pievo new --json '{...}'` to create your first loop", "Run `pievo daemon connect` if this machine isn't connected yet"]));
5890
5767
  return doc(countLine(loops.length), listBlock("loops", fields, loops.map((l) => fields.map((f) => loopCell(l, f)))), helpBlock(["Run `pievo show <id>` to see a loop's full config", "Run `pievo log <id>` to see a loop's recent runs"]));
5891
5768
  }
5892
- /** One run's result cell, derived from phase + status. */
5893
5769
  function runResultToken(r) {
5894
5770
  if (r.status === "block") return `block/${r.phase}`;
5895
5771
  if (r.phase === "canceled") return "canceled";
@@ -6069,7 +5945,6 @@ var ArtifactSync = class {
6069
5945
  body: { ok: true }
6070
5946
  };
6071
5947
  }
6072
- /** Read stored content-addressed bytes, or null when absent. */
6073
5948
  readBlob(hash) {
6074
5949
  if (!isValidHash(hash)) return Promise.resolve(null);
6075
5950
  return this.blobStore.get(hash);
@@ -6090,20 +5965,16 @@ var ArtifactSync = class {
6090
5965
  * a changed text file degrades to the size-delta marker, like binary/oversize.
6091
5966
  */
6092
5967
  var MAX_DIFF_BYTES = 512 * 1024;
6093
- /** A file is text-diffable only when it has stored bytes and isn't binary/oversize. */
6094
5968
  function isText(e) {
6095
5969
  return !!e && !e.binary && !e.oversize && !!e.hash;
6096
5970
  }
6097
- /** Within the inline-diff size cap (unknown size ⇒ allowed). */
6098
5971
  function withinDiffCap(e) {
6099
5972
  return !!e && (e.size == null || e.size <= MAX_DIFF_BYTES);
6100
5973
  }
6101
- /** Load one snapshot blob, or null when recorded bytes are absent. */
6102
5974
  async function bytesOf(e) {
6103
5975
  if (!e.hash) return null;
6104
5976
  return await (await getArtifactSync()).readBlob(e.hash) ?? null;
6105
5977
  }
6106
- /** Unified diff between two versions of a path (empty string ⇒ added/removed side). */
6107
5978
  function unified(path, oldText, newText, maxEditLength) {
6108
5979
  return maxEditLength === void 0 ? createTwoFilesPatch(path, path, oldText, newText, "previous run", "this run") : createTwoFilesPatch(path, path, oldText, newText, "previous run", "this run", { maxEditLength });
6109
5980
  }
@@ -6606,7 +6477,6 @@ var CliGateway = class {
6606
6477
  async cli(token, argv) {
6607
6478
  return finalizeCli(token.startsWith("dk_") ? await this.deviceCli(token, argv) : await this.runCli(token, argv));
6608
6479
  }
6609
- /** DEVICE-credential branch of the unified CLI. */
6610
6480
  async deviceCli(deviceToken, argv) {
6611
6481
  const auth = await authenticateDeviceToken(deviceToken, { allowUnknown: true });
6612
6482
  if (!auth.ok) return auth.response;
@@ -6865,7 +6735,6 @@ var CliGateway = class {
6865
6735
  body: { text: `run already finished: ${runResultToken(stopped)}` }
6866
6736
  };
6867
6737
  }
6868
- /** RUN-credential branch of the unified CLI: report and its help only. */
6869
6738
  async runCli(runToken, argv) {
6870
6739
  const lease = await resolveLease(runToken);
6871
6740
  if (!lease) return {
@@ -6949,7 +6818,6 @@ var CliGateway = class {
6949
6818
  text: renderReportedText(status, true)
6950
6819
  } : derr(applied.status ?? 409, applied.detail ?? "this run is no longer active", applied.code);
6951
6820
  }
6952
- /** The complete run-token command surface: report plus help for report. */
6953
6821
  helpText(_lease) {
6954
6822
  return doc(listBlock("verbs", ["verb", "syntax"], [["report", "--status keep|no-change|block --message <summary>"]]), helpBlock(["Call exactly one pievo report before finishing", "No other command is accepted from a run credential"]));
6955
6823
  }
@@ -7026,7 +6894,6 @@ var CliGateway = class {
7026
6894
  })), scoped.elsewhere, await recentMachineRuns(loops, 3));
7027
6895
  }
7028
6896
  };
7029
- /** Shared refusal for terminal-report-only grace after reclaim. */
7030
6897
  var TERMINAL_GRACE_MSG = "this run is terminal and no longer accepts commands; its final result is delivered via the terminal report";
7031
6898
  /** Verbs that require device authority. `report` is run-only and rejected there. */
7032
6899
  var DEVICE_ONLY_VERBS = new Set([
@@ -7111,21 +6978,18 @@ function finalizeCli(res) {
7111
6978
  }
7112
6979
  return res;
7113
6980
  }
7114
- /** Render the compact run-status confirmation. */
7115
6981
  function renderReportedText(status, hasMessage) {
7116
6982
  const parts = [];
7117
6983
  if (status) parts.push(`status=${status}`);
7118
6984
  if (hasMessage) parts.push("message recorded");
7119
6985
  return `reported: ${parts.length ? parts.join(" · ") : "recorded"}`;
7120
6986
  }
7121
- /** RUN-credential verb help (in-run `rk_` lease). */
7122
6987
  var RUN_VERB_HELP = { report: {
7123
6988
  syntax: "report --status keep|no-change|block --message <text>",
7124
6989
  summary: "record this run's required status and non-empty summary",
7125
6990
  avail: () => "always available",
7126
6991
  help: ["Run `pievo report --status no-change --message \"no actionable change\"` to close this run", "Run `pievo report --status keep --message \"completed the requested work\"` to retain a result"]
7127
6992
  } };
7128
- /** DEVICE-credential verb help (owner `dk_` device token). */
7129
6993
  var DEVICE_VERB_HELP = {
7130
6994
  new: {
7131
6995
  syntax: "new --json '<config>' [--dry-run] [--connect-key <dk_…>] [--server-url <url>]",
@@ -7178,25 +7042,19 @@ var DEVICE_VERB_HELP = {
7178
7042
  help: ["Run `pievo log <id> --json` for structured history", "Run `pievo log <id> --run 12 --diff` for bounded run detail and artifact diff"]
7179
7043
  }
7180
7044
  };
7181
- /** Render per-verb help. A run lease selects the report-only help;
7182
- * no lease means the device (owner) surface. Returns undefined for a verb
7183
- * with no help spec, so the caller falls back to its unknown-command handling. */
7184
7045
  function verbHelpText(verb, lease) {
7185
7046
  const spec = lease ? RUN_VERB_HELP[verb] : DEVICE_VERB_HELP[verb];
7186
7047
  if (!spec) return void 0;
7187
7048
  return doc(kvLine("verb", verb), kvLine("syntax", spec.syntax), kvLine("summary", spec.summary), lease && spec.avail ? kvLine("availability", spec.avail(lease)) : null, helpBlock(spec.help));
7188
7049
  }
7189
- /** The canonical editable config emitted by `show --json`. */
7190
7050
  function loopEnvelope(loop) {
7191
7051
  return { ...canonicalLoopEnvelope(loop) };
7192
7052
  }
7193
- /** Render a large prompt in full or as a compact presence hint. */
7194
7053
  function contentField(value, full) {
7195
7054
  if (value == null) return "absent";
7196
7055
  if (full) return value;
7197
7056
  return { raw: `present, ${value.length} bytes — use --full to see` };
7198
7057
  }
7199
- /** The next cadence fire, formatted in the loop's timezone. */
7200
7058
  function nextFireDisplay(loop) {
7201
7059
  const schedule = scheduleFromLoop(loop);
7202
7060
  if (schedule.mode === "continuous") return `after exec terminal + ${schedule.delayMinutes}m`;
@@ -7204,7 +7062,6 @@ function nextFireDisplay(loop) {
7204
7062
  if (!iso) return "(never)";
7205
7063
  return fmtTimeZoned(iso, schedule.timezone, { seconds: true });
7206
7064
  }
7207
- /** `pievo show` renders the editable envelope followed by derived read-only state. */
7208
7065
  function renderShowText(loop, env, totalRuns, lastRun, opts = {}) {
7209
7066
  const full = opts.full === true;
7210
7067
  const block = detailBlock("loop", [
@@ -7228,14 +7085,12 @@ function renderShowText(loop, env, totalRuns, lastRun, opts = {}) {
7228
7085
  ];
7229
7086
  return doc(block, kvLine("nextFire", nextFireDisplay(loop)), `lifecycle: ${loop.enabled ? "active" : "paused"}`, `runs: ${runsTally}`, !loop.enabled ? kvLine("pauseCause", loop.pauseCause?.kind === "failure-streak" ? `failure-streak (run ${loop.pauseCause.runId}, count ${loop.pauseCause.count})` : loop.pauseCause?.kind === "blocked" ? `blocked (run ${loop.pauseCause.runId})` : loop.pauseCause?.kind === "owner" ? "owner" : "unknown") : null, lastRun?.reportIncident ? kvLine("reportIncident", `${lastRun.reportIncident.code} · ${lastRun.reportIncident.faultDomain} · ${lastRun.reportIncident.reason}`) : null, helpBlock(help));
7230
7087
  }
7231
- /** The static one-line description in the home header. */
7232
7088
  var HOME_DESCRIPTION = "Run your scheduled Pievo agent loops on this machine with your own coding agent.";
7233
7089
  /** Expand a leading `~/` against the daemon-supplied home dir (the SERVER's own home
7234
7090
  * is irrelevant — a loop's paths are the daemon machine's). Absent home ⇒ unchanged. */
7235
7091
  function expandHome(p, home) {
7236
7092
  return home && p.startsWith("~/") ? path.join(home, p.slice(2)) : p;
7237
7093
  }
7238
- /** A loop's folder on the daemon machine is its configured workdir. */
7239
7094
  function scopeLoopDir(workdir, home) {
7240
7095
  return path.resolve(expandHome(workdir, home));
7241
7096
  }
@@ -7264,8 +7119,6 @@ function scopeLoopsByCwd(loops, cwd, home) {
7264
7119
  elsewhere: loops.length - matched.length
7265
7120
  };
7266
7121
  }
7267
- /** The most recent runs across ALL of a machine's loops, newest-first, for the home
7268
- * `recent[]` block. Merges each loop's newest few then globally sorts by ts. */
7269
7122
  async function recentMachineRuns(loops, n) {
7270
7123
  const rows = [];
7271
7124
  for (const l of loops) for (const r of await listRuns(l.id, n)) rows.push({
@@ -7396,7 +7249,6 @@ var Scheduler = class {
7396
7249
  enqueueInitialExec(id) {
7397
7250
  return this.enqueue(id, { requestedBy: "system" });
7398
7251
  }
7399
- /** Poll fallback and timer callback converge here. */
7400
7252
  async advanceDueSchedules(machineId, loopId) {
7401
7253
  const advanced = await advanceDueSchedules((/* @__PURE__ */ new Date()).toISOString(), {
7402
7254
  machineId,