@qodeca/xezar 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/README.md +45 -15
  2. package/dist/agent-config/account-identity.d.ts +2 -8
  3. package/dist/agent-config/account-identity.js +54 -10
  4. package/dist/agent-config/account-identity.js.map +1 -1
  5. package/dist/agent-config/catalog.d.ts +49 -7
  6. package/dist/agent-config/catalog.js +107 -0
  7. package/dist/agent-config/catalog.js.map +1 -1
  8. package/dist/agent-config/model-settings/pi.d.ts +12 -6
  9. package/dist/agent-config/model-settings/pi.js +12 -6
  10. package/dist/agent-config/model-settings/pi.js.map +1 -1
  11. package/dist/agent-config/seed.js +10 -0
  12. package/dist/agent-config/seed.js.map +1 -1
  13. package/dist/contract/github.d.ts +15 -0
  14. package/dist/contract/index.d.ts +9 -0
  15. package/dist/contract/index.js +1527 -895
  16. package/dist/contract/mcp-api-reference.d.ts +174 -0
  17. package/dist/contract/mcp-audit.d.ts +103 -0
  18. package/dist/contract/mcp-discovery.d.ts +257 -0
  19. package/dist/contract/mcp-event-catalog.d.ts +121 -0
  20. package/dist/contract/mcp-event-catalog.test.d.ts +1 -0
  21. package/dist/contract/mcp-idempotency.d.ts +200 -0
  22. package/dist/contract/mcp-journal.d.ts +268 -0
  23. package/dist/contract/mcp-leader.d.ts +88 -0
  24. package/dist/contract/mcp-ownership.d.ts +78 -0
  25. package/dist/contract/mcp-versioning.d.ts +139 -0
  26. package/dist/contract/runs.d.ts +81 -0
  27. package/dist/core/agent-profiles.d.ts +15 -4
  28. package/dist/core/agent-profiles.js +22 -8
  29. package/dist/core/agent-profiles.js.map +1 -1
  30. package/dist/core/opencode-server-runner.js +71 -11
  31. package/dist/core/opencode-server-runner.js.map +1 -1
  32. package/dist/core/secret-redaction.d.ts +7 -1
  33. package/dist/core/secret-redaction.js +44 -10
  34. package/dist/core/secret-redaction.js.map +1 -1
  35. package/dist/index.js +138 -27
  36. package/dist/index.js.map +1 -1
  37. package/dist/machine-identity.d.ts +66 -0
  38. package/dist/machine-identity.js +96 -0
  39. package/dist/machine-identity.js.map +1 -0
  40. package/dist/mcp/adapters/claude-code.d.ts +41 -0
  41. package/dist/mcp/adapters/claude-code.js +40 -0
  42. package/dist/mcp/adapters/claude-code.js.map +1 -0
  43. package/dist/mcp/adapters/codex.d.ts +140 -0
  44. package/dist/mcp/adapters/codex.js +323 -0
  45. package/dist/mcp/adapters/codex.js.map +1 -0
  46. package/dist/mcp/adapters/opencode.d.ts +148 -0
  47. package/dist/mcp/adapters/opencode.js +528 -0
  48. package/dist/mcp/adapters/opencode.js.map +1 -0
  49. package/dist/mcp/adapters/pi-link.d.ts +102 -0
  50. package/dist/mcp/adapters/pi-link.js +235 -0
  51. package/dist/mcp/adapters/pi-link.js.map +1 -0
  52. package/dist/mcp/adapters/pi.d.ts +188 -0
  53. package/dist/mcp/adapters/pi.js +418 -0
  54. package/dist/mcp/adapters/pi.js.map +1 -0
  55. package/dist/mcp/api-reference.d.ts +26 -0
  56. package/dist/mcp/api-reference.js +133 -0
  57. package/dist/mcp/api-reference.js.map +1 -0
  58. package/dist/mcp/audit-trail.d.ts +162 -0
  59. package/dist/mcp/audit-trail.js +306 -0
  60. package/dist/mcp/audit-trail.js.map +1 -0
  61. package/dist/mcp/bridge.d.ts +81 -0
  62. package/dist/mcp/bridge.js +407 -0
  63. package/dist/mcp/bridge.js.map +1 -0
  64. package/dist/mcp/connection-file.d.ts +49 -0
  65. package/dist/mcp/connection-file.js +63 -0
  66. package/dist/mcp/connection-file.js.map +1 -0
  67. package/dist/mcp/echo-guard.d.ts +94 -0
  68. package/dist/mcp/echo-guard.js +160 -0
  69. package/dist/mcp/echo-guard.js.map +1 -0
  70. package/dist/mcp/event-catalog.d.ts +120 -0
  71. package/dist/mcp/event-catalog.js +306 -0
  72. package/dist/mcp/event-catalog.js.map +1 -0
  73. package/dist/mcp/event-controller.d.ts +230 -0
  74. package/dist/mcp/event-controller.js +596 -0
  75. package/dist/mcp/event-controller.js.map +1 -0
  76. package/dist/mcp/event-journal.d.ts +57 -0
  77. package/dist/mcp/event-journal.js +423 -0
  78. package/dist/mcp/event-journal.js.map +1 -0
  79. package/dist/mcp/index.d.ts +63 -0
  80. package/dist/mcp/index.js +386 -0
  81. package/dist/mcp/index.js.map +1 -0
  82. package/dist/mcp/ipc.d.ts +202 -0
  83. package/dist/mcp/ipc.js +206 -0
  84. package/dist/mcp/ipc.js.map +1 -0
  85. package/dist/mcp/leader-delivery.d.ts +119 -0
  86. package/dist/mcp/leader-delivery.js +450 -0
  87. package/dist/mcp/leader-delivery.js.map +1 -0
  88. package/dist/mcp/operation-receipts.d.ts +210 -0
  89. package/dist/mcp/operation-receipts.js +723 -0
  90. package/dist/mcp/operation-receipts.js.map +1 -0
  91. package/dist/mcp/project-catalogs.d.ts +23 -0
  92. package/dist/mcp/project-catalogs.js +25 -0
  93. package/dist/mcp/project-catalogs.js.map +1 -0
  94. package/dist/mcp/project-leaders.d.ts +26 -0
  95. package/dist/mcp/project-leaders.js +17 -0
  96. package/dist/mcp/project-leaders.js.map +1 -0
  97. package/dist/mcp/protocol.d.ts +50 -0
  98. package/dist/mcp/protocol.js +47 -0
  99. package/dist/mcp/protocol.js.map +1 -0
  100. package/dist/mcp/reconnect.d.ts +297 -0
  101. package/dist/mcp/reconnect.js +465 -0
  102. package/dist/mcp/reconnect.js.map +1 -0
  103. package/dist/mcp/resource-ownership.d.ts +228 -0
  104. package/dist/mcp/resource-ownership.js +390 -0
  105. package/dist/mcp/resource-ownership.js.map +1 -0
  106. package/dist/mcp/service-adapter.d.ts +16488 -0
  107. package/dist/mcp/service-adapter.js +148 -0
  108. package/dist/mcp/service-adapter.js.map +1 -0
  109. package/dist/mcp/service.d.ts +83 -0
  110. package/dist/mcp/service.js +233 -0
  111. package/dist/mcp/service.js.map +1 -0
  112. package/dist/mcp/session-binding.d.ts +105 -0
  113. package/dist/mcp/session-binding.js +163 -0
  114. package/dist/mcp/session-binding.js.map +1 -0
  115. package/dist/mcp/stale-write.d.ts +145 -0
  116. package/dist/mcp/stale-write.js +219 -0
  117. package/dist/mcp/stale-write.js.map +1 -0
  118. package/dist/mcp/tool.d.ts +48 -0
  119. package/dist/mcp/tool.js +24 -0
  120. package/dist/mcp/tool.js.map +1 -0
  121. package/dist/mcp/tools/discovery.d.ts +40 -0
  122. package/dist/mcp/tools/discovery.js +232 -0
  123. package/dist/mcp/tools/discovery.js.map +1 -0
  124. package/dist/mcp/tools/execution-control.d.ts +171 -0
  125. package/dist/mcp/tools/execution-control.js +513 -0
  126. package/dist/mcp/tools/execution-control.js.map +1 -0
  127. package/dist/mcp/tools/handoff-git.d.ts +26 -0
  128. package/dist/mcp/tools/handoff-git.js +544 -0
  129. package/dist/mcp/tools/handoff-git.js.map +1 -0
  130. package/dist/mcp/tools/index.d.ts +2 -0
  131. package/dist/mcp/tools/index.js +23 -0
  132. package/dist/mcp/tools/index.js.map +1 -0
  133. package/dist/mcp/tools/leader-events.d.ts +46 -0
  134. package/dist/mcp/tools/leader-events.js +126 -0
  135. package/dist/mcp/tools/leader-events.js.map +1 -0
  136. package/dist/mcp/tools/local-handoff.d.ts +102 -0
  137. package/dist/mcp/tools/local-handoff.js +306 -0
  138. package/dist/mcp/tools/local-handoff.js.map +1 -0
  139. package/dist/mcp/tools/project-config.d.ts +340 -0
  140. package/dist/mcp/tools/project-config.js +1075 -0
  141. package/dist/mcp/tools/project-config.js.map +1 -0
  142. package/dist/mcp/tools/results-evidence.d.ts +112 -0
  143. package/dist/mcp/tools/results-evidence.js +1045 -0
  144. package/dist/mcp/tools/results-evidence.js.map +1 -0
  145. package/dist/mcp/tools/task-create.d.ts +70 -0
  146. package/dist/mcp/tools/task-create.js +473 -0
  147. package/dist/mcp/tools/task-create.js.map +1 -0
  148. package/dist/mcp/tools/task-reads.d.ts +102 -0
  149. package/dist/mcp/tools/task-reads.js +689 -0
  150. package/dist/mcp/tools/task-reads.js.map +1 -0
  151. package/dist/mcp/tools/work-organisation.d.ts +2 -0
  152. package/dist/mcp/tools/work-organisation.js +502 -0
  153. package/dist/mcp/tools/work-organisation.js.map +1 -0
  154. package/dist/paths.d.ts +17 -7
  155. package/dist/paths.js +18 -8
  156. package/dist/paths.js.map +1 -1
  157. package/dist/runs/project-writer.js +76 -11
  158. package/dist/runs/project-writer.js.map +1 -1
  159. package/dist/runs/retention.d.ts +24 -1
  160. package/dist/runs/retention.js +62 -1
  161. package/dist/runs/retention.js.map +1 -1
  162. package/dist/server/forge/github.js +44 -0
  163. package/dist/server/forge/github.js.map +1 -1
  164. package/dist/server/forge/types.d.ts +16 -0
  165. package/dist/server/project-context.d.ts +81 -8
  166. package/dist/server/project-context.js +143 -35
  167. package/dist/server/project-context.js.map +1 -1
  168. package/dist/server/server.d.ts +1050 -53
  169. package/dist/server/server.js +326 -120
  170. package/dist/server/server.js.map +1 -1
  171. package/dist/server/validators.d.ts +26 -6
  172. package/dist/server/validators.js +17 -0
  173. package/dist/server/validators.js.map +1 -1
  174. package/dist/skills-remote.js +64 -6
  175. package/dist/skills-remote.js.map +1 -1
  176. package/dist/workflows/run.d.ts +277 -12
  177. package/dist/workflows/run.js +554 -59
  178. package/dist/workflows/run.js.map +1 -1
  179. package/dist/workspace/agent-profiles.d.ts +30 -3
  180. package/dist/workspace/agent-profiles.js +39 -4
  181. package/dist/workspace/agent-profiles.js.map +1 -1
  182. package/dist/workspace/config.js +25 -4
  183. package/dist/workspace/config.js.map +1 -1
  184. package/dist/workspace/project-owner.d.ts +210 -0
  185. package/dist/workspace/project-owner.js +518 -0
  186. package/dist/workspace/project-owner.js.map +1 -0
  187. package/package.json +5 -3
  188. package/scripts/pi-leader-extension.ts +420 -0
  189. package/scripts/sync-readme.mjs +83 -2
  190. package/web/dist/assets/{alert-dialog-BVsVcbFy.js → alert-dialog-D6rP6d39.js} +1 -1
  191. package/web/dist/assets/arrow-down-DMOfFIzW.js +1 -0
  192. package/web/dist/assets/arrow-left-C4Ec2BUg.js +1 -0
  193. package/web/dist/assets/{centered-state-Djgly6Ni.js → centered-state-B0ZqrPaa.js} +12 -12
  194. package/web/dist/assets/chevron-right-mHJ-crvn.js +1 -0
  195. package/web/dist/assets/{collapsible-DZ4D-DHG.js → collapsible-DchEHgUM.js} +1 -1
  196. package/web/dist/assets/{commit-list-B-z1JL9G.js → commit-list-BpMim1Rv.js} +1 -1
  197. package/web/dist/assets/{compare-variants-D62m4j3B.js → compare-variants-D5ItQbAq.js} +1 -1
  198. package/web/dist/assets/{diff-BKew9rQX.js → diff-HqqZ_WDR.js} +2 -2
  199. package/web/dist/assets/{diff-stat-D0mc5V6Z.js → diff-stat-NsU-3ecr.js} +1 -1
  200. package/web/dist/assets/{diff-view-xpIcuLIC.js → diff-view-B7c11Xux.js} +1 -1
  201. package/web/dist/assets/{dropdown-menu-CEQziH7X.js → dropdown-menu-CtTpnyod.js} +1 -1
  202. package/web/dist/assets/{editable-title-BCX3T2BY.js → editable-title-BXtuGQry.js} +1 -1
  203. package/web/dist/assets/{ellipsis-vertical-ByVdDc4z.js → ellipsis-vertical-MHtGZSH6.js} +1 -1
  204. package/web/dist/assets/{file-xBSB5hXO.js → file-DKLw5SOb.js} +1 -1
  205. package/web/dist/assets/{folder-CldgbZ5A.js → folder-Drw6-ufH.js} +1 -1
  206. package/web/dist/assets/{git-toolbar-DJANqg7Y.js → git-toolbar-BQH8bORV.js} +1 -1
  207. package/web/dist/assets/github-ynnpKFCJ.js +1 -0
  208. package/web/dist/assets/{image-preview-Cv5mZr0n.js → image-preview-DLhMN_R9.js} +1 -1
  209. package/web/dist/assets/index-BEsrNdXt.js +29 -0
  210. package/web/dist/assets/index-Da8DOBau.css +2 -0
  211. package/web/dist/assets/{markdown-CgEEE2ND.js → markdown-q365eN6Y.js} +1 -1
  212. package/web/dist/assets/{new-task-form-1r2bgsdD.js → new-task-form-CW-hNIWQ.js} +1 -1
  213. package/web/dist/assets/{pill-q-x22_ej.js → pill-B_TFulXa.js} +1 -1
  214. package/web/dist/assets/{project-router-VDN4jQM8.js → project-router-CLPc-ImZ.js} +1 -1
  215. package/web/dist/assets/{prompt-templates-BmZtCe-L.js → prompt-templates-CLc4RxyO.js} +1 -1
  216. package/web/dist/assets/{repo-git-btG0sdbO.js → repo-git-Is7R6-Tj.js} +1 -1
  217. package/web/dist/assets/{run-diff-vguEMJzh.js → run-diff-DHMvDlVo.js} +2 -2
  218. package/web/dist/assets/run-header-DvZJbQ8I.js +1 -0
  219. package/web/dist/assets/{search-x-QmhaAbg5.js → search-x-Ce2MMetu.js} +1 -1
  220. package/web/dist/assets/{skill-empty-hint-BrzUwByZ.js → skill-empty-hint-C_xCSvzB.js} +1 -1
  221. package/web/dist/assets/skills-CivqD4si.js +1 -0
  222. package/web/dist/assets/{sparkles-CP8QDOgX.js → sparkles-BCqPoj5g.js} +1 -1
  223. package/web/dist/assets/{square-terminal-CGypshSD.js → square-terminal-ikj5g-oK.js} +1 -1
  224. package/web/dist/assets/{tab-link-D4lFzzI1.js → tab-link-VsEjFV1Y.js} +1 -1
  225. package/web/dist/assets/{task-changes-BtZId14z.js → task-changes-CH-E81-_.js} +1 -1
  226. package/web/dist/assets/{task-commits-BoeyJXrv.js → task-commits-DqfkMFxp.js} +1 -1
  227. package/web/dist/assets/{task-files-DIy2oUnj.js → task-files-D_nkHZ_a.js} +2 -2
  228. package/web/dist/assets/task-thread-bxQompxJ.js +9 -0
  229. package/web/dist/assets/{textarea-CyBCyKIk.js → textarea-xajuW1L_.js} +1 -1
  230. package/web/dist/assets/{thread-loading-Cuq2jchC.js → thread-loading-C3JQZbIE.js} +1 -1
  231. package/web/dist/assets/{trash-2-B6Dp3ZNs.js → trash-2-hJrBhpUD.js} +1 -1
  232. package/web/dist/assets/{triangle-alert-xBya8FUp.js → triangle-alert-D1u82y1a.js} +1 -1
  233. package/web/dist/assets/{upload-D6MXEB9p.js → upload-Bn6FBZ59.js} +1 -1
  234. package/web/dist/assets/{use-desktop-CPBwzQ7M.js → use-desktop-DOIMfHfY.js} +1 -1
  235. package/web/dist/assets/{use-submit-shortcut-JTpNzU-u.js → use-submit-shortcut-Dj4DHTdE.js} +1 -1
  236. package/web/dist/assets/utils-YwBpOoRN.js +64 -0
  237. package/web/dist/assets/{workflows-DG115scU.js → workflows-CTaAKz2m.js} +3 -3
  238. package/web/dist/assets/{zoomable-image-GTYwLtkS.js → zoomable-image-C1-6P3tg.js} +1 -1
  239. package/web/dist/index.html +23 -23
  240. package/web/dist/assets/arrow-down-CYgAlZtW.js +0 -1
  241. package/web/dist/assets/arrow-left-BOqE0a3k.js +0 -1
  242. package/web/dist/assets/chevron-right-Cc9BWnbN.js +0 -1
  243. package/web/dist/assets/github-D6vK0jFn.js +0 -1
  244. package/web/dist/assets/index-CkmR6h7s.css +0 -2
  245. package/web/dist/assets/index-DVDqF4bM.js +0 -9
  246. package/web/dist/assets/refresh-cw-DcNCj2nu.js +0 -1
  247. package/web/dist/assets/run-header-DM7MiKj9.js +0 -1
  248. package/web/dist/assets/skills-DMfxQtrD.js +0 -1
  249. package/web/dist/assets/task-thread-CbMPmrWq.js +0 -9
  250. package/web/dist/assets/utils--z9hRtl3.js +0 -64
