@visual-z/xeno 0.8.4 → 0.9.1

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 (350) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +31 -746
  3. package/bin/xeno.exe +26 -0
  4. package/install.cjs +88 -0
  5. package/package.json +18 -48
  6. package/docs/app-server.md +0 -115
  7. package/docs/foundations.md +0 -88
  8. package/docs/install.md +0 -244
  9. package/docs/providers.md +0 -172
  10. package/docs/research/foundation-clients-2026-10-09.md +0 -27
  11. package/docs/research/foundation-editing-2026-10-09.md +0 -56
  12. package/docs/research/foundation-network-2026-10-09.md +0 -37
  13. package/docs/research/foundation-workflow-2026-10-09.md +0 -37
  14. package/src/apps/acp-server.ts +0 -41
  15. package/src/apps/agent-profiles.ts +0 -19
  16. package/src/apps/app-server.ts +0 -30
  17. package/src/apps/approve.ts +0 -49
  18. package/src/apps/cli.ts +0 -99
  19. package/src/apps/commands.ts +0 -319
  20. package/src/apps/completions.ts +0 -87
  21. package/src/apps/control-transport.ts +0 -90
  22. package/src/apps/export-cli.ts +0 -24
  23. package/src/apps/goal-flow.ts +0 -70
  24. package/src/apps/image-draft.ts +0 -46
  25. package/src/apps/input.ts +0 -312
  26. package/src/apps/model-catalog.ts +0 -69
  27. package/src/apps/preferences.ts +0 -20
  28. package/src/apps/project-trust.ts +0 -9
  29. package/src/apps/provider-connect.ts +0 -346
  30. package/src/apps/question-ui.ts +0 -27
  31. package/src/apps/render.ts +0 -181
  32. package/src/apps/resources.ts +0 -66
  33. package/src/apps/review.ts +0 -109
  34. package/src/apps/session-hub.ts +0 -96
  35. package/src/apps/session-lifecycle.ts +0 -64
  36. package/src/apps/shell.ts +0 -2071
  37. package/src/apps/startup-input.ts +0 -135
  38. package/src/apps/startup.ts +0 -1677
  39. package/src/apps/ui/app.ts +0 -1818
  40. package/src/apps/ui/control-model.ts +0 -43
  41. package/src/apps/ui/extension-host.ts +0 -194
  42. package/src/apps/ui/image-cache.ts +0 -121
  43. package/src/apps/ui/palette.ts +0 -88
  44. package/src/apps/ui/project.ts +0 -127
  45. package/src/apps/ui/standalone.ts +0 -148
  46. package/src/apps/ui/status-model.ts +0 -68
  47. package/src/apps/ui/surface.ts +0 -91
  48. package/src/apps/ui/theme.ts +0 -86
  49. package/src/apps/update-check.ts +0 -112
  50. package/src/apps/workflow.ts +0 -58
  51. package/src/core/acceptance.ts +0 -139
  52. package/src/core/action-defaults.ts +0 -58
  53. package/src/core/activity-status.ts +0 -52
  54. package/src/core/agent-profile.ts +0 -130
  55. package/src/core/attachment-input.ts +0 -17
  56. package/src/core/blind-checks.ts +0 -72
  57. package/src/core/boundary.ts +0 -174
  58. package/src/core/branch-receipt.ts +0 -26
  59. package/src/core/catalog-merge.ts +0 -19
  60. package/src/core/checkpoints.ts +0 -85
  61. package/src/core/churn.ts +0 -61
  62. package/src/core/command-artifacts.ts +0 -94
  63. package/src/core/command-contract.ts +0 -151
  64. package/src/core/completion-action.ts +0 -20
  65. package/src/core/completion-review.ts +0 -98
  66. package/src/core/context-detail.ts +0 -37
  67. package/src/core/context-policy.ts +0 -133
  68. package/src/core/context-window.ts +0 -54
  69. package/src/core/continuation.ts +0 -130
  70. package/src/core/control-protocol.ts +0 -69
  71. package/src/core/control-view.ts +0 -39
  72. package/src/core/cycle.ts +0 -17
  73. package/src/core/data-query.ts +0 -201
  74. package/src/core/deadline.ts +0 -40
  75. package/src/core/derive.ts +0 -516
  76. package/src/core/draft-images.ts +0 -56
  77. package/src/core/edit-plan.ts +0 -200
  78. package/src/core/environment.ts +0 -181
  79. package/src/core/event-validation.ts +0 -220
  80. package/src/core/events.ts +0 -670
  81. package/src/core/evidence-ladder.ts +0 -62
  82. package/src/core/evidence-links.ts +0 -68
  83. package/src/core/evidence.ts +0 -240
  84. package/src/core/execution.ts +0 -18
  85. package/src/core/exhaustive.ts +0 -18
  86. package/src/core/export.ts +0 -179
  87. package/src/core/extension-flags.ts +0 -14
  88. package/src/core/external-effects.ts +0 -488
  89. package/src/core/failure-recovery.ts +0 -173
  90. package/src/core/file-precondition.ts +0 -78
  91. package/src/core/file-references.ts +0 -35
  92. package/src/core/frontmatter.ts +0 -213
  93. package/src/core/git-status.ts +0 -29
  94. package/src/core/goal-view.ts +0 -64
  95. package/src/core/goal.ts +0 -200
  96. package/src/core/handoff.ts +0 -479
  97. package/src/core/history-query.ts +0 -139
  98. package/src/core/history.ts +0 -237
  99. package/src/core/image-limits.ts +0 -72
  100. package/src/core/images.ts +0 -46
  101. package/src/core/integration-health.ts +0 -34
  102. package/src/core/json-event.ts +0 -131
  103. package/src/core/json-pointer.ts +0 -46
  104. package/src/core/keymap.ts +0 -153
  105. package/src/core/lsp-framing.ts +0 -34
  106. package/src/core/mcp-config.ts +0 -68
  107. package/src/core/model-picker.ts +0 -64
  108. package/src/core/model-preferences.ts +0 -28
  109. package/src/core/model-replay.ts +0 -22
  110. package/src/core/model-scope.ts +0 -53
  111. package/src/core/note-guidance.ts +0 -34
  112. package/src/core/oauth-fields.ts +0 -44
  113. package/src/core/observation-view.ts +0 -152
  114. package/src/core/outcome.ts +0 -44
  115. package/src/core/overflow.ts +0 -59
  116. package/src/core/packages.ts +0 -47
  117. package/src/core/patch.ts +0 -130
  118. package/src/core/path-suggestions.ts +0 -59
  119. package/src/core/permissions.ts +0 -410
  120. package/src/core/process-receipt.ts +0 -105
  121. package/src/core/process-state.ts +0 -37
  122. package/src/core/program-status.ts +0 -40
  123. package/src/core/progress.ts +0 -150
  124. package/src/core/project.ts +0 -431
  125. package/src/core/prompt-policy.ts +0 -46
  126. package/src/core/prompts.ts +0 -111
  127. package/src/core/question.ts +0 -94
  128. package/src/core/queue.ts +0 -78
  129. package/src/core/read-bounds.ts +0 -24
  130. package/src/core/read-observation.ts +0 -106
  131. package/src/core/recovery-sources.ts +0 -74
  132. package/src/core/regex-dialect.ts +0 -25
  133. package/src/core/request-budget.ts +0 -23
  134. package/src/core/request-progress.ts +0 -87
  135. package/src/core/request-state.ts +0 -273
  136. package/src/core/resource-selection.ts +0 -36
  137. package/src/core/response-recovery.ts +0 -59
  138. package/src/core/resume.ts +0 -59
  139. package/src/core/review.ts +0 -68
  140. package/src/core/ripgrep-release.ts +0 -64
  141. package/src/core/runtime-target.ts +0 -94
  142. package/src/core/scheduling.ts +0 -8
  143. package/src/core/search-observation.ts +0 -97
  144. package/src/core/secrets.ts +0 -188
  145. package/src/core/session-archive.ts +0 -4
  146. package/src/core/session-browser.ts +0 -82
  147. package/src/core/session-entry.ts +0 -61
  148. package/src/core/session-exit.ts +0 -42
  149. package/src/core/session-format.ts +0 -169
  150. package/src/core/session-position.ts +0 -26
  151. package/src/core/session-tree.ts +0 -147
  152. package/src/core/settings.ts +0 -192
  153. package/src/core/skills.ts +0 -165
  154. package/src/core/sql-query.ts +0 -211
  155. package/src/core/stall.ts +0 -128
  156. package/src/core/stop-report.ts +0 -68
  157. package/src/core/text-normalize.ts +0 -81
  158. package/src/core/theme.ts +0 -166
  159. package/src/core/thinking.ts +0 -26
  160. package/src/core/tokens.ts +0 -151
  161. package/src/core/tool-contract.ts +0 -153
  162. package/src/core/tool-discovery.ts +0 -87
  163. package/src/core/tool-failure.ts +0 -81
  164. package/src/core/tool-input.ts +0 -44
  165. package/src/core/transcript.ts +0 -108
  166. package/src/core/tui-mode.ts +0 -29
  167. package/src/core/update-check.ts +0 -157
  168. package/src/core/version.ts +0 -55
  169. package/src/core/web-content.ts +0 -80
  170. package/src/core/work-state.ts +0 -154
  171. package/src/core/workflow-mode.ts +0 -34
  172. package/src/core/working-set.ts +0 -119
  173. package/src/core/yaml.ts +0 -282
  174. package/src/runtime/acp-agent.ts +0 -289
  175. package/src/runtime/agent-profiles.ts +0 -21
  176. package/src/runtime/auth.ts +0 -367
  177. package/src/runtime/blind-check.ts +0 -36
  178. package/src/runtime/branch-jobs.ts +0 -36
  179. package/src/runtime/catalog.ts +0 -285
  180. package/src/runtime/clipboard.ts +0 -125
  181. package/src/runtime/command-artifacts.ts +0 -64
  182. package/src/runtime/connection-reuse.ts +0 -40
  183. package/src/runtime/control-client.ts +0 -149
  184. package/src/runtime/control-connection.ts +0 -139
  185. package/src/runtime/counter-check.ts +0 -74
  186. package/src/runtime/environment-tools.ts +0 -60
  187. package/src/runtime/evidence-notes.ts +0 -35
  188. package/src/runtime/exec-sessions.ts +0 -456
  189. package/src/runtime/execution-control.ts +0 -40
  190. package/src/runtime/extension-loader.ts +0 -37
  191. package/src/runtime/extension-ui.ts +0 -47
  192. package/src/runtime/extensions.ts +0 -370
  193. package/src/runtime/formatter-tool.ts +0 -38
  194. package/src/runtime/formatter.ts +0 -50
  195. package/src/runtime/foundation.ts +0 -116
  196. package/src/runtime/history-cache.ts +0 -57
  197. package/src/runtime/history-tools.ts +0 -168
  198. package/src/runtime/images.ts +0 -80
  199. package/src/runtime/integrations.ts +0 -216
  200. package/src/runtime/loop.ts +0 -3108
  201. package/src/runtime/lsp-client.ts +0 -201
  202. package/src/runtime/mcp-client.ts +0 -264
  203. package/src/runtime/mcp-oauth.ts +0 -168
  204. package/src/runtime/mcp-schema.ts +0 -51
  205. package/src/runtime/model-config.ts +0 -232
  206. package/src/runtime/model-data.LICENSE +0 -27
  207. package/src/runtime/model-data.ts +0 -21
  208. package/src/runtime/model-discovery.ts +0 -208
  209. package/src/runtime/model-replay.ts +0 -10
  210. package/src/runtime/model-scope.ts +0 -20
  211. package/src/runtime/model-sources.ts +0 -151
  212. package/src/runtime/model.ts +0 -686
  213. package/src/runtime/models-fetch.ts +0 -57
  214. package/src/runtime/oauth-device.ts +0 -86
  215. package/src/runtime/oauth.ts +0 -853
  216. package/src/runtime/open-image.ts +0 -42
  217. package/src/runtime/openai-responses.ts +0 -304
  218. package/src/runtime/openai.ts +0 -697
  219. package/src/runtime/output-schema.ts +0 -26
  220. package/src/runtime/package-manager.ts +0 -125
  221. package/src/runtime/patch-tool.ts +0 -38
  222. package/src/runtime/permission-resources.ts +0 -18
  223. package/src/runtime/pi-provider.ts +0 -423
  224. package/src/runtime/program-status.ts +0 -38
  225. package/src/runtime/progress.ts +0 -26
  226. package/src/runtime/provider-access.ts +0 -21
  227. package/src/runtime/provider-connect.ts +0 -109
  228. package/src/runtime/provider-presets.ts +0 -98
  229. package/src/runtime/provider-protocols.ts +0 -29
  230. package/src/runtime/provider-request.ts +0 -87
  231. package/src/runtime/provider-tool-policy.ts +0 -33
  232. package/src/runtime/provider.ts +0 -580
  233. package/src/runtime/question-tool.ts +0 -62
  234. package/src/runtime/retry.ts +0 -447
  235. package/src/runtime/review-tool.ts +0 -48
  236. package/src/runtime/rich-text.ts +0 -36
  237. package/src/runtime/runtime-target.ts +0 -149
  238. package/src/runtime/secrets.ts +0 -79
  239. package/src/runtime/self-update.ts +0 -74
  240. package/src/runtime/session-control.ts +0 -265
  241. package/src/runtime/session-hub.ts +0 -85
  242. package/src/runtime/stream-idle.ts +0 -80
  243. package/src/runtime/tool-broker.ts +0 -137
  244. package/src/runtime/tool-discovery.ts +0 -58
  245. package/src/runtime/tool-queue.ts +0 -77
  246. package/src/runtime/tools.ts +0 -1820
  247. package/src/runtime/turn-effects.ts +0 -31
  248. package/src/runtime/update-check.ts +0 -48
  249. package/src/runtime/usage.ts +0 -253
  250. package/src/runtime/version.ts +0 -12
  251. package/src/runtime/web-http.ts +0 -56
  252. package/src/runtime/web-tools.ts +0 -88
  253. package/src/runtime/working-notes.ts +0 -60
  254. package/src/runtime/workspace-lifecycle.ts +0 -141
  255. package/src/store/acceptance.ts +0 -15
  256. package/src/store/agent-profile-files.ts +0 -13
  257. package/src/store/atomic.ts +0 -31
  258. package/src/store/auth-store.ts +0 -50
  259. package/src/store/blind-checks.ts +0 -32
  260. package/src/store/blobs.ts +0 -143
  261. package/src/store/checkpoint-history.ts +0 -32
  262. package/src/store/command-artifacts.ts +0 -96
  263. package/src/store/command.ts +0 -111
  264. package/src/store/config.ts +0 -69
  265. package/src/store/context-files.ts +0 -436
  266. package/src/store/file-checkpoints.ts +0 -108
  267. package/src/store/file-index.ts +0 -174
  268. package/src/store/foundation-config.ts +0 -24
  269. package/src/store/git-checkpoints.ts +0 -67
  270. package/src/store/git-status.ts +0 -15
  271. package/src/store/history-search.ts +0 -19
  272. package/src/store/log.ts +0 -725
  273. package/src/store/lsp-document.ts +0 -30
  274. package/src/store/mcp-credentials.ts +0 -85
  275. package/src/store/models.ts +0 -60
  276. package/src/store/observations.ts +0 -224
  277. package/src/store/packages.ts +0 -98
  278. package/src/store/patch.ts +0 -92
  279. package/src/store/permission-paths.ts +0 -34
  280. package/src/store/process-receipts.ts +0 -34
  281. package/src/store/query-source.ts +0 -49
  282. package/src/store/recovery.ts +0 -44
  283. package/src/store/replay.ts +0 -76
  284. package/src/store/resource-index.ts +0 -170
  285. package/src/store/resources.ts +0 -285
  286. package/src/store/restore.ts +0 -110
  287. package/src/store/review.ts +0 -109
  288. package/src/store/runtime-target.ts +0 -64
  289. package/src/store/search.ts +0 -728
  290. package/src/store/secrets.ts +0 -44
  291. package/src/store/serial.ts +0 -13
  292. package/src/store/session-archive.ts +0 -165
  293. package/src/store/session-browser.ts +0 -68
  294. package/src/store/session-position.ts +0 -27
  295. package/src/store/sessions.ts +0 -525
  296. package/src/store/source-cancellation.ts +0 -6
  297. package/src/store/tool-binaries.ts +0 -90
  298. package/src/store/trust.ts +0 -82
  299. package/src/store/update-cache.ts +0 -36
  300. package/src/store/user-settings.ts +0 -30
  301. package/src/store/workspace-clone.ts +0 -42
  302. package/src/store/workspace-observation.ts +0 -113
  303. package/src/store/workspace.ts +0 -115
  304. package/src/store/worktree.ts +0 -278
  305. package/src/tui/ansi-spans.ts +0 -126
  306. package/src/tui/diff.ts +0 -160
  307. package/src/tui/exhaustive.ts +0 -14
  308. package/src/tui/fuzzy.ts +0 -75
  309. package/src/tui/style.ts +0 -208
  310. package/src/tui/ui/atomic-draft.ts +0 -121
  311. package/src/tui/ui/branch-panel.ts +0 -47
  312. package/src/tui/ui/canvas.ts +0 -96
  313. package/src/tui/ui/colors.ts +0 -70
  314. package/src/tui/ui/composer.ts +0 -199
  315. package/src/tui/ui/control-panel.ts +0 -25
  316. package/src/tui/ui/control-sidebar.ts +0 -49
  317. package/src/tui/ui/dialog.ts +0 -398
  318. package/src/tui/ui/draft.ts +0 -23
  319. package/src/tui/ui/duration.ts +0 -16
  320. package/src/tui/ui/edge.ts +0 -186
  321. package/src/tui/ui/image-size.ts +0 -44
  322. package/src/tui/ui/ink.ts +0 -210
  323. package/src/tui/ui/items.ts +0 -42
  324. package/src/tui/ui/keys.ts +0 -40
  325. package/src/tui/ui/layout.ts +0 -74
  326. package/src/tui/ui/list-model.ts +0 -86
  327. package/src/tui/ui/list-view.ts +0 -172
  328. package/src/tui/ui/mascot.ts +0 -67
  329. package/src/tui/ui/messages.ts +0 -518
  330. package/src/tui/ui/motion.ts +0 -28
  331. package/src/tui/ui/paths.ts +0 -10
  332. package/src/tui/ui/pictures.ts +0 -108
  333. package/src/tui/ui/prompt-history.ts +0 -60
  334. package/src/tui/ui/screen.ts +0 -579
  335. package/src/tui/ui/status.ts +0 -403
  336. package/src/tui/ui/styled.ts +0 -85
  337. package/src/tui/ui/tool-card.ts +0 -150
  338. package/src/tui/width.ts +0 -567
  339. package/src/vendor/pi-rich/LICENSE.upstream +0 -21
  340. package/src/vendor/pi-rich/UPSTREAM.json +0 -17
  341. package/src/vendor/pi-rich/clipboard-command.ts +0 -44
  342. package/src/vendor/pi-rich/exif-orientation.ts +0 -182
  343. package/src/vendor/pi-rich/html.ts +0 -51
  344. package/src/vendor/pi-rich/image-convert.ts +0 -49
  345. package/src/vendor/pi-rich/image-resize-core.ts +0 -164
  346. package/src/vendor/pi-rich/image-resize-worker.ts +0 -42
  347. package/src/vendor/pi-rich/image-resize.ts +0 -123
  348. package/src/vendor/pi-rich/mime.ts +0 -116
  349. package/src/vendor/pi-rich/photon.ts +0 -139
  350. package/src/vendor/pi-rich/syntax-highlight.ts +0 -214
