@qodeca/xezar 0.13.0 → 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 +72 -39
  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 +25 -2
  160. package/dist/runs/retention.js +63 -2
  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 +599 -79
  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-CdS8r8E2.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-BYlnf4qf.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-C2On8SlI.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-6aopqAmV.js → task-changes-CH-E81-_.js} +1 -1
  226. package/web/dist/assets/{task-commits-u-dwaCyw.js → task-commits-DqfkMFxp.js} +1 -1
  227. package/web/dist/assets/{task-files-IMOZnIUK.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-fLDKEJoB.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-BY3KTkFw.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-CqGK62Lw.js +0 -1
  244. package/web/dist/assets/index-3nxVmdn5.css +0 -2
  245. package/web/dist/assets/index-qX7n11FX.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-Bj-uG_Hz.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,418 @@
1
+ import { MCP_JOURNAL_RETAINED_ROWS } from '../../contract/index.js';
2
+ export class PiDeliveryBlocked extends Error {
3
+ blocker;
4
+ constructor(code, message, fix) {
5
+ super(message);
6
+ this.name = 'PiDeliveryBlocked';
7
+ this.blocker = { code, recoverable: true, message, fix };
8
+ }
9
+ }
10
+ /** pi's refusal of a plain `prompt` during a turn, as 0.85.1 words it. Matched loosely on purpose. */
11
+ const BUSY_REFUSAL = /already processing/i;
12
+ /** How many of this adapter's own markers are kept, so a long-lived link cannot grow without bound. */
13
+ const MARKER_MEMORY = MCP_JOURNAL_RETAINED_ROWS;
14
+ export class PiReactionAdapter {
15
+ projectId;
16
+ #opts;
17
+ #unsubscribe;
18
+ /** Rows already handed to the model through this session, insertion-ordered, bounded by B-19. */
19
+ #submitted = new Map();
20
+ /** Submissions pi accepted whose model request has not been seen yet: marker → highest journalSeq. */
21
+ #awaiting = new Map();
22
+ /** Markers seen in the conversation before the request that sent them was answered. */
23
+ #seenMarkers = new Set();
24
+ /** Keeps this adapter's markers apart from an earlier adapter's on the same resumed session. */
25
+ #tag;
26
+ /** pi is inside a turn: set by `agent_start`, cleared by `agent_settled` (measured, `rpc-probe`). */
27
+ #busy = false;
28
+ /** The last attempt may have reached pi without us seeing the answer. */
29
+ #uncertain = false;
30
+ #conversationRead = false;
31
+ #blocker;
32
+ #sequence = 0;
33
+ #closed = false;
34
+ constructor(opts) {
35
+ this.#opts = opts;
36
+ this.projectId = opts.projectId;
37
+ // Not `randomUUID`: a counter plus the wall clock is enough to separate two adapters on one
38
+ // resumed pi session, and it keeps this file free of a `node:` import (the test asserts that).
39
+ this.#tag = `${Date.now().toString(36)}${(++adapterOrdinal).toString(36)}`;
40
+ this.#unsubscribe = opts.link.subscribe((message) => this.#observe(message));
41
+ }
42
+ status() {
43
+ return {
44
+ route: this.#blocker ? 'blocked' : 'rpc-prompt',
45
+ ...(this.#blocker ? { blocker: this.#blocker } : {}),
46
+ submittedRows: this.#submitted.size,
47
+ turnsAwaited: this.#awaiting.size,
48
+ };
49
+ }
50
+ async deliver(dispatch, signal) {
51
+ if (dispatch.projectId !== this.projectId) {
52
+ throw new Error(`pi adapter for project ${this.projectId} refused a dispatch for another project`);
53
+ }
54
+ this.#requireLink();
55
+ let rows = this.#fresh(dispatch.events);
56
+ if (rows.length === 0 && dispatch.recovery === undefined)
57
+ return { handedThrough: null };
58
+ // What pi has already been told, from its own conversation — on the first delivery, and after an
59
+ // attempt whose answer we lost, because that attempt may well have been accepted.
60
+ if (!this.#conversationRead || this.#uncertain) {
61
+ await this.#readConversation(signal);
62
+ rows = this.#fresh(dispatch.events);
63
+ if (rows.length === 0 && dispatch.recovery === undefined)
64
+ return { handedThrough: null };
65
+ }
66
+ const marker = `${MARKER_PREFIX}${this.projectId}:${this.#tag}:${++this.#sequence}`;
67
+ const toSeq = rows.at(-1)?.journalSeq ?? null;
68
+ const message = renderPiDispatch(dispatch, rows, this.#opts.roleInstruction, marker);
69
+ // Registered BEFORE the write: pi may emit the user message carrying the marker before the
70
+ // command's own `response` reaches us, and a reaction seen then must not be dropped.
71
+ if (toSeq !== null)
72
+ this.#awaiting.set(marker, toSeq);
73
+ this.#uncertain = true;
74
+ try {
75
+ await this.#submit(message, signal);
76
+ }
77
+ catch (err) {
78
+ // Deliberately NOT cleared here: an attempt that threw may still have reached pi, so the next
79
+ // one must read the conversation before it decides. Only an accepted submission is certain.
80
+ this.#awaiting.delete(marker);
81
+ throw err;
82
+ }
83
+ this.#uncertain = false;
84
+ for (const row of rows)
85
+ this.#remember(rowKey(row));
86
+ // A marker pi had already put into the conversation before its `response` arrived is a reaction
87
+ // nobody has reported yet; report it now rather than waiting for an event that has been and gone.
88
+ if (this.#seenMarkers.delete(marker))
89
+ this.#reacted(marker);
90
+ return { handedThrough: toSeq };
91
+ }
92
+ /** Non-model liveness (N-06): pi's own session metadata. Starts no turn and reaches no model. */
93
+ async heartbeat(signal) {
94
+ this.#requireLink();
95
+ const state = await this.#command({ type: 'get_state' }, signal);
96
+ if (state.success !== true)
97
+ throw this.#block('pi-refused', `pi refused xezar's liveness check: ${errorText(state)}`);
98
+ // pi's own answer is more trustworthy than the events we inferred `busy` from, and this is the
99
+ // one call that asks. A link that dropped an `agent_settled` would otherwise steer for ever.
100
+ if (typeof state.data?.isStreaming === 'boolean')
101
+ this.#busy = state.data.isStreaming;
102
+ }
103
+ /** Stop listening. The pi process and its session belong to whoever opened the link. */
104
+ close() {
105
+ if (this.#closed)
106
+ return;
107
+ this.#closed = true;
108
+ this.#unsubscribe();
109
+ }
110
+ // ---- internals -----------------------------------------------------------------------------
111
+ /**
112
+ * One submission, by the rung that pi accepts right now: `prompt` while idle, `steer` during a
113
+ * turn. `#busy` is inferred from an event stream that can lag or lose a frame, so BOTH directions
114
+ * of a wrong belief have to be safe. They were not (QA on #358, finding 2):
115
+ *
116
+ * - Believed idle, really busy: pi refuses the plain `prompt` and the fallback steers. SAFE, and
117
+ * the refusal fallback stays because pi can start a turn of its own between check and write.
118
+ * - Believed busy, really idle: what a `steer` does then depends on the transport, and only one
119
+ * of the two is ever used in production. Over the leader extension — the only producer of a
120
+ * `PiRpcLink` outside tests — an idle `steer` starts a turn and reaches the model at once
121
+ * (real pi 0.85.1, M1: one model request carrying the text, `pendingMessageCount` still 0), so
122
+ * the wrong belief costs nothing. Over pi's RAW stdio RPC, which nothing in xezar speaks, the
123
+ * same command is ACCEPTED and PARKED (M2: `success: true`, ZERO model requests,
124
+ * `pendingMessageCount` 0 -> 1) and the text waits for the person's next turn.
125
+ *
126
+ * The confirmations below therefore make this adapter correct over a transport that parks rather
127
+ * than rescue it from one it has. They are kept because they are cheap and because `success` is
128
+ * acceptance and never hand-over on ANY transport: a believed `busy` is CONFIRMED against pi
129
+ * before steering, and a steer is confirmed after the fact as well, because pi can settle between
130
+ * the two. A steer that cannot be confirmed to have reached the model is reported as not handed
131
+ * over (the controller retries it, down the `prompt` rung, after re-reading pi's conversation).
132
+ */
133
+ async #submit(message, signal) {
134
+ if (this.#busy)
135
+ this.#busy = await this.#reallyBusy(signal);
136
+ if (!this.#busy) {
137
+ const answer = await this.#command({ type: 'prompt', message }, signal);
138
+ if (answer.success === true)
139
+ return;
140
+ const text = errorText(answer);
141
+ if (!BUSY_REFUSAL.test(text))
142
+ throw this.#block('pi-refused', `pi refused the event submission: ${text}`);
143
+ this.#busy = true;
144
+ }
145
+ const steered = await this.#command({ type: 'steer', message }, signal);
146
+ if (steered.success !== true)
147
+ throw this.#block('pi-refused', `pi refused the steered event submission: ${errorText(steered)}`);
148
+ await this.#requireSteerLanded(signal);
149
+ }
150
+ /**
151
+ * Is a turn really running? Read from pi rather than from the event stream.
152
+ *
153
+ * An unreadable answer deliberately says NO, which sends the caller down the `prompt` rung: a
154
+ * prompt into a busy pi is refused and recovers on the spot, while a steer into an idle pi is
155
+ * harmless over the extension and parks in silence over raw RPC. When the two failure modes are
156
+ * not symmetric on some transport, guess towards the one that is recoverable on every transport.
157
+ */
158
+ async #reallyBusy(signal) {
159
+ const state = await this.#command({ type: 'get_state' }, signal);
160
+ if (state.success !== true)
161
+ return false;
162
+ const streaming = state.data?.isStreaming;
163
+ return typeof streaming === 'boolean' ? streaming : false;
164
+ }
165
+ /**
166
+ * A steer pi accepted is only handed over if a turn actually took it.
167
+ *
168
+ * WHEN a steer reaches the model, measured on this branch against real pi 0.85.1 over the leader
169
+ * extension's socket, on one clock. pi queues it (`queue_update`) and delivers it once THE TURN IN
170
+ * FLIGHT ends — a turn being one assistant response plus the tool calls it makes — and the next
171
+ * turn of the same run carries it to the model. Two runs, shaped to take the two halves apart,
172
+ * because a turn's model call and its tools are different intervals:
173
+ * · long MODEL CALL, no tools (`two-steers-result.json`): steer accepted +4.0s, the call ends and
174
+ * `turn_end` fires +20.018s, the model request carrying it +20.020s, `agent_settled` +20.023s.
175
+ * · instant model call, long TOOL (`tool-turn-result.json`): the model call ended at +0.016s
176
+ * returning a `bash` call, `sleep 25` ran, the steer was accepted at +5.0s with that call long
177
+ * over — and ZERO model requests happened until the tool finished. `turn_end` +25.037s, the
178
+ * model request carrying it +25.040s.
179
+ * The second run is the one that decides it: the model call the first run credited had already
180
+ * finished, and the steer still waited 20 more seconds. So the wait is the TURN, TOOLS INCLUDED.
181
+ *
182
+ * Three earlier spellings of this comment were wrong, each asserting more than its measurement
183
+ * could support (QA on #358, then two rounds of QA on #366): "at the end of the running turn" reads
184
+ * as "not until the run is over"; "at once" ignores the wait entirely; and "the remainder of the
185
+ * model call in flight" named the wrong interval, because every run behind it had no tools, which
186
+ * is exactly the condition under which a turn and its model call cannot be told apart.
187
+ *
188
+ * What follows for this check: "pi is idle now" alone does not mean it parked — the run may simply
189
+ * have carried it and ended, which is the common case over this transport. `pendingMessageCount` is
190
+ * the unambiguous half: still queued AND nothing running means only the person can unpark it, which
191
+ * is not delivery.
192
+ */
193
+ async #requireSteerLanded(signal) {
194
+ const state = await this.#command({ type: 'get_state' }, signal);
195
+ if (state.success !== true)
196
+ return;
197
+ const streaming = state.data?.isStreaming;
198
+ const pending = state.data?.pendingMessageCount;
199
+ if (typeof streaming !== 'boolean' || typeof pending !== 'number')
200
+ return;
201
+ if (streaming)
202
+ return;
203
+ this.#busy = false;
204
+ if (pending > 0)
205
+ throw this.#block('pi-steer-parked', PARKED_MESSAGE);
206
+ }
207
+ /**
208
+ * Learn which rows this pi session has already been given, from its own conversation. Also reports
209
+ * a reaction that happened for a submission whose answer we lost, so it is not lost with it.
210
+ */
211
+ async #readConversation(signal) {
212
+ const answer = await this.#command({ type: 'get_messages' }, signal);
213
+ if (answer.success !== true)
214
+ throw this.#block('pi-refused', `pi refused to list its conversation: ${errorText(answer)}`);
215
+ const messages = Array.isArray(answer.data?.messages) ? answer.data.messages : [];
216
+ for (const message of messages) {
217
+ const text = userMessageText(message);
218
+ if (text === undefined)
219
+ continue;
220
+ for (const key of rowKeysIn(text))
221
+ this.#remember(key);
222
+ const marker = markerIn(text);
223
+ if (marker !== undefined)
224
+ this.#reacted(marker);
225
+ }
226
+ this.#conversationRead = true;
227
+ }
228
+ async #command(command, signal) {
229
+ this.#requireLink();
230
+ try {
231
+ return await abortable(this.#opts.link.request(command), signal);
232
+ }
233
+ catch (err) {
234
+ if (signal.aborted || this.#closed)
235
+ throw err;
236
+ if (this.#opts.link.closed)
237
+ throw this.#block('pi-session-closed', CLOSED_MESSAGE);
238
+ throw err;
239
+ }
240
+ }
241
+ #requireLink() {
242
+ if (this.#closed || this.#opts.link.closed)
243
+ throw this.#block('pi-session-closed', CLOSED_MESSAGE);
244
+ if (this.#blocker?.code === 'pi-session-closed')
245
+ this.#blocker = undefined;
246
+ }
247
+ #block(code, message, fix = BLOCKER_FIX) {
248
+ const error = new PiDeliveryBlocked(code, message, fix);
249
+ this.#blocker = error.blocker;
250
+ return error;
251
+ }
252
+ /** Rows not yet submitted, minus the leader's own echo (drop only `leader` + own `causedBy`). */
253
+ #fresh(events) {
254
+ const isOwn = this.#opts.isOwnOperation ?? (() => false);
255
+ return events.filter((row) => !this.#submitted.has(rowKey(row)) && !(row.origin === 'leader' && row.causedBy !== null && isOwn(row.causedBy)));
256
+ }
257
+ #remember(key) {
258
+ this.#submitted.set(key, true);
259
+ // A row older than retention can never be re-dispatched, so neither can its key matter.
260
+ if (this.#submitted.size > MARKER_MEMORY)
261
+ this.#submitted.delete(this.#submitted.keys().next().value);
262
+ }
263
+ /**
264
+ * pi's stream, as far as this adapter reads it. `agent_start` / `agent_settled` are the busy
265
+ * boundary; a `user` message carrying one of our markers is the reaction — pi emits it immediately
266
+ * before the model request that carries it, and never while the text is still queued (`rpc-probe`).
267
+ */
268
+ #observe(message) {
269
+ const type = typeof message.type === 'string' ? message.type : undefined;
270
+ if (type === 'agent_start') {
271
+ this.#busy = true;
272
+ return;
273
+ }
274
+ if (type === 'agent_settled') {
275
+ this.#busy = false;
276
+ return;
277
+ }
278
+ if (type !== 'message_start' && type !== 'message_end')
279
+ return;
280
+ const text = userMessageText(message.message);
281
+ if (text === undefined)
282
+ return;
283
+ const marker = markerIn(text);
284
+ if (marker === undefined)
285
+ return;
286
+ if (this.#awaiting.has(marker))
287
+ this.#reacted(marker);
288
+ else if (marker.startsWith(`${MARKER_PREFIX}${this.projectId}:${this.#tag}:`))
289
+ this.#seenMarkers.add(marker);
290
+ }
291
+ #reacted(marker) {
292
+ const seq = this.#awaiting.get(marker);
293
+ if (seq === undefined)
294
+ return;
295
+ this.#awaiting.delete(marker);
296
+ try {
297
+ this.#opts.onReaction?.(seq);
298
+ }
299
+ catch {
300
+ /* a reporting failure is `reactedSeq` lagging — never a reason to submit again */
301
+ }
302
+ }
303
+ }
304
+ /** Separates two adapters' markers on one resumed pi session without reaching for `node:crypto`. */
305
+ let adapterOrdinal = 0;
306
+ const MARKER_PREFIX = 'xezar-event:';
307
+ const CLOSED_MESSAGE = 'The pi RPC session xezar was given is closed, so no event can be handed to it.';
308
+ const PARKED_MESSAGE = 'pi accepted the steered event but settled before a turn took it, so it is parked in pi\'s queue rather than in front of the model. xezar has not treated it as delivered and will hand it over again.';
309
+ const BLOCKER_FIX = 'Read events from your leader with the leader_events tool; nothing is lost while push is unavailable.';
310
+ const PI_EXTENSION_FIX =
311
+ // The file name is the SHIPPED one and a test pins that it exists in this package: this string is
312
+ // the only remedy a blocked pi leader is given, and it named `pi-leader-extension.mjs` for one
313
+ // round of review. Nothing of that name is built, packed or committed — the extension ships as
314
+ // `scripts/pi-leader-extension.ts`, which is what pi loads (extensions go through jiti).
315
+ 'Start your pi leader with xezar\'s leader extension (`pi --extension <xezar>/scripts/pi-leader-extension.ts`, or install it once in your pi settings), then attach again. Until then, read events with the leader_events tool — nothing is lost.';
316
+ /**
317
+ * Where a project's leader events go in pi. A live `link`: delivered through it. Absent or closed: a
318
+ * recoverable blocker the cockpit can show, naming WHY there is no link, because since `pi-link.ts`
319
+ * exists that is an answerable question — the leader extension is not running, or the pi that wrote
320
+ * the descriptor is gone — rather than the flat "pi has no address" it used to be. The rows stay in
321
+ * the journal either way, and the leader reads them with `leader_events`.
322
+ */
323
+ export function piReactionTarget(opts) {
324
+ if (opts.link !== undefined && !opts.link.closed) {
325
+ return { kind: 'rpc', adapter: new PiReactionAdapter({ ...opts, link: opts.link }) };
326
+ }
327
+ const because = opts.unreachable === undefined ? '' : ` (${opts.unreachable})`;
328
+ return {
329
+ kind: 'blocked',
330
+ blocker: {
331
+ code: 'pi-not-addressable',
332
+ recoverable: true,
333
+ message: `xezar has no live link to a pi leader for this project${because}. pi speaks RPC over its own stdin and stdout only — it has no port, socket or attach mode — so a pi you run yourself can be reached only from inside it, by the xezar leader extension. Events stay in the project journal and nothing is lost.`,
334
+ fix: PI_EXTENSION_FIX,
335
+ },
336
+ };
337
+ }
338
+ /**
339
+ * The text a pi leader receives for one dispatch. Pure: the same inputs always render the same text.
340
+ *
341
+ * It names xezar as the source and says what it is not, before any row — an event must never read as
342
+ * the user's instruction or approval (§ 12). The role instruction rides along because pi keeps no
343
+ * system prompt in a resumed session. Each row's summary is quoted as data, so text inside a summary
344
+ * cannot pose as the adapter's own framing; rows are the journal's own summaries, already scrubbed of
345
+ * secrets (F-15), never a payload. The marker line is how a later look at pi's conversation
346
+ * recognises this submission — `<eventId>@<ts>` per row, because `journalSeq` restarts in a recreated
347
+ * journal and the pair does not.
348
+ */
349
+ export function renderPiDispatch(dispatch, rows, roleInstruction, marker) {
350
+ const lines = [
351
+ '[xezar event notification]',
352
+ `Source: xezar, project ${dispatch.projectId}. Sent automatically by xezar. It is not a message from the user, not an instruction and not an approval.`,
353
+ `Your role: ${roleInstruction}`,
354
+ ];
355
+ if (rows.length > 0) {
356
+ lines.push(`Significant events (${rows.length}, oldest first):`);
357
+ for (const row of rows) {
358
+ const subject = `${row.subject.type} ${row.subject.id}${row.subject.version === null ? '' : ` @${row.subject.version}`}`;
359
+ lines.push(`- ${row.eventId} ${row.category} ${row.kind} on ${subject} (origin ${row.origin}): ${JSON.stringify(row.summary)}`);
360
+ }
361
+ }
362
+ if (dispatch.recovery) {
363
+ lines.push(`Gap: ${dispatch.recovery.message} (oldest retained ${dispatch.recovery.oldestSeq ?? 'none'}, latest ${dispatch.recovery.latestSeq}).`);
364
+ }
365
+ lines.push('Read the current state with the xezar tools before acting, and acknowledge the events you have taken into account.');
366
+ lines.push(`${marker} rows=${rows.map(rowKey).join(',')}`);
367
+ return lines.join('\n');
368
+ }
369
+ function rowKey(row) {
370
+ return `${row.eventId}@${row.ts}`;
371
+ }
372
+ /** The marker this adapter writes into every submission, as found in text pi echoed back. */
373
+ function markerIn(text) {
374
+ return new RegExp(`${MARKER_PREFIX}[^\\s]+`).exec(text)?.[0];
375
+ }
376
+ /** The row keys a marked submission listed, so a restart learns what pi was already told. */
377
+ function rowKeysIn(text) {
378
+ const listed = /\brows=([^\s]*)/.exec(text)?.[1];
379
+ return listed === undefined || listed === '' ? [] : listed.split(',').filter((key) => key !== '');
380
+ }
381
+ /** The text of a `user` message in pi's own shape, or undefined for anything else. */
382
+ function userMessageText(value) {
383
+ const message = value;
384
+ if (message?.role !== 'user')
385
+ return undefined;
386
+ if (typeof message.content === 'string')
387
+ return message.content;
388
+ if (!Array.isArray(message.content))
389
+ return undefined;
390
+ const parts = message.content
391
+ .filter((part) => part?.type === 'text' && typeof part.text === 'string')
392
+ .map((part) => part.text);
393
+ return parts.length > 0 ? parts.join('\n') : undefined;
394
+ }
395
+ function errorText(answer) {
396
+ const error = answer.error;
397
+ if (typeof error === 'string')
398
+ return error;
399
+ if (typeof error?.message === 'string')
400
+ return error.message;
401
+ return 'pi gave no reason';
402
+ }
403
+ function abortable(work, signal) {
404
+ if (signal.aborted)
405
+ return Promise.reject(new Error('aborted'));
406
+ return new Promise((resolve, reject) => {
407
+ const onAbort = () => reject(new Error('aborted'));
408
+ signal.addEventListener('abort', onAbort, { once: true });
409
+ work.then((value) => {
410
+ signal.removeEventListener('abort', onAbort);
411
+ resolve(value);
412
+ }, (err) => {
413
+ signal.removeEventListener('abort', onAbort);
414
+ reject(err instanceof Error ? err : new Error(String(err)));
415
+ });
416
+ });
417
+ }
418
+ //# sourceMappingURL=pi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi.js","sourceRoot":"","sources":["../../../src/mcp/adapters/pi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { type McpApiReference, type McpNotExposed } from '../contract/index.js';
2
+ /**
3
+ * #284 — what the cockpit's MCP API page shows, built from the RUNNING code (spec
4
+ * `mcp-api-reference-spec.md` § 11, R-06). Shipped, not a testkit: the route loads it lazily.
5
+ *
6
+ * `tools` is exactly what `tools/list` answers, produced by the same `toolListing()` the bridge
7
+ * calls (`bridge.ts`), so the page cannot disagree with the wire or with the committed
8
+ * `docs/features/mcp-server/mcp-api.json`. The registry is static code: nothing here needs the MCP
9
+ * service to be running, which is the point — a reader can review the API on a machine where MCP
10
+ * did not start (N-07).
11
+ *
12
+ * Reads only. No socket, no process, no network, no disk, no audit entry, no journal event. There
13
+ * is deliberately nothing here that could run a tool (spec § 13).
14
+ */
15
+ /**
16
+ * Listed arguments that are never accepted. The listing cannot say so — `project_config` lists
17
+ * `projectId` with an empty schema, which reads as "optional, any type" — so it is named here and
18
+ * its reason is its OWN description from the live listing. A test holds each entry to the listing.
19
+ */
20
+ export declare const REFUSED_ARGUMENTS: readonly {
21
+ readonly tool: string;
22
+ readonly argument: string;
23
+ }[];
24
+ /** What the server deliberately does not expose (J-4), each with the requirements that forbid it. */
25
+ export declare const NOT_EXPOSED: readonly McpNotExposed[];
26
+ export declare function buildMcpApiReference(xezarVersion: string): McpApiReference;
@@ -0,0 +1,133 @@
1
+ import { mcpToolListingSchema, } from '../contract/index.js';
2
+ import { HEALTH_TOOL } from './bridge.js';
3
+ import { SERVER_CAPABILITIES, SUPPORTED_PROTOCOL_VERSIONS } from './protocol.js';
4
+ import { toolListing } from './tool.js';
5
+ import { tools } from './tools/index.js';
6
+ import { REFUSED_ACTIONS } from './tools/project-config.js';
7
+ /**
8
+ * #284 — what the cockpit's MCP API page shows, built from the RUNNING code (spec
9
+ * `mcp-api-reference-spec.md` § 11, R-06). Shipped, not a testkit: the route loads it lazily.
10
+ *
11
+ * `tools` is exactly what `tools/list` answers, produced by the same `toolListing()` the bridge
12
+ * calls (`bridge.ts`), so the page cannot disagree with the wire or with the committed
13
+ * `docs/features/mcp-server/mcp-api.json`. The registry is static code: nothing here needs the MCP
14
+ * service to be running, which is the point — a reader can review the API on a machine where MCP
15
+ * did not start (N-07).
16
+ *
17
+ * Reads only. No socket, no process, no network, no disk, no audit entry, no journal event. There
18
+ * is deliberately nothing here that could run a tool (spec § 13).
19
+ */
20
+ /**
21
+ * Listed arguments that are never accepted. The listing cannot say so — `project_config` lists
22
+ * `projectId` with an empty schema, which reads as "optional, any type" — so it is named here and
23
+ * its reason is its OWN description from the live listing. A test holds each entry to the listing.
24
+ */
25
+ export const REFUSED_ARGUMENTS = [
26
+ { tool: 'project_config', argument: 'projectId' },
27
+ ];
28
+ /** What the server deliberately does not expose (J-4), each with the requirements that forbid it. */
29
+ export const NOT_EXPOSED = [
30
+ {
31
+ what: 'Resources, prompts and logging',
32
+ detail: 'The server advertises the tools capability only. Any other method is answered with "method not found".',
33
+ forbiddenBy: ['D-05'],
34
+ },
35
+ {
36
+ what: 'Account identity',
37
+ detail: 'No email, login, organisation, plan or credential appears in a tool answer. Account administration is refused.',
38
+ forbiddenBy: ['F-12', 'N-01'],
39
+ },
40
+ {
41
+ what: 'Secrets',
42
+ detail: 'No launch key, credential, token or environment value appears in any tool answer.',
43
+ forbiddenBy: ['F-15', 'A-12'],
44
+ },
45
+ {
46
+ what: 'Host-process control',
47
+ detail: 'No argument takes a process id, a signal, a command or a host path.',
48
+ forbiddenBy: ['F-08', 'M-04'],
49
+ },
50
+ {
51
+ what: 'Other projects',
52
+ detail: 'A session is bound to one project. No workspace setting, project registry or other project’s data is reachable.',
53
+ forbiddenBy: ['F-01', 'N-01'],
54
+ },
55
+ ];
56
+ /** Refusal-only actions, per tool. Today only `project_config` has them (`REFUSED_ACTIONS`). */
57
+ const REFUSED_ACTIONS_BY_TOOL = {
58
+ project_config: REFUSED_ACTIONS,
59
+ };
60
+ const DISCRIMINATORS = ['action', 'view', 'read'];
61
+ /** A listed schema's discriminator argument and its values, or null when it has none. */
62
+ function discriminatorOf(inputSchema) {
63
+ const properties = (inputSchema.properties ?? {});
64
+ for (const name of DISCRIMINATORS) {
65
+ const values = properties[name]?.enum;
66
+ if (Array.isArray(values))
67
+ return { name, values: values.filter((v) => typeof v === 'string') };
68
+ }
69
+ return null;
70
+ }
71
+ const discriminatorValues = (inputSchema) => discriminatorOf(inputSchema)?.values ?? [];
72
+ /** The two guards only MCP carries: the stale-write token (#250) and the idempotency key. */
73
+ const GUARDS = ['expectedVersion', 'operationId'];
74
+ /**
75
+ * #301 — which actions of `tool` are refused without each guard it lists, asked of the tool's OWN
76
+ * input schema: every performing action is validated with nothing but its discriminator, and an
77
+ * issue on the guard's path means that action needs it. The listing cannot answer this (a flat
78
+ * schema lists the guard as optional at the top), and the tool's description is prose.
79
+ *
80
+ * This reads what the schema ENFORCES, so it is only as complete as the schema: a rule a tool checks
81
+ * in its handler instead is invisible here. `mcp-reference-route.test.ts` pins the real registry's
82
+ * answer, so moving a rule out of a schema shows up as a failing test, not as a quietly wrong page.
83
+ */
84
+ function guardsOf(tool, listed, refused) {
85
+ const properties = (listed.inputSchema.properties ?? {});
86
+ const discriminator = discriminatorOf(listed.inputSchema);
87
+ const needs = (argument, probe) => {
88
+ const parsed = tool.inputSchema.safeParse(probe);
89
+ return !parsed.success && parsed.error.issues.some((issue) => issue.path[0] === argument);
90
+ };
91
+ return GUARDS.filter((argument) => Object.hasOwn(properties, argument)).map((argument) => {
92
+ if (!discriminator)
93
+ return { tool: tool.name, argument, everyCall: needs(argument, {}), requiredBy: [] };
94
+ const performing = discriminator.values.filter((action) => !Object.hasOwn(refused, action));
95
+ const requiredBy = performing.filter((action) => needs(argument, { [discriminator.name]: action }));
96
+ return { tool: tool.name, argument, everyCall: performing.length > 0 && requiredBy.length === performing.length, requiredBy };
97
+ });
98
+ }
99
+ export function buildMcpApiReference(xezarVersion) {
100
+ // Parsed through the contract's STRICT schema: a key `toolListing()` gains that the contract
101
+ // does not describe throws here, instead of being stripped from the page without a word.
102
+ const listing = mcpToolListingSchema.array().parse([HEALTH_TOOL, ...tools.map(toolListing)]);
103
+ const byName = new Map(listing.map((tool) => [tool.name, tool]));
104
+ const guards = tools.flatMap((tool) => guardsOf(tool, byName.get(tool.name), REFUSED_ACTIONS_BY_TOOL[tool.name] ?? {}));
105
+ const refusedActions = listing.flatMap((tool) => {
106
+ const refused = REFUSED_ACTIONS_BY_TOOL[tool.name] ?? {};
107
+ return discriminatorValues(tool.inputSchema)
108
+ .filter((action) => Object.hasOwn(refused, action))
109
+ .map((action) => ({
110
+ tool: tool.name,
111
+ action,
112
+ boundary: refused[action].boundary.replaceAll('-', ' '),
113
+ reason: refused[action].reason,
114
+ }));
115
+ });
116
+ const refusedArguments = REFUSED_ARGUMENTS.flatMap(({ tool, argument }) => {
117
+ const properties = (byName.get(tool)?.inputSchema.properties ?? {});
118
+ const description = properties[argument]?.description;
119
+ return typeof description === 'string' ? [{ tool, argument, reason: description }] : [];
120
+ });
121
+ return {
122
+ available: true,
123
+ xezarVersion,
124
+ protocolVersions: [...SUPPORTED_PROTOCOL_VERSIONS],
125
+ capabilities: { tools: { listChanged: SERVER_CAPABILITIES.tools.listChanged } },
126
+ tools: listing,
127
+ refusedActions,
128
+ refusedArguments,
129
+ guards,
130
+ notExposed: NOT_EXPOSED.map((item) => ({ ...item, forbiddenBy: [...item.forbiddenBy] })),
131
+ };
132
+ }
133
+ //# sourceMappingURL=api-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-reference.js","sourceRoot":"","sources":["../../src/mcp/api-reference.ts"],"names":[],"mappings":"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"}