@@ -0,0 +1,228 @@
1
+ import type { AutomationStore } from '../automations/store.ts';
2
+ import type { AutomationDefinition, AutomationReceipt } from '../automations/types.ts';
3
+ import type { QueuedMessage, RunRecord, RunStore } from '../runs/store.ts';
4
+ import type { WorkflowDef } from '../workflows/types.ts';
5
+ /**
6
+ * Per-resource ownership validation for the MCP project leader (#88).
7
+ *
8
+ * F-02: every identifier a leader supplies — a task, a message nested inside a task, a variant
9
+ * group and every run in it, a file path, a worktree, a workflow, an automation and its results,
10
+ * a pagination cursor, and every element of a bulk list — is proved to belong to the BOUND
11
+ * project before anything reads or changes it. A foreign identifier causes no read and no side
12
+ * effect.
13
+ *
14
+ * How ownership is proved. Each project has its own `RunStore` and `AutomationStore`, opened on
15
+ * its own `.local/xezar`, and the scope below holds exactly one project's pair. So "is this id
16
+ * A's?" is answered by A's own services (N-02: no JSON or NDJSON is read here, and nothing is
17
+ * written), and B's stores are simply not reachable from A's scope. Three things a store lookup
18
+ * alone does NOT prove, and which this module checks explicitly:
19
+ *
20
+ * - A record's FILESYSTEM reach. A run record carries an absolute `worktreePath`; the pick and
21
+ * reclaim flows delete that directory and the file and diff reads open it. The only path xezar
22
+ * ever creates for a run is `<root>/.local/xezar/worktrees/<runId>` (`worktreePathFor`,
23
+ * `createWorktree`), so a record naming anything else — another project's worktree, a moved
24
+ * root, a hand-edited index — is refused, and a worktree directory that is itself a symlink is
25
+ * refused too.
26
+ * - A manual automation check (`GET /automation-checks/:checkId`) lives in a WORKSPACE-level map
27
+ * that records no project (M-18). Its owner is stamped beside it with `stampOwner`; an
28
+ * unstamped check has no provable owner and is refused.
29
+ * - A cursor carries no project. MCP cursors are sealed with `sealCursor`, which binds them to
30
+ * the project AND the resource they page; any other cursor is refused.
31
+ *
32
+ * Refusals never name the resource (N-01). A foreign id and an id that does not exist anywhere
33
+ * answer the same `not_found`, with one fixed message that never interpolates caller input — an
34
+ * error that echoed an absolute path or an id would name the other project's resource back.
35
+ * Every refusal is also reported to the optional `audit` sink, the leader-visible log, as
36
+ * `{ check, code, index? }` only: the entry type has no field that could carry an identifier.
37
+ *
38
+ * Degradation is the caller's policy only for git/worktree HELPERS (AGENTS.md); an ownership
39
+ * check never inherits one. Every check here fails CLOSED: anything that cannot be proved —
40
+ * an unreadable directory, a missing owner stamp, a malformed id — is a refusal, never a pass.
41
+ *
42
+ * ## Partial-success policy (A-03)
43
+ *
44
+ * This is the documented outcome of a list that mixes this project's resources with foreign or
45
+ * unknown ones. It is a deliverable, not an implementation detail; the tests assert it exactly.
46
+ *
47
+ * 1. **Caller-supplied id lists** (`partitionOwned`). Every element is validated before ANY side
48
+ * effect, and the caller acts only afterwards, and only on `allowed`.
49
+ * - Owned elements PROCEED — this is a partial success, not an all-or-nothing batch.
50
+ * - Refused elements are reported by their input POSITION (`index`) with a code. The id itself
51
+ * is never echoed, and a foreign id is indistinguishable from a nonexistent one.
52
+ * - `outcome` states the case: `all` (every element owned), `partial` (some owned, some
53
+ * refused), `none` (elements were supplied and every one was refused — the caller performs
54
+ * no side effect and reports a refusal) and `empty` (nothing was supplied — which must not
55
+ * read the same as "every element was refused").
56
+ * - A repeated element is acted on once: its later positions are refused with `duplicate`
57
+ * when the first was allowed, and with the first position's own code when it was refused.
58
+ * 2. **A variant group is ONE resource, not a bulk list** (M-07). If any run in the group fails
59
+ * ownership, the whole group is refused — reading or picking a group means reading or
60
+ * changing every member, so a group with one foreign member is not this project's group.
61
+ * 3. **Server-computed sweeps** (`ownSweep`: archive-finished, read-all, reclaim). The candidate
62
+ * set comes only from the bound project's own store, so no caller id is involved. Archive and
63
+ * read-all change only store records and are owned by construction. Reclaim DELETES worktree
64
+ * directories, so `ownSweep` refuses the whole sweep unless every worktree-bearing record is
65
+ * contained. No production path calls that branch (#288): the enforcer itself
66
+ * (`reclaimWorktrees` in `runs/retention.ts`) now leaves out, per record, every worktree it
67
+ * cannot prove with `ownRun` / `ownWorktree`, for the route and the unattended sweeps alike.
68
+ */
69
+ /** Why a resource was refused. Deliberately coarse: a finer code would be an existence oracle. */
70
+ export type OwnershipCode = 'not_found' | 'forbidden_path' | 'invalid_cursor' | 'duplicate';
71
+ /** Which check refused — the only other thing the leader-visible log learns about a refusal. */
72
+ export type OwnershipCheck = 'run' | 'queued-message' | 'group' | 'group-member' | 'worktree' | 'file' | 'automation' | 'automation-receipt' | 'automation-check' | 'workflow' | 'cursor' | 'sweep';
73
+ /** One leader-visible log entry. No field can hold an identifier, a path or a name, by type. */
74
+ export interface OwnershipAuditEntry {
75
+ check: OwnershipCheck;
76
+ code: OwnershipCode;
77
+ /** Position in a caller-supplied list, for bulk refusals only. */
78
+ index?: number;
79
+ }
80
+ export interface OwnershipRefusal {
81
+ ok: false;
82
+ code: OwnershipCode;
83
+ /** One fixed sentence per code. Never built from caller input or from a resource. */
84
+ message: string;
85
+ }
86
+ export type Owned<T> = {
87
+ ok: true;
88
+ value: T;
89
+ } | OwnershipRefusal;
90
+ /**
91
+ * The bound project, as far as ownership needs it. Built from the project's context
92
+ * (`ProjectContext` in `server/project-context.ts` satisfies it structurally) — never from a
93
+ * caller-supplied project id, which E-BIND forbids.
94
+ */
95
+ export interface OwnershipProject {
96
+ root: string;
97
+ store: Pick<RunStore, 'getRun' | 'listRuns'>;
98
+ automationStore: Pick<AutomationStore, 'get' | 'latestReceipts'>;
99
+ }
100
+ export interface OwnershipScope {
101
+ /** Canonical (realpath'd) project root — the project's identity, free of the `default` alias. */
102
+ readonly root: string;
103
+ readonly store: OwnershipProject['store'];
104
+ readonly automationStore: OwnershipProject['automationStore'];
105
+ readonly audit: (entry: OwnershipAuditEntry) => void;
106
+ }
107
+ export declare function ownershipScope(project: OwnershipProject, audit?: (entry: OwnershipAuditEntry) => void): OwnershipScope;
108
+ /**
109
+ * A run of the bound project. Refused as `not_found` when the id is not in this project's store
110
+ * OR when the record's worktree path is not this project's — such a record would name another
111
+ * tree in every response and drive deletes into it.
112
+ */
113
+ export declare function ownRun(scope: OwnershipScope, runId: unknown): Owned<RunRecord>;
114
+ /** A queued message (#472), proved to sit in the stack of a run of this project. */
115
+ export declare function ownQueuedMessage(scope: OwnershipScope, runId: unknown, msgId: unknown): Owned<{
116
+ run: RunRecord;
117
+ message: QueuedMessage;
118
+ }>;
119
+ /**
120
+ * A variant group, with EVERY member proved to belong (M-07) — the same members, in the same
121
+ * order, `groupRuns` in `server.ts` feeds the read and the pick.
122
+ */
123
+ export declare function ownGroup(scope: OwnershipScope, groupId: unknown): Promise<Owned<{
124
+ groupId: string;
125
+ runs: RunRecord[];
126
+ }>>;
127
+ /** The pick's winner: the group is owned as a whole AND the named run is one of its members. */
128
+ export declare function ownGroupMember(scope: OwnershipScope, groupId: unknown, runId: unknown): Promise<Owned<{
129
+ groupId: string;
130
+ runs: RunRecord[];
131
+ run: RunRecord;
132
+ }>>;
133
+ /** A run's existing worktree directory, proved to be this project's (delete one worktree, diffs). */
134
+ export declare function ownWorktree(scope: OwnershipScope, runId: unknown): Promise<Owned<{
135
+ run: RunRecord;
136
+ path: string;
137
+ }>>;
138
+ /**
139
+ * The directory a run's files are read from — its worktree, or the project root for a
140
+ * `worktree: false` run — mirroring `workingDirectoryOf` in `server.ts`.
141
+ */
142
+ export declare function ownWorkingDirectory(scope: OwnershipScope, runId: unknown): Promise<Owned<{
143
+ run: RunRecord;
144
+ directory: string;
145
+ }>>;
146
+ /**
147
+ * A path inside a run's working directory (A-04). Refused BEFORE any content is read when it is
148
+ * absolute, contains a `..` segment, a backslash, a NUL or a `.git` segment, starts in the
149
+ * engine's `.local/` runtime folder, or when ANY
150
+ * component on the way to it is a symlink — each component is `lstat`ed in order, so the walk
151
+ * never follows a link into another tree. A missing component is `not_found`.
152
+ *
153
+ * This runs IN FRONT of `readWorktreePath`, never instead of it: that reader keeps its own
154
+ * traversal, symlink, size-cap and `.git` checks, and the raw route keeps its image allowlist,
155
+ * so the file surface is checked twice and weakened nowhere. The returned `path` is normalized
156
+ * (`/`-separated, no empty or `.` segments) and is what the caller passes on.
157
+ */
158
+ export declare function ownWorktreeFile(scope: OwnershipScope, runId: unknown, relPath: unknown): Promise<Owned<{
159
+ run: RunRecord;
160
+ directory: string;
161
+ path: string;
162
+ }>>;
163
+ /** An automation of this project. */
164
+ export declare function ownAutomation(scope: OwnershipScope, automationId: unknown): Owned<AutomationDefinition>;
165
+ /**
166
+ * An automation RESULT — a launch receipt (`POST /automation-log/:receiptId/retry`) — from this
167
+ * project's own receipt journal, whose automation is still this project's.
168
+ */
169
+ export declare function ownAutomationReceipt(scope: OwnershipScope, receiptId: unknown): Owned<AutomationReceipt>;
170
+ /**
171
+ * Record which project created `record`. NO production code calls this, nor `ownAutomationCheck`
172
+ * below (#288): the only reader of a check, `GET /automation-checks/:checkId`, is workspace-level
173
+ * and has no bound project to compare a stamp against. Nothing is protected by this pair today —
174
+ * the MCP `get_automation_check` action proves a check's owner another way, by looking its
175
+ * automation up in the bound project.
176
+ */
177
+ export declare function stampOwner(record: object, project: Pick<OwnershipProject, 'root'>): void;
178
+ /**
179
+ * A manual automation check (M-18: "checks and logs need a project owner even behind an
180
+ * unscoped endpoint"). The check must carry this project's owner stamp AND name an automation
181
+ * this project still has. An unstamped check has no provable owner and is refused.
182
+ */
183
+ export declare function ownAutomationCheck<T extends {
184
+ automationId: string;
185
+ }>(scope: OwnershipScope, checkId: unknown, checks: ReadonlyMap<string, T>): Owned<T>;
186
+ /** A workflow from this project's own catalog (built-ins plus `.xezar/workflows`), by name. */
187
+ export declare function ownWorkflow(scope: OwnershipScope, name: unknown): Promise<Owned<WorkflowDef>>;
188
+ /** D-09 B-04: every MCP cursor is at most 2 048 bytes — `MAX_CURSOR_BYTES` in
189
+ * `runs/event-history.ts`, which that module does not export. */
190
+ export declare const MCP_CURSOR_MAX_BYTES = 2048;
191
+ /**
192
+ * Wrap a page cursor so it is valid only for this project AND this resource (`resource` is the
193
+ * caller's own stable key, e.g. `run:<id>:history`). The project is bound through the tag, never
194
+ * written into the cursor, so a cursor names nothing. Throws when the result would exceed
195
+ * B-04 — a server-produced inner cursor that large is a bug, and B-03 forbids cutting it.
196
+ */
197
+ export declare function sealCursor(scope: OwnershipScope, resource: string, inner: string): string;
198
+ /**
199
+ * The inner cursor, when `cursor` was sealed for this project and this resource. A cursor from
200
+ * another project, for another resource, tampered with, malformed or oversized is one answer:
201
+ * `invalid_cursor`.
202
+ */
203
+ export declare function openCursor(scope: OwnershipScope, resource: string, cursor: unknown): Owned<string>;
204
+ export interface BulkOwnership<T> {
205
+ /** See "Partial-success policy" in the module comment. */
206
+ outcome: 'all' | 'partial' | 'none' | 'empty';
207
+ allowed: Array<{
208
+ index: number;
209
+ value: T;
210
+ }>;
211
+ refused: Array<{
212
+ index: number;
213
+ code: OwnershipCode;
214
+ }>;
215
+ }
216
+ /**
217
+ * Validate EVERY element of a caller-supplied list before any side effect, and partition it per
218
+ * the documented policy. `own` is one of the single-resource checks above; its own refusal audit
219
+ * is replaced by one entry per refused position, so the log carries the index and no id.
220
+ */
221
+ export declare function partitionOwned<T>(scope: OwnershipScope, check: OwnershipCheck, ids: readonly unknown[], own: (scope: OwnershipScope, id: unknown) => Owned<T> | Promise<Owned<T>>): Promise<BulkOwnership<T>>;
222
+ export type SweepKind = 'archive-finished' | 'read-all' | 'reclaim-worktrees';
223
+ /**
224
+ * A server-computed sweep over the bound project's own store (policy point 3). Reclaim is refused
225
+ * as a whole when any record's worktree cannot be proved this project's, because its service
226
+ * deletes every candidate directory and cannot be told to skip one.
227
+ */
228
+ export declare function ownSweep(scope: OwnershipScope, kind: SweepKind): Promise<Owned<SweepKind>>;
@@ -0,0 +1,390 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
2
+ import { realpathSync } from 'node:fs';
3
+ import { lstat, realpath } from 'node:fs/promises';
4
+ import { join, resolve } from 'node:path';
5
+ import { z } from 'zod';
6
+ import { projectDataDir } from '../project-data-paths.js';
7
+ import { loadWorkflows } from '../workflows/load.js';
8
+ const MESSAGES = {
9
+ not_found: 'not found in this project',
10
+ forbidden_path: 'path not allowed — use a relative path inside the task worktree, without "..", symlinks or .git',
11
+ invalid_cursor: 'invalid cursor — request the first page again',
12
+ duplicate: 'listed more than once — acted on once',
13
+ };
14
+ export function ownershipScope(project, audit) {
15
+ return {
16
+ root: canonical(project.root),
17
+ store: project.store,
18
+ automationStore: project.automationStore,
19
+ audit: audit ?? (() => { }),
20
+ };
21
+ }
22
+ function canonical(path) {
23
+ try {
24
+ return realpathSync(path);
25
+ }
26
+ catch {
27
+ // A root that cannot be resolved stays lexical; every containment check below then compares
28
+ // realpaths against it and fails closed, which is the right answer for a vanished project.
29
+ return resolve(path);
30
+ }
31
+ }
32
+ function refuse(scope, check, code, index) {
33
+ try {
34
+ scope.audit(index === undefined ? { check, code } : { check, code, index });
35
+ }
36
+ catch {
37
+ // The log is the sink's problem; a throwing sink must not turn a refusal into a pass.
38
+ }
39
+ return { ok: false, code, message: MESSAGES[code] };
40
+ }
41
+ const MAX_ID_LENGTH = 256;
42
+ /** A caller-supplied id worth looking up at all. Anything else is simply not found. */
43
+ function isId(value) {
44
+ return typeof value === 'string' && value.length > 0 && value.length <= MAX_ID_LENGTH && !value.includes('\0');
45
+ }
46
+ /** A run id that is safe as ONE path segment — the precondition for deriving its worktree path. */
47
+ const SAFE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
48
+ /** The one worktree path xezar creates for a run of this project. */
49
+ function expectedWorktreePath(scope, runId) {
50
+ if (!SAFE_SEGMENT.test(runId) || runId.length > MAX_ID_LENGTH)
51
+ return null;
52
+ return join(projectDataDir(scope.root), 'worktrees', runId);
53
+ }
54
+ /** Lexical half of worktree containment: synchronous, touches no filesystem. */
55
+ function worktreePathIsOurs(scope, run) {
56
+ if (run.worktreePath === undefined)
57
+ return true;
58
+ const expected = expectedWorktreePath(scope, run.id);
59
+ return expected !== null && resolve(run.worktreePath) === expected;
60
+ }
61
+ /**
62
+ * Filesystem half: an EXISTING worktree directory must be a real directory at exactly the
63
+ * expected path — not a symlink, and with no symlinked component between the root and it
64
+ * (`realpath` would differ). An absent directory (reclaimed) has nothing to reach and passes.
65
+ * Only `lstat`/`realpath` of this project's own paths run here: no content is read.
66
+ */
67
+ async function worktreeDirIsOurs(scope, run) {
68
+ if (!worktreePathIsOurs(scope, run))
69
+ return false;
70
+ if (run.worktreePath === undefined)
71
+ return true;
72
+ const expected = resolve(run.worktreePath);
73
+ let info;
74
+ try {
75
+ info = await lstat(expected);
76
+ }
77
+ catch (err) {
78
+ return err.code === 'ENOENT';
79
+ }
80
+ if (info.isSymbolicLink() || !info.isDirectory())
81
+ return false;
82
+ try {
83
+ return (await realpath(expected)) === expected;
84
+ }
85
+ catch {
86
+ return false;
87
+ }
88
+ }
89
+ /**
90
+ * A run of the bound project. Refused as `not_found` when the id is not in this project's store
91
+ * OR when the record's worktree path is not this project's — such a record would name another
92
+ * tree in every response and drive deletes into it.
93
+ */
94
+ export function ownRun(scope, runId) {
95
+ if (!isId(runId))
96
+ return refuse(scope, 'run', 'not_found');
97
+ const run = scope.store.getRun(runId);
98
+ if (!run || run.id !== runId || !worktreePathIsOurs(scope, run))
99
+ return refuse(scope, 'run', 'not_found');
100
+ return { ok: true, value: run };
101
+ }
102
+ /** A queued message (#472), proved to sit in the stack of a run of this project. */
103
+ export function ownQueuedMessage(scope, runId, msgId) {
104
+ const run = ownRun(scope, runId);
105
+ if (!run.ok)
106
+ return run;
107
+ const message = isId(msgId) ? (run.value.queuedMessages ?? []).find((m) => m.id === msgId) : undefined;
108
+ if (!message)
109
+ return refuse(scope, 'queued-message', 'not_found');
110
+ return { ok: true, value: { run: run.value, message } };
111
+ }
112
+ /**
113
+ * A variant group, with EVERY member proved to belong (M-07) — the same members, in the same
114
+ * order, `groupRuns` in `server.ts` feeds the read and the pick.
115
+ */
116
+ export async function ownGroup(scope, groupId) {
117
+ if (!isId(groupId))
118
+ return refuse(scope, 'group', 'not_found');
119
+ const runs = scope.store
120
+ .listRuns()
121
+ .filter((r) => r.groupId === groupId)
122
+ .sort((a, b) => (a.variant ?? '').localeCompare(b.variant ?? ''));
123
+ if (runs.length === 0)
124
+ return refuse(scope, 'group', 'not_found');
125
+ for (const run of runs) {
126
+ if (scope.store.getRun(run.id) !== run || !(await worktreeDirIsOurs(scope, run))) {
127
+ return refuse(scope, 'group', 'not_found');
128
+ }
129
+ }
130
+ return { ok: true, value: { groupId, runs } };
131
+ }
132
+ /** The pick's winner: the group is owned as a whole AND the named run is one of its members. */
133
+ export async function ownGroupMember(scope, groupId, runId) {
134
+ const group = await ownGroup(scope, groupId);
135
+ if (!group.ok)
136
+ return group;
137
+ const run = isId(runId) ? group.value.runs.find((r) => r.id === runId) : undefined;
138
+ if (!run)
139
+ return refuse(scope, 'group-member', 'not_found');
140
+ return { ok: true, value: { ...group.value, run } };
141
+ }
142
+ /** A run's existing worktree directory, proved to be this project's (delete one worktree, diffs). */
143
+ export async function ownWorktree(scope, runId) {
144
+ const run = ownRun(scope, runId);
145
+ if (!run.ok)
146
+ return run;
147
+ const path = run.value.worktreePath;
148
+ if (path === undefined || !(await worktreeDirIsOurs(scope, run.value)))
149
+ return refuse(scope, 'worktree', 'not_found');
150
+ try {
151
+ if (!(await lstat(path)).isDirectory())
152
+ return refuse(scope, 'worktree', 'not_found');
153
+ }
154
+ catch {
155
+ return refuse(scope, 'worktree', 'not_found');
156
+ }
157
+ return { ok: true, value: { run: run.value, path: resolve(path) } };
158
+ }
159
+ /**
160
+ * The directory a run's files are read from — its worktree, or the project root for a
161
+ * `worktree: false` run — mirroring `workingDirectoryOf` in `server.ts`.
162
+ */
163
+ export async function ownWorkingDirectory(scope, runId) {
164
+ const run = ownRun(scope, runId);
165
+ if (!run.ok)
166
+ return run;
167
+ if (run.value.worktree === false)
168
+ return { ok: true, value: { run: run.value, directory: scope.root } };
169
+ const worktree = await ownWorktree(scope, runId);
170
+ if (!worktree.ok)
171
+ return worktree;
172
+ return { ok: true, value: { run: run.value, directory: worktree.value.path } };
173
+ }
174
+ const MAX_PATH_LENGTH = 4_096;
175
+ /**
176
+ * A path inside a run's working directory (A-04). Refused BEFORE any content is read when it is
177
+ * absolute, contains a `..` segment, a backslash, a NUL or a `.git` segment, starts in the
178
+ * engine's `.local/` runtime folder, or when ANY
179
+ * component on the way to it is a symlink — each component is `lstat`ed in order, so the walk
180
+ * never follows a link into another tree. A missing component is `not_found`.
181
+ *
182
+ * This runs IN FRONT of `readWorktreePath`, never instead of it: that reader keeps its own
183
+ * traversal, symlink, size-cap and `.git` checks, and the raw route keeps its image allowlist,
184
+ * so the file surface is checked twice and weakened nowhere. The returned `path` is normalized
185
+ * (`/`-separated, no empty or `.` segments) and is what the caller passes on.
186
+ */
187
+ export async function ownWorktreeFile(scope, runId, relPath) {
188
+ const dir = await ownWorkingDirectory(scope, runId);
189
+ if (!dir.ok)
190
+ return dir;
191
+ const raw = relPath ?? '';
192
+ if (typeof raw !== 'string' || raw.length > MAX_PATH_LENGTH)
193
+ return refuse(scope, 'file', 'forbidden_path');
194
+ if (raw.includes('\0') || raw.includes('\\') || raw.startsWith('/') || /^[A-Za-z]:/.test(raw)) {
195
+ return refuse(scope, 'file', 'forbidden_path');
196
+ }
197
+ const segments = raw.split('/').filter((s) => s !== '' && s !== '.');
198
+ // `.git` compared case-insensitively: on a case-insensitive filesystem `.GIT` IS `.git`.
199
+ if (segments.some((s) => s === '..' || s.toLowerCase() === '.git'))
200
+ return refuse(scope, 'file', 'forbidden_path');
201
+ // A `worktree: false` run reads from the project root, where `.local/` holds the engine's
202
+ // runtime state — every task's transcript and the launch key, which F-15 forbids returning.
203
+ if (segments[0]?.toLowerCase() === '.local')
204
+ return refuse(scope, 'file', 'forbidden_path');
205
+ let current = dir.value.directory;
206
+ for (const [i, segment] of segments.entries()) {
207
+ current = join(current, segment);
208
+ let info;
209
+ try {
210
+ info = await lstat(current);
211
+ }
212
+ catch (err) {
213
+ return err.code === 'ENOENT' || err.code === 'ENOTDIR'
214
+ ? refuse(scope, 'file', 'not_found')
215
+ : refuse(scope, 'file', 'forbidden_path');
216
+ }
217
+ if (info.isSymbolicLink())
218
+ return refuse(scope, 'file', 'forbidden_path');
219
+ if (i < segments.length - 1 && !info.isDirectory())
220
+ return refuse(scope, 'file', 'not_found');
221
+ }
222
+ // No component is a link, so the realpath can only differ through something else entirely
223
+ // (a bind mount, a race): fail closed rather than reason about it.
224
+ try {
225
+ if ((await realpath(current)) !== current)
226
+ return refuse(scope, 'file', 'forbidden_path');
227
+ }
228
+ catch {
229
+ return refuse(scope, 'file', 'not_found');
230
+ }
231
+ return { ok: true, value: { run: dir.value.run, directory: dir.value.directory, path: segments.join('/') } };
232
+ }
233
+ /** An automation of this project. */
234
+ export function ownAutomation(scope, automationId) {
235
+ const automation = isId(automationId) ? scope.automationStore.get(automationId) : undefined;
236
+ if (!automation || automation.id !== automationId)
237
+ return refuse(scope, 'automation', 'not_found');
238
+ return { ok: true, value: automation };
239
+ }
240
+ /**
241
+ * An automation RESULT — a launch receipt (`POST /automation-log/:receiptId/retry`) — from this
242
+ * project's own receipt journal, whose automation is still this project's.
243
+ */
244
+ export function ownAutomationReceipt(scope, receiptId) {
245
+ if (!isId(receiptId))
246
+ return refuse(scope, 'automation-receipt', 'not_found');
247
+ const receipt = [...scope.automationStore.latestReceipts().values()].find((row) => row.receiptId === receiptId);
248
+ if (!receipt || scope.automationStore.get(receipt.automationId) === undefined) {
249
+ return refuse(scope, 'automation-receipt', 'not_found');
250
+ }
251
+ return { ok: true, value: receipt };
252
+ }
253
+ /**
254
+ * Owners of records that live OUTSIDE any project's store — today the manual automation checks,
255
+ * which sit in one workspace-level map. Held beside the record rather than on it, so the record's
256
+ * wire shape (`automationCheckSchema`) is unchanged and no project identity is ever serialized.
257
+ * Weak, so an evicted check takes its owner with it.
258
+ */
259
+ const owners = new WeakMap();
260
+ /**
261
+ * Record which project created `record`. NO production code calls this, nor `ownAutomationCheck`
262
+ * below (#288): the only reader of a check, `GET /automation-checks/:checkId`, is workspace-level
263
+ * and has no bound project to compare a stamp against. Nothing is protected by this pair today —
264
+ * the MCP `get_automation_check` action proves a check's owner another way, by looking its
265
+ * automation up in the bound project.
266
+ */
267
+ export function stampOwner(record, project) {
268
+ owners.set(record, canonical(project.root));
269
+ }
270
+ /**
271
+ * A manual automation check (M-18: "checks and logs need a project owner even behind an
272
+ * unscoped endpoint"). The check must carry this project's owner stamp AND name an automation
273
+ * this project still has. An unstamped check has no provable owner and is refused.
274
+ */
275
+ export function ownAutomationCheck(scope, checkId, checks) {
276
+ const check = isId(checkId) ? checks.get(checkId) : undefined;
277
+ if (!check || owners.get(check) !== scope.root || scope.automationStore.get(check.automationId) === undefined) {
278
+ return refuse(scope, 'automation-check', 'not_found');
279
+ }
280
+ return { ok: true, value: check };
281
+ }
282
+ /** A workflow from this project's own catalog (built-ins plus `.xezar/workflows`), by name. */
283
+ export async function ownWorkflow(scope, name) {
284
+ if (!isId(name))
285
+ return refuse(scope, 'workflow', 'not_found');
286
+ const { workflows } = await loadWorkflows(scope.root);
287
+ const workflow = workflows.find((w) => w.name === name);
288
+ if (!workflow)
289
+ return refuse(scope, 'workflow', 'not_found');
290
+ return { ok: true, value: workflow };
291
+ }
292
+ // ---- cursors ---------------------------------------------------------------
293
+ /** D-09 B-04: every MCP cursor is at most 2 048 bytes — `MAX_CURSOR_BYTES` in
294
+ * `runs/event-history.ts`, which that module does not export. */
295
+ export const MCP_CURSOR_MAX_BYTES = 2_048;
296
+ /**
297
+ * Per-process key. Zero config: nothing to store or rotate. The cost is that a cursor does not
298
+ * survive a service restart — it then answers `invalid_cursor`, and the leader re-reads the first
299
+ * page, the same recovery an expired history cursor already asks for.
300
+ */
301
+ const CURSOR_KEY = randomBytes(32);
302
+ const sealedCursorSchema = z.object({ v: z.literal(1), c: z.string(), t: z.string() }).strict();
303
+ function cursorTag(scope, resource, inner) {
304
+ return createHmac('sha256', CURSOR_KEY).update(`${scope.root}\0${resource}\0${inner}`).digest().subarray(0, 16);
305
+ }
306
+ /**
307
+ * Wrap a page cursor so it is valid only for this project AND this resource (`resource` is the
308
+ * caller's own stable key, e.g. `run:<id>:history`). The project is bound through the tag, never
309
+ * written into the cursor, so a cursor names nothing. Throws when the result would exceed
310
+ * B-04 — a server-produced inner cursor that large is a bug, and B-03 forbids cutting it.
311
+ */
312
+ export function sealCursor(scope, resource, inner) {
313
+ const cursor = Buffer.from(JSON.stringify({ v: 1, c: inner, t: cursorTag(scope, resource, inner).toString('base64url') }), 'utf8').toString('base64url');
314
+ if (cursor.length > MCP_CURSOR_MAX_BYTES)
315
+ throw new RangeError(`sealed cursor exceeds ${MCP_CURSOR_MAX_BYTES} bytes`);
316
+ return cursor;
317
+ }
318
+ /**
319
+ * The inner cursor, when `cursor` was sealed for this project and this resource. A cursor from
320
+ * another project, for another resource, tampered with, malformed or oversized is one answer:
321
+ * `invalid_cursor`.
322
+ */
323
+ export function openCursor(scope, resource, cursor) {
324
+ if (typeof cursor !== 'string' || cursor.length === 0 || cursor.length > MCP_CURSOR_MAX_BYTES) {
325
+ return refuse(scope, 'cursor', 'invalid_cursor');
326
+ }
327
+ let parsed;
328
+ try {
329
+ parsed = sealedCursorSchema.safeParse(JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')));
330
+ }
331
+ catch {
332
+ return refuse(scope, 'cursor', 'invalid_cursor');
333
+ }
334
+ if (!parsed.success)
335
+ return refuse(scope, 'cursor', 'invalid_cursor');
336
+ const expected = cursorTag(scope, resource, parsed.data.c);
337
+ const given = Buffer.from(parsed.data.t, 'base64url');
338
+ if (given.length !== expected.length || !timingSafeEqual(given, expected)) {
339
+ return refuse(scope, 'cursor', 'invalid_cursor');
340
+ }
341
+ return { ok: true, value: parsed.data.c };
342
+ }
343
+ /**
344
+ * Validate EVERY element of a caller-supplied list before any side effect, and partition it per
345
+ * the documented policy. `own` is one of the single-resource checks above; its own refusal audit
346
+ * is replaced by one entry per refused position, so the log carries the index and no id.
347
+ */
348
+ export async function partitionOwned(scope, check, ids, own) {
349
+ const quiet = { ...scope, audit: () => { } };
350
+ const allowed = [];
351
+ const refused = [];
352
+ const firstSeen = new Map();
353
+ for (const [index, id] of ids.entries()) {
354
+ const seen = firstSeen.get(id);
355
+ if (seen) {
356
+ const code = seen.code ?? 'duplicate';
357
+ refused.push({ index, code });
358
+ refuse(scope, check, code, index);
359
+ continue;
360
+ }
361
+ const result = await own(quiet, id);
362
+ if (result.ok) {
363
+ allowed.push({ index, value: result.value });
364
+ firstSeen.set(id, { index });
365
+ }
366
+ else {
367
+ refused.push({ index, code: result.code });
368
+ firstSeen.set(id, { index, code: result.code });
369
+ refuse(scope, check, result.code, index);
370
+ }
371
+ }
372
+ const outcome = ids.length === 0 ? 'empty' : refused.length === 0 ? 'all' : allowed.length === 0 ? 'none' : 'partial';
373
+ return { outcome, allowed, refused };
374
+ }
375
+ /**
376
+ * A server-computed sweep over the bound project's own store (policy point 3). Reclaim is refused
377
+ * as a whole when any record's worktree cannot be proved this project's, because its service
378
+ * deletes every candidate directory and cannot be told to skip one.
379
+ */
380
+ export async function ownSweep(scope, kind) {
381
+ if (kind === 'reclaim-worktrees') {
382
+ for (const run of scope.store.listRuns()) {
383
+ if (run.worktreePath !== undefined && !(await worktreeDirIsOurs(scope, run))) {
384
+ return refuse(scope, 'sweep', 'not_found');
385
+ }
386
+ }
387
+ }
388
+ return { ok: true, value: kind };
389
+ }
390
+ //# sourceMappingURL=resource-ownership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-ownership.js","sourceRoot":"","sources":["../../src/mcp/resource-ownership.ts"],"names":[],"mappings":"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"}