@@ -1,273 +0,0 @@
1
- /**
2
- * The request ledger (S.1, Q23 / D8 / D11): what the current request has
3
- * evidence for, derived only from the selected log ancestry.
4
- *
5
- * Every view of "is this request done" -- the completion check, the goal
6
- * panel, terminal status, branch receipts -- maps from this one derivation,
7
- * so they cannot disagree. Only harness-observed facts move it: command
8
- * receipts with their workspace fingerprints, file versions, and the user's
9
- * acceptance decisions. A model note can describe a blocker; it never changes
10
- * the status.
11
- */
12
- import type { LogEvent } from "./events.ts";
13
- import { counterVerdict, deriveAcceptance, type AcceptanceState, type CounterVerdict } from "./acceptance.ts";
14
- import { changedPaths, workspaceChanged } from "./checkpoints.ts";
15
- import { evidenceStale, isCommandResult, resolveEvidence } from "./evidence.ts";
16
- import { executionOutcome, modelOutcome, type ExecutionOutcome } from "./outcome.ts";
17
- import { deriveWorkItems, type WorkItem } from "./work-state.ts";
18
- import { deriveRequestProgress, type RequestProgress, type RequestRecord, type RequestSource } from "./request-progress.ts";
19
-
20
- export type RequestPart = { node: number; events: readonly LogEvent[] };
21
- export type RequestCommand = {
22
- source: RequestSource; command: string; exit: number; status: "pass" | "fail" | "stale" | "superseded";
23
- gate?: string; workspace_sha256?: string; workspace_scope?: "source-v1"; criteria: string[];
24
- };
25
- export type RequestArtifact = { source: RequestSource; path: string; sha256: string | null;
26
- kind: "read" | "edit" | "checkpoint" | "command_output" };
27
-
28
- /**
29
- * One requirement this request must meet. With a declared contract these are
30
- * its criteria. Without one, a request that changed the workspace still has
31
- * one requirement: a successful check after its last change.
32
- */
33
- export type RequestCriterion = {
34
- id: string; kind: "command" | "human" | "fresh_check"; description: string;
35
- status: "pass" | "fail" | "stale" | "not_run" | "awaiting_human" | "accepted";
36
- command?: string; evidence?: string;
37
- /** A declared guard: it must pass before and after the work, rather than fail before it (S.7). */
38
- guard?: true;
39
- /** The latest counter-check of this criterion in the request, when one ran, read against its role. */
40
- counter?: CounterVerdict;
41
- /**
42
- * Consecutive runs of this exact command that failed with the same exit code
43
- * against the same workspace. Two or more mean rerunning it unchanged will
44
- * not change the answer: the work, or the check itself, must change.
45
- */
46
- repeated_failures?: number;
47
- /** A fresh check under strict verification: only a configured gate counts (O.11). */
48
- gate_only?: true;
49
- };
50
-
51
- /** The rungs of the evidence ladder this derivation can establish. */
52
- export type RequestEvidence = {
53
- /** Every required check ran successfully against the current workspace. */
54
- ran: "pass" | "fail" | "stale" | "missing" | "not_applicable";
55
- /** Every required human criterion was accepted by the user. */
56
- human: "accepted" | "pending" | "not_required";
57
- /**
58
- * Every passing check meant to test the change failed once this request's
59
- * changes were undone (feat-counter-check). `missing`: every check is a guard
60
- * that held, so nothing shows the work changed anything (S.7).
61
- */
62
- discriminating: "pass" | "vacuous" | "unavailable" | "not_run" | "missing" | "not_applicable";
63
- /** Blind checks the model never touched passed on the final result (feat-blind-checks). */
64
- independent: "pass" | "fail" | "exposed" | "unavailable" | "not_run" | "not_applicable";
65
- };
66
-
67
- export type RequestStatus = "not_applicable" | "unchecked" | "checked" | "needs_work" | "awaiting_human" | "verified";
68
-
69
- export type RequestState = {
70
- request: RequestSource; inputs: RequestSource[];
71
- /**
72
- * Without a contract: not_applicable (nothing changed), unchecked, or
73
- * checked. With one: needs_work, awaiting_human, verified, or
74
- * not_applicable for a changes-scoped contract over an unchanged workspace.
75
- */
76
- status: RequestStatus;
77
- criteria: RequestCriterion[]; evidence: RequestEvidence; unmet: string[];
78
- execution?: ExecutionOutcome; acceptance?: AcceptanceState;
79
- commands: RequestCommand[]; artifacts: RequestArtifact[]; work: WorkItem[];
80
- /** What the model said blocks it. Shown to the user; never a verdict (D11). */
81
- reported_blockers: { source: RequestSource; reason: string }[];
82
- progress: RequestProgress;
83
- changed: boolean; used_tools: boolean; reviewed: boolean;
84
- final?: { source: RequestSource; text: string; has_tools: boolean };
85
- };
86
-
87
- export type LedgerOptions = {
88
- /** The CLI's strict verification: only a configured gate command counts as the fresh check. */
89
- strict?: boolean;
90
- /** The session declares blind checks, so their absence means "not run" rather than "not applicable". */
91
- blind?: boolean;
92
- };
93
-
94
- /** New steering events refer to their original user event. A user event without that link opens a request. */
95
- export function currentRequestRecords(parts: readonly RequestPart[]): RequestRecord[] {
96
- const records = parts.flatMap(part => part.events.map(event => ({ node: part.node, event })));
97
- const last = records.findLastIndex(({ event }) => event.t === "user");
98
- if (last < 0) return [];
99
- const user = records[last]!.event;
100
- if (user.t !== "user") return [];
101
- const start = user.request === undefined ? last : records.findIndex(({ event }) => event.t === "user" && event.id === user.request);
102
- // Never reach through an unrelated user request because of an invalid reference.
103
- if (start < 0 || records.slice(start + 1, last + 1).some(({ event }) => event.t === "user" && event.request !== user.request)) return records.slice(last);
104
- return records.slice(start);
105
- }
106
-
107
- /**
108
- * The current request's events in one flat stream: the one slicer every
109
- * per-request notice uses, so steering never restarts a request's budget,
110
- * notices or failure counts. Empty when the stream holds no user event.
111
- */
112
- export function currentRequestEvents(events: readonly LogEvent[]): LogEvent[] {
113
- return currentRequestRecords([{ node: 0, events }]).map(record => record.event);
114
- }
115
-
116
- const FRESH_CHECK = "a successful check after the last change";
117
-
118
- export function deriveRequestState(parts: readonly RequestPart[], options: LedgerOptions = {}): RequestState | undefined {
119
- const records = currentRequestRecords(parts);
120
- const first = records[0];
121
- if (!first) return undefined;
122
- const events = records.map(record => record.event);
123
- const source = ({ node, event }: RequestRecord): RequestSource => ({ node, event: event.id });
124
- const request = source(first);
125
- const acceptance = deriveAcceptance(undefined, events.filter(event => event.t !== "acceptance_contract" || event.request === request.event));
126
- const commands: (RequestCommand & { index: number })[] = [], artifacts: RequestArtifact[] = [];
127
- const lastCommands = new Map<string, string>();
128
- for (const event of events) if (isCommandResult(event)) lastCommands.set(event.effect.cmd, event.id);
129
- let work: WorkItem[] = [];
130
- const ids = new Set(events.map(event => event.id)), completed = new Set<string>();
131
- // Keep the existing work-item same-segment evidence contract (D11/O.2).
132
- for (const part of parts) {
133
- work = deriveWorkItems(work, part.events);
134
- for (let index = 0; index < part.events.length; index++) {
135
- const event = part.events[index]!;
136
- // Untyped completed claims supersede a same-named blocker only when the
137
- // original same-segment evidence resolves, just as in deriveHandoff.
138
- if (event.t === "claim" && event.work_id === undefined && ids.has(event.id) &&
139
- resolveEvidence(part.events.slice(0, index), event.evidence).ok) completed.add(event.what);
140
- }
141
- }
142
- work = work.filter(item => ids.has(item.opened_at) || ids.has(item.updated_at));
143
- const reported_blockers: RequestState["reported_blockers"] = [];
144
- let execution: ExecutionOutcome | undefined, final: RequestState["final"], changed = false, lastChange = -1;
145
- records.forEach((record, index) => {
146
- const event = record.event, ref = source(record);
147
- if (event.t === "user") { execution = undefined; final = undefined; }
148
- else if (event.t === "assistant") {
149
- final = { source: ref, text: event.blocks.flatMap(block => block.type === "text" ? [block.text] : []).join("\n").trim(),
150
- has_tools: event.blocks.some(block => block.type === "tool_use") };
151
- execution = final.has_tools ? undefined : modelOutcome(event.stop_reason ?? null);
152
- } else if (event.t === "outcome") execution = { ...event.outcome };
153
- else if (event.t === "interrupt") execution = executionOutcome("cancelled");
154
- else if (event.t === "guidance" && (event.reason === "completion_review" || event.reason === "output_recovery")) execution = undefined;
155
- if (event.t === "blocked" && (event.work_id ? work.some(item => item.id === event.work_id && item.status === "blocked") : !completed.has(event.what))) {
156
- reported_blockers.push({ source: ref, reason: event.reason });
157
- }
158
- if (workspaceChanged(event)) { changed = true; lastChange = index; }
159
- if (isCommandResult(event)) {
160
- const effect = event.effect;
161
- const evidence = { command: effect.cmd, exit: effect.exit, event_id: event.id, at: event.at,
162
- ...(effect.workspace_sha256 === undefined ? {} : { workspace_sha256: effect.workspace_sha256 }),
163
- ...(effect.workspace_scope === undefined ? {} : { workspace_scope: effect.workspace_scope }) };
164
- commands.push({ index, source: ref, command: effect.cmd, exit: effect.exit,
165
- status: !event.ok || effect.exit !== 0 ? "fail" : effect.workspace_observed === false || evidenceStale(events, evidence) ? "stale"
166
- : lastCommands.get(effect.cmd) !== event.id ? "superseded" : "pass",
167
- ...(effect.gate === undefined ? {} : { gate: effect.gate }),
168
- ...(effect.workspace_sha256 === undefined ? {} : { workspace_sha256: effect.workspace_sha256 }),
169
- ...(effect.workspace_scope === undefined ? {} : { workspace_scope: effect.workspace_scope }),
170
- criteria: acceptance?.criteria.filter(item => item.kind === "command" && item.command === effect.cmd).map(item => item.id) ?? [] });
171
- for (const output of effect.artifacts?.outputs ?? []) if (output.after.state === "observed") artifacts.push({
172
- source: ref, path: output.path, sha256: output.after.sha256, kind: "command_output" });
173
- } else if (event.t === "tool_result" && (event.effect?.kind === "read" || event.effect?.kind === "edit")) {
174
- const effect = event.effect;
175
- artifacts.push({ source: ref, path: effect.path, sha256: effect.kind === "read" ? effect.sha256 : effect.sha256_after, kind: effect.kind });
176
- } else if (event.t === "checkpoint" && event.phase === "applied") {
177
- for (const path of changedPaths(event.changes)) artifacts.push({ source: ref, path, sha256: event.changes[path]!.after?.sha256 ?? null, kind: "checkpoint" });
178
- }
179
- });
180
-
181
- const ledger = acceptance ? contractLedger(acceptance) : freshCheckLedger(commands, changed, lastChange, options);
182
- const { criteria, status } = ledger;
183
- for (const event of events) if (event.t === "counter_check" && event.request === request.event) {
184
- const criterion = criteria.find(item => item.id === event.criterion && item.kind === "command");
185
- if (criterion) criterion.counter = counterVerdict(event);
186
- }
187
- for (const criterion of criteria) if (criterion.kind === "command" && criterion.command && criterion.status === "fail") {
188
- const runs = commands.filter(command => command.command === criterion.command);
189
- const last = runs.at(-1);
190
- let streak = 0;
191
- for (let i = runs.length - 1; i >= 0; i--) {
192
- const run = runs[i]!;
193
- if (run.status !== "fail" || run.exit !== last!.exit || run.workspace_sha256 === undefined || run.workspace_sha256 !== last!.workspace_sha256) break;
194
- streak++;
195
- }
196
- if (streak >= 2) criterion.repeated_failures = streak;
197
- }
198
- const blind = new Map<string, Extract<LogEvent, { t: "blind_check" }>>();
199
- for (const event of events) if (event.t === "blind_check" && event.request === request.event) blind.set(event.check, event);
200
- const blindRuns = [...blind.values()];
201
- const independent: RequestEvidence["independent"] = blindRuns.length === 0 ? (options.blind ? "not_run" : "not_applicable")
202
- : blindRuns.some(run => "exit" in run && run.exit !== 0) ? "fail" : blindRuns.some(run => "reason" in run) ? "unavailable"
203
- : blindRuns.some(run => run.exposed) ? "exposed" : "pass";
204
- const evidence: RequestEvidence = { ...ledger.evidence, independent, discriminating: testsTheChange(criteria) };
205
- const unmet = criteria.filter(item => item.status !== "pass" && item.status !== "accepted").map(item => `${item.description}: ${item.status}`);
206
- return { request, inputs: records.filter(record => record.event.t === "user").map(source),
207
- status, criteria, evidence, unmet,
208
- ...(execution ? { execution } : {}), ...(acceptance ? { acceptance } : {}),
209
- commands: commands.map(({ index: _index, ...command }) => command), artifacts, work, reported_blockers,
210
- progress: deriveRequestProgress(records), changed,
211
- used_tools: events.some(event => event.t === "tool_result"),
212
- reviewed: events.some(event => event.t === "guidance" && event.reason === "completion_review"),
213
- ...(final ? { final } : {}) };
214
- }
215
-
216
- /**
217
- * The discriminating rung (S.1, S.7), decided by the checks meant to test the
218
- * change: each must fail once the request's changes are undone. A guard that
219
- * held is what a guard should do, so it neither fails the rung nor holds it
220
- * back. A guard that did not hold does test the change -- the observation
221
- * outranks the declaration (D11) -- so it counts for the rung. With only
222
- * guards, all held, nothing shows the work changed anything: missing. A
223
- * passing fresh check is a check like any declared command and must test the
224
- * change too.
225
- */
226
- function testsTheChange(criteria: readonly RequestCriterion[]): RequestEvidence["discriminating"] {
227
- const tests = criteria.filter(item => item.kind === "command" && !item.guard || item.kind === "fresh_check" && item.status === "pass");
228
- const guards = criteria.filter(item => item.kind === "command" && item.guard);
229
- if (tests.length === 0 && guards.length === 0) return "not_applicable";
230
- if (tests.some(item => item.counter === "vacuous")) return "vacuous";
231
- if (tests.some(item => item.counter === "unavailable")) return "unavailable";
232
- if (tests.some(item => item.counter !== "discriminating")) return "not_run";
233
- if (tests.length > 0 || guards.some(item => item.counter === "not_held")) return "pass";
234
- if (guards.some(item => item.counter === "unavailable")) return "unavailable";
235
- return guards.every(item => item.counter === "held") ? "missing" : "not_run";
236
- }
237
-
238
- /** A declared contract defines what this request must finish (O.11); nothing is inferred beyond it. */
239
- type LedgerCore = Pick<RequestState, "criteria" | "status"> & { evidence: Omit<RequestEvidence, "discriminating" | "independent"> };
240
-
241
- function contractLedger(acceptance: AcceptanceState): LedgerCore {
242
- const criteria: RequestCriterion[] = acceptance.criteria.map(item => ({ id: item.id, kind: item.kind, description: item.description,
243
- status: item.status, ...(item.kind === "command" ? { command: item.command, ...(item.guard ? { guard: true as const } : {}) } : {}),
244
- ...(item.evidence ? { evidence: item.evidence.event_id } : item.accepted_event ? { evidence: item.accepted_event } : {}) }));
245
- const checks = criteria.filter(item => item.kind === "command"), humans = criteria.filter(item => item.kind === "human");
246
- const targetsReady = (acceptance.targets ?? []).every(target => target.status === "ready");
247
- const ran: RequestEvidence["ran"] = checks.length === 0 ? "not_applicable"
248
- : checks.every(item => item.status === "pass") && targetsReady ? "pass"
249
- : checks.some(item => item.status === "fail") ? "fail" : checks.some(item => item.status === "stale") ? "stale" : "missing";
250
- return { criteria, status: acceptance.status,
251
- evidence: { ran, human: humans.length === 0 ? "not_required" : humans.every(item => item.status === "accepted") ? "accepted" : "pending" } };
252
- }
253
-
254
- /**
255
- * Without a contract, a request that changed the workspace still owes one
256
- * thing: a successful check against the changed workspace. That is all an
257
- * exit code can say -- not that the request's words were met -- so the status
258
- * is "checked", never "verified".
259
- */
260
- function freshCheckLedger(commands: readonly (RequestCommand & { index: number })[], changed: boolean, lastChange: number,
261
- options: LedgerOptions): LedgerCore {
262
- const none = { human: "not_required" as const };
263
- if (!changed) return { criteria: [], status: "not_applicable", evidence: { ran: "not_applicable", ...none } };
264
- const after = commands.filter(command => command.index > lastChange && (!options.strict || command.gate !== undefined));
265
- const pass = after.find(command => command.status === "pass");
266
- const status: RequestCriterion["status"] = pass ? "pass" : after.some(command => command.status === "fail") ? "fail"
267
- : after.some(command => command.status === "stale") ? "stale" : "not_run";
268
- const criterion: RequestCriterion = { id: "fresh-check", kind: "fresh_check",
269
- description: options.strict ? `${FRESH_CHECK} (a configured gate)` : FRESH_CHECK, status, ...(options.strict ? { gate_only: true as const } : {}),
270
- ...(pass ? { command: pass.command, evidence: pass.source.event } : {}) };
271
- return { criteria: [criterion], status: pass ? "checked" : "unchecked",
272
- evidence: { ran: status === "pass" ? "pass" : status === "not_run" ? "missing" : status, ...none } };
273
- }
@@ -1,36 +0,0 @@
1
- /** Pi +/-/! resource choices; discovery and path resolution belong to store. */
2
- export type ResourceKind = "skills" | "prompts" | "themes" | "extensions";
3
- export type ResourceScope = "user" | "project" | "cli";
4
- export type ResourceOverride = "inherit" | "load" | "skip";
5
- export interface ResourceEntry {
6
- kind: ResourceKind;
7
- path: string;
8
- name: string;
9
- scope: ResourceScope;
10
- source: string;
11
- enabled: boolean;
12
- inherited: boolean;
13
- override: ResourceOverride;
14
- trusted: boolean;
15
- package?: { source: string; identity: string; root: string };
16
- }
17
- export interface ResourcePattern { pattern: string; enabled: boolean; explicit: boolean }
18
- export function parseResourcePattern(raw: string): ResourcePattern {
19
- const first = raw[0];
20
- return { pattern: ["+", "-", "!"].includes(first ?? "") ? raw.slice(1) : raw,
21
- enabled: first !== "-" && first !== "!", explicit: first === "+" || first === "-" };
22
- }
23
- export function resourceOverride(patterns: readonly string[], matches: (pattern: string) => boolean): ResourceOverride {
24
- let result: ResourceOverride = "inherit";
25
- for (const raw of patterns) { const choice = parseResourcePattern(raw); if (matches(choice.pattern)) result = choice.enabled ? "load" : "skip"; }
26
- return result;
27
- }
28
- export function updateResourcePatterns(patterns: readonly string[], path: string, state: ResourceOverride,
29
- same: (pattern: string) => boolean): string[] {
30
- const next = patterns.filter(raw => !same(parseResourcePattern(raw).pattern));
31
- if (state !== "inherit") next.push((state === "load" ? "+" : "-") + path);
32
- return next;
33
- }
34
- export function nextResourceOverride(state: ResourceOverride): ResourceOverride {
35
- return state === "inherit" ? "load" : state === "load" ? "skip" : "inherit";
36
- }
@@ -1,59 +0,0 @@
1
- /** N.12: a returned response is durable evidence, not necessarily a completed action. */
2
- import type { AssistantBlock, GuidanceEvent, LogEvent } from "./events.ts";
3
- import { currentRequestEvents } from "./request-state.ts";
4
-
5
- // One appended attempt is an operational assumption, not a measured optimum.
6
- // It shares the existing request ceiling and never replays a tool automatically.
7
- export const DEFAULT_OUTPUT_RECOVERY_LIMIT = 1;
8
-
9
- export function validateOutputRecoveryLimit(limit: number): void {
10
- if (!Number.isSafeInteger(limit) || limit < 0) throw new RangeError("output recovery limit must be a nonnegative safe integer");
11
- }
12
-
13
- /** Both wire dialects have explicit terminal reasons; an unknown reason is not success. */
14
- export function isCompleteResponse(reason: string | null | undefined): boolean {
15
- return reason !== undefined && reason !== null && ["stop", "end_turn", "stop_sequence", "tool_calls", "tool_use"].includes(reason);
16
- }
17
-
18
- /**
19
- * A stop that asks for tool execution must carry a tool call. One that does not
20
- * is a malformed provider response, not the model's answer: taken as a final
21
- * reply it ends the request with outcome unknown and nothing on screen
22
- * (session mv28chr8-oc3x, an Anthropic-compatible gateway returning
23
- * `stop_reason: tool_use` with no content). Adapters reject it inside their
24
- * retry so the loop only sees a coherent turn or a definite failure.
25
- */
26
- export function isToolStopWithoutCall(stopReason: string | null | undefined, blocks: readonly AssistantBlock[]): boolean {
27
- return (stopReason === "tool_use" || stopReason === "tool_calls") && !blocks.some(block => block.type === "tool_use");
28
- }
29
-
30
- export function isOutputLimit(reason: string | null | undefined): boolean {
31
- return reason === "length" || reason === "max_tokens";
32
- }
33
-
34
- export function incompleteToolText(reason: string | null | undefined): string {
35
- return `not executed: the model response ${isOutputLimit(reason) ? "reached its output limit" : "did not finish successfully"}; ` +
36
- "no tools from that response were run. Regenerate a complete call only if the action is still needed.";
37
- }
38
-
39
- /** Explicit metadata proves non-execution; legacy pending calls remain unknown (D11). */
40
- export function knownUnexecutedTool(events: readonly LogEvent[], callId: string): string | null {
41
- const owner = events.findLast(event => event.t === "assistant" && event.blocks.some(block => block.type === "tool_use" && block.call_id === callId));
42
- return owner?.t === "assistant" && owner.stop_reason !== undefined && !isCompleteResponse(owner.stop_reason)
43
- ? incompleteToolText(owner.stop_reason) : null;
44
- }
45
-
46
- /** Supply effective ancestry, not only the current segment: cutting must not reset this limit. */
47
- export function outputRecoveryNotice(events: readonly LogEvent[], limit: number, remainingRequests: number):
48
- Pick<GuidanceEvent, "reason" | "sources" | "text"> | null {
49
- validateOutputRecoveryLimit(limit);
50
- const current = currentRequestEvents(events).slice(1);
51
- const assistant = current.findLast(event => event.t === "assistant");
52
- const notices = current.filter(event => event.t === "guidance" && event.reason === "output_recovery");
53
- if (assistant?.t !== "assistant" || !isOutputLimit(assistant.stop_reason) || notices.length >= limit || remainingRequests < 1 ||
54
- notices.some(event => event.t === "guidance" && event.sources.includes(assistant.id))) return null;
55
- return { reason: "output_recovery", sources: [assistant.id], text:
56
- "Harness: the previous response hit its output token limit and is incomplete. All tool calls in that response were left unexecuted. " +
57
- "Continue from the existing evidence, avoid repeating the earlier analysis, and give a concise result or the next necessary complete tool call. " +
58
- "Do not claim unexecuted actions succeeded. This recovery consumes the existing request allowance." };
59
- }
@@ -1,59 +0,0 @@
1
- /**
2
- * Picking a session back up where it stopped.
3
- *
4
- * Pure: it decides, given a recorded cursor and the model about to run, what
5
- * should happen. Doing the reading and the refusing in separate places keeps
6
- * the one interesting rule -- when a resume is unsafe -- testable without a
7
- * filesystem.
8
- */
9
-
10
- import type { SavedModel } from "./session-entry.ts";
11
-
12
- export type ResumePlan<T extends SavedModel> =
13
- | { kind: "fresh" }
14
- | { kind: "resume"; cursor: T }
15
- | { kind: "refuse"; reason: string };
16
-
17
- /**
18
- * A session's thinking blocks belong to the model that produced them.
19
- *
20
- * Same-model resume preserves the original projection. An explicit model
21
- * change uses Session.switchModel after opening with the recorded identity
22
- * (N.8); it never reinterprets signed or opaque thinking during replay.
23
- */
24
- export function planResume<T extends SavedModel>(
25
- cursor: T | null,
26
- model: string,
27
- hasNodes: boolean,
28
- provider?: string,
29
- ): ResumePlan<T> {
30
- if (cursor === null) {
31
- // No cursor and no nodes is simply a new session. No cursor but nodes on
32
- // disk is a log this build cannot place itself in -- written before the
33
- // cursor existed, or with a manifest that was lost. Refused rather than
34
- // guessed: the whole reason the cursor is recorded is that the tree shape
35
- // cannot be read backwards to find it, and inventing an answer here would
36
- // reintroduce exactly the ambiguity it exists to remove.
37
- return hasNodes
38
- ? {
39
- kind: "refuse",
40
- reason:
41
- "this log has nodes but no recorded cursor, so there is no way to " +
42
- "know which one the session was in. It is intact and nothing will " +
43
- "be touched -- start a separate session with --fresh",
44
- }
45
- : { kind: "fresh" };
46
- }
47
- if (cursor.model !== model || (cursor.provider !== undefined && cursor.provider !== provider)) {
48
- const saved = cursor.provider === undefined ? cursor.model : `${cursor.provider}/${cursor.model}`;
49
- const target = provider === undefined ? model : `${provider}/${model}`;
50
- return {
51
- kind: "refuse",
52
- reason:
53
- `this session ran under ${saved}, and ${target} cannot replay its ` +
54
- `thinking blocks. Resume it with --model ${saved}, or start a ` +
55
- `separate session with --fresh`,
56
- };
57
- }
58
- return { kind: "resume", cursor };
59
- }
@@ -1,68 +0,0 @@
1
- /** Review scopes and checkpoint projection are data, never authorization (D8 / D11). */
2
- import type { LogEvent } from "./events.ts";
3
- import { sameVersion, changedPaths, type FileChanges } from "./checkpoints.ts";
4
- export type ReviewScope = { kind: "working" } | { kind: "branch"; base: string } |
5
- { kind: "commit"; commit: string } | { kind: "last-turn" };
6
- export interface ReviewSnapshot {
7
- scope: ReviewScope;
8
- label: string;
9
- diff: string;
10
- paths: string[];
11
- limitations: string[];
12
- /** Resolved commits are immutable review premises, never raw shell expressions. */
13
- base?: string;
14
- }
15
-
16
- /** Fold observed operations after the latest request; uncaptured effects remain unknown. */
17
- export function lastTurnChanges(events: readonly LogEvent[]): { changes: FileChanges; limitations: string[] } {
18
- let start = 0;
19
- for (let i = events.length - 1; i >= 0; i--) if (events[i]!.t === "user" || events[i]!.t === "user_bash") { start = i + 1; break; }
20
- const changes: FileChanges = Object.create(null) as FileChanges;
21
- const limitations = new Set<string>(["Last-turn diff covers durable file checkpoints; uncaptured command or external changes are unknown."]);
22
- const suffix = events.slice(start);
23
- const finalPhases = new Map(suffix.filter(event => event.t === "checkpoint").map(event => [event.operation, event.phase]));
24
- for (const event of suffix) {
25
- if (event.t !== "checkpoint") continue;
26
- if (event.phase === "unavailable" && finalPhases.get(event.operation) !== "applied") limitations.add(event.reason ?? "A file checkpoint is unavailable.");
27
- if (event.phase !== "applied" || finalPhases.get(event.operation) !== "applied") continue;
28
- for (const path of changedPaths(event.changes)) {
29
- const next = event.changes[path]!, previous = changes[path];
30
- if (previous && !sameVersion(previous.after, next.before)) throw new Error(`discontinuous last-turn checkpoint: ${path}; external changes cannot be attributed`);
31
- changes[path] = { before: previous ? previous.before : next.before, after: next.after };
32
- }
33
- }
34
- return { changes: Object.fromEntries(changedPaths(changes).map(path => [path, changes[path]!])), limitations: [...limitations] };
35
- }
36
-
37
- /** A linear contiguous hunk avoids quadratic whole-file diff memory. No bytes are omitted. */
38
- export function unifiedFileDiff(path: string, before: string | null, after: string | null, destination = path): string {
39
- if (before === after && path === destination) return "";
40
- const lines = (text: string | null): { text: string; newline: boolean }[] => {
41
- if (!text) return [];
42
- const result = text.split("\n");
43
- if (text.endsWith("\n")) result.pop();
44
- return result.map((line, index) => ({ text: line, newline: index < result.length - 1 || text.endsWith("\n") }));
45
- };
46
- const oldLines = lines(before), newLines = lines(after);
47
- const equal = (left: typeof oldLines[number] | undefined, right: typeof oldLines[number] | undefined) =>
48
- left !== undefined && right !== undefined && left.text === right.text && left.newline === right.newline;
49
- let prefix = 0, suffix = 0;
50
- while (prefix < oldLines.length && prefix < newLines.length && equal(oldLines[prefix], newLines[prefix])) prefix++;
51
- while (suffix < oldLines.length - prefix && suffix < newLines.length - prefix && equal(oldLines.at(-suffix - 1), newLines.at(-suffix - 1))) suffix++;
52
- // Assumed familiar Git default: retain three lines of unchanged context per side.
53
- const start = Math.max(0, prefix - 3), oldEnd = Math.min(oldLines.length, oldLines.length - suffix + 3), newEnd = Math.min(newLines.length, newLines.length - suffix + 3);
54
- const quoted = (value: string) => /[\s"\\]/.test(value) ? JSON.stringify(value) : value;
55
- const emit = (entries: typeof oldLines, marker: string) => entries.flatMap(line =>
56
- [marker + line.text, ...(line.newline ? [] : ["\"])]);
57
- const body = [...emit(oldLines.slice(start, prefix), " "),
58
- ...emit(oldLines.slice(prefix, oldLines.length - suffix), "-"),
59
- ...emit(newLines.slice(prefix, newLines.length - suffix), "+"),
60
- ...emit(oldLines.slice(oldLines.length - suffix, oldEnd), " ")];
61
- const range = (count: number) => count ? start + 1 : 0;
62
- const oldCount = oldEnd - start, newCount = newEnd - start;
63
- const headers = [`diff --git ${quoted("a/" + path)} ${quoted("b/" + destination)}`,
64
- ...(path === destination ? [] : [`rename from ${quoted(path)}`, `rename to ${quoted(destination)}`]),
65
- `--- ${before === null ? "/dev/null" : quoted("a/" + path)}`, `+++ ${after === null ? "/dev/null" : quoted("b/" + destination)}`];
66
- if (body.length) headers.push(`@@ -${range(oldCount)},${oldCount} +${range(newCount)},${newCount} @@`, ...body);
67
- return headers.join("\n") + "\n";
68
- }
@@ -1,64 +0,0 @@
1
- /**
2
- * The one ripgrep build xeno will install, per platform.
3
- *
4
- * Models write `rg` in shell commands because the harnesses they were trained
5
- * in ship it (Claude Code bundles it; Pi downloads it on first use). On a
6
- * machine without it every such command exits 127 and costs a request. xeno
7
- * installs it only when the user has none, and only this exact archive:
8
- * running a binary fetched from the network needs proof that it is the one
9
- * reviewed, which a pinned digest gives and "latest" (Pi's choice) does not.
10
- *
11
- * Digests are the upstream release's published `.sha256` files for 14.1.1;
12
- * the aarch64-apple-darwin archive was also downloaded and hashed locally on
13
- * 2026-10-11 and matched. Upgrading means changing this table and releasing.
14
- */
15
- import { truthy } from "./update-check.ts";
16
-
17
- export const RIPGREP_VERSION = "14.1.1";
18
-
19
- export interface RipgrepAsset {
20
- readonly url: string;
21
- readonly sha256: string;
22
- /** Archive member holding the executable; nothing else is extracted. */
23
- readonly member: string;
24
- }
25
-
26
- const DIGESTS: Readonly<Record<string, string>> = {
27
- "aarch64-apple-darwin": "24ad76777745fbff131c8fbc466742b011f925bfa4fffa2ded6def23b5b937be",
28
- "x86_64-apple-darwin": "fc87e78f7cb3fea12d69072e7ef3b21509754717b746368fd40d88963630e2b3",
29
- // musl is static, so it runs on glibc and musl distributions alike.
30
- "x86_64-unknown-linux-musl": "4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e",
31
- // 14.1.1 publishes no aarch64 musl build; this one needs glibc.
32
- "aarch64-unknown-linux-gnu": "c827481c4ff4ea10c9dc7a4022c8de5db34a5737cb74484d62eb94a95841ab2f",
33
- };
34
-
35
- function target(platform: string, arch: string): string | null {
36
- if (platform === "darwin") return arch === "arm64" ? "aarch64-apple-darwin" : arch === "x64" ? "x86_64-apple-darwin" : null;
37
- if (platform === "linux") return arch === "arm64" ? "aarch64-unknown-linux-gnu" : arch === "x64" ? "x86_64-unknown-linux-musl" : null;
38
- return null;
39
- }
40
-
41
- /** Null for platforms without a pinned build: those keep the TypeScript search. */
42
- export function ripgrepAsset(platform: string, arch: string): RipgrepAsset | null {
43
- const triple = target(platform, arch);
44
- if (triple === null) return null;
45
- const name = `ripgrep-${RIPGREP_VERSION}-${triple}`;
46
- return {
47
- url: `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}/${name}.tar.gz`,
48
- sha256: DIGESTS[triple]!,
49
- member: `${name}/rg`,
50
- };
51
- }
52
-
53
- /**
54
- * Whether xeno may fetch the binary on its own, in the background.
55
- *
56
- * The same occasions the update check stays off the network (core/update-check.ts):
57
- * offline, CI, and a source checkout -- the last is also what keeps test
58
- * subprocesses, which run the CLI from this repository, from downloading.
59
- * XENO_NO_TOOL_INSTALL opts out explicitly; the user's own rg makes it moot.
60
- */
61
- export function toolInstallAllowed(input: { env: Readonly<Record<string, string | undefined>>; offline: boolean; install: string }): boolean {
62
- const on = (name: string) => truthy(input.env[name]);
63
- return !input.offline && input.install !== "checkout" && !on("XENO_OFFLINE") && !on("XENO_NO_TOOL_INSTALL") && !on("CI") && !on("CONTINUOUS_INTEGRATION");
64
- }