@coreplane/switchboard 1.244.0 → 1.246.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/dist/assets/config/config.example.yaml +6 -3
  2. package/dist/assets/deploy/cloudflare/worker.ts +2 -0
  3. package/dist/assets/deploy/cloudflare-resident/threadErr.ts +337 -0
  4. package/dist/assets/deploy/cloudflare-resident/tsconfig.json +1 -1
  5. package/dist/assets/deploy/cloudflare-resident/worker.ts +40 -272
  6. package/dist/assets/deploy/cloudflare-sandbox/worker.ts +22 -11
  7. package/dist/assets/deploy/secrets.manifest.json +12 -0
  8. package/dist/assets/package-lock.json +3 -3
  9. package/dist/assets/package.json +1 -1
  10. package/dist/assets/source.json +3 -3
  11. package/dist/assets/src/agents/registry.ts +34 -10
  12. package/dist/assets/src/config/profile.ts +17 -1
  13. package/dist/assets/src/core/authz/types.ts +8 -0
  14. package/dist/assets/src/core/budgets.ts +43 -0
  15. package/dist/assets/src/core/coordinator/contract.ts +29 -4
  16. package/dist/assets/src/core/coordinator/driver.ts +7 -1
  17. package/dist/assets/src/core/reviewVerdict.ts +239 -30
  18. package/dist/assets/src/core/runEvents.ts +55 -2
  19. package/dist/assets/src/core/runRecord.ts +56 -0
  20. package/dist/assets/src/core/ship/coordinator.ts +48 -23
  21. package/dist/assets/src/core/ship/renewal.ts +10 -3
  22. package/dist/assets/src/execution/bashTimeout.ts +46 -2
  23. package/dist/assets/src/execution/residentRefresh.ts +34 -11
  24. package/dist/assets/src/execution/sandboxErrors.ts +9 -4
  25. package/dist/assets/web/dist/.vite/manifest.json +481 -462
  26. package/dist/assets/web/dist/assets/AppShell-B4EUiSwP.js +1 -0
  27. package/dist/assets/web/dist/assets/CostsPage-D8VjAtuH.js +2 -0
  28. package/dist/assets/web/dist/assets/DeliveryPage-DpQi5XUI.js +1 -0
  29. package/dist/assets/web/dist/assets/HomePage-CvKuIGqg.js +2 -0
  30. package/dist/assets/web/dist/assets/InputMenu-DmqAxkn4.js +1 -0
  31. package/dist/assets/web/dist/assets/NotFoundPage-DDogyiXA.js +1 -0
  32. package/dist/assets/web/dist/assets/PendingTurnRow-BoSLlR6j.js +1 -0
  33. package/dist/assets/web/dist/assets/ResidentDetailPage-CiBdbzkE.js +1 -0
  34. package/dist/assets/web/dist/assets/ResidentsIndexPage-DgtHtd9K.js +1 -0
  35. package/dist/assets/web/dist/assets/{RunFoldRow-BPMQxTcl.js → RunFoldRow-IF4xbz-3.js} +1 -1
  36. package/dist/assets/web/dist/assets/RunRoutePage-DHYa7rHK.js +9 -0
  37. package/dist/assets/web/dist/assets/RunsIndexPage-aWhNTv8r.js +1 -0
  38. package/dist/assets/web/dist/assets/RunsTabs-CY_tdGTU.js +1 -0
  39. package/dist/assets/web/dist/assets/ScheduledPage-gPEBZjcx.js +1 -0
  40. package/dist/assets/web/dist/assets/SettingSelect-VDanrTZi.js +1 -0
  41. package/dist/assets/web/dist/assets/SettingsPage-CFoltSRD.js +1 -0
  42. package/dist/assets/web/dist/assets/StatusDot-DA3eTVrT.js +1 -0
  43. package/dist/assets/web/dist/assets/Tooltip-BgbCkM12.js +1 -0
  44. package/dist/assets/web/dist/assets/UnitRoutePage-DibegaJJ.js +1 -0
  45. package/dist/assets/web/dist/assets/{angular-html-Dail07rr.js → angular-html-D_3slZqf.js} +1 -1
  46. package/dist/assets/web/dist/assets/{angular-ts-BReExio8.js → angular-ts-D1Fv2cpN.js} +1 -1
  47. package/dist/assets/web/dist/assets/{apl-DhNO6Yyx.js → apl-60Nm_trN.js} +1 -1
  48. package/dist/assets/web/dist/assets/{astro-BYotTY4C.js → astro-CvyaM3Q9.js} +1 -1
  49. package/dist/assets/web/dist/assets/{blade-mUxTtD1X.js → blade-yan3VBls.js} +1 -1
  50. package/dist/assets/web/dist/assets/{c-DhuiwcSQ.js → c-Ddgepap2.js} +1 -1
  51. package/dist/assets/web/dist/assets/{chapel-GD2vFtRi.js → chapel-CgNY6dxC.js} +1 -1
  52. package/dist/assets/web/dist/assets/{cobol-BhFDwkes.js → cobol-C7q82cdP.js} +1 -1
  53. package/dist/assets/web/dist/assets/{coffee-DWUgbE0r.js → coffee-DgRMF_ni.js} +1 -1
  54. package/dist/assets/web/dist/assets/{cpp-CO9XFjGW.js → cpp-B8Lf-aeo.js} +1 -1
  55. package/dist/assets/web/dist/assets/{crystal-B9pVurpn.js → crystal-CkA_dhEO.js} +1 -1
  56. package/dist/assets/web/dist/assets/{css-CFVVV6lI.js → css-Cyu-C_oA.js} +1 -1
  57. package/dist/assets/web/dist/assets/{dist-BtPs2AkQ.js → dist-DS-gMjLZ.js} +2 -2
  58. package/dist/assets/web/dist/assets/{durationTone-BobbycC-.js → durationTone-l6aVrHb8.js} +1 -1
  59. package/dist/assets/web/dist/assets/{edge-BDhIDQIb.js → edge-BxV3rKNI.js} +1 -1
  60. package/dist/assets/web/dist/assets/{elixir-DG3J8WoD.js → elixir-pNSEK39d.js} +1 -1
  61. package/dist/assets/web/dist/assets/{elm-FiOdiaOE.js → elm-zgwWE33W.js} +1 -1
  62. package/dist/assets/web/dist/assets/{erb-vxbtW503.js → erb-yzcYAHp_.js} +1 -1
  63. package/dist/assets/web/dist/assets/{eventSource-tRprhqm7.js → eventSource-Ce94ho6M.js} +1 -1
  64. package/dist/assets/web/dist/assets/{git-rebase-DINz8M3H.js → git-rebase-Dg7Ous1-.js} +1 -1
  65. package/dist/assets/web/dist/assets/{glimmer-js-yann6r9i.js → glimmer-js-CbZEwX27.js} +1 -1
  66. package/dist/assets/web/dist/assets/{glimmer-ts-CXTbrZn_.js → glimmer-ts-D3NIqALy.js} +1 -1
  67. package/dist/assets/web/dist/assets/{glsl-DNYC-hq0.js → glsl-y4-3WrI2.js} +1 -1
  68. package/dist/assets/web/dist/assets/{graphql-CwzhHzi9.js → graphql-D5d0al7K.js} +1 -1
  69. package/dist/assets/web/dist/assets/{hack-BBXF1cwR.js → hack-D8fD6kEl.js} +1 -1
  70. package/dist/assets/web/dist/assets/{haml-oEqBcsTy.js → haml-BWeFFc-V.js} +1 -1
  71. package/dist/assets/web/dist/assets/{handlebars-YE5thkT1.js → handlebars-gIm5FeE_.js} +1 -1
  72. package/dist/assets/web/dist/assets/{html-derivative-DCT1GYtQ.js → html-derivative-B3ySSBrQ.js} +1 -1
  73. package/dist/assets/web/dist/assets/{html-Dp4jRQ1j.js → html-fXJTVLSM.js} +1 -1
  74. package/dist/assets/web/dist/assets/{http-BPcGbmzJ.js → http-DrJfQUmw.js} +1 -1
  75. package/dist/assets/web/dist/assets/{hurl-CQnNHwyd.js → hurl-Bvbnsf55.js} +1 -1
  76. package/dist/assets/web/dist/assets/{java-Dm8Ovj6W.js → java-CUSpb-i-.js} +1 -1
  77. package/dist/assets/web/dist/assets/{javascript-Btm9oJK9.js → javascript-BWRH5fbY.js} +1 -1
  78. package/dist/assets/web/dist/assets/{jinja-VZYjWF_U.js → jinja-D3Ymci8y.js} +1 -1
  79. package/dist/assets/web/dist/assets/{jison-Dcmjxa9x.js → jison-Gt9SO7ZC.js} +1 -1
  80. package/dist/assets/web/dist/assets/{json-DoWiOGZ8.js → json-Cfcblhgn.js} +1 -1
  81. package/dist/assets/web/dist/assets/{jsx-Dvmo55ma.js → jsx-C0366fFM.js} +1 -1
  82. package/dist/assets/web/dist/assets/{julia-d8AtfV26.js → julia-CyBOTbxS.js} +1 -1
  83. package/dist/assets/web/dist/assets/{just-Cc5EStA-.js → just-FPXZcIOd.js} +1 -1
  84. package/dist/assets/web/dist/assets/{latex-B5fKKIH7.js → latex-Bd-c88v0.js} +1 -1
  85. package/dist/assets/web/dist/assets/{liquid-DUYI9JND.js → liquid-5tu9Hc0k.js} +1 -1
  86. package/dist/assets/web/dist/assets/{lua-BhMwKWpw.js → lua-BZ5fdPP8.js} +1 -1
  87. package/dist/assets/web/dist/assets/main-B4A6x82d.js +28 -0
  88. package/dist/assets/web/dist/assets/main-CpkwzHX6.css +1 -0
  89. package/dist/assets/web/dist/assets/{marko-Cnf2cPAi.js → marko-C_mmj4p0.js} +1 -1
  90. package/dist/assets/web/dist/assets/{mdc-FDDXoir9.js → mdc-CtaNqWjY.js} +1 -1
  91. package/dist/assets/web/dist/assets/{nginx-q-8U_1jR.js → nginx-Ddm5Mb8J.js} +1 -1
  92. package/dist/assets/web/dist/assets/{nim-DIA0TUWs.js → nim-COhfroYB.js} +1 -1
  93. package/dist/assets/web/dist/assets/{org-CR88hy7S.js → org-CCHFgmTb.js} +1 -1
  94. package/dist/assets/web/dist/assets/{perl-Bkd8N7ja.js → perl-CXzOE-Z-.js} +1 -1
  95. package/dist/assets/web/dist/assets/{php-BOs84mvy.js → php-1V6jAIhU.js} +1 -1
  96. package/dist/assets/web/dist/assets/{pug-BbpZBb3w.js → pug-uS37efts.js} +1 -1
  97. package/dist/assets/web/dist/assets/{qml-D2jQpjFE.js → qml-Bj1A0Ben.js} +1 -1
  98. package/dist/assets/web/dist/assets/{r-DKzcAQ22.js → r-CMbKbLsS.js} +1 -1
  99. package/dist/assets/web/dist/assets/{razor-SOQMWcC9.js → razor-DVRq_Ndk.js} +1 -1
  100. package/dist/assets/web/dist/assets/{regexp-B6KK4W8d.js → regexp-AbaOr4oB.js} +1 -1
  101. package/dist/assets/web/dist/assets/{rst-kPSg-NPf.js → rst-Dq_QWgOF.js} +1 -1
  102. package/dist/assets/web/dist/assets/{ruby-cKS-JYLH.js → ruby-CVSsAj7T.js} +1 -1
  103. package/dist/assets/web/dist/assets/{sas-DNxzE5gs.js → sas-C-dGfxUi.js} +1 -1
  104. package/dist/assets/web/dist/assets/{scss-DGcHQ5OO.js → scss-DxALxYPA.js} +1 -1
  105. package/dist/assets/web/dist/assets/seed-BhxEVasY.js +6 -0
  106. package/dist/assets/web/dist/assets/{settingsApi-priOPiwu.js → settingsApi-Dm6eaQcu.js} +1 -1
  107. package/dist/assets/web/dist/assets/{shellscript-CRZeZBa5.js → shellscript--qXcvQ5L.js} +1 -1
  108. package/dist/assets/web/dist/assets/{shellsession-DSNEL0HX.js → shellsession-DeqnW8Ux.js} +1 -1
  109. package/dist/assets/web/dist/assets/{soy-zisNbkDO.js → soy-DreoUIMt.js} +1 -1
  110. package/dist/assets/web/dist/assets/{sql-MG06KMMb.js → sql-o3i2VTG6.js} +1 -1
  111. package/dist/assets/web/dist/assets/{sseReplay-DaRtc7nI.js → sseReplay-qF-gpbAY.js} +3 -3
  112. package/dist/assets/web/dist/assets/{stata-CB_wKwv9.js → stata-CGAT41H7.js} +1 -1
  113. package/dist/assets/web/dist/assets/{surrealql-DYdSt7re.js → surrealql-CBU0QuGV.js} +1 -1
  114. package/dist/assets/web/dist/assets/{svelte-DHO-F7c7.js → svelte-CfHaLZiG.js} +1 -1
  115. package/dist/assets/web/dist/assets/{templ-CUL5L4Lr.js → templ-D02MUcma.js} +1 -1
  116. package/dist/assets/web/dist/assets/{tex-_JNEKjVW.js → tex-DjprHYXl.js} +1 -1
  117. package/dist/assets/web/dist/assets/{ts-tags--9EpEV_8.js → ts-tags-DftDOzP-.js} +1 -1
  118. package/dist/assets/web/dist/assets/{tsx-BcNq2x2u.js → tsx-BOdvPohJ.js} +1 -1
  119. package/dist/assets/web/dist/assets/{twig-EsY5Dk_D.js → twig-B_yLS56B.js} +1 -1
  120. package/dist/assets/web/dist/assets/{typescript-CyA_HiUH.js → typescript-CdPCa4nU.js} +1 -1
  121. package/dist/assets/web/dist/assets/{typst-SzaHlDt4.js → typst-C1oIFsUl.js} +1 -1
  122. package/dist/assets/web/dist/assets/{vue-D3cn5acV.js → vue-CcszJEX8.js} +1 -1
  123. package/dist/assets/web/dist/assets/{vue-html-6MxhUxOa.js → vue-html-DcpxPcuT.js} +1 -1
  124. package/dist/assets/web/dist/assets/{vue-vine-qJnqhgo9.js → vue-vine-D26bIKGj.js} +1 -1
  125. package/dist/assets/web/dist/assets/wallClock-DPTuP2FD.js +1 -0
  126. package/dist/assets/web/dist/assets/{xml-DyRpC7xv.js → xml-D8zl6W5P.js} +1 -1
  127. package/dist/assets/web/dist/assets/{xsl-BXMRPAsg.js → xsl-D86Bii28.js} +1 -1
  128. package/dist/assets/web/dist/assets/{yaml-BNmPCtAN.js → yaml-g6Px4KFU.js} +1 -1
  129. package/dist/cli.js +3023 -1049
  130. package/package.json +1 -1
  131. package/dist/assets/web/dist/assets/AppShell-Ck4hSZDi.js +0 -1
  132. package/dist/assets/web/dist/assets/CostsPage-DvhjDMka.js +0 -2
  133. package/dist/assets/web/dist/assets/DeliveryPage-DBlI_Yq2.js +0 -1
  134. package/dist/assets/web/dist/assets/HomePage-XFWPxDiJ.js +0 -2
  135. package/dist/assets/web/dist/assets/NotFoundPage-CiGrVOyv.js +0 -1
  136. package/dist/assets/web/dist/assets/PendingTurnRow-BHCSpsPf.js +0 -1
  137. package/dist/assets/web/dist/assets/ResidentDetailPage-C5USV_8P.js +0 -1
  138. package/dist/assets/web/dist/assets/ResidentsIndexPage-CSKisWnX.js +0 -1
  139. package/dist/assets/web/dist/assets/RunRoutePage-CZe9Yodw.js +0 -9
  140. package/dist/assets/web/dist/assets/RunsIndexPage-BfA333Qy.js +0 -1
  141. package/dist/assets/web/dist/assets/RunsTabs-BWRHzhtT.js +0 -1
  142. package/dist/assets/web/dist/assets/ScheduledPage-DBaChcJZ.js +0 -1
  143. package/dist/assets/web/dist/assets/SettingsPage-CqVi9-Ac.js +0 -1
  144. package/dist/assets/web/dist/assets/StatusDot-_Q_bmReh.js +0 -1
  145. package/dist/assets/web/dist/assets/Tooltip-Cafsgd4m.js +0 -1
  146. package/dist/assets/web/dist/assets/UnitRoutePage-CdoBZwtf.js +0 -1
  147. package/dist/assets/web/dist/assets/main-C4366PZF.css +0 -1
  148. package/dist/assets/web/dist/assets/main-DlTNAC6c.js +0 -28
  149. package/dist/assets/web/dist/assets/seed-DeKSHrt2.js +0 -6
  150. package/dist/assets/web/dist/assets/wallClock-61NpVkY6.js +0 -1
