@coreplane/switchboard 1.260.3 → 1.260.5

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 (144) hide show
  1. package/dist/assets/deploy/cloudflare/package.json +2 -2
  2. package/dist/assets/deploy/cloudflare-docs/package.json +1 -1
  3. package/dist/assets/deploy/cloudflare-memory/package.json +2 -2
  4. package/dist/assets/deploy/cloudflare-memory/worker.ts +205 -8
  5. package/dist/assets/deploy/cloudflare-resident/Dockerfile +11 -2
  6. package/dist/assets/deploy/cloudflare-resident/package.json +2 -2
  7. package/dist/assets/deploy/cloudflare-resident/worker.ts +124 -40
  8. package/dist/assets/deploy/cloudflare-sandbox/package.json +2 -2
  9. package/dist/assets/package-lock.json +712 -97
  10. package/dist/assets/package.json +7 -6
  11. package/dist/assets/project.json +4 -0
  12. package/dist/assets/source.json +3 -3
  13. package/dist/assets/src/core/budgets.ts +5 -0
  14. package/dist/assets/src/core/coordinator/contract.ts +5 -0
  15. package/dist/assets/src/core/coordinator/driver.ts +118 -29
  16. package/dist/assets/src/core/pipelineStanding.ts +2 -0
  17. package/dist/assets/src/core/plane/decide.ts +19 -2
  18. package/dist/assets/src/core/provider.ts +380 -0
  19. package/dist/assets/src/core/refusal.ts +1 -0
  20. package/dist/assets/src/core/runEvents.ts +8 -0
  21. package/dist/assets/src/core/runFriction.ts +1 -1
  22. package/dist/assets/src/core/runLedger/types.ts +10 -1
  23. package/dist/assets/src/core/runRecord.ts +31 -0
  24. package/dist/assets/src/core/ship/contract.ts +3 -1
  25. package/dist/assets/src/core/ship/coordinator.ts +518 -56
  26. package/dist/assets/src/core/ship/renewal.ts +3 -1
  27. package/dist/assets/src/core/types.ts +6 -0
  28. package/dist/assets/src/execution/bashTimeout.ts +1 -1
  29. package/dist/assets/src/execution/residentRebind.ts +15 -0
  30. package/dist/assets/src/execution/residentReuse.ts +23 -2
  31. package/dist/assets/src/execution/sandboxErrors.ts +6 -6
  32. package/dist/assets/web/dist/.vite/manifest.json +505 -505
  33. package/dist/assets/web/dist/assets/{AppShell-CSXDCV4x.js → AppShell-DPJVhUGP.js} +1 -1
  34. package/dist/assets/web/dist/assets/{CostChart-DsuPS2g7.js → CostChart-4TeDdbvJ.js} +1 -1
  35. package/dist/assets/web/dist/assets/CostsPage-8HDgvuWB.js +1 -0
  36. package/dist/assets/web/dist/assets/{DeliveryPage-62qCSLvk.js → DeliveryPage-Cf1joUyA.js} +1 -1
  37. package/dist/assets/web/dist/assets/{HomePage-Dk3HRBc3.js → HomePage-DZEAnCCv.js} +1 -1
  38. package/dist/assets/web/dist/assets/{InputMenu-BdZXnibG.js → InputMenu-BRV35UWP.js} +1 -1
  39. package/dist/assets/web/dist/assets/{MetricsPage-o5fSaCTe.js → MetricsPage-BQBuIuYU.js} +1 -1
  40. package/dist/assets/web/dist/assets/{NotFoundPage-DHuWyjlq.js → NotFoundPage-CDjMfCcj.js} +1 -1
  41. package/dist/assets/web/dist/assets/{PendingTurnRow-CeTGOXUy.js → PendingTurnRow-4B8JwETU.js} +1 -1
  42. package/dist/assets/web/dist/assets/{PlanePage-RD0M6O6W.js → PlanePage-CHU2szQs.js} +1 -1
  43. package/dist/assets/web/dist/assets/{ResidentDetailPage-Dl2KId95.js → ResidentDetailPage-L5SRcN_O.js} +1 -1
  44. package/dist/assets/web/dist/assets/{ResidentsIndexPage-DBy43I1R.js → ResidentsIndexPage-BIFf8qyE.js} +1 -1
  45. package/dist/assets/web/dist/assets/RunFoldRow-DzPIshJx.js +1 -0
  46. package/dist/assets/web/dist/assets/{RunRoutePage-Du1HqLZs.js → RunRoutePage-Cru5IVux.js} +3 -3
  47. package/dist/assets/web/dist/assets/{RunsIndexPage-BZphy3Hr.js → RunsIndexPage-CBkdPAdj.js} +1 -1
  48. package/dist/assets/web/dist/assets/{RunsTabs-kHbbxVHx.js → RunsTabs-C7Ua3XX5.js} +1 -1
  49. package/dist/assets/web/dist/assets/{ScheduledPage-B7jh898t.js → ScheduledPage-D_R55BDk.js} +1 -1
  50. package/dist/assets/web/dist/assets/{SettingSelect-BfID5nF4.js → SettingSelect-CEeA4iuC.js} +1 -1
  51. package/dist/assets/web/dist/assets/{SettingsPage-3uLEsiiE.js → SettingsPage-DlmZvAvf.js} +1 -1
  52. package/dist/assets/web/dist/assets/{SilentTurn-CKxSxnQC.js → SilentTurn-PWMDMbMG.js} +1 -1
  53. package/dist/assets/web/dist/assets/{SlackMark-CtiHrNjr.js → SlackMark-DB0MGiF5.js} +1 -1
  54. package/dist/assets/web/dist/assets/{StatusDot-x7vcK0iE.js → StatusDot-Bq0mYMFo.js} +1 -1
  55. package/dist/assets/web/dist/assets/{Tooltip-wVXCWhMp.js → Tooltip-Bs6zd7yE.js} +1 -1
  56. package/dist/assets/web/dist/assets/{UnitRoutePage-V5BXK7FH.js → UnitRoutePage-BMGEZCvn.js} +1 -1
  57. package/dist/assets/web/dist/assets/{angular-html-C8b5YTHf.js → angular-html-BRRhsdQE.js} +1 -1
  58. package/dist/assets/web/dist/assets/{angular-ts-C3f4WYNe.js → angular-ts-BFj-mqCm.js} +1 -1
  59. package/dist/assets/web/dist/assets/{apl-DSHxUJc3.js → apl-CsBuy1IL.js} +1 -1
  60. package/dist/assets/web/dist/assets/{astro-BSGCCBP6.js → astro-cVaqxBZq.js} +1 -1
  61. package/dist/assets/web/dist/assets/{blade-DFeYrMKk.js → blade-MEP5mWha.js} +1 -1
  62. package/dist/assets/web/dist/assets/{browser-B6T5hjfR.js → browser-CCImen5O.js} +1 -1
  63. package/dist/assets/web/dist/assets/budgets-0szUBIMv.js +1 -0
  64. package/dist/assets/web/dist/assets/{c-L_QOnFVv.js → c-D4NGQ_Pd.js} +1 -1
  65. package/dist/assets/web/dist/assets/{chapel-DLQWUwhu.js → chapel-BkWImsIy.js} +1 -1
  66. package/dist/assets/web/dist/assets/{cobol-1DlMmrEO.js → cobol-RPDSPSET.js} +1 -1
  67. package/dist/assets/web/dist/assets/{coffee-BTB6os6N.js → coffee-DZ5z7oVX.js} +1 -1
  68. package/dist/assets/web/dist/assets/{cpp-BvJI2z3u.js → cpp-CrBtIaRZ.js} +1 -1
  69. package/dist/assets/web/dist/assets/{crystal-B11wGW_h.js → crystal-DJesYi6f.js} +1 -1
  70. package/dist/assets/web/dist/assets/{css-BiIWx5Pw.js → css-DO9mjau-.js} +1 -1
  71. package/dist/assets/web/dist/assets/{dist-C-XGnvM4.js → dist-EkKVbIM-.js} +12 -12
  72. package/dist/assets/web/dist/assets/{edge-nSglEXaD.js → edge-C3VzbSW0.js} +1 -1
  73. package/dist/assets/web/dist/assets/{elixir-x7o8qMl8.js → elixir-BkD_Vhfe.js} +1 -1
  74. package/dist/assets/web/dist/assets/{elm-DGrRyqTQ.js → elm-KIM0mFPk.js} +1 -1
  75. package/dist/assets/web/dist/assets/{erb--_dh58Nw.js → erb-D2ruCqb2.js} +1 -1
  76. package/dist/assets/web/dist/assets/{eventSource-Bj_dZf6M.js → eventSource-Cv0AFlMi.js} +1 -1
  77. package/dist/assets/web/dist/assets/{git-rebase-DbpUXwyg.js → git-rebase-CnfbQTxA.js} +1 -1
  78. package/dist/assets/web/dist/assets/{glimmer-js-BG0CuZG5.js → glimmer-js-CbiwFXWH.js} +1 -1
  79. package/dist/assets/web/dist/assets/{glimmer-ts-CQIl5qyR.js → glimmer-ts-DV8ii0Pv.js} +1 -1
  80. package/dist/assets/web/dist/assets/{glsl-9PJNQTcw.js → glsl-CgmznWkA.js} +1 -1
  81. package/dist/assets/web/dist/assets/{graphql-CzVTzjEI.js → graphql-BK-bBYfH.js} +1 -1
  82. package/dist/assets/web/dist/assets/{hack-BhCmAz3Q.js → hack-DnwTTP5n.js} +1 -1
  83. package/dist/assets/web/dist/assets/{haml-BmI12COR.js → haml-DAVJsWwT.js} +1 -1
  84. package/dist/assets/web/dist/assets/{handlebars-Cib-ISTS.js → handlebars-voOVfxFL.js} +1 -1
  85. package/dist/assets/web/dist/assets/{html-D3lDapvI.js → html-3FTqwaPk.js} +1 -1
  86. package/dist/assets/web/dist/assets/{html-derivative-DRPY8vOK.js → html-derivative-CSlhLDQn.js} +1 -1
  87. package/dist/assets/web/dist/assets/{http-B9VttK6m.js → http-Vx4ilPfS.js} +1 -1
  88. package/dist/assets/web/dist/assets/{hurl-C0saNJYj.js → hurl-DxyZDE3R.js} +1 -1
  89. package/dist/assets/web/dist/assets/{indexRow-Dxg7C8s9.js → indexRow-5s3hNzZ9.js} +1 -1
  90. package/dist/assets/web/dist/assets/{java-BCFbtYAj.js → java-nWqdjNJG.js} +1 -1
  91. package/dist/assets/web/dist/assets/{javascript-nwBjnrhh.js → javascript-Dr82HOwp.js} +1 -1
  92. package/dist/assets/web/dist/assets/{jinja-CMYO9byN.js → jinja-BhjLrPOt.js} +1 -1
  93. package/dist/assets/web/dist/assets/{jison-Cko8HnE_.js → jison-DilGIUa8.js} +1 -1
  94. package/dist/assets/web/dist/assets/{json-Dzv__YsX.js → json-CrOcvmhJ.js} +1 -1
  95. package/dist/assets/web/dist/assets/{jsx-CTFxagih.js → jsx-BW_RX-fo.js} +1 -1
  96. package/dist/assets/web/dist/assets/{julia-_Xvten1-.js → julia-CWH071DB.js} +1 -1
  97. package/dist/assets/web/dist/assets/{just-bLps4qOu.js → just-DkhhoWBy.js} +1 -1
  98. package/dist/assets/web/dist/assets/{latex-D6UaYsUP.js → latex-cuHJ4TUT.js} +1 -1
  99. package/dist/assets/web/dist/assets/{liquid-CoDIeULa.js → liquid-DG6afYQN.js} +1 -1
  100. package/dist/assets/web/dist/assets/{lua-D1KFRnVf.js → lua-C5CMnCPS.js} +1 -1
  101. package/dist/assets/web/dist/assets/{main-DAL--hyj.js → main-Disot0n_.js} +3 -3
  102. package/dist/assets/web/dist/assets/{marko-B5N5XcOw.js → marko-Dy2_DuC4.js} +1 -1
  103. package/dist/assets/web/dist/assets/{mdc-uxzjQO3q.js → mdc-BMKb_EeA.js} +1 -1
  104. package/dist/assets/web/dist/assets/{nginx-BMJxs2TK.js → nginx-og0RgMKf.js} +1 -1
  105. package/dist/assets/web/dist/assets/{nim-DqQ3-8p1.js → nim-CQxTQQP0.js} +1 -1
  106. package/dist/assets/web/dist/assets/{org-DsY6LK59.js → org-b5lkq_Pl.js} +1 -1
  107. package/dist/assets/web/dist/assets/{perl--pq9iJFp.js → perl-D9X5_i_J.js} +1 -1
  108. package/dist/assets/web/dist/assets/{php-RHCMbsM2.js → php-CwUxvLX0.js} +1 -1
  109. package/dist/assets/web/dist/assets/{pug-CFNq_FCp.js → pug-uOIOExr0.js} +1 -1
  110. package/dist/assets/web/dist/assets/{qml-C9Acx9E8.js → qml-g5RoADf-.js} +1 -1
  111. package/dist/assets/web/dist/assets/{r-DxlU8FBd.js → r-5LdAopD3.js} +1 -1
  112. package/dist/assets/web/dist/assets/{razor-CLJ2kKih.js → razor-DwrvFLYQ.js} +1 -1
  113. package/dist/assets/web/dist/assets/{regexp-CzmdGoEp.js → regexp-dGtAaxC-.js} +1 -1
  114. package/dist/assets/web/dist/assets/{rst-D9yuxBET.js → rst-wFdW0brr.js} +1 -1
  115. package/dist/assets/web/dist/assets/{ruby-CIRcEvnD.js → ruby-DPglN6I9.js} +1 -1
  116. package/dist/assets/web/dist/assets/{sas-LOl-uUPh.js → sas-CUbCs40d.js} +1 -1
  117. package/dist/assets/web/dist/assets/{scss-BX2_sZHY.js → scss-B-DUiI1c.js} +1 -1
  118. package/dist/assets/web/dist/assets/{shellscript-4d-QvKkY.js → shellscript-lGOV7Ram.js} +1 -1
  119. package/dist/assets/web/dist/assets/{shellsession-DhB16UGy.js → shellsession-BDWtAAjr.js} +1 -1
  120. package/dist/assets/web/dist/assets/{soy-Cm1xTUBr.js → soy-zod_dcOy.js} +1 -1
  121. package/dist/assets/web/dist/assets/{sql-BsfcBJdu.js → sql-BG0HSSoM.js} +1 -1
  122. package/dist/assets/web/dist/assets/{sseReplay-BqgOggyQ.js → sseReplay-9gcMpylx.js} +1 -1
  123. package/dist/assets/web/dist/assets/{stata-Dwat3BSw.js → stata-BVUMLp1W.js} +1 -1
  124. package/dist/assets/web/dist/assets/{surrealql-P4rtxzAW.js → surrealql-DiSUbHKF.js} +1 -1
  125. package/dist/assets/web/dist/assets/{svelte-DTcsi8S8.js → svelte-DZenT45Y.js} +1 -1
  126. package/dist/assets/web/dist/assets/{templ-D-kP0Tg5.js → templ-BWKRNM0-.js} +1 -1
  127. package/dist/assets/web/dist/assets/{tex-D-4vuedK.js → tex-CbMzBlkK.js} +1 -1
  128. package/dist/assets/web/dist/assets/{ts-tags-jEEWYxsu.js → ts-tags-CZsVorDN.js} +1 -1
  129. package/dist/assets/web/dist/assets/{tsx-CFnlrVzT.js → tsx-R1r3mH4W.js} +1 -1
  130. package/dist/assets/web/dist/assets/{twig-D5rGrPjs.js → twig-BjPMyjKH.js} +1 -1
  131. package/dist/assets/web/dist/assets/{typescript-nYIQ35or.js → typescript-Bx-66VN_.js} +1 -1
  132. package/dist/assets/web/dist/assets/{typst-3-UX8dna.js → typst-fW037Eo6.js} +1 -1
  133. package/dist/assets/web/dist/assets/{vue-CLIPIX03.js → vue-D1V_GNxW.js} +1 -1
  134. package/dist/assets/web/dist/assets/{vue-html-Ci9CpkHU.js → vue-html-C-4pTZ-s.js} +1 -1
  135. package/dist/assets/web/dist/assets/{vue-vine-DL5pEYYk.js → vue-vine-C-QsX3hR.js} +1 -1
  136. package/dist/assets/web/dist/assets/{wallClock-DmaC1sKA.js → wallClock-x0hjr0J_.js} +1 -1
  137. package/dist/assets/web/dist/assets/{xml-bO80qikC.js → xml-BJTJXPgC.js} +1 -1
  138. package/dist/assets/web/dist/assets/{xsl-BPze60Ej.js → xsl-Xc9lukqe.js} +1 -1
  139. package/dist/assets/web/dist/assets/{yaml-BQUSon_o.js → yaml-Dw-FMsQa.js} +1 -1
  140. package/dist/cli.js +1988 -546
  141. package/package.json +4 -4
  142. package/dist/assets/web/dist/assets/CostsPage-BaeWnm-o.js +0 -1
  143. package/dist/assets/web/dist/assets/RunFoldRow-CwHDL-Mo.js +0 -1
  144. package/dist/assets/web/dist/assets/budgets-KNNkT4PZ.js +0 -1
