toolroll 0.9.92 → 0.9.94

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 (341) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/dist/activity-line.d.ts +7 -4
  3. package/dist/activity-line.js +9 -7
  4. package/dist/agent-fence.d.ts +4 -0
  5. package/dist/agent-fence.js +4 -1
  6. package/dist/approval-authority.d.ts +7 -1
  7. package/dist/approval-authority.js +11 -2
  8. package/dist/approval-password.d.ts +31 -0
  9. package/dist/approval-password.js +26 -0
  10. package/dist/assignment-status.d.ts +1 -1
  11. package/dist/assignment-ui.js +3 -3
  12. package/dist/assignment.js +16 -3
  13. package/dist/boot-identity.d.ts +2 -1
  14. package/dist/boot-identity.js +6 -4
  15. package/dist/bounded-spawn.d.ts +57 -0
  16. package/dist/bounded-spawn.js +220 -0
  17. package/dist/browser/terminal.js +42 -42
  18. package/dist/browser/workspace.css +2 -2
  19. package/dist/browser/workspace.js +26 -26
  20. package/dist/browser-crew.js +2 -2
  21. package/dist/browser-shell.d.ts +1 -1
  22. package/dist/browser-shell.js +2 -2
  23. package/dist/browser-workspace.d.ts +48 -7
  24. package/dist/browser-workspace.js +3 -3
  25. package/dist/builder-restart.d.ts +21 -0
  26. package/dist/builder-restart.js +50 -0
  27. package/dist/builder.d.ts +4 -0
  28. package/dist/builder.js +73 -25
  29. package/dist/chat-acceptance.js +1 -1
  30. package/dist/chat-actions.d.ts +12 -2
  31. package/dist/chat-actions.js +25 -2
  32. package/dist/chat-channel.js +11 -0
  33. package/dist/chat-decide.d.ts +7 -2
  34. package/dist/chat-decide.js +37 -9
  35. package/dist/chat-quiet.js +3 -3
  36. package/dist/chat-voice.d.ts +1 -1
  37. package/dist/chat-voice.js +2 -2
  38. package/dist/check-facts.d.ts +126 -0
  39. package/dist/check-facts.js +318 -0
  40. package/dist/check-failure.d.ts +22 -3
  41. package/dist/check-failure.js +57 -13
  42. package/dist/check-levels-ui.d.ts +4 -0
  43. package/dist/check-levels-ui.js +10 -1
  44. package/dist/check-load.d.ts +85 -0
  45. package/dist/check-load.js +147 -0
  46. package/dist/check-only-standing.d.ts +36 -0
  47. package/dist/check-only-standing.js +39 -0
  48. package/dist/check-only.d.ts +15 -0
  49. package/dist/check-only.js +99 -0
  50. package/dist/check-report.d.ts +7 -0
  51. package/dist/check-report.js +4 -3
  52. package/dist/check-stamp.d.ts +46 -0
  53. package/dist/check-stamp.js +93 -0
  54. package/dist/checkout-cleanup.d.ts +36 -5
  55. package/dist/checkout-cleanup.js +166 -14
  56. package/dist/cli-help.d.ts +4 -1
  57. package/dist/cli-help.js +47 -30
  58. package/dist/cli.js +16 -1
  59. package/dist/code-extract.d.ts +44 -0
  60. package/dist/code-extract.js +88 -0
  61. package/dist/code-map-acts.d.ts +24 -0
  62. package/dist/code-map-acts.js +53 -0
  63. package/dist/code-map-admission.d.ts +52 -0
  64. package/dist/code-map-admission.js +295 -0
  65. package/dist/code-map-build.d.ts +63 -0
  66. package/dist/code-map-build.js +415 -0
  67. package/dist/code-map-cli.d.ts +153 -0
  68. package/dist/code-map-cli.js +103 -0
  69. package/dist/code-map-main.d.ts +1 -0
  70. package/dist/code-map-main.js +48 -0
  71. package/dist/code-map-read.d.ts +106 -0
  72. package/dist/code-map-read.js +171 -0
  73. package/dist/code-map-report.d.ts +37 -0
  74. package/dist/code-map-report.js +95 -0
  75. package/dist/code-map-shape.d.ts +172 -0
  76. package/dist/code-map-shape.js +327 -0
  77. package/dist/code-map-store.d.ts +136 -0
  78. package/dist/code-map-store.js +233 -0
  79. package/dist/coding-provider.d.ts +2 -0
  80. package/dist/coding-provider.js +3 -2
  81. package/dist/coding-update.js +3 -2
  82. package/dist/coding-workspace.js +4 -4
  83. package/dist/console-chrome.js +2 -2
  84. package/dist/containment.d.ts +5 -0
  85. package/dist/containment.js +14 -1
  86. package/dist/contracts/chat-actions.d.ts +9 -0
  87. package/dist/contracts/chat-actions.js +2 -0
  88. package/dist/contracts/cli.d.ts +52 -0
  89. package/dist/contracts/cli.js +4 -0
  90. package/dist/contracts/console-api.d.ts +12 -5
  91. package/dist/contracts/console-api.js +12 -5
  92. package/dist/contracts/gateway-tools.d.ts +17 -0
  93. package/dist/contracts/gateway-tools.js +3 -1
  94. package/dist/contracts/lead-tools.d.ts +61 -0
  95. package/dist/contracts/lead-tools.js +10 -1
  96. package/dist/contracts/project-knowledge.d.ts +38 -5
  97. package/dist/contracts/project-knowledge.js +11 -5
  98. package/dist/contracts/project-memory.d.ts +3 -1
  99. package/dist/contracts/project-memory.js +3 -2
  100. package/dist/contracts/release-check.d.ts +25 -1
  101. package/dist/contracts/release-check.js +19 -0
  102. package/dist/contracts/storage.d.ts +14 -0
  103. package/dist/contracts/storage.js +3 -1
  104. package/dist/contracts/store-json.d.ts +2 -0
  105. package/dist/contracts/youiyoux-request.d.ts +1 -1
  106. package/dist/contracts/youiyoux-request.js +1 -1
  107. package/dist/desktop-access.js +6 -2
  108. package/dist/desktop-update.d.ts +2 -2
  109. package/dist/desktop-update.js +13 -9
  110. package/dist/dispatch.js +18 -15
  111. package/dist/dispose.js +8 -2
  112. package/dist/easy-start.js +2 -3
  113. package/dist/eval-hooks.d.ts +55 -0
  114. package/dist/eval-hooks.js +227 -4
  115. package/dist/evals.js +2 -1
  116. package/dist/fast-start.d.ts +136 -0
  117. package/dist/fast-start.js +431 -0
  118. package/dist/fast-terms.d.ts +9 -0
  119. package/dist/fast-terms.js +23 -0
  120. package/dist/fenced-check-coverage.d.ts +62 -0
  121. package/dist/fenced-check-coverage.js +54 -0
  122. package/dist/first-run.js +1 -1
  123. package/dist/flaky-ci.d.ts +31 -0
  124. package/dist/flaky-ci.js +149 -0
  125. package/dist/flaky-cli.d.ts +60 -0
  126. package/dist/flaky-cli.js +60 -0
  127. package/dist/flaky.d.ts +83 -0
  128. package/dist/flaky.js +264 -0
  129. package/dist/flow-card-task.js +3 -0
  130. package/dist/flow-triggers.d.ts +1 -1
  131. package/dist/flow-triggers.js +1 -1
  132. package/dist/github-import.d.ts +62 -0
  133. package/dist/github-import.js +146 -0
  134. package/dist/guides.js +15 -10
  135. package/dist/invoke.d.ts +5 -1
  136. package/dist/invoke.js +49 -14
  137. package/dist/job-object-helper.ps1 +10 -3
  138. package/dist/keymap.d.ts +41 -0
  139. package/dist/keymap.js +51 -0
  140. package/dist/knowledge-cli.js +1 -1
  141. package/dist/knowledge-ui.d.ts +21 -4
  142. package/dist/knowledge-ui.js +75 -7
  143. package/dist/laptop-background.d.ts +16 -0
  144. package/dist/laptop-background.js +108 -2
  145. package/dist/lead-context.js +1 -1
  146. package/dist/lead-contract.d.ts +1 -1
  147. package/dist/lead-contract.js +2 -2
  148. package/dist/lead-progress.js +3 -0
  149. package/dist/lead-status.d.ts +2 -0
  150. package/dist/lead-status.js +6 -2
  151. package/dist/lead-tools.js +51 -2
  152. package/dist/lead.js +1 -1
  153. package/dist/lesson-notes.d.ts +81 -0
  154. package/dist/lesson-notes.js +336 -0
  155. package/dist/limits-ui.js +1 -1
  156. package/dist/local-owner.d.ts +1 -1
  157. package/dist/local-owner.js +11 -0
  158. package/dist/machine-check.d.ts +16 -0
  159. package/dist/machine-check.js +26 -0
  160. package/dist/machine-identity.d.ts +75 -0
  161. package/dist/machine-identity.js +170 -0
  162. package/dist/mcp-core.js +11 -1
  163. package/dist/memory-cli.d.ts +300 -1
  164. package/dist/memory-cli.js +62 -9
  165. package/dist/memory-pass.d.ts +16 -0
  166. package/dist/memory-pass.js +67 -3
  167. package/dist/memory-tidy.d.ts +181 -0
  168. package/dist/memory-tidy.js +523 -0
  169. package/dist/memory-use.d.ts +35 -0
  170. package/dist/memory-use.js +109 -0
  171. package/dist/merge-checklist.d.ts +55 -0
  172. package/dist/merge-checklist.js +182 -0
  173. package/dist/needs-you.d.ts +32 -13
  174. package/dist/needs-you.js +108 -49
  175. package/dist/nightly-github.d.ts +2 -0
  176. package/dist/nightly-github.js +4 -4
  177. package/dist/operate-remote-arguments.js +6 -1
  178. package/dist/operate-remote.js +3 -1
  179. package/dist/operate.d.ts +6 -1
  180. package/dist/operate.js +540 -70
  181. package/dist/phase-routing.js +2 -2
  182. package/dist/plain-schema.d.ts +23 -0
  183. package/dist/plain-schema.js +94 -0
  184. package/dist/plain-words.d.ts +20 -0
  185. package/dist/plain-words.js +66 -0
  186. package/dist/planner.js +2 -1
  187. package/dist/process-liveness.d.ts +2 -0
  188. package/dist/process-liveness.js +61 -17
  189. package/dist/project-delete-ui.d.ts +7 -2
  190. package/dist/project-delete-ui.js +12 -1
  191. package/dist/project-delete.js +13 -2
  192. package/dist/project-knowledge.d.ts +18 -11
  193. package/dist/project-knowledge.js +39 -9
  194. package/dist/project-learning.d.ts +2 -1
  195. package/dist/project-learning.js +15 -45
  196. package/dist/project-memory.d.ts +5 -0
  197. package/dist/project-memory.js +22 -2
  198. package/dist/proof.js +30 -5
  199. package/dist/provider-auth.js +1 -1
  200. package/dist/publish.js +9 -1
  201. package/dist/pull-request-flow.js +5 -0
  202. package/dist/push.d.ts +26 -3
  203. package/dist/push.js +55 -15
  204. package/dist/release-adapters.d.ts +9 -0
  205. package/dist/release-adapters.js +87 -1
  206. package/dist/release-approval.d.ts +191 -0
  207. package/dist/release-approval.js +382 -0
  208. package/dist/release-auto.js +26 -17
  209. package/dist/release-card.d.ts +74 -6
  210. package/dist/release-card.js +179 -23
  211. package/dist/release-carry.d.ts +7 -2
  212. package/dist/release-carry.js +39 -9
  213. package/dist/release-cli.d.ts +4 -0
  214. package/dist/release-cli.js +38 -23
  215. package/dist/release-coverage.js +1 -1
  216. package/dist/release-next.d.ts +48 -0
  217. package/dist/release-next.js +161 -0
  218. package/dist/release.d.ts +50 -1
  219. package/dist/release.js +118 -11
  220. package/dist/remote-destination.d.ts +36 -0
  221. package/dist/remote-destination.js +217 -0
  222. package/dist/remote-main.d.ts +48 -0
  223. package/dist/remote-main.js +84 -5
  224. package/dist/repository-context.d.ts +3 -2
  225. package/dist/repository-context.js +112 -70
  226. package/dist/restart-certification.d.ts +1 -0
  227. package/dist/restart-certification.js +8 -6
  228. package/dist/result-acts.d.ts +29 -21
  229. package/dist/result-acts.js +36 -25
  230. package/dist/result-follow-ups.d.ts +2 -2
  231. package/dist/result-follow-ups.js +18 -4
  232. package/dist/result-review.d.ts +1 -1
  233. package/dist/route-change-check.d.ts +4 -0
  234. package/dist/route-change-check.js +13 -0
  235. package/dist/route-change.d.ts +22 -0
  236. package/dist/route-change.js +89 -0
  237. package/dist/route-hooks.js +3 -2
  238. package/dist/route-results.d.ts +6 -1
  239. package/dist/route-results.js +14 -5
  240. package/dist/run-closeout.d.ts +1 -1
  241. package/dist/run-closeout.js +7 -5
  242. package/dist/runner-process.js +3 -2
  243. package/dist/runner.js +2 -1
  244. package/dist/scope.d.ts +6 -1
  245. package/dist/scope.js +22 -9
  246. package/dist/serve.d.ts +1 -1
  247. package/dist/serve.js +24 -18
  248. package/dist/server/chat.js +14 -12
  249. package/dist/server/chrome.d.ts +1 -1
  250. package/dist/server/chrome.js +26 -35
  251. package/dist/server/console.css +23 -0
  252. package/dist/server/guards.js +4 -0
  253. package/dist/server/home.d.ts +6 -0
  254. package/dist/server/home.js +21 -13
  255. package/dist/server/http.js +1 -1
  256. package/dist/server/kit.js +1 -1
  257. package/dist/server/merge-ready-view.d.ts +10 -0
  258. package/dist/server/merge-ready-view.js +30 -0
  259. package/dist/server/pages-assets.d.ts +1 -0
  260. package/dist/server/pages-assets.js +8 -5
  261. package/dist/server/pages.js +103 -82
  262. package/dist/server/render-chat.js +2 -2
  263. package/dist/server/render-code-map.d.ts +30 -0
  264. package/dist/server/render-code-map.js +137 -0
  265. package/dist/server/render-flaky.d.ts +20 -0
  266. package/dist/server/render-flaky.js +59 -0
  267. package/dist/server/render-history.js +1 -1
  268. package/dist/server/render-new-task.d.ts +26 -1
  269. package/dist/server/render-new-task.js +30 -5
  270. package/dist/server/render-pages.d.ts +1 -175
  271. package/dist/server/render-pages.js +38 -313
  272. package/dist/server/render-projects.d.ts +2 -1
  273. package/dist/server/render-projects.js +9 -7
  274. package/dist/server/render-release.d.ts +19 -0
  275. package/dist/server/render-release.js +84 -0
  276. package/dist/server/render-results.d.ts +2 -2
  277. package/dist/server/render-results.js +71 -73
  278. package/dist/server/render-tasks.d.ts +13 -2
  279. package/dist/server/render-tasks.js +156 -139
  280. package/dist/server/retired-routes.d.ts +17 -0
  281. package/dist/server/retired-routes.js +53 -0
  282. package/dist/server/route-table.d.ts +4 -1
  283. package/dist/server/route-table.js +52 -13
  284. package/dist/server/settings.js +129 -22
  285. package/dist/server/task-views.js +61 -26
  286. package/dist/server/tasks.d.ts +17 -6
  287. package/dist/server/tasks.js +273 -338
  288. package/dist/server/welcome.js +1 -1
  289. package/dist/settings-groups-ui.js +1 -1
  290. package/dist/setup-guide.d.ts +2 -2
  291. package/dist/stop-proof.d.ts +67 -0
  292. package/dist/stop-proof.js +295 -0
  293. package/dist/storage-sweep-process.js +1 -1
  294. package/dist/storage-sweep.d.ts +5 -3
  295. package/dist/storage-sweep.js +30 -9
  296. package/dist/storage-ui.js +28 -17
  297. package/dist/storage.d.ts +27 -2
  298. package/dist/storage.js +39 -2
  299. package/dist/store.d.ts +148 -34
  300. package/dist/store.js +626 -177
  301. package/dist/surface.js +46 -5
  302. package/dist/sweep-quarantine.d.ts +1 -0
  303. package/dist/sweep-quarantine.js +6 -4
  304. package/dist/tamper-scan.d.ts +4 -0
  305. package/dist/tamper-scan.js +90 -0
  306. package/dist/task-aliases.d.ts +5 -0
  307. package/dist/task-aliases.js +9 -0
  308. package/dist/task-control.js +4 -1
  309. package/dist/task-outcome-cli.d.ts +2 -0
  310. package/dist/task-outcome-cli.js +36 -17
  311. package/dist/task-sizing.d.ts +5 -0
  312. package/dist/task-sizing.js +2 -0
  313. package/dist/task-source.d.ts +7 -0
  314. package/dist/task-source.js +14 -0
  315. package/dist/task-status.d.ts +9 -0
  316. package/dist/task-status.js +40 -49
  317. package/dist/teardown-git.d.ts +16 -0
  318. package/dist/teardown-git.js +63 -5
  319. package/dist/telegram-progress.js +5 -3
  320. package/dist/telegram-status.js +6 -4
  321. package/dist/test-infra.d.ts +8 -0
  322. package/dist/test-infra.js +18 -0
  323. package/dist/tidy.js +1 -1
  324. package/dist/toolroll-update-ui.js +2 -2
  325. package/dist/toolroll-update.js +18 -5
  326. package/dist/tui-contract.d.ts +24 -0
  327. package/dist/tui-source.js +67 -8
  328. package/dist/tui.js +51 -51
  329. package/dist/vanished-retry.d.ts +30 -0
  330. package/dist/vanished-retry.js +174 -0
  331. package/dist/work-index.d.ts +2 -0
  332. package/dist/work-index.js +82 -70
  333. package/dist/work-summary.d.ts +2 -2
  334. package/dist/work-summary.js +2 -0
  335. package/dist/workspace-ui.d.ts +4 -2
  336. package/dist/workspace-ui.js +28 -22
  337. package/dist/worktree.d.ts +12 -3
  338. package/dist/worktree.js +68 -13
  339. package/package.json +1 -1
  340. package/dist/server/render-next.d.ts +0 -63
  341. package/dist/server/render-next.js +0 -109
