@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,206 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { join } from 'node:path';
3
+ import { mcpProjectOccupiedErrorSchema, mcpSessionExpiredErrorSchema } from '../contract/index.js';
4
+ import { z } from 'zod';
5
+ import { xezarHomeDir } from '../paths.js';
6
+ /**
7
+ * The IPC leg between `xez mcp` (the bridge) and the running xezar service — D-01
8
+ * (docs/features/mcp-server/mcp-d01-transport-decision.md) § 1.2–1.5.
9
+ *
10
+ * One Unix domain socket per registered project, owned by the service, at
11
+ * `<xezarHomeDir()>/ipc/<projectId>.sock` (dir 0700, socket 0600). Frames are
12
+ * newline-delimited JSON, the same framing as the stdio leg. The socket IS the
13
+ * project binding (§ 1.5): no frame carries a project field, and the service answers
14
+ * only for the project it opened the socket for.
15
+ *
16
+ * Both sides import this file, so the path rule and the frame shapes cannot drift.
17
+ * They may still come from different installed versions (`npx` vs a global
18
+ * install), which is what `IPC_PROTOCOL_VERSION` is for.
19
+ */
20
+ /**
21
+ * Bumped only when a frame shape changes incompatibly. Carried on every request.
22
+ *
23
+ * 2 (#302): a connection is a SESSION. The bridge keeps one connection for its whole life, opens
24
+ * the session on it with `session/open` (which acquires the project's owner slot, D-02), and every
25
+ * `health` / `tools/call` needs that live session. A version-1 bridge — one connection per call,
26
+ * no session — would be refused on every call anyway; the version check refuses it legibly first.
27
+ */
28
+ export const IPC_PROTOCOL_VERSION = 2;
29
+ /**
30
+ * Largest frame either leg accepts, in bytes: 32 MiB + 64 KiB. Taken from D-09 B-08
31
+ * (qodeca/xezar#206, not yet merged at the time of writing) so the bridge does not
32
+ * invent a second number. A larger frame is refused with an explicit error and
33
+ * closes nothing else.
34
+ */
35
+ export const MAX_FRAME_BYTES = 33_619_968;
36
+ /**
37
+ * How long the bridge waits for the service to answer one request — D-09 B-09
38
+ * (qodeca/xezar#206): 55 s. On expiry the bridge answers with a readable result;
39
+ * the service operation is not cancelled. There is no retry (D-01 § 5, D-09 B-16).
40
+ */
41
+ export const IPC_REQUEST_TIMEOUT_MS = 55_000;
42
+ /**
43
+ * How long the bridge waits to connect and open its session. Acquisition itself is bounded far
44
+ * below this (D-02.2: 5 attempts, at most 375 ms of backoff), so only a hung service reaches it.
45
+ * It is short because `initialize` waits on it, and a client gives up on a server whose handshake
46
+ * is slow — Codex's documented default startup timeout is 10 s (not re-measured here). On expiry
47
+ * the handshake still succeeds with no session (N-07) and the next call tries again.
48
+ */
49
+ export const IPC_SESSION_OPEN_TIMEOUT_MS = 5_000;
50
+ // ---- socket path ---------------------------------------------------------------
51
+ /**
52
+ * The longest socket path the OS accepts, in bytes. An operating-system constant
53
+ * (`sun_path`), not a xezar choice: macOS/BSD measured 104 OK and 105 EINVAL
54
+ * (D-01 E5); Linux reserves 108 bytes including the terminating NUL.
55
+ */
56
+ function maxSocketPathBytes(platform) {
57
+ return platform === 'linux' ? 107 : 104;
58
+ }
59
+ /**
60
+ * Where the service listens for `projectId`, and where the bridge connects. Pure:
61
+ * both sides compute it from the same registry entry, so they always agree.
62
+ *
63
+ * D-01 § 1.4: when `<home>/ipc/<projectId>.sock` is longer than the OS allows, the
64
+ * first 12 hex characters of SHA-256(project root) replace the id. When even that
65
+ * is too long (a very long `XEZ_HOME`), there is no socket — the caller degrades.
66
+ * Windows named pipes are untested and their naming is undecided (D-01 § 10.2), so
67
+ * Windows is reported as unavailable rather than guessed.
68
+ */
69
+ export function mcpSocketLocation(project, env = process.env, platform = process.platform) {
70
+ if (platform === 'win32') {
71
+ return { kind: 'unavailable', reason: 'the xezar MCP bridge is not supported on Windows yet' };
72
+ }
73
+ const dir = mcpSocketDir(env);
74
+ const limit = maxSocketPathBytes(platform);
75
+ const primary = join(dir, `${project.id}.sock`);
76
+ if (Buffer.byteLength(primary) <= limit)
77
+ return { kind: 'socket', path: primary };
78
+ const hashed = join(dir, `${createHash('sha256').update(project.root).digest('hex').slice(0, 12)}.sock`);
79
+ if (Buffer.byteLength(hashed) <= limit)
80
+ return { kind: 'socket', path: hashed };
81
+ return {
82
+ kind: 'unavailable',
83
+ reason: `the xezar home path is too long for a local socket on this system (limit ${limit} bytes) — point XEZ_HOME at a shorter directory`,
84
+ };
85
+ }
86
+ /** `<xezarHomeDir()>/ipc` — the only directory the service creates for MCP. */
87
+ export function mcpSocketDir(env = process.env) {
88
+ return join(xezarHomeDir(env), 'ipc');
89
+ }
90
+ // ---- frames --------------------------------------------------------------------
91
+ /** A tool result exactly as MCP carries it: the text block is authoritative. */
92
+ export const toolResultSchema = z.object({
93
+ content: z.array(z.object({ type: z.literal('text'), text: z.string() })),
94
+ structuredContent: z.record(z.string(), z.unknown()).optional(),
95
+ isError: z.boolean().optional(),
96
+ });
97
+ /** What `health` answers. No account identity and no path — F-12, N-01, F-15. */
98
+ export const healthResultSchema = z.object({
99
+ ipcVersion: z.number().int(),
100
+ xezarVersion: z.string().max(64),
101
+ project: z.object({ id: z.string().max(64), name: z.string().max(200) }),
102
+ });
103
+ export const toolCallParamsSchema = z.object({
104
+ name: z.string().min(1).max(128),
105
+ arguments: z.record(z.string(), z.unknown()).optional(),
106
+ });
107
+ /**
108
+ * Bridge → service. `v` is checked BEFORE the method is looked at, so a service
109
+ * can refuse a bridge from another protocol version legibly. `bridgeVersion` is the
110
+ * bridge's xezar version, for diagnostics only.
111
+ */
112
+ export const ipcRequestSchema = z.object({
113
+ v: z.number().int(),
114
+ id: z.number().int().nonnegative(),
115
+ bridgeVersion: z.string().max(64).optional(),
116
+ method: z.string().min(1).max(64),
117
+ params: z.unknown().optional(),
118
+ });
119
+ export const ipcErrorCodeSchema = z.enum([
120
+ 'bad-frame',
121
+ 'version-mismatch',
122
+ 'unknown-method',
123
+ 'invalid-params',
124
+ 'unknown-tool',
125
+ 'internal',
126
+ /** `session/open` while another session owns the project (D-02 § 4, `-32080`). */
127
+ 'project-occupied',
128
+ /** A call on a connection whose session does not own the project (D-02 § 4, `-32081`). */
129
+ 'session-expired',
130
+ ]);
131
+ /**
132
+ * The JSON-RPC error the bridge hands the client verbatim for the two ownership refusals. The
133
+ * contract's schemas are `.strict()`, so nothing about the competing owner can ride along (N-01).
134
+ */
135
+ export const ownershipRpcErrorSchema = z.union([mcpProjectOccupiedErrorSchema, mcpSessionExpiredErrorSchema]);
136
+ /** What `session/open` answers: this connection's session now owns the project. */
137
+ export const sessionOpenResultSchema = z.object({ owner: z.literal(true) });
138
+ /** Service → bridge. */
139
+ export const ipcResponseSchema = z.discriminatedUnion('ok', [
140
+ z.object({ v: z.number().int(), id: z.number().int().nullable(), ok: z.literal(true), result: z.unknown() }),
141
+ z.object({
142
+ v: z.number().int(),
143
+ id: z.number().int().nullable(),
144
+ ok: z.literal(false),
145
+ error: z.object({ code: ipcErrorCodeSchema, message: z.string().max(2000) }),
146
+ /** The service's xezar version, so a mismatch message can name both sides. */
147
+ serviceVersion: z.string().max(64).optional(),
148
+ /** Only on `project-occupied` / `session-expired`: the D-02 § 4 error the client receives. */
149
+ rpcError: ownershipRpcErrorSchema.optional(),
150
+ }),
151
+ ]);
152
+ // ---- framing -------------------------------------------------------------------
153
+ /**
154
+ * Split a byte stream into newline-delimited frames. A frame over `maxBytes` is
155
+ * reported once through `onOversize` and skipped up to its newline; the stream
156
+ * keeps working (D-09 B-08: "closes nothing else").
157
+ */
158
+ export class LineFramer {
159
+ onFrame;
160
+ onOversize;
161
+ maxBytes;
162
+ chunks = [];
163
+ size = 0;
164
+ discarding = false;
165
+ constructor(onFrame, onOversize, maxBytes = MAX_FRAME_BYTES) {
166
+ this.onFrame = onFrame;
167
+ this.onOversize = onOversize;
168
+ this.maxBytes = maxBytes;
169
+ }
170
+ push(chunk) {
171
+ let start = 0;
172
+ for (let nl = chunk.indexOf(0x0a, start); nl !== -1; nl = chunk.indexOf(0x0a, start)) {
173
+ this.append(chunk.subarray(start, nl));
174
+ this.flushFrame();
175
+ start = nl + 1;
176
+ }
177
+ this.append(chunk.subarray(start));
178
+ }
179
+ append(part) {
180
+ if (this.discarding || part.length === 0)
181
+ return;
182
+ if (this.size + part.length > this.maxBytes) {
183
+ this.chunks = [];
184
+ this.size = 0;
185
+ this.discarding = true;
186
+ this.onOversize();
187
+ return;
188
+ }
189
+ this.chunks.push(part);
190
+ this.size += part.length;
191
+ }
192
+ flushFrame() {
193
+ const wasDiscarding = this.discarding;
194
+ const line = Buffer.concat(this.chunks, this.size).toString('utf8').replace(/\r$/, '');
195
+ this.chunks = [];
196
+ this.size = 0;
197
+ this.discarding = false;
198
+ if (!wasDiscarding && line.trim() !== '')
199
+ this.onFrame(line);
200
+ }
201
+ }
202
+ /** One frame on the wire. `JSON.stringify` never emits a raw newline. */
203
+ export function encodeFrame(value) {
204
+ return `${JSON.stringify(value)}\n`;
205
+ }
206
+ //# sourceMappingURL=ipc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipc.js","sourceRoot":"","sources":["../../src/mcp/ipc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ import type { McpLeaderActionInput, McpLeaderStatus } from '../contract/index.js';
2
+ import type { ProjectOwnership } from '../workspace/project-owner.ts';
3
+ import { type PiLeaderDescriptor, type PiLeaderLink, readPiLeaderDescriptor } from './adapters/pi-link.ts';
4
+ import type { EchoGuard } from './echo-guard.ts';
5
+ import { type DeliveryReceipt, type EventDispatch, type LeaderRecord, type ReactionAdapter } from './event-controller.ts';
6
+ import type { EventJournal } from './event-journal.ts';
7
+ import type { LeaderActResult, ProjectLeaderPort } from './project-leaders.ts';
8
+ /**
9
+ * Push delivery, connected (#309, Phase 6 of #73). Until this module, `EventController` (#107) and
10
+ * the reaction adapters (#108–#110) were complete, tested — and constructed by nothing outside their
11
+ * own tests, so no event ever reached a client. This is the one place the running service builds
12
+ * the controller, composed once per project by `startMcpService`.
13
+ *
14
+ * WHAT IS ON BY DEFAULT. Every MCP session that becomes the project's owner (`session/open`,
15
+ * D-02.2) gets an event controller at once, with this object as its adapter; the controller ends
16
+ * when that session's connection closes (D-02.4). No flag, no setting: an owner session always has
17
+ * a dispatcher following the journal for it.
18
+ *
19
+ * WHO IT CAN REACH, AND WHY ONLY THEM. A dispatch must reach a MODEL, and generic MCP notifications
20
+ * start no turn in Claude Code, Codex, OpenCode or pi (D-05 § 4; #330 run A for pi; each adapter's
21
+ * evidence record). xezar NEVER starts an agent process for a leader (owner decision on #311): the
22
+ * person runs their own leader and connects it to xezar over MCP. So an event reaches a model only
23
+ * through a session the person runs AND tells xezar where to find — `attach`. Two clients can be
24
+ * told today:
25
+ *
26
+ * - **OpenCode** names an `opencode serve` session by URL and session id (#110).
27
+ * - **pi** names nothing, because it cannot: pi's RPC is stdio-only and spawn-only, so a pi the
28
+ * person started has no address. The address therefore comes from INSIDE it — xezar's pi leader
29
+ * extension opens a socket and announces it in the project's data directory, and `#piTarget()`
30
+ * dials that (#330 WP2, `adapters/pi-link.ts`). With no extension running there is no descriptor
31
+ * and the attach is refused with pi's own recoverable reason, which is exactly the behaviour that
32
+ * existed before the extension did.
33
+ *
34
+ * A Claude Code or Codex session in a terminal still has no address at all: for it the controller
35
+ * keeps the rows in the journal, reports `disconnected`, retries at its heartbeat, and
36
+ * `status().blocker` says so. Nothing is lost either way — such a leader reads its events with the
37
+ * `leader_events` tool (#251), and the next session resumes after its last acknowledgement.
38
+ *
39
+ * THE ECHO GUARD HOLDS HERE, FOR EVERY CLIENT. The door records each mutation's operation id as the
40
+ * leader's own before it runs (`EchoGuard.issue`, #106), including the ids it mints for tools that
41
+ * carry no `operationId`. A `leader` row caused by one of them is dropped before the adapter sees it,
42
+ * so a client's own guard — which knows only the ids it saw — cannot miss a door-minted one. Only the
43
+ * echo rule: the guard's `duplicate` rule must NOT be applied, because redelivery after a reconnect
44
+ * is at-least-once by contract.
45
+ *
46
+ * ## Every state this relies on, and who fires each exit
47
+ *
48
+ * Controller states (`EventControllerState`), as wired here:
49
+ *
50
+ * | State | Exit | Who fires it (on by default) |
51
+ * | --- | --- | --- |
52
+ * | inert | — | never entered: this object is always the adapter |
53
+ * | idle | → dispatching | a journal append, or the 30 s heartbeat tick |
54
+ * | dispatching | → idle / → recovering | `deliver` resolving / rejecting or timing out (one heartbeat) |
55
+ * | recovering | → idle / → disconnected | the bounded round itself (5 attempts) |
56
+ * | disconnected | → dispatching | the heartbeat tick, every 30 s; or `wake()`, which `attach` fires at once |
57
+ * | any | → ended | the session's connection closing (`sessionClosed`), another session taking the project (`sessionOpened`), the lease lapsing (found at the next tick), or the service closing |
58
+ *
59
+ * `disconnected` with nothing attached is the one state that waits on something outside xezar, and
60
+ * it does not wait on a human alone: the heartbeat retries it for as long as the session owns the
61
+ * project, at no model cost (N-06). It is the recoverable blocker the requirements ask for.
62
+ *
63
+ * Leader states: none → attached (`act` attach); attached → none (`act` stop, or the service
64
+ * closing). An attached OpenCode session that goes away is the adapter's own recoverable blocker,
65
+ * reported by `status()`; the controller retries it at the heartbeat. A pi leader that goes away
66
+ * takes its socket with it, which closes the link and reports the same way.
67
+ *
68
+ * What xezar OPENS to reach a leader is released on every one of those exits: `#detach` closes the
69
+ * adapter and then its `dispose` (pi's socket today), and `close()` goes through `#detach`.
70
+ *
71
+ * What reaches a terminal state BECAUSE of this module: its controllers. No process, run, lease,
72
+ * queue slot or worktree — it starts nothing and stops nothing but its own objects.
73
+ */
74
+ /** xezar's base role, sent with every event to an attached leader. Per-project customisation is not built yet. */
75
+ export declare const LEADER_ROLE_INSTRUCTION: string;
76
+ export interface LeaderDeliveryOptions {
77
+ readonly projectId: string;
78
+ readonly projectRoot: string;
79
+ readonly journal: EventJournal;
80
+ readonly ownership: Pick<ProjectOwnership, 'projectId' | 'sessionToken' | 'state'>;
81
+ /** The door's echo guard. Absent (it could not be built): no row is dropped as an echo. */
82
+ readonly guard: Pick<EchoGuard, 'isOwn'> | undefined;
83
+ /**
84
+ * The leader's acknowledgement record (`LeaderCursors`, #251) — the ONE acknowledgement the
85
+ * controller resumes by, filters by and reports (#332). Absent: the controller's own.
86
+ */
87
+ readonly leaderRecord?: LeaderRecord;
88
+ readonly warn: (message: string) => void;
89
+ /** Test seam. Production uses the controller's 30 s. */
90
+ readonly heartbeatMs?: number;
91
+ /**
92
+ * Where this project's state lives, for the pi leader descriptor (`adapters/pi-link.ts`). Absent
93
+ * it is DERIVED from `projectRoot`, which is what the service already does, so nothing upstream
94
+ * has to pass it for pi attach to work. A store with a relocated `dataDir` may pass its own.
95
+ */
96
+ readonly dataDir?: string;
97
+ /** Test seams for the pi link, so a case never touches a real socket. Production uses the module. */
98
+ readonly piLeader?: {
99
+ read?: (dataDir: string) => ReturnType<typeof readPiLeaderDescriptor>;
100
+ connect?: (descriptor: PiLeaderDescriptor, opts: {
101
+ warn?: (message: string) => void;
102
+ }) => PiLeaderLink;
103
+ };
104
+ }
105
+ export declare class LeaderDelivery implements ReactionAdapter, ProjectLeaderPort {
106
+ #private;
107
+ readonly projectId: string;
108
+ constructor(opts: LeaderDeliveryOptions);
109
+ /** `session/open` made `sessionKey` the owner. Never throws into the transport (N-07). */
110
+ sessionOpened(sessionKey: string): void;
111
+ /** The session's connection closed (D-02.4): its controller ends. The journal keeps every row. */
112
+ sessionClosed(sessionKey: string): void;
113
+ deliver(dispatch: EventDispatch, signal: AbortSignal): Promise<DeliveryReceipt>;
114
+ heartbeat(signal: AbortSignal): Promise<void>;
115
+ status(): McpLeaderStatus;
116
+ act(input: McpLeaderActionInput): Promise<LeaderActResult>;
117
+ /** The service is stopping: every controller ends and the attached session is let go. */
118
+ close(): void;
119
+ }