@@ -21,9 +21,9 @@
21
21
  "@cloudflare/containers": "^0.3.7"
22
22
  },
23
23
  "devDependencies": {
24
- "@cloudflare/workers-types": "^5.20260904.1",
24
+ "@cloudflare/workers-types": "^5.20260914.1",
25
25
  "typescript": "^5.9.3",
26
26
  "vitest": "^5.0.0",
27
- "wrangler": "^4.129.1"
27
+ "wrangler": "^4.131.2"
28
28
  }
29
29
  }
@@ -13,6 +13,6 @@
13
13
  "tail": "wrangler tail"
14
14
  },
15
15
  "devDependencies": {
16
- "wrangler": "^4.129.1"
16
+ "wrangler": "^4.131.2"
17
17
  }
18
18
  }
@@ -17,9 +17,9 @@
17
17
  },
18
18
  "devDependencies": {
19
19
  "@cloudflare/vitest-pool-workers": "^0.22.0",
20
- "@cloudflare/workers-types": "^5.20260904.1",
20
+ "@cloudflare/workers-types": "^5.20260914.1",
21
21
  "typescript": "^5.9.3",
22
22
  "vitest": "^4.1.11",
23
- "wrangler": "^4.129.1"
23
+ "wrangler": "^4.131.2"
24
24
  }