@@ -24,8 +24,12 @@ export interface ProgressInput {
24
24
  /** The heads the segment's coding run pushed (`RunRecord.pushed`). */
25
25
  pushed: readonly PushedHeadFact[];
26
26
  /** The head the segment started from — the previous segment's recorded sha.
27
- * Absent on a fresh branch, where any push is progress. */
27
+ * Absent on a fresh branch, where the base head stands in for it. */
28
28
  startHead?: string;
29
+ /** The base branch's head the unit's branch was created at. On a fresh
30
+ * branch (no `startHead`) a push of this head is the branch's creation,
31
+ * not the unit's progress; with neither known, any push is progress. */
32
+ baseHead?: string;
29
33
  /** When the segment's lease began; a push stamped before it is not this segment's. */
30
34
  leaseStartedAt?: number;
31
35
  /** The previous segment's handoff and this one's, when both exist. */
@@ -53,7 +57,10 @@ export function progressOf(input: ProgressInput): Progress {
53
57
  if (last !== undefined) {
54
58
  const newerThanLease =
55
59
  input.leaseStartedAt === undefined || last.at === undefined || last.at >= input.leaseStartedAt;
56
- const moved = input.startHead === undefined || !sameSha(last.sha, input.startHead);
60
+ // A fresh branch starts at the base head, so that head is the bar a push
61
+ // must move past when no segment recorded a start.
62
+ const startHead = input.startHead ?? input.baseHead;
63
+ const moved = startHead === undefined || !sameSha(last.sha, startHead);
57
64
  if (newerThanLease && moved) return { progressed: true, by: "push", sha: last.sha };
58
65
  }
59
66
  const pushWhy = `no head newer than the lease's start was pushed to \`${input.branch}\``;
@@ -95,7 +102,7 @@ export function renewalDecision(input: RenewalInput): RenewalDecision {
95
102
  const detail =
96
103
  input.grant.renewals === 0
97
104
  ? "the grant holds no renewals"
98
- : `the grant's ${input.grant.renewals} renewal${input.grant.renewals === 1 ? "" : "s"} are spent`;
105
+ : `the grant's ${input.grant.renewals} renewal${input.grant.renewals === 1 ? " is" : "s are"} spent`;
99
106
  return { renew: false, why: "grant_exhausted", detail, renewalsLeft };
100
107
  }
101
108
  const cap = input.grant.costCapUsd;
@@ -4,7 +4,7 @@
4
4
  // Deliberately free of node: imports so wrangler can bundle it into Workers.
5
5
  // The numbers are rows of src/core/budgets.ts (docs/decisions/0046); this file
6
6
  // keeps their names for the readers that learned them here.
7
- import { ALLOWANCES, BASH_COMMAND, MINUTE_MS } from "../core/budgets.js";
7
+ import { ALLOWANCES, ATTACH_REQUEST_MIN_MS, BASH_COMMAND, MINUTE_MS } from "../core/budgets.js";
8
8
 
9
9
  /** Default per-command budget when the caller passes no timeoutMs. */
10
10
  export const BASH_TIMEOUT_MS = BASH_COMMAND.defaultMinutes * MINUTE_MS;
@@ -50,8 +50,10 @@ export function clampBashTimeout(requested: unknown): number {
50
50
  export type RunBudget =
51
51
  { kind: "unchanged" } | { kind: "clipped"; timeoutMs: number; note: string } | { kind: "exhausted"; note: string };
52
52
 
53
+ /** Whole seconds for the notes below, never negative: the two clip notes phrase the reserve alike. */
54
+ const secs = (ms: number): number => Math.max(0, Math.round(ms / 1000));
55
+
53
56
  export function bashBudgetWithinRun(wantedMs: number, remainingMs: number): RunBudget {
54
- const secs = (ms: number) => Math.max(0, Math.round(ms / 1000));
55
57
  const cap = Math.trunc(remainingMs - RUN_DEADLINE_RESERVE_MS);
56
58
  if (cap < BASH_TIMEOUT_MIN_MS) {
57
59
  return {
@@ -69,6 +71,48 @@ export function bashBudgetWithinRun(wantedMs: number, remainingMs: number): RunB
69
71
  };
70
72
  }
71
73
 
74
+ /** The bound on an attach request an executor opens while its run has
75
+ * `remainingMs` of wall clock left (`ResidentExecutorOptions.remainingMs`):
76
+ * `bounded` by the attach's own default, `BASH_TIMEOUT_MS` — an attach may
77
+ * clone and install deps — clipped to what the run has left less the same
78
+ * write-up reserve a command keeps back, so an attach never holds a run past
79
+ * its lease and the model keeps its write-up; never raised above the default.
80
+ * Inside the reserve, or with less than an attach's floor past it
81
+ * (`ATTACH_REQUEST_MIN_MS`: a bound too short to clone would be cut and
82
+ * struck as a rollout), `exhausted`: the request is not opened at all — the
83
+ * resident would run the attach to its end server-side for a run that is
84
+ * ending, and the caller would get a deadline it could read as waitable — and
85
+ * the note names the run's clock and the bound that refused (the reserve, or
86
+ * the floor past it): it is the one sentence every reader carries, the
87
+ * executor's refusal and the relaunch's note alike. The command's own budget
88
+ * never bounds an attach: the attach is not the command, and the command's
89
+ * one-second floor is the model's to choose. */
90
+ export type AttachBound = { kind: "bounded"; timeoutMs: number } | { kind: "exhausted"; note: string };
91
+
92
+ export function attachBoundWithinRun(remainingMs: number): AttachBound {
93
+ const left = Math.trunc(remainingMs - RUN_DEADLINE_RESERVE_MS);
94
+ // The one sentence every reader of an exhausted bound carries — the
95
+ // executor's refusal, the relaunch's note — worded by the bound that refused.
96
+ if (left <= 0) {
97
+ return {
98
+ kind: "exhausted",
99
+ note:
100
+ `the run has ${secs(remainingMs)}s of wall clock left, inside the ${secs(RUN_DEADLINE_RESERVE_MS)}s ` +
101
+ "write-up reserve, so no attach was opened",
102
+ };
103
+ }
104
+ if (left < ATTACH_REQUEST_MIN_MS) {
105
+ return {
106
+ kind: "exhausted",
107
+ note:
108
+ `the run has ${secs(remainingMs)}s of wall clock left, only ${secs(left)}s past the ` +
109
+ `${secs(RUN_DEADLINE_RESERVE_MS)}s write-up reserve — under the ${secs(ATTACH_REQUEST_MIN_MS)}s an attach needs — ` +
110
+ "so no attach was opened",
111
+ };
112
+ }
113
+ return { kind: "bounded", timeoutMs: Math.min(BASH_TIMEOUT_MS, left) };
114
+ }
115
+
72
116
  /** The line a timed-out command shows the model: names the limit that fired
73
117
  * and the knob that raises it, so the model can self-correct (re-run with a
74
118
  * larger timeoutMs, split the command, or background it) instead of guessing
@@ -180,11 +180,15 @@ export function killStaleBuildProcessesCommand(user: string, dir: string): strin
180
180
  }
181
181
 
182
182
  /** Signature of a step killed from OUTSIDE its own budget: the exit status of
183
- * SIGTERM (128 + 15), bash's "Session terminated" on a killed login shell, or
184
- * a tool naming the signal. A bare "killed" is NOT enough — compilers and
185
- * OOM messages say it too — and a step the cycle itself timed out is a real
186
- * failure however it died. */
187
- const INTERRUPTION_SIGNATURE = /\bexit 143\b|Session terminated|SIGTERM|^restore-interrupted:/;
183
+ * SIGTERM (128 + 15) — as a shell reports it (`exit 143`) or as the platform
184
+ * words a container that died under a short exec ("Container exited with
185
+ * unexpected exit code: 143"; seen live on the wake path's ready-stamp probe
186
+ * when a stop landed on it) — bash's "Session terminated" on a killed login
187
+ * shell, or a tool naming the signal. Only 143: the platform's same wording
188
+ * with any other code (1, 137, 139) is a genuine crash and stays a failure.
189
+ * A bare "killed" is NOT enough — compilers and OOM messages say it too —
190
+ * and a step the cycle itself timed out is a real failure however it died. */
191
+ const INTERRUPTION_SIGNATURE = /\bexit 143\b|exit code: 143\b|Session terminated|SIGTERM|^restore-interrupted:/;
188
192
 
189
193
  /** The wordings git gives when the MIRROR itself is broken: its `origin`
190
194
  * remote gone from `config`, the repository not a repository, an object store
@@ -241,14 +245,17 @@ export function fetchFailureIsMirrors(message: string): boolean {
241
245
  export const RUNTIME_MOVED_WORDING =
242
246
  /previous runtime incarnation|interrupted because the runtime changed|runtime identity is no longer active|sandbox lifetime is no longer current|platform was updating the sandbox runtime|no longer identifies pid/i;
243
247
  /** The platform's words for a container that is down under a command (see
244
- * `RUNTIME_MOVED_WORDING`): the spawn refusal of a stopped container and the
245
- * binding's not-running refusal, anchored to the full phrase so a genuine
248
+ * `RUNTIME_MOVED_WORDING`): the spawn refusal of a stopped container, the
249
+ * binding's not-running refusal, and the SDK's "The container just exited"
250
+ * for a container that died under a live exec (a roll landing mid-step; the
251
+ * bot side reads the same words as a roll, residentWake's
252
+ * CONTAINER_GONE_WORDING), each anchored to the full phrase so a genuine
246
253
  * crash ("container exited with unexpected exit code") and the readiness
247
254
  * probe ("the container is not listening") never match. The harness's
248
255
  * container seam reads the same two as a container down under its one more
249
256
  * command — a wait, never a judgement (harness.md item 6). */
250
257
  export const STOPPED_CONTAINER_WORDING =
251
- /process supervisor is closed|container is not running, consider calling start/i;
258
+ /process supervisor is closed|container is not running, consider calling start|the container just exited/i;
252
259
  export const RUNTIME_REPLACEMENT_WORDING = new RegExp(
253
260
  `${RUNTIME_MOVED_WORDING.source}|${STOPPED_CONTAINER_WORDING.source}`,
254
261
  "i",
@@ -358,12 +365,28 @@ export const SDK_RUNTIME_RECORD_KEY = "currentRuntimeIdentity";
358
365
  /** The DOMException the SDK's connect abort raises — `fetchUpgradeAttempt`
359
366
  * calls `controller.abort()` with no reason when `DEFAULT_CONNECT_TIMEOUT_MS`
360
367
  * passes, so the runtime's own `AbortError` with the message exactly `The
361
- * operation was aborted`. Anchored on the WHOLE message, so a command's own
362
- * `Aborted (core dumped)` never matches and neither does a `TimeoutError`'s
368
+ * operation was aborted`. Anchored on the WHOLE message: a command's own
369
+ * `Aborted (core dumped)` never matches, and neither does a `TimeoutError`'s
363
370
  * `The operation was aborted due to timeout` — the text `AbortSignal.timeout`
364
- * raises, a route's own bound on a slow GitHub, not a silent runtime. */
371
+ * raises, a route's own bound on a slow GitHub, not a silent runtime. A
372
+ * wrapper is read by its `cause`, where the DOMException rides whole; a
373
+ * wrapper that only pasted the sentence into a longer message is not the
374
+ * signal. */
365
375
  export const RUNTIME_UNREACHABLE_WORDING = /^The operation was aborted\.?$/;
366
376
 
377
+ /** `err` and its `cause` chain, bounded like the SDK's own `selfAndCauses`
378
+ * walker: the SDK wraps platform errors, so the telling message can sit one or
379
+ * two links down. The one walker for every reader of a throw's chain — the
380
+ * resident Worker's classifiers and its thread-error builders, the sandbox
381
+ * Worker's — so the depth bound and the shape are decided once. */
382
+ export function* selfAndCauses(err: unknown): Generator<unknown> {
383
+ let current = err;
384
+ for (let depth = 0; depth < 8 && current != null; depth++) {
385
+ yield current;
386
+ current = typeof current === "object" ? (current as { cause?: unknown }).cause : undefined;
387
+ }
388
+ }
389
+
367
390
  /** Is this one link of an error's cause chain the SDK's connect abort? By
368
391
  * name first (the DOMException's `AbortError`), by the DOMException's message
369
392
  * when a wrapper copied only that. Applied by the Worker to the SPAWN-phase
@@ -154,10 +154,15 @@ export const SANDBOX_STARTING_EXPLANATION =
154
154
  "the thread's sandbox container is starting (image pull, boot, runtime) — nothing ran yet; the request is re-sent once it is up";
155
155
 
156
156
  /** The executor waits at most this long for a container to start, whatever
157
- * the command's own budget: the platform's own start allowances (30 s for an
158
- * instance, 90 s for the port) plus a slow image pull fit inside it, and a
159
- * 60 s command is never killed by a two-minute start it did not cause. */
160
- export const SANDBOX_START_WAIT_MAX_MS = 5 * 60_000;
157
+ * the command's own budget: the platform's own start allowances (the
158
+ * instance grant, 90 s for the port) plus a slow image pull fit inside it,
159
+ * and a 60 s command is never killed by a two-minute start it did not cause.
160
+ * Ten minutes, not five: under a midday burst the platform admitted about
161
+ * seven starts at once and granted the rest 2.5 to 5 minutes later — two
162
+ * threads of twenty-four died at the five-minute mark with their container
163
+ * a minute away. A run that waits ten minutes for its container starts; one
164
+ * that dies at five does not, and nothing else in the run was in progress. */
165
+ export const SANDBOX_START_WAIT_MAX_MS = 10 * 60_000;
161
166
 
162
167
  /** Backoff between re-sends while a container starts: 5 s, 10 s, then 15 s.
163
168
  * A start takes tens of seconds, not minutes, so the poll is denser than the