@@ -0,0 +1,233 @@
1
+ /**
2
+ * The code map's records (docs/plans/code-map.md §3): immutable versions, the pointer to the current one, and the
3
+ * per-project refresh record whose lease fences every later write. Delivery and measurement rows sit here too. The
4
+ * rows are Toolroll's own; nothing goes in a repository.
5
+ */
6
+ import { createHash } from "node:crypto";
7
+ import { gunzipSync, gzipSync } from "node:zlib";
8
+ import { trustedRemoteIdentity } from "./store.js";
9
+ import { LIMITS } from "./code-map-shape.js";
10
+ export const V152_SCHEMA = `
11
+ CREATE TABLE IF NOT EXISTS code_map_version (
12
+ id INTEGER PRIMARY KEY, repo TEXT NOT NULL, remote_digest TEXT NOT NULL, remote_record INTEGER NOT NULL, commit_sha TEXT NOT NULL,
13
+ extractor TEXT NOT NULL, built_at TEXT NOT NULL, manifest BLOB NOT NULL, manifest_sha TEXT NOT NULL, graph BLOB NOT NULL, graph_sha TEXT NOT NULL, report TEXT NOT NULL
14
+ );
15
+ CREATE INDEX IF NOT EXISTS code_map_version_repo ON code_map_version (repo, id);
16
+ CREATE TABLE IF NOT EXISTS code_map_current (repo TEXT PRIMARY KEY, version INTEGER NOT NULL REFERENCES code_map_version(id));
17
+ CREATE TABLE IF NOT EXISTS code_map_refresh (
18
+ repo TEXT PRIMARY KEY, lease_token INTEGER NOT NULL DEFAULT 0, lease_owner TEXT, lease_until TEXT,
19
+ request_id INTEGER NOT NULL DEFAULT 0, requested_at TEXT, requested_by TEXT,
20
+ failures INTEGER NOT NULL DEFAULT 0, retry_after TEXT, last_error TEXT, last_checked_at TEXT,
21
+ seen_digest TEXT, seen_record INTEGER, seen_commit TEXT, seen_extractor TEXT, seen_state TEXT,
22
+ spawn_pid INTEGER, spawn_pgid INTEGER, spawn_started TEXT, spawn_folder TEXT, enforced TEXT, proxy TEXT
23
+ );
24
+ CREATE TABLE IF NOT EXISTS code_map_family (
25
+ root_task TEXT PRIMARY KEY, repo TEXT NOT NULL, arm TEXT NOT NULL CHECK (arm IN ('map','holdout')), enrolled_at TEXT NOT NULL, observe_until TEXT NOT NULL
26
+ );
27
+ CREATE INDEX IF NOT EXISTS code_map_family_repo ON code_map_family (repo);
28
+ CREATE TABLE IF NOT EXISTS code_map_snapshot (
29
+ run INTEGER PRIMARY KEY, state TEXT NOT NULL CHECK (state IN ('map','off','none','remote-changed','holdout','unavailable')),
30
+ version INTEGER, excerpt TEXT NOT NULL, excerpt_sha TEXT NOT NULL, file_selected INTEGER NOT NULL CHECK (file_selected IN (0,1))
31
+ );
32
+ CREATE INDEX IF NOT EXISTS code_map_snapshot_version ON code_map_snapshot (version);
33
+ CREATE TABLE IF NOT EXISTS code_map_delivery (
34
+ id INTEGER PRIMARY KEY, run INTEGER NOT NULL, attempt INTEGER NOT NULL, at TEXT NOT NULL,
35
+ outcome TEXT NOT NULL CHECK (outcome IN ('delivered','not-delivered')),
36
+ reason TEXT CHECK (reason IS NULL OR reason IN ('no-confinement','write-failed','sha-mismatch','version-pruned','not-selected')), path_sha TEXT,
37
+ UNIQUE (run, attempt)
38
+ );
39
+ CREATE TABLE IF NOT EXISTS code_map_meta (id INTEGER PRIMARY KEY CHECK (id = 1), run_watermark INTEGER NOT NULL);
40
+ CREATE TRIGGER IF NOT EXISTS code_map_version_no_update BEFORE UPDATE ON code_map_version BEGIN SELECT RAISE(ABORT,'a code map version is immutable'); END;
41
+ CREATE TRIGGER IF NOT EXISTS code_map_version_guarded_delete BEFORE DELETE ON code_map_version
42
+ WHEN EXISTS (SELECT 1 FROM code_map_current WHERE version = OLD.id)
43
+ OR EXISTS (SELECT 1 FROM code_map_snapshot s JOIN run r ON r.id = s.run WHERE s.version = OLD.id AND r.outcome IS NULL)
44
+ BEGIN SELECT RAISE(ABORT,'a code map version in use stays'); END;
45
+ CREATE TRIGGER IF NOT EXISTS code_map_family_no_update BEFORE UPDATE ON code_map_family BEGIN SELECT RAISE(ABORT,'a family arm is frozen'); END;
46
+ CREATE TRIGGER IF NOT EXISTS code_map_family_no_delete BEFORE DELETE ON code_map_family BEGIN SELECT RAISE(ABORT,'a family arm is frozen'); END;
47
+ CREATE TRIGGER IF NOT EXISTS code_map_snapshot_no_update BEFORE UPDATE ON code_map_snapshot BEGIN SELECT RAISE(ABORT,'a code map snapshot is immutable'); END;
48
+ CREATE TRIGGER IF NOT EXISTS code_map_snapshot_no_delete BEFORE DELETE ON code_map_snapshot BEGIN SELECT RAISE(ABORT,'a code map snapshot is immutable'); END;
49
+ CREATE TRIGGER IF NOT EXISTS code_map_delivery_no_update BEFORE UPDATE ON code_map_delivery BEGIN SELECT RAISE(ABORT,'a code map delivery is immutable'); END;
50
+ CREATE TRIGGER IF NOT EXISTS code_map_delivery_no_delete BEFORE DELETE ON code_map_delivery BEGIN SELECT RAISE(ABORT,'a code map delivery is immutable'); END;
51
+ CREATE TRIGGER IF NOT EXISTS code_map_meta_no_update BEFORE UPDATE ON code_map_meta BEGIN SELECT RAISE(ABORT,'the watermark is set once'); END;
52
+ CREATE TRIGGER IF NOT EXISTS code_map_meta_no_delete BEFORE DELETE ON code_map_meta BEGIN SELECT RAISE(ABORT,'the watermark is set once'); END;
53
+ `;
54
+ /** The tables and triggers V152_SCHEMA makes, for the startup check and the tests. */
55
+ export const V152_TABLES = Object.freeze(["code_map_version", "code_map_current", "code_map_refresh", "code_map_family", "code_map_snapshot", "code_map_delivery", "code_map_meta"]);
56
+ export const V152_TRIGGERS = Object.freeze(["code_map_version_no_update", "code_map_version_guarded_delete", "code_map_family_no_update", "code_map_family_no_delete", "code_map_snapshot_no_update",
57
+ "code_map_snapshot_no_delete", "code_map_delivery_no_update", "code_map_delivery_no_delete", "code_map_meta_no_update", "code_map_meta_no_delete"]);
58
+ /** v152: a saved project reads the map on, with no enrollment yet. */
59
+ export const V152_ADDED_COLUMNS = Object.freeze({
60
+ project: {
61
+ code_map: { definition: "INTEGER NOT NULL DEFAULT 1 CHECK (code_map IN (0,1))", saved: "1" },
62
+ code_map_enroll_until: { definition: "TEXT", saved: "NULL" },
63
+ },
64
+ });
65
+ const sha = (value) => createHash("sha256").update(value).digest("hex");
66
+ export const digestOfUrl = (url) => sha(url);
67
+ export const CODE_MAP_ACT = "code map check";
68
+ const sameIdentity = (a, b) => a !== null && b !== null && a.url === b.url && a.record === b.record;
69
+ export function codeMapOn(store, repo) {
70
+ const row = store.handle.prepare("SELECT code_map FROM project WHERE path = ?").get(repo);
71
+ return row !== undefined && Number(row["code_map"]) === 1;
72
+ }
73
+ const textOf = (value) => (typeof value === "string" ? value : null);
74
+ const intOf = (value) => (value === null || value === undefined ? null : Number(value));
75
+ function refreshOf(row) {
76
+ return {
77
+ repo: String(row["repo"]), leaseToken: Number(row["lease_token"]), leaseOwner: textOf(row["lease_owner"]), leaseUntil: textOf(row["lease_until"]), requestId: Number(row["request_id"]),
78
+ requestedAt: textOf(row["requested_at"]), requestedBy: textOf(row["requested_by"]), failures: Number(row["failures"]), retryAfter: textOf(row["retry_after"]), lastError: textOf(row["last_error"]),
79
+ lastCheckedAt: textOf(row["last_checked_at"]), seen: { digest: textOf(row["seen_digest"]), record: intOf(row["seen_record"]), commit: textOf(row["seen_commit"]), extractor: textOf(row["seen_extractor"]), state: textOf(row["seen_state"]) },
80
+ spawn: { pid: intOf(row["spawn_pid"]), pgid: intOf(row["spawn_pgid"]), startedAt: textOf(row["spawn_started"]), folder: textOf(row["spawn_folder"]) }, enforced: textOf(row["enforced"]), proxy: textOf(row["proxy"]),
81
+ };
82
+ }
83
+ export function readRefresh(store, repo) {
84
+ const row = store.handle.prepare("SELECT * FROM code_map_refresh WHERE repo = ?").get(repo);
85
+ return row === undefined ? null : refreshOf(row);
86
+ }
87
+ const ensureRefresh = (store, repo) => { store.handle.prepare("INSERT OR IGNORE INTO code_map_refresh (repo) VALUES (?)").run(repo); };
88
+ export const LEASE_MS = 10 * 60_000;
89
+ /** Take the lease in one statement. A takeover always gets a higher token; null when it is held. */
90
+ export function takeLease(store, repo, owner, now) {
91
+ ensureRefresh(store, repo);
92
+ const before = readRefresh(store, repo);
93
+ const taken = store.handle.prepare(`UPDATE code_map_refresh SET lease_token = lease_token + 1, lease_owner = ?, lease_until = ?
94
+ WHERE repo = ? AND (lease_owner IS NULL OR lease_until < ?)`).run(owner, new Date(now.getTime() + LEASE_MS).toISOString(), repo, now.toISOString());
95
+ if (Number(taken.changes) !== 1)
96
+ return null;
97
+ const after = readRefresh(store, repo);
98
+ return { repo, token: after.leaseToken, owner, requestId: after.requestId, previous: before.spawn };
99
+ }
100
+ /** A fenced write: changes rows only while this lease stands. Zero rows means the lease was lost. */
101
+ function fenced(store, lease, set, values, extra = "", extraValues = []) {
102
+ const ran = store.handle.prepare(`UPDATE code_map_refresh SET ${set} WHERE repo = ? AND lease_token = ? AND lease_owner = ?${extra}`).run(...values, lease.repo, lease.token, lease.owner, ...extraValues);
103
+ return Number(ran.changes) === 1;
104
+ }
105
+ export const extendLease = (store, lease, now) => fenced(store, lease, "lease_until = ?", [new Date(now.getTime() + LEASE_MS).toISOString()]);
106
+ export const releaseLease = (store, lease) => fenced(store, lease, "lease_owner = NULL, lease_until = NULL, spawn_pid = NULL, spawn_pgid = NULL, spawn_started = NULL, spawn_folder = NULL", []);
107
+ export const clearOldSpawn = (store, lease) => fenced(store, lease, "spawn_pid = NULL, spawn_pgid = NULL, spawn_started = NULL, spawn_folder = NULL", []);
108
+ export const recordFolder = (store, lease, folder) => fenced(store, lease, "spawn_folder = ?", [folder]);
109
+ export const recordSpawn = (store, lease, spawn) => fenced(store, lease, "spawn_pid = ?, spawn_pgid = ?, spawn_started = ?", [spawn.pid, spawn.pgid, spawn.startedAt]);
110
+ /** Clear the request this lease saw, and only that one: a request made during the build stays queued. */
111
+ export const clearRequest = (store, lease) => fenced(store, lease, "requested_at = NULL, requested_by = NULL", [], " AND request_id = ?", [lease.requestId]);
112
+ /** A finished check that found nothing wrong: failures reset. */
113
+ export const recordCheck = (store, lease, facts, now) => fenced(store, lease, "failures = 0, retry_after = NULL, last_error = NULL, last_checked_at = ?, seen_digest = ?, seen_record = ?, seen_commit = ?, seen_extractor = ?, seen_state = ?, enforced = COALESCE(?, enforced), proxy = ?", [now.toISOString(), facts.digest, facts.record, facts.commit, facts.extractor, facts.state, facts.enforced ?? null, facts.proxy ?? null]);
114
+ /** What the peek saw, kept even if the build then fails: the page's "Main has moved to …". */
115
+ export const recordPeek = (store, lease, commit) => fenced(store, lease, "seen_commit = ?", [commit]);
116
+ export const BACKOFF_MS = [30 * 60_000, 2 * 3_600_000, 6 * 3_600_000];
117
+ /** A failed check: 30 minutes, then 2 hours, then 6 hours. */
118
+ export function recordFailure(store, lease, why, state, now) {
119
+ const failures = readRefresh(store, lease.repo)?.failures ?? 0;
120
+ const wait = BACKOFF_MS[Math.min(failures, BACKOFF_MS.length - 1)];
121
+ return fenced(store, lease, "failures = failures + 1, retry_after = ?, last_error = ?, last_checked_at = ?, seen_state = ?", [new Date(now.getTime() + wait).toISOString(), why.slice(0, 300), now.toISOString(), state]);
122
+ }
123
+ export const REQUEST_GAP_MS = 10 * 60_000;
124
+ /** Queue a manual check: at most one per project per 10 minutes. It never bypasses back-off. */
125
+ export function requestRefresh(store, repo, by, now) {
126
+ return store.transact(() => {
127
+ ensureRefresh(store, repo);
128
+ const held = readRefresh(store, repo);
129
+ if (held.requestedAt !== null)
130
+ return "already-queued";
131
+ const last = store.handle.prepare("SELECT at FROM action_ledger WHERE repo = ? AND action = 'code map refresh asked' ORDER BY id DESC LIMIT 1").get(repo);
132
+ if (last !== undefined && now.getTime() - Date.parse(String(last["at"])) < REQUEST_GAP_MS)
133
+ return "already-queued";
134
+ store.handle.prepare("UPDATE code_map_refresh SET request_id = request_id + 1, requested_at = ?, requested_by = ? WHERE repo = ?").run(now.toISOString(), by, repo);
135
+ store.recordAction({ at: now.toISOString(), actor: by, repo, taskId: null, runId: null, action: "code map refresh asked", outcome: "queued", source: "request", detail: null });
136
+ return "queued";
137
+ });
138
+ }
139
+ /** The remote-change transaction's part: the pointer clears and a build still running for the old remote is fenced out.
140
+ * Called from store.recordProjectRemote, inside the transaction that records the new remote. */
141
+ export function codeMapRemoteChanged(store, repo) {
142
+ store.handle.prepare("DELETE FROM code_map_current WHERE repo = ?").run(repo);
143
+ store.handle.prepare("UPDATE code_map_refresh SET lease_token = lease_token + 1, lease_owner = NULL, lease_until = NULL, seen_commit = NULL, seen_digest = NULL, seen_record = NULL, failures = 0, retry_after = NULL WHERE repo = ?").run(repo);
144
+ }
145
+ const versionOf = (row) => ({ id: Number(row["id"]), repo: String(row["repo"]), remoteDigest: String(row["remote_digest"]), remoteRecord: Number(row["remote_record"]),
146
+ commit: String(row["commit_sha"]), extractor: String(row["extractor"]), builtAt: String(row["built_at"]), report: String(row["report"]) });
147
+ const VERSION_COLUMNS = "id, repo, remote_digest, remote_record, commit_sha, extractor, built_at, report";
148
+ export function currentVersion(store, repo) {
149
+ const row = store.handle.prepare(`SELECT ${VERSION_COLUMNS} FROM code_map_version WHERE id = (SELECT version FROM code_map_current WHERE repo = ?)`).get(repo);
150
+ return row === undefined ? null : versionOf(row);
151
+ }
152
+ export function versionRow(store, id) {
153
+ const row = store.handle.prepare(`SELECT ${VERSION_COLUMNS} FROM code_map_version WHERE id = ?`).get(id);
154
+ return row === undefined ? null : versionOf(row);
155
+ }
156
+ /** The stored bytes, checked against their digests before they are parsed; null when they don't verify. */
157
+ export function readVersionMap(store, id) {
158
+ const row = store.handle.prepare("SELECT manifest, manifest_sha, graph, graph_sha FROM code_map_version WHERE id = ?").get(id);
159
+ if (row === undefined)
160
+ return null;
161
+ try {
162
+ const manifest = gunzipSync(row["manifest"], { maxOutputLength: LIMITS.storedBytes * 2 });
163
+ const graph = gunzipSync(row["graph"], { maxOutputLength: LIMITS.storedBytes * 2 });
164
+ if (sha(manifest) !== row["manifest_sha"] || sha(graph) !== row["graph_sha"])
165
+ return null;
166
+ return { ...JSON.parse(manifest.toString("utf8")), ...JSON.parse(graph.toString("utf8")) };
167
+ }
168
+ catch {
169
+ return null;
170
+ }
171
+ }
172
+ export function encodeMap(map) {
173
+ const { edges, ...rest } = map;
174
+ const manifest = Buffer.from(JSON.stringify(rest));
175
+ const graph = Buffer.from(JSON.stringify({ edges }));
176
+ return { manifest: gzipSync(manifest), manifestSha: sha(manifest), graph: gzipSync(graph), graphSha: sha(graph), bytes: manifest.length + graph.length };
177
+ }
178
+ /**
179
+ * Store a built version and, in the same transaction, make it current only if the identity captured before the fetch
180
+ * still stands, the map is still on, the build still holds its lease, and the pointer is where it found it. A build that
181
+ * lost its lease stores nothing at all.
182
+ */
183
+ export function publishVersion(store, lease, input, now) {
184
+ return store.transact(() => {
185
+ const held = readRefresh(store, lease.repo);
186
+ if (held === null || held.leaseToken !== lease.token || held.leaseOwner !== lease.owner)
187
+ return null;
188
+ const { map, encoded } = input;
189
+ const stored = store.handle.prepare(`INSERT INTO code_map_version (repo, remote_digest, remote_record, commit_sha, extractor, built_at, manifest, manifest_sha, graph, graph_sha, report)
190
+ VALUES (?,?,?,?,?,?,?,?,?,?,?)`).run(lease.repo, map.remote.urlDigest, map.remote.record, map.commit, map.extractor, now.toISOString(), encoded.manifest, encoded.manifestSha, encoded.graph, encoded.graphSha, input.report);
191
+ const version = Number(stored.lastInsertRowid);
192
+ const pointer = store.handle.prepare("SELECT version FROM code_map_current WHERE repo = ?").get(lease.repo);
193
+ const at = pointer === undefined ? null : Number(pointer["version"]);
194
+ if (!sameIdentity(input.identity, trustedRemoteIdentity(store, lease.repo)))
195
+ return { version, current: false, why: "remote-changed" };
196
+ if (!codeMapOn(store, lease.repo))
197
+ return { version, current: false, why: "off" };
198
+ if (at !== input.startedFrom)
199
+ return { version, current: false, why: "pointer-moved" };
200
+ store.handle.prepare("INSERT INTO code_map_current (repo, version) VALUES (?, ?) ON CONFLICT (repo) DO UPDATE SET version = excluded.version").run(lease.repo, version);
201
+ return { version, current: true, why: "published" };
202
+ });
203
+ }
204
+ /** Keep the current version, the 10 before it and any named by an unfinished run. */
205
+ export function tidyVersions(store, repo) {
206
+ return store.transact(() => {
207
+ const current = store.handle.prepare("SELECT version FROM code_map_current WHERE repo = ?").get(repo);
208
+ if (current === undefined)
209
+ return 0;
210
+ const ran = store.handle.prepare(`DELETE FROM code_map_version WHERE repo = ? AND id < ? AND id NOT IN (
211
+ SELECT id FROM code_map_version WHERE repo = ? AND id < ? ORDER BY id DESC LIMIT 10)
212
+ AND id NOT IN (SELECT s.version FROM code_map_snapshot s JOIN run r ON r.id = s.run WHERE s.version IS NOT NULL AND r.outcome IS NULL)`).run(repo, Number(current["version"]), repo, Number(current["version"]));
213
+ return Number(ran.changes);
214
+ });
215
+ }
216
+ /** A project removed from the lists: its map rows go with it, except what stays with its runs (snapshots, deliveries,
217
+ * family arms) and any version an unfinished run still names. */
218
+ export function forgetCodeMap(store, repo) {
219
+ store.handle.prepare("DELETE FROM code_map_current WHERE repo = ?").run(repo);
220
+ store.handle.prepare("DELETE FROM code_map_refresh WHERE repo = ?").run(repo);
221
+ store.handle.prepare(`DELETE FROM code_map_version WHERE repo = ? AND id NOT IN (SELECT s.version FROM code_map_snapshot s JOIN run r ON r.id = s.run WHERE s.version IS NOT NULL AND r.outcome IS NULL)`).run(repo);
222
+ }
223
+ /** The startup check: a store at this version holds every table and trigger, or its map can't be trusted. */
224
+ export function codeMapShapeProblem(db) {
225
+ const has = db.prepare("SELECT 1 FROM sqlite_master WHERE type = ? AND name = ?");
226
+ for (const table of V152_TABLES)
227
+ if (has.get("table", table) === undefined)
228
+ return `the code map table ${table} is missing`;
229
+ for (const trigger of V152_TRIGGERS)
230
+ if (has.get("trigger", trigger) === undefined)
231
+ return `the code map trigger ${trigger} is missing`;
232
+ return null;
233
+ }
@@ -15,6 +15,8 @@ export type CodingCustody = {
15
15
  observationFailures?: ProcessObservationFailure[];
16
16
  host: string;
17
17
  bootId: string | null;
18
+ /** v150: this machine's stable id (machine-identity.ts); absent in custody saved before it. */
19
+ machineId?: string | null;
18
20
  container?: {
19
21
  backend: string;
20
22
  id: string;
@@ -6,6 +6,7 @@ import { dirname, join } from "node:path";
6
6
  import { codexFenceConfig, fenceFor, FENCE_POLICY_CURRENT, runFolders, taskBoundary, trustedWorktreeBinding } from "./agent-fence.js";
7
7
  import { poolRootOf } from "./worktree.js";
8
8
  import { currentBootId } from "./boot-identity.js";
9
+ import { currentMachineId } from "./machine-identity.js";
9
10
  import { createContainer, currentContainment } from "./containment.js";
10
11
  import { processMayBeAlive } from "./process-liveness.js";
11
12
  import { observeProcessTree, sampleProcessTree, stopProcessTree } from "./process-tree.js";
@@ -82,7 +83,7 @@ export function createCodexCodingProvider(options = {}) {
82
83
  const descendants = new Map();
83
84
  let ancestryUnknown = false;
84
85
  const observationFailures = [];
85
- const host = hostname();
86
+ const host = hostname(), machineId = currentMachineId();
86
87
  // Bind the boot at creation, not when an old snapshot is requested later.
87
88
  const bootId = currentBootId();
88
89
  let nextId = 0;
@@ -94,7 +95,7 @@ export function createCodexCodingProvider(options = {}) {
94
95
  return {
95
96
  pid, group: pid !== null && process.platform !== "win32",
96
97
  descendants: [...descendants.values()].map(one => ({ ...one })),
97
- observationUnknown: ancestryUnknown, host, bootId,
98
+ observationUnknown: ancestryUnknown, host, bootId, machineId,
98
99
  ...(observationFailures.length === 0 ? {} : { observationFailures: observationFailures.map(one => ({ ...one })) }),
99
100
  ...(container === null ? {} : { container: { backend: container.backend, id: container.id, ...(container.identity === undefined ? {} : { identity: container.identity }) } }),
100
101
  };
@@ -1,6 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { chmodSync, closeSync, fsyncSync, lstatSync, openSync } from 'node:fs';
3
- import { hostname } from 'node:os';
3
+ import { isThisMachine } from './machine-identity.js';
4
4
  import { sqliteRuntime } from './sqlite-runtime.js';
5
5
  import { processMayBeAlive } from './process-liveness.js';
6
6
  const gatePrefix = 'so_coding_update_';
@@ -141,7 +141,8 @@ export function releaseStaleCodingOwner(orders, stoppedPids, alive = processMayB
141
141
  c = {};
142
142
  }
143
143
  const processes = Array.isArray(c.descendants) ? c.descendants : null;
144
- if (c.host !== hostname() || c.container || c.observationUnknown !== false || processes === null)
144
+ if (typeof c.host !== 'string' || (c.machineId !== undefined && c.machineId !== null && typeof c.machineId !== 'string')
145
+ || !isThisMachine({ host: c.host, machineId: (c.machineId ?? null) }) || c.container || c.observationUnknown !== false || processes === null)
145
146
  refuse("the agent's tool processes could not be accounted for");
146
147
  const recorded = [...(c.pid === null ? [] : [{ pid: c.pid, group: c.group }]), ...processes];
147
148
  for (const one of recorded) {
@@ -2,8 +2,8 @@ import { checkoutGit } from './teardown-git.js';
2
2
  import { createHash, randomUUID } from 'node:crypto';
3
3
  import { mkdirSync, chmodSync, realpathSync } from 'node:fs';
4
4
  import { dirname, join } from 'node:path';
5
- import { hostname } from 'node:os';
6
- import { provenDeadByBootChange } from './boot-identity.js';
5
+ import { currentBootId, provenDeadByBootChange } from './boot-identity.js';
6
+ import { isThisMachine } from './machine-identity.js';
7
7
  import { containerEmptiness } from './container-state.js';
8
8
  import { sqliteRuntime } from './sqlite-runtime.js';
9
9
  import { run } from './exec.js';
@@ -536,9 +536,9 @@ export class CodingWorkspace {
536
536
  return false;
537
537
  try {
538
538
  const c = readCodingCustodyWitness(String(row['payload']));
539
- if (c === null || c.host !== hostname())
539
+ if (c === null || typeof c.host !== 'string' || !isThisMachine(c))
540
540
  return false;
541
- if (provenDeadByBootChange(c))
541
+ if (provenDeadByBootChange(c, { host: c.host, bootId: currentBootId() }))
542
542
  return true;
543
543
  if (c.container)
544
544
  return containerEmptiness(c.container.backend, c.container.id, process.platform, c.container.identity) === 'empty';
@@ -49,11 +49,11 @@ export function projectSummaryOf(store, now, access, actor, projects, evidenceRo
49
49
  };
50
50
  const rows = projects.map(one => {
51
51
  const state = states.get(one.path);
52
- return { path: one.path, name: one.name, href: `/work?project=${encodeURIComponent(one.path)}`, state: state?.state ?? "idle", counts: state?.counts ?? { ...NO_COUNTS }, ...railOf(state) };
52
+ return { path: one.path, name: one.name, href: `/tasks?project=${encodeURIComponent(one.path)}`, state: state?.state ?? "idle", counts: state?.counts ?? { ...NO_COUNTS }, ...railOf(state) };
53
53
  });
54
54
  const unplaced = states.get(null);
55
55
  if (unplaced !== undefined && unplaced.state !== "idle")
56
- rows.push({ path: null, name: "No project", href: "/work", state: unplaced.state, counts: unplaced.counts, ...railOf(unplaced) });
56
+ rows.push({ path: null, name: "No project", href: "/tasks", state: unplaced.state, counts: unplaced.counts, ...railOf(unplaced) });
57
57
  return boundProjectSummary(rows);
58
58
  }
59
59
  /** One access, canonically and in full (every field it has, so a field added later keys too): the same reader and
@@ -161,6 +161,11 @@ export type Container = {
161
161
  waitEmpty(timeoutMs: number): Promise<boolean>;
162
162
  /** Remove the OS object (only an empty one can go) and dismiss the janitor. */
163
163
  release(): void;
164
+ /** Hard limits on the whole object, set before `launch`; false when this object can't enforce them. */
165
+ limit?(limits: {
166
+ memoryBytes: number;
167
+ pids: number;
168
+ }): boolean;
164
169
  };
165
170
  export type ContainerFactory = (effective: EffectiveContainment, label: string) => Container;
166
171
  /** Tests: substitute the OS object (a scripted or file-backed stand-in). */
@@ -353,6 +353,17 @@ export function cgroup2Container(capability, label, mountOverride) {
353
353
  backend: "cgroup2",
354
354
  id: dir,
355
355
  identity,
356
+ limit({ memoryBytes, pids }) {
357
+ // The controllers exist here only if the delegated parent enabled them for its children.
358
+ try {
359
+ writeFileSync(join(dir, "memory.max"), String(memoryBytes));
360
+ writeFileSync(join(dir, "pids.max"), String(pids));
361
+ return true;
362
+ }
363
+ catch {
364
+ return false;
365
+ }
366
+ },
356
367
  launch(file, args) {
357
368
  return {
358
369
  file: "/bin/sh",
@@ -445,6 +456,7 @@ export function jobObjectContainer(capability, label) {
445
456
  let authorized = false;
446
457
  let cancelled = false;
447
458
  let helperChild = null;
459
+ let memoryLimit = 0;
448
460
  const fail = (detail) => { failure ??= detail; if (state !== "empty")
449
461
  state = "lost"; connection?.destroy(); };
450
462
  const authorize = () => {
@@ -517,10 +529,11 @@ export function jobObjectContainer(capability, label) {
517
529
  };
518
530
  return {
519
531
  backend: "job-object", id, identity,
532
+ limit({ memoryBytes }) { memoryLimit = memoryBytes; return true; },
520
533
  launch(file, args) {
521
534
  return {
522
535
  file: capability.powershell,
523
- args: ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", capability.helper, "-Pipe", pipe, "-Encoded", Buffer.from(JSON.stringify([file, ...args]), "utf8").toString("base64"), "-JobName", id],
536
+ args: ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", capability.helper, "-Pipe", pipe, "-Encoded", Buffer.from(JSON.stringify([file, ...args]), "utf8").toString("base64"), "-JobName", id, ...(memoryLimit > 0 ? ["-MemoryLimit", String(memoryLimit)] : [])],
524
537
  extraStdio: [],
525
538
  async attach(child) {
526
539
  // Called only after the durable onSpawn callback succeeded.
@@ -260,6 +260,13 @@ export declare const chatActionRequestSchemas: {
260
260
  repo: z.ZodString;
261
261
  trial: z.ZodInt;
262
262
  }, z.core.$strict>;
263
+ readonly code_map_switch: z.ZodObject<{
264
+ repo: z.ZodString;
265
+ on: z.ZodBoolean;
266
+ }, z.core.$strict>;
267
+ readonly code_map_holdout_end: z.ZodObject<{
268
+ repo: z.ZodString;
269
+ }, z.core.$strict>;
263
270
  /** The line, and (from 1) the line it replaces; 0 or none adds it. */
264
271
  readonly lead_about_you: z.ZodObject<{
265
272
  line: z.ZodString;
@@ -295,6 +302,8 @@ export declare const SHARED_ACTION_VERSION = 1;
295
302
  export declare const sharedActionSchema: z.ZodObject<{
296
303
  version: z.ZodLiteral<1>;
297
304
  operation: z.ZodEnum<{
305
+ code_map_holdout_end: "code_map_holdout_end";
306
+ code_map_switch: "code_map_switch";
298
307
  decision_record: "decision_record";
299
308
  decision_retire: "decision_retire";
300
309
  flow_browser: "flow_browser";
@@ -85,6 +85,8 @@ export const chatActionRequestSchemas = {
85
85
  suggestion_not_now: z.strictObject({ repo: words, suggestion: words }),
86
86
  // Slice 3: stopping a trial (starting one stays in Settings → Models, behind a step-up).
87
87
  trial_stop: z.strictObject({ repo: words, trial: id }),
88
+ code_map_switch: z.strictObject({ repo: words, on: z.boolean() }),
89
+ code_map_holdout_end: z.strictObject({ repo: words }),
88
90
  /** The line, and (from 1) the line it replaces; 0 or none adds it. */
89
91
  lead_about_you: z.strictObject({ line: words, replaces: z.int().min(0).optional() }),
90
92
  scope_approve: z.strictObject({ task: words }),
@@ -566,6 +566,58 @@ export declare const COMMAND_OUTPUTS: {
566
566
  reason: z.ZodString;
567
567
  message: z.ZodOptional<z.ZodString>;
568
568
  }, z.core.$loose>], "ok">;
569
+ readonly "task ready": z.ZodDiscriminatedUnion<[z.ZodObject<{
570
+ envelopeVersion: z.ZodLiteral<1>;
571
+ ok: z.ZodLiteral<true>;
572
+ command: z.ZodLiteral<"task ready">;
573
+ taskId: z.ZodString;
574
+ activeTaskId: z.ZodString;
575
+ run: z.ZodNullable<z.ZodNumber>;
576
+ head: z.ZodNullable<z.ZodString>;
577
+ answer: z.ZodEnum<{
578
+ merged: "merged";
579
+ no: "no";
580
+ "not-yet": "not-yet";
581
+ yes: "yes";
582
+ }>;
583
+ sentence: z.ZodString;
584
+ rows: z.ZodArray<z.ZodObject<{
585
+ key: z.ZodEnum<{
586
+ build: "build";
587
+ checks: "checks";
588
+ comments: "comments";
589
+ conflicts: "conflicts";
590
+ plan: "plan";
591
+ "pr-ci": "pr-ci";
592
+ "pull-request": "pull-request";
593
+ release: "release";
594
+ requirements: "requirements";
595
+ "up-to-date": "up-to-date";
596
+ }>;
597
+ label: z.ZodString;
598
+ mark: z.ZodEnum<{
599
+ no: "no";
600
+ skip: "skip";
601
+ unknown: "unknown";
602
+ waiting: "waiting";
603
+ yes: "yes";
604
+ }>;
605
+ say: z.ZodString;
606
+ act: z.ZodNullable<z.ZodObject<{
607
+ kind: z.ZodEnum<{
608
+ fix: "fix";
609
+ retry: "retry";
610
+ "run-checks": "run-checks";
611
+ }>;
612
+ }, z.core.$loose>>;
613
+ }, z.core.$loose>>;
614
+ }, z.core.$loose>, z.ZodObject<{
615
+ envelopeVersion: z.ZodLiteral<1>;
616
+ ok: z.ZodLiteral<false>;
617
+ command: z.ZodLiteral<"task ready">;
618
+ reason: z.ZodString;
619
+ message: z.ZodOptional<z.ZodString>;
620
+ }, z.core.$loose>], "ok">;
569
621
  readonly "task hold": z.ZodDiscriminatedUnion<[z.ZodObject<{
570
622
  envelopeVersion: z.ZodLiteral<1>;
571
623
  ok: z.ZodLiteral<true>;
@@ -183,6 +183,10 @@ export const COMMAND_OUTPUTS = {
183
183
  // `status` is the observation's own answer (the one merges read); the rows are each check's timing.
184
184
  "task ship": answers("task ship", { task: str, pullRequest: int, githubRepo: str, head: str, observedAt: str, status: z.enum(["passing", "failing", "running", "none"]),
185
185
  checks: rows({ name: str, workflow: nullable(str), state: z.enum(["queued", "running", "passed", "failed", "skipped", "cancelled", "neutral"]), startedAt: nullable(str), completedAt: nullable(str), url: nullable(str), kind: z.enum(["run", "status"]), seconds: nullable(int) }) }),
186
+ // "Can I merge?" (merge-checklist.ts): the answer, its sentence, and one row per fact, each blocker with its act.
187
+ "task ready": answers("task ready", { taskId: str, activeTaskId: str, run: nullable(int), head: nullable(str), answer: z.enum(["yes", "no", "not-yet", "merged"]), sentence: str,
188
+ rows: rows({ key: z.enum(["plan", "build", "checks", "requirements", "pull-request", "pr-ci", "up-to-date", "conflicts", "comments", "release"]), label: str,
189
+ mark: z.enum(["yes", "no", "waiting", "unknown", "skip"]), say: str, act: nullable(z.looseObject({ kind: z.enum(["run-checks", "retry", "fix"]) })) }) }),
186
190
  "task hold": answers("task hold", { id: str, reason: str, until: nullable(str) }),
187
191
  "task unhold": answers("task unhold", { id: str }),
188
192
  "task wait": answers("task wait", { task: str, outcome: str }),
@@ -98,7 +98,7 @@ export declare const CONSOLE_FORMS: {
98
98
  readonly signIn: FormContract<"action" | "client-id" | "client-secret" | "groups-claim" | "issuer" | "label" | "password" | "passwords" | "scopes" | `group-${string}` | `projects-${string}` | `role-${string}`>;
99
99
  readonly updates: FormContract<"password" | "version" | "when">;
100
100
  readonly retention: FormContract<"chat" | "checkouts" | "evidence" | "notifications" | "password">;
101
- readonly storage: FormContract<"cleanup" | "password" | "path" | "preview">;
101
+ readonly storage: FormContract<"cleanup" | "idle" | "password" | "path" | "preview">;
102
102
  readonly pullRequests: FormContract<"act" | "base" | "github" | "method" | "password" | "repo" | "when-green">;
103
103
  readonly checks: FormContract<"act" | "command" | "level" | "on" | "password" | "repo" | "timeout">;
104
104
  readonly backups: FormContract<"every" | "folder" | "keep" | "live_every" | "live_folder" | "password">;
@@ -115,6 +115,8 @@ export declare const CONSOLE_FORMS: {
115
115
  readonly modelsRules: FormContract<"id" | "password" | "rule" | "scope">;
116
116
  readonly knowledgeRefresh: FormContract<"repo">;
117
117
  readonly knowledgeProposal: FormContract<"decision" | "proposal" | "repo">;
118
+ readonly knowledgeNote: FormContract<"claim" | "decision" | "proposal" | "repo">;
119
+ readonly knowledgeTidy: FormContract<"action" | "choice" | "claim" | "proposal" | "repo" | "revision" | "target" | "why">;
118
120
  readonly knowledgeDecision: FormContract<"action" | "claim" | "decision" | "reason" | "repo" | "why">;
119
121
  readonly knowledgeChange: FormContract<"action" | "content" | "id" | "identity" | "instructions" | "path" | "repo" | "restore" | "revision" | "title">;
120
122
  readonly learningChange: FormContract<"action" | "identity" | "lesson" | "repo" | "revision" | "sha" | "version">;
@@ -140,7 +142,12 @@ export declare const CONSOLE_FORMS: {
140
142
  readonly projectsSelect: FormContract<"path" | "return">;
141
143
  readonly projectsRemove: FormContract<"repo">;
142
144
  readonly projectsOpen: FormContract<"path" | "return">;
143
- readonly tasksAdd: FormContract<"acceptance" | "after" | "goal" | "id" | "not" | "permission-mode" | "plan-first" | "planning-policy" | "projectRevision" | "quality-mode" | "repo" | "scout" | "title" | "touches">;
145
+ readonly codeMap: FormContract<"on" | "repo">;
146
+ readonly codeMapRefresh: FormContract<never>;
147
+ readonly tasksFast: FormContract<"digest" | "preview">;
148
+ readonly keepGrown: FormContract<never>;
149
+ readonly releaseGoOn: FormContract<"password">;
150
+ readonly tasksAdd: FormContract<"acceptance" | "after" | "from" | "from-head" | "from-read" | "goal" | "id" | "not" | "permission-mode" | "plain" | "plan-first" | "planning-policy" | "projectRevision" | "quality-mode" | "repo" | "scout" | "title" | "touches">;
144
151
  readonly queue: FormContract<"before" | "column" | "from" | "note" | "projectRevision" | "queueRevision" | "respond" | "runner" | "task">;
145
152
  readonly runnerRegister: FormContract<"capacity" | "name" | "token">;
146
153
  readonly mode: FormContract<"auto-approve" | "chat-approve" | "days" | "digest" | "expiry" | "name" | "nonce" | "plan-auto" | "publication" | "repair-auto" | "review-auto" | "review-retry-auto" | "token">;
@@ -150,7 +157,7 @@ export declare const CONSOLE_FORMS: {
150
157
  readonly peopleRevoke: FormContract<"name" | "token">;
151
158
  readonly runnerRetire: FormContract<"name" | "token">;
152
159
  readonly decisionAnswer: FormContract<"choice" | "confirm" | "note" | "return">;
153
- readonly confirmStopped: FormContract<"checked" | "return" | "run" | "token">;
160
+ readonly recheckStopped: FormContract<"return" | "run">;
154
161
  readonly merge: FormContract<"anyway" | "run" | "token">;
155
162
  readonly onboard: FormContract<"big-ok" | "nonce" | "repo" | "root" | "token">;
156
163
  readonly pushSubscribe: FormContract<"auth" | "endpoint" | "p256dh" | "token">;
@@ -198,8 +205,8 @@ export declare const CONSOLE_FORMS: {
198
205
  readonly taskRevision: FormContract<"revision-id" | "token">;
199
206
  readonly taskCancel: FormContract<"reason">;
200
207
  readonly taskRoute: FormContract<"agent" | "clear-phase" | "model" | "phase" | "provider" | "return" | "risky" | "sawDigest" | "size">;
201
- readonly taskScope: FormContract<"acceptance" | "budget-microusd" | "budget-usd" | "goal" | "goal-brief" | "not" | "permission-mode" | "quality-mode" | "requirement" | "requirement-new" | "sawDigest" | "touches">;
202
- readonly taskApprove: FormContract<"digest" | "nonce" | "rebuild" | "return" | "token">;
208
+ readonly taskScope: FormContract<"acceptance" | "budget-microusd" | "budget-usd" | "goal" | "goal-brief" | "not" | "permission-mode" | "quality-mode" | "requirement" | "requirement-how" | "requirement-new" | "requirement-new-how" | "sawDigest" | "touches">;
209
+ readonly taskApprove: FormContract<"digest" | "nonce" | "rebuild" | "return" | "sheet" | "token">;
203
210
  readonly taskAcceptProof: FormContract<"note" | "return" | "run">;
204
211
  readonly taskComplete: FormContract<"accept" | "note" | "publish" | "receipt" | "return" | "run">;
205
212
  readonly taskStop: FormContract<"return" | "run">;
@@ -107,7 +107,7 @@ export const CONSOLE_FORMS = {
107
107
  signIn: formContract("POST /settings/sign-in", ["action", "password", "issuer", "client-id", "client-secret", "label", "scopes", "groups-claim", "passwords"], { prefixes: ["group-", "role-", "projects-"] }),
108
108
  updates: formContract("POST /settings/updates, /settings/updates/seen, /settings/updates/cancel", ["password", "version", "when"]),
109
109
  retention: formContract("POST /settings/retention", ["password", "evidence", "checkouts", "chat", "notifications"]),
110
- storage: formContract("POST /settings/storage, /settings/storage/clean, /settings/storage/discard", ["password", "cleanup", "preview", "path"]),
110
+ storage: formContract("POST /settings/storage, /settings/storage/clean, /settings/storage/discard", ["password", "cleanup", "idle", "preview", "path"]),
111
111
  pullRequests: formContract("POST /settings/pull-requests", ["repo", "act", "password", "github", "base", "method", "when-green"]),
112
112
  checks: formContract("POST /settings/checks", ["repo", "password", "act", "on", "level", "command", "timeout"]),
113
113
  backups: formContract("POST /settings/backups", ["password", "every", "keep", "folder", "live_every", "live_folder"]),
@@ -129,6 +129,8 @@ export const CONSOLE_FORMS = {
129
129
  modelsRules: formContract("POST /settings/models/accept, /settings/models/keep, /settings/models/suggest-on, /settings/models/turn-off, /settings/models/not-now, /settings/models/suggest-off, /settings/models/try, /settings/models/stop", ["password", "scope", "id", "rule"]),
130
130
  knowledgeRefresh: formContract("POST /settings/knowledge/refresh", ["repo"]),
131
131
  knowledgeProposal: formContract("POST /settings/knowledge/proposal", ["repo", "decision", "proposal"]),
132
+ knowledgeNote: formContract("POST /settings/knowledge/note", ["repo", "decision", "proposal", "claim"]),
133
+ knowledgeTidy: formContract("POST /settings/knowledge/tidy", ["repo", "action", "proposal", "choice", "claim", "why", "target", "revision"]),
132
134
  knowledgeDecision: formContract("POST /settings/knowledge/decision", ["repo", "action", "claim", "why", "decision", "reason"]),
133
135
  knowledgeChange: formContract("POST /settings/knowledge/change", ["repo", "action", "identity", "revision", "instructions", "title", "content", "path", "id", "restore"]),
134
136
  learningChange: formContract("POST /settings/learning/change", ["repo", "action", "identity", "revision", "lesson", "version", "sha"]),
@@ -155,7 +157,12 @@ export const CONSOLE_FORMS = {
155
157
  projectsSelect: formContract("POST /projects/select", ["path", "return"]),
156
158
  projectsRemove: formContract("POST /projects/remove", ["repo"]),
157
159
  projectsOpen: formContract("POST /projects/open", ["path", "return"]),
158
- tasksAdd: formContract("POST /tasks/add", ["projectRevision", "id", "title", "repo", "goal", "not", "touches", "scout", "permission-mode", "quality-mode", "after", "acceptance", "planning-policy", "plan-first"]),
160
+ codeMap: formContract("POST /settings/project/code-map, /settings/project/code-map/holdout", ["repo", "on"]),
161
+ codeMapRefresh: formContract("POST /projects/<project>/map/refresh", []),
162
+ tasksFast: formContract("POST /tasks/fast", ["preview", "digest"]),
163
+ keepGrown: formContract("POST /t/<id>/keep-grown", []),
164
+ releaseGoOn: formContract("POST /t/<id>/release/go-on", ["password"]),
165
+ tasksAdd: formContract("POST /tasks/add", ["projectRevision", "plain", "from", "from-head", "from-read", "id", "title", "repo", "goal", "not", "touches", "scout", "permission-mode", "quality-mode", "after", "acceptance", "planning-policy", "plan-first"]),
159
166
  queue: formContract("POST /queue/move, /queue/note", ["runner", "note", "from", "projectRevision", "respond", "task", "column", "before", "queueRevision"]),
160
167
  runnerRegister: formContract("POST /fleet/runner/register", ["token", "name", "capacity"]),
161
168
  mode: formContract("POST /mode/confirm, /mode/sign", ["name", "days", "auto-approve", "plan-auto", "chat-approve", "publication", "expiry", "nonce", "digest", "token", "review-auto", "review-retry-auto", "repair-auto"]),
@@ -165,7 +172,7 @@ export const CONSOLE_FORMS = {
165
172
  peopleRevoke: formContract("POST /people/revoke", ["token", "name"]),
166
173
  runnerRetire: formContract("POST /fleet/runner/retire", ["token", "name"]),
167
174
  decisionAnswer: formContract("POST /d/<id>/answer", ["return", "choice", "confirm", "note"]),
168
- confirmStopped: formContract("POST /t/<task>/confirm-stopped", ["run", "token", "checked", "return"]),
175
+ recheckStopped: formContract("POST /t/<task>/recheck-stopped", ["run", "return"]),
169
176
  merge: formContract("POST /t/<task>/merge", ["run", "token", "anyway"]),
170
177
  onboard: formContract("POST /projects/onboard-preview, /projects/onboard-confirm", ["repo", "root", "token", "nonce", "big-ok"]),
171
178
  pushSubscribe: formContract("POST /push/subscribe", ["token", "endpoint", "p256dh", "auth"]),
@@ -216,8 +223,8 @@ export const CONSOLE_FORMS = {
216
223
  taskRevision: formContract("POST /t/<task>/accept-revision|reject-revision", ["revision-id", "token"]),
217
224
  taskCancel: formContract("POST /t/<task>/cancel", ["reason"]),
218
225
  taskRoute: formContract("POST /t/<task>/route", ["size", "risky", "phase", "clear-phase", "agent", "provider", "model", "sawDigest", "return"]),
219
- taskScope: formContract("POST /t/<task>/scope", ["sawDigest", "permission-mode", "quality-mode", "budget-usd", "budget-microusd", "requirement", "requirement-new", "goal-brief", "goal", "not", "touches", "acceptance"]),
220
- taskApprove: formContract("POST /t/<task>/approve", ["return", "digest", "token", "nonce", "rebuild"]),
226
+ taskScope: formContract("POST /t/<task>/scope", ["sawDigest", "permission-mode", "quality-mode", "budget-usd", "budget-microusd", "requirement", "requirement-new", "requirement-how", "requirement-new-how", "goal-brief", "goal", "not", "touches", "acceptance"]),
227
+ taskApprove: formContract("POST /t/<task>/approve", ["return", "digest", "token", "nonce", "rebuild", "sheet"]),
221
228
  taskAcceptProof: formContract("POST /t/<task>/accept-proof", ["run", "note", "return"]),
222
229
  taskComplete: formContract("POST /t/<task>/complete", ["receipt", "run", "accept", "note", "publish", "return"]),
223
230
  taskStop: formContract("POST /t/<task>/stop", ["run", "return"]),