25
25
  }
@@ -77,7 +77,8 @@ import {
77
77
  reclaimPhase,
78
78
  selectReclaim,
79
79
  } from "../../src/core/runLedger/decisions.ts";
80
- import { intakeReceiptRetentionMs, minutesToMs, PLANE } from "../../src/core/budgets.ts";
80
+ import { INTAKE_DELIVERY_CLAIM_MS, intakeReceiptRetentionMs, minutesToMs, PLANE } from "../../src/core/budgets.ts";
81
+ import { PROVIDER_FAILURE_CAUSES, type ProviderFailureCause } from "../../src/core/provider.ts";
81
82
  import { holdBackgroundTask } from "./backgroundTasks.ts";
82
83
  import {
83
84
  causeOfClose,
@@ -1696,6 +1697,12 @@ export class RunHistoryDO extends DurableObject<Env> {
1696
1697
  );
1697
1698
  CREATE INDEX IF NOT EXISTS intake_thread ON intake_receipts(thread_key, decided_at);
1698
1699
  CREATE INDEX IF NOT EXISTS intake_prune ON intake_receipts(prune_after);
1700
+ CREATE TABLE IF NOT EXISTS intake_deliveries (
1701
+ receipt_key TEXT PRIMARY KEY,
1702
+ poster TEXT NOT NULL,
1703
+ claim_until INTEGER NOT NULL,
1704
+ delivered INTEGER NOT NULL
1705
+ );
1699
1706
  `);
1700
1707
  // The coordinator's parent records (run-history item 49): one row per
1701
1708
  // instance, written by the bot at the instance's creation and read by the
@@ -1718,6 +1725,14 @@ export class RunHistoryDO extends DurableObject<Env> {
1718
1725
  updated_at INTEGER NOT NULL,
1719
1726
  PRIMARY KEY (instance_id, unit)
1720
1727
  );
1728
+ CREATE TABLE IF NOT EXISTS decision_record_reservations (
1729
+ repo TEXT NOT NULL,
1730
+ task_key TEXT NOT NULL,
1731
+ number TEXT NOT NULL,
1732
+ created_at INTEGER NOT NULL,
1733
+ PRIMARY KEY (repo, task_key),
1734
+ UNIQUE (repo, number)
1735
+ );
1721
1736
  `);
1722
1737
  // The thread events of a unit-owned thread (record 0051's reply-as-event rule): a
1723
1738
  // sibling table of the unit rows, never a field on them — `putUnits`
@@ -1786,6 +1801,7 @@ export class RunHistoryDO extends DurableObject<Env> {
1786
1801
  side TEXT NOT NULL,
1787
1802
  reported_at INTEGER NOT NULL,
1788
1803
  generation TEXT NOT NULL,
1804
+ cause TEXT,
1789
1805
  PRIMARY KEY (resident, name)
1790
1806
  );
1791
1807
  CREATE TABLE IF NOT EXISTS plane_endings (
@@ -1795,6 +1811,13 @@ export class RunHistoryDO extends DurableObject<Env> {
1795
1811
  at INTEGER NOT NULL
1796
1812
  );
1797
1813
  `);
1814
+ const planeLevelColumns = new Set(
1815
+ this.sql
1816
+ .exec<{ name: string }>(`PRAGMA table_info(plane_levels)`)
1817
+ .toArray()
1818
+ .map((column) => column.name),
1819
+ );
1820
+ if (!planeLevelColumns.has("cause")) this.sql.exec(`ALTER TABLE plane_levels ADD COLUMN cause TEXT`);
1798
1821
  }
1799
1822
 
1800
1823
  // ---- the orchestration plane (record 0064; orchestration-plane.md) ----------
@@ -1874,9 +1897,14 @@ export class RunHistoryDO extends DurableObject<Env> {
1874
1897
  .map((r) => [r.run_id, r.seq]),
1875
1898
  );
1876
1899
  const levels = this.sql
1877
- .exec<{ resident: string; name: string; side: string; reported_at: number; generation: string }>(
1878
- `SELECT * FROM plane_levels`,
1879
- )
1900
+ .exec<{
1901
+ resident: string;
1902
+ name: string;
1903
+ side: string;
1904
+ reported_at: number;
1905
+ generation: string;
1906
+ cause: string | null;
1907
+ }>(`SELECT * FROM plane_levels`)
1880
1908
  .toArray()
1881
1909
  .map((r): PlaneLevelRow => ({
1882
1910
  resident: r.resident,
@@ -1884,6 +1912,9 @@ export class RunHistoryDO extends DurableObject<Env> {
1884
1912
  side: r.side as PlaneLevelRow["side"],
1885
1913
  reportedAt: r.reported_at,
1886
1914
  generation: r.generation,
1915
+ ...(r.cause !== null && (PROVIDER_FAILURE_CAUSES as readonly string[]).includes(r.cause)
1916
+ ? { cause: r.cause as ProviderFailureCause }
1917
+ : {}),
1887
1918
  }));
1888
1919
  return { queue, liveThreads, liveRuns, inboxSeqs, reservations, openWindows, levels };
1889
1920
  }
@@ -1945,12 +1976,13 @@ export class RunHistoryDO extends DurableObject<Env> {
1945
1976
  this.sql.exec(`DELETE FROM plane_windows WHERE kind = ?`, w.window);
1946
1977
  } else if (w.table === "plane_levels") {
1947
1978
  this.sql.exec(
1948
- `INSERT OR REPLACE INTO plane_levels (resident, name, side, reported_at, generation) VALUES (?, ?, ?, ?, ?)`,
1979
+ `INSERT OR REPLACE INTO plane_levels (resident, name, side, reported_at, generation, cause) VALUES (?, ?, ?, ?, ?, ?)`,
1949
1980
  w.row.resident,
1950
1981
  w.row.name,
1951
1982
  w.row.side,
1952
1983
  w.row.reportedAt,
1953
1984
  w.row.generation,
1985
+ w.row.cause ?? null,
1954
1986
  );
1955
1987
  } else if (w.table === "plane_findings") {
1956
1988
  // A finding (record 0064, "Endings and the watches"): keyed by watch
@@ -2104,14 +2136,20 @@ export class RunHistoryDO extends DurableObject<Env> {
2104
2136
  planeLevel(
2105
2137
  post:
2106
2138
  | { resident: string; name: "seat" | "memory" | "drain"; side: "below" | "above"; generation: string }
2107
- | { provider: string; name: "provider"; side: "up" | "down" },
2139
+ | { provider: string; name: "provider"; side: "up" | "down"; cause?: ProviderFailureCause },
2108
2140
  now: number,
2109
2141
  ): { admitted: number } {
2110
2142
  // The model proxy's provider level (record 0064): `up` re-issues every
2111
2143
  // held turn parked on the provider — the steers land as inbox writes in the
2112
2144
  // decider's transaction — and walks anything queued on `provider_up`.
2113
2145
  if (post.name === "provider") {
2114
- const r = this.planeApply({ kind: "provider_level", at: now, provider: post.provider, level: post.side });
2146
+ const r = this.planeApply({
2147
+ kind: "provider_level",
2148
+ at: now,
2149
+ provider: post.provider,
2150
+ level: post.side,
2151
+ ...(post.side === "down" && post.cause !== undefined ? { cause: post.cause } : {}),
2152
+ });
2115
2153
  const admitted = r.effects.filter((e) => e.kind === "admit").length;
2116
2154
  console.log(`[plane/level] provider ${post.provider} ${post.side} — ${admitted} admission(s)`);
2117
2155
  return { admitted };
@@ -2601,6 +2639,71 @@ export class RunHistoryDO extends DurableObject<Env> {
2601
2639
  return out;
2602
2640
  }
2603
2641
 
2642
+ // ---- decision-record reservations (agent-ship item 16) ---------------------
2643
+
2644
+ /** One atomic durable allocation across bot processes. Existing unit and run
2645
+ * rows seed the claim set for reservations written before this ledger existed. */
2646
+ async reserveDecisionRecord(
2647
+ repo: string,
2648
+ taskKey: string,
2649
+ claimed: string[],
2650
+ existing: string | undefined,
2651
+ now: number,
2652
+ ): Promise<{ number: string }> {
2653
+ let number = "";
2654
+ this.ctx.storage.transactionSync(() => {
2655
+ const prior = this.sql
2656
+ .exec<{ number: string }>(
2657
+ `SELECT number FROM decision_record_reservations WHERE repo = ? AND task_key = ?`,
2658
+ repo,
2659
+ taskKey,
2660
+ )
2661
+ .toArray()[0];
2662
+ if (prior !== undefined) {
2663
+ number = prior.number;
2664
+ return;
2665
+ }
2666
+
2667
+ const used = new Set(claimed);
2668
+ for (const row of this.sql
2669
+ .exec<{ number: string }>(`SELECT number FROM decision_record_reservations WHERE repo = ?`, repo)
2670
+ .toArray())
2671
+ used.add(row.number);
2672
+ for (const row of this.sql
2673
+ .exec<{ instance_json: string; unit_json: string }>(
2674
+ `SELECT i.json AS instance_json, u.json AS unit_json
2675
+ FROM coordinator_units u JOIN coordinator_instances i ON i.instance_id = u.instance_id`,
2676
+ )
2677
+ .toArray()) {
2678
+ const instance = JSON.parse(row.instance_json) as CoordinatorInstance;
2679
+ const unit = JSON.parse(row.unit_json) as CoordinatorUnit;
2680
+ if (instance.repo === repo && unit.record !== undefined) used.add(unit.record);
2681
+ }
2682
+ for (const row of this.sql
2683
+ .exec<{ summary_json: string }>(`SELECT summary_json FROM runs WHERE repo = ?`, repo)
2684
+ .toArray()) {
2685
+ const record = JSON.parse(row.summary_json) as { record?: unknown };
2686
+ if (typeof record.record === "string" && /^\d{4}$/.test(record.record)) used.add(record.record);
2687
+ }
2688
+ for (const row of this.sql.exec<{ meta_json: string }>(`SELECT meta_json FROM live_runs`).toArray()) {
2689
+ const meta = JSON.parse(row.meta_json) as { repo?: unknown; record?: unknown };
2690
+ if (meta.repo === repo && typeof meta.record === "string" && /^\d{4}$/.test(meta.record)) used.add(meta.record);
2691
+ }
2692
+
2693
+ const highest = [...used].reduce((max, value) => (/^\d{4}$/.test(value) ? Math.max(max, Number(value)) : max), 0);
2694
+ number = existing ?? String(highest + 1).padStart(4, "0");
2695
+ if (!/^\d{4}$/.test(number)) throw new Error(`decision-record numbers exhausted for ${repo}`);
2696
+ this.sql.exec(
2697
+ `INSERT INTO decision_record_reservations (repo, task_key, number, created_at) VALUES (?, ?, ?, ?)`,
2698
+ repo,
2699
+ taskKey,
2700
+ number,
2701
+ now,
2702
+ );
2703
+ });
2704
+ return { number };
2705
+ }
2706
+
2604
2707
  // ---- the units of the plan an instance runs (run-history item 50) -----------
2605
2708
 
2606
2709
  /** Each row replaced whole under its (instance, unit); a replace keeps the row's place. */
@@ -3223,6 +3326,48 @@ export class RunHistoryDO extends DurableObject<Env> {
3223
3326
  return this.intakeRow(key) ?? null;
3224
3327
  }
3225
3328
 
3329
+ /** The receipt key and poster become one durable, atomic right to publish.
3330
+ * A dead claimant can be replaced only after its bounded claim expires. */
3331
+ async claimIntakeDelivery(key: string, poster: string, claimedAt: number): Promise<boolean> {
3332
+ let claimed = false;
3333
+ this.ctx.storage.transactionSync(() => {
3334
+ if (this.intakeRow(key)?.providerFailure === undefined) return;
3335
+ const existing = this.sql
3336
+ .exec<{ claim_until: number; delivered: number }>(
3337
+ `SELECT claim_until, delivered FROM intake_deliveries WHERE receipt_key = ?`,
3338
+ key,
3339
+ )
3340
+ .toArray()[0];
3341
+ if (existing?.delivered === 1 || (existing !== undefined && existing.claim_until > claimedAt)) return;
3342
+ this.sql.exec(
3343
+ `INSERT INTO intake_deliveries (receipt_key, poster, claim_until, delivered) VALUES (?, ?, ?, 0)
3344
+ ON CONFLICT(receipt_key) DO UPDATE SET poster = excluded.poster, claim_until = excluded.claim_until, delivered = 0`,
3345
+ key,
3346
+ poster,
3347
+ claimedAt + INTAKE_DELIVERY_CLAIM_MS,
3348
+ );
3349
+ claimed = true;
3350
+ });
3351
+ return claimed;
3352
+ }
3353
+
3354
+ async finishIntakeDelivery(key: string, poster: string, delivered: boolean): Promise<void> {
3355
+ this.ctx.storage.transactionSync(() => {
3356
+ if (delivered)
3357
+ this.sql.exec(
3358
+ `UPDATE intake_deliveries SET delivered = 1 WHERE receipt_key = ? AND poster = ? AND delivered = 0`,
3359
+ key,
3360
+ poster,
3361
+ );
3362
+ else
3363
+ this.sql.exec(
3364
+ `DELETE FROM intake_deliveries WHERE receipt_key = ? AND poster = ? AND delivered = 0`,
3365
+ key,
3366
+ poster,
3367
+ );
3368
+ });
3369
+ }
3370
+
3226
3371
  /** A thread's receipts, or the receipts since an instant, oldest first. */
3227
3372
  async listIntake(query: IntakeQuery): Promise<IntakeReceipt[]> {
3228
3373
  const clauses: string[] = [];
@@ -3574,6 +3719,10 @@ export class RunHistoryDO extends DurableObject<Env> {
3574
3719
  receipts = this.sql
3575
3720
  .exec<{ n: number }>(`SELECT COUNT(*) AS n FROM intake_receipts WHERE prune_after <= ?`, now)
3576
3721
  .one().n;
3722
+ this.sql.exec(
3723
+ `DELETE FROM intake_deliveries WHERE receipt_key IN (SELECT key FROM intake_receipts WHERE prune_after <= ?)`,
3724
+ now,
3725
+ );
3577
3726
  this.sql.exec(`DELETE FROM intake_receipts WHERE prune_after <= ?`, now);
3578
3727
  // The sessions no kept run names any more (session-log item 7): decided
3579
3728
  // here, on the rows this transaction leaves; dropped after it.
@@ -4955,6 +5104,7 @@ const LEDGER_ROUTES = new Set([
4955
5104
  "/runs/coordinator/events/list",
4956
5105
  "/runs/coordinator/events/mark-consumed",
4957
5106
  "/runs/coordinator/wake",
5107
+ "/runs/decision-record/reserve",
4958
5108
  "/runs/claim",
4959
5109
  "/runs/heartbeat",
4960
5110
  "/runs/append",
@@ -4972,6 +5122,8 @@ const LEDGER_ROUTES = new Set([
4972
5122
  "/runs/live-events",
4973
5123
  "/runs/intake",
4974
5124
  "/runs/intake/read",
5125
+ "/runs/intake/delivery/claim",
5126
+ "/runs/intake/delivery/finish",
4975
5127
  "/runs/intake/list",
4976
5128
  "/runs/transcript/owner",
4977
5129
  "/runs/transcript/write",
@@ -5125,7 +5277,22 @@ async function handlePlane(pathname: string, body: unknown, env: Env): Promise<R
5125
5277
  if (typeof b.provider !== "string" || b.provider.length === 0)
5126
5278
  return json({ error: "provider must be a non-empty string" }, 400);
5127
5279
  if (b.side !== "up" && b.side !== "down") return json({ error: "side must be up or down" }, 400);
5128
- return json(await stub.planeLevel({ provider: b.provider, name: "provider", side: b.side }, now));
5280
+ if (
5281
+ b.cause !== undefined &&
5282
+ (typeof b.cause !== "string" || !(PROVIDER_FAILURE_CAUSES as readonly string[]).includes(b.cause))
5283
+ )
5284
+ return json({ error: `cause must be one of ${PROVIDER_FAILURE_CAUSES.join(", ")}` }, 400);
5285
+ return json(
5286
+ await stub.planeLevel(
5287
+ {
5288
+ provider: b.provider,
5289
+ name: "provider",
5290
+ side: b.side,
5291
+ ...(b.side === "down" && typeof b.cause === "string" ? { cause: b.cause as ProviderFailureCause } : {}),
5292
+ },
5293
+ now,
5294
+ ),
5295
+ );
5129
5296
  }
5130
5297
  // A resident's level report (record 0064): forwarded by the bot from the levels a
5131
5298
  // resident answer carried, or from the registry's drain outbox.
@@ -5567,6 +5734,21 @@ async function handleLedger(pathname: string, body: unknown, env: Env): Promise<
5567
5734
  return json(r);
5568
5735
  }
5569
5736
 
5737
+ // Decision-record allocation is one atomic write on the same durable object
5738
+ // that holds unit and run rows, so a bot restart cannot forget a claim.
5739
+ if (pathname === "/runs/decision-record/reserve") {
5740
+ if (typeof b.repo !== "string" || !REPO_SLUG.test(b.repo)) return json({ error: "repo must be owner/name" }, 400);
5741
+ if (typeof b.taskKey !== "string" || !/^[0-9a-f]{16}$/.test(b.taskKey))
5742
+ return json({ error: "taskKey must be a decision-record task key" }, 400);
5743
+ if (!Array.isArray(b.claimed) || !b.claimed.every((number) => typeof number === "string" && /^\d{4}$/.test(number)))
5744
+ return json({ error: "claimed must be an array of decision-record numbers" }, 400);
5745
+ if (b.existing !== undefined && (typeof b.existing !== "string" || !/^\d{4}$/.test(b.existing)))
5746
+ return json({ error: "existing must be a decision-record number" }, 400);
5747
+ return json(
5748
+ await stub.reserveDecisionRecord(b.repo, b.taskKey, b.claimed as string[], b.existing as string | undefined, now),
5749
+ );
5750
+ }
5751
+
5570
5752
  // The coordinator's parent records (run-history item 49): the record whole,
5571
5753
  // validated by the shared contract; a read by instance id.
5572
5754
  if (pathname === "/runs/coordinator/put") {
@@ -5662,6 +5844,21 @@ async function handleLedger(pathname: string, body: unknown, env: Env): Promise<
5662
5844
  }
5663
5845
 
5664
5846
  // The intake receipts (run-history item 59): keyed by the message, not a run.
5847
+ if (pathname === "/runs/intake/delivery/claim" || pathname === "/runs/intake/delivery/finish") {
5848
+ const receiptKey = b.key;
5849
+ if (typeof receiptKey !== "string" || receiptKey.length === 0 || receiptKey.length > 256)
5850
+ return json({ error: "key must be a non-empty string of at most 256 characters" }, 400);
5851
+ if (typeof b.poster !== "string" || b.poster.length === 0 || b.poster.length > 256)
5852
+ return json({ error: "poster must be a non-empty string of at most 256 characters" }, 400);
5853
+ if (pathname === "/runs/intake/delivery/claim") {
5854
+ if (typeof b.claimedAt !== "number" || !Number.isFinite(b.claimedAt))
5855
+ return json({ error: "claimedAt must be a number" }, 400);
5856
+ return json({ claimed: await stub.claimIntakeDelivery(receiptKey, b.poster, b.claimedAt) });
5857
+ }
5858
+ if (typeof b.delivered !== "boolean") return json({ error: "delivered must be a boolean" }, 400);
5859
+ await stub.finishIntakeDelivery(receiptKey, b.poster, b.delivered);
5860
+ return json({ ok: true });
5861
+ }
5665
5862
  if (pathname === "/runs/intake" || pathname === "/runs/intake/read") {
5666
5863
  const receiptKey = b.key;
5667
5864
  if (typeof receiptKey !== "string" || receiptKey.length === 0 || receiptKey.length > 256)
@@ -152,10 +152,19 @@ RUN rm -f /usr/local/bin/bun \
152
152
  # The build asserts the binary with the probe the extract script uses at run
153
153
  # time (`command -v`): `unsquashfs -version` exits 1 on squashfs-tools 4.5 (this
154
154
  # base) when no filesystem is named, and failed the 1.2.0 image build that way.
155
+ #
156
+ # ripgrep (`rg`) + fd: the grep/find tools call these binaries directly. Ubuntu
157
+ # packages fd as `fd-find` with the binary named `fdfind`, so the image exposes
158
+ # the conventional `fd` name from /usr/local/bin (ahead of /usr/bin on PATH).
159
+ # Both are image tools, never downloaded by a run; their PATH probes make a
160
+ # missing package or link fail the image build instead of every resident run.
155
161
  RUN apt-get update \
156
- && apt-get install -y --no-install-recommends squashfs-tools \
162
+ && apt-get install -y --no-install-recommends squashfs-tools ripgrep fd-find \
163
+ && ln -s /usr/bin/fdfind /usr/local/bin/fd \
157
164
  && rm -rf /var/lib/apt/lists/* \
158
- && command -v unsquashfs >/dev/null
165
+ && command -v unsquashfs >/dev/null \
166
+ && command -v rg >/dev/null \
167
+ && command -v fd >/dev/null
159
168
 
160
169
  # The toolchain for what a run BUILDS and LOOKS AT — the same layer as the
161
170
  # sandbox image's (deploy/cloudflare-sandbox/Dockerfile);
@@ -21,9 +21,9 @@
21
21
  "@cloudflare/sandbox": "0.13.0-next.751.1"
22
22
  },
23
23
  "devDependencies": {
24
- "@cloudflare/workers-types": "^5.20260904.1",
24
+ "@cloudflare/workers-types": "^5.20260914.1",
25
25
  "typescript": "^5.9.3",
26
26
  "vitest": "^5.0.0",
27
- "wrangler": "^4.129.1"
27
+ "wrangler": "^4.131.2"
28
28
  }
29
29
  }