@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.
- package/README.md +72 -39
- package/dist/agent-config/account-identity.d.ts +2 -8
- package/dist/agent-config/account-identity.js +54 -10
- package/dist/agent-config/account-identity.js.map +1 -1
- package/dist/agent-config/catalog.d.ts +49 -7
- package/dist/agent-config/catalog.js +107 -0
- package/dist/agent-config/catalog.js.map +1 -1
- package/dist/agent-config/model-settings/pi.d.ts +12 -6
- package/dist/agent-config/model-settings/pi.js +12 -6
- package/dist/agent-config/model-settings/pi.js.map +1 -1
- package/dist/agent-config/seed.js +10 -0
- package/dist/agent-config/seed.js.map +1 -1
- package/dist/contract/github.d.ts +15 -0
- package/dist/contract/index.d.ts +9 -0
- package/dist/contract/index.js +1527 -895
- package/dist/contract/mcp-api-reference.d.ts +174 -0
- package/dist/contract/mcp-audit.d.ts +103 -0
- package/dist/contract/mcp-discovery.d.ts +257 -0
- package/dist/contract/mcp-event-catalog.d.ts +121 -0
- package/dist/contract/mcp-event-catalog.test.d.ts +1 -0
- package/dist/contract/mcp-idempotency.d.ts +200 -0
- package/dist/contract/mcp-journal.d.ts +268 -0
- package/dist/contract/mcp-leader.d.ts +88 -0
- package/dist/contract/mcp-ownership.d.ts +78 -0
- package/dist/contract/mcp-versioning.d.ts +139 -0
- package/dist/contract/runs.d.ts +81 -0
- package/dist/core/agent-profiles.d.ts +15 -4
- package/dist/core/agent-profiles.js +22 -8
- package/dist/core/agent-profiles.js.map +1 -1
- package/dist/core/opencode-server-runner.js +71 -11
- package/dist/core/opencode-server-runner.js.map +1 -1
- package/dist/core/secret-redaction.d.ts +7 -1
- package/dist/core/secret-redaction.js +44 -10
- package/dist/core/secret-redaction.js.map +1 -1
- package/dist/index.js +138 -27
- package/dist/index.js.map +1 -1
- package/dist/machine-identity.d.ts +66 -0
- package/dist/machine-identity.js +96 -0
- package/dist/machine-identity.js.map +1 -0
- package/dist/mcp/adapters/claude-code.d.ts +41 -0
- package/dist/mcp/adapters/claude-code.js +40 -0
- package/dist/mcp/adapters/claude-code.js.map +1 -0
- package/dist/mcp/adapters/codex.d.ts +140 -0
- package/dist/mcp/adapters/codex.js +323 -0
- package/dist/mcp/adapters/codex.js.map +1 -0
- package/dist/mcp/adapters/opencode.d.ts +148 -0
- package/dist/mcp/adapters/opencode.js +528 -0
- package/dist/mcp/adapters/opencode.js.map +1 -0
- package/dist/mcp/adapters/pi-link.d.ts +102 -0
- package/dist/mcp/adapters/pi-link.js +235 -0
- package/dist/mcp/adapters/pi-link.js.map +1 -0
- package/dist/mcp/adapters/pi.d.ts +188 -0
- package/dist/mcp/adapters/pi.js +418 -0
- package/dist/mcp/adapters/pi.js.map +1 -0
- package/dist/mcp/api-reference.d.ts +26 -0
- package/dist/mcp/api-reference.js +133 -0
- package/dist/mcp/api-reference.js.map +1 -0
- package/dist/mcp/audit-trail.d.ts +162 -0
- package/dist/mcp/audit-trail.js +306 -0
- package/dist/mcp/audit-trail.js.map +1 -0
- package/dist/mcp/bridge.d.ts +81 -0
- package/dist/mcp/bridge.js +407 -0
- package/dist/mcp/bridge.js.map +1 -0
- package/dist/mcp/connection-file.d.ts +49 -0
- package/dist/mcp/connection-file.js +63 -0
- package/dist/mcp/connection-file.js.map +1 -0
- package/dist/mcp/echo-guard.d.ts +94 -0
- package/dist/mcp/echo-guard.js +160 -0
- package/dist/mcp/echo-guard.js.map +1 -0
- package/dist/mcp/event-catalog.d.ts +120 -0
- package/dist/mcp/event-catalog.js +306 -0
- package/dist/mcp/event-catalog.js.map +1 -0
- package/dist/mcp/event-controller.d.ts +230 -0
- package/dist/mcp/event-controller.js +596 -0
- package/dist/mcp/event-controller.js.map +1 -0
- package/dist/mcp/event-journal.d.ts +57 -0
- package/dist/mcp/event-journal.js +423 -0
- package/dist/mcp/event-journal.js.map +1 -0
- package/dist/mcp/index.d.ts +63 -0
- package/dist/mcp/index.js +386 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/ipc.d.ts +202 -0
- package/dist/mcp/ipc.js +206 -0
- package/dist/mcp/ipc.js.map +1 -0
- package/dist/mcp/leader-delivery.d.ts +119 -0
- package/dist/mcp/leader-delivery.js +450 -0
- package/dist/mcp/leader-delivery.js.map +1 -0
- package/dist/mcp/operation-receipts.d.ts +210 -0
- package/dist/mcp/operation-receipts.js +723 -0
- package/dist/mcp/operation-receipts.js.map +1 -0
- package/dist/mcp/project-catalogs.d.ts +23 -0
- package/dist/mcp/project-catalogs.js +25 -0
- package/dist/mcp/project-catalogs.js.map +1 -0
- package/dist/mcp/project-leaders.d.ts +26 -0
- package/dist/mcp/project-leaders.js +17 -0
- package/dist/mcp/project-leaders.js.map +1 -0
- package/dist/mcp/protocol.d.ts +50 -0
- package/dist/mcp/protocol.js +47 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/reconnect.d.ts +297 -0
- package/dist/mcp/reconnect.js +465 -0
- package/dist/mcp/reconnect.js.map +1 -0
- package/dist/mcp/resource-ownership.d.ts +228 -0
- package/dist/mcp/resource-ownership.js +390 -0
- package/dist/mcp/resource-ownership.js.map +1 -0
- package/dist/mcp/service-adapter.d.ts +16488 -0
- package/dist/mcp/service-adapter.js +148 -0
- package/dist/mcp/service-adapter.js.map +1 -0
- package/dist/mcp/service.d.ts +83 -0
- package/dist/mcp/service.js +233 -0
- package/dist/mcp/service.js.map +1 -0
- package/dist/mcp/session-binding.d.ts +105 -0
- package/dist/mcp/session-binding.js +163 -0
- package/dist/mcp/session-binding.js.map +1 -0
- package/dist/mcp/stale-write.d.ts +145 -0
- package/dist/mcp/stale-write.js +219 -0
- package/dist/mcp/stale-write.js.map +1 -0
- package/dist/mcp/tool.d.ts +48 -0
- package/dist/mcp/tool.js +24 -0
- package/dist/mcp/tool.js.map +1 -0
- package/dist/mcp/tools/discovery.d.ts +40 -0
- package/dist/mcp/tools/discovery.js +232 -0
- package/dist/mcp/tools/discovery.js.map +1 -0
- package/dist/mcp/tools/execution-control.d.ts +171 -0
- package/dist/mcp/tools/execution-control.js +513 -0
- package/dist/mcp/tools/execution-control.js.map +1 -0
- package/dist/mcp/tools/handoff-git.d.ts +26 -0
- package/dist/mcp/tools/handoff-git.js +544 -0
- package/dist/mcp/tools/handoff-git.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +2 -0
- package/dist/mcp/tools/index.js +23 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/leader-events.d.ts +46 -0
- package/dist/mcp/tools/leader-events.js +126 -0
- package/dist/mcp/tools/leader-events.js.map +1 -0
- package/dist/mcp/tools/local-handoff.d.ts +102 -0
- package/dist/mcp/tools/local-handoff.js +306 -0
- package/dist/mcp/tools/local-handoff.js.map +1 -0
- package/dist/mcp/tools/project-config.d.ts +340 -0
- package/dist/mcp/tools/project-config.js +1075 -0
- package/dist/mcp/tools/project-config.js.map +1 -0
- package/dist/mcp/tools/results-evidence.d.ts +112 -0
- package/dist/mcp/tools/results-evidence.js +1045 -0
- package/dist/mcp/tools/results-evidence.js.map +1 -0
- package/dist/mcp/tools/task-create.d.ts +70 -0
- package/dist/mcp/tools/task-create.js +473 -0
- package/dist/mcp/tools/task-create.js.map +1 -0
- package/dist/mcp/tools/task-reads.d.ts +102 -0
- package/dist/mcp/tools/task-reads.js +689 -0
- package/dist/mcp/tools/task-reads.js.map +1 -0
- package/dist/mcp/tools/work-organisation.d.ts +2 -0
- package/dist/mcp/tools/work-organisation.js +502 -0
- package/dist/mcp/tools/work-organisation.js.map +1 -0
- package/dist/paths.d.ts +17 -7
- package/dist/paths.js +18 -8
- package/dist/paths.js.map +1 -1
- package/dist/runs/project-writer.js +76 -11
- package/dist/runs/project-writer.js.map +1 -1
- package/dist/runs/retention.d.ts +25 -2
- package/dist/runs/retention.js +63 -2
- package/dist/runs/retention.js.map +1 -1
- package/dist/server/forge/github.js +44 -0
- package/dist/server/forge/github.js.map +1 -1
- package/dist/server/forge/types.d.ts +16 -0
- package/dist/server/project-context.d.ts +81 -8
- package/dist/server/project-context.js +143 -35
- package/dist/server/project-context.js.map +1 -1
- package/dist/server/server.d.ts +1050 -53
- package/dist/server/server.js +326 -120
- package/dist/server/server.js.map +1 -1
- package/dist/server/validators.d.ts +26 -6
- package/dist/server/validators.js +17 -0
- package/dist/server/validators.js.map +1 -1
- package/dist/skills-remote.js +64 -6
- package/dist/skills-remote.js.map +1 -1
- package/dist/workflows/run.d.ts +277 -12
- package/dist/workflows/run.js +599 -79
- package/dist/workflows/run.js.map +1 -1
- package/dist/workspace/agent-profiles.d.ts +30 -3
- package/dist/workspace/agent-profiles.js +39 -4
- package/dist/workspace/agent-profiles.js.map +1 -1
- package/dist/workspace/config.js +25 -4
- package/dist/workspace/config.js.map +1 -1
- package/dist/workspace/project-owner.d.ts +210 -0
- package/dist/workspace/project-owner.js +518 -0
- package/dist/workspace/project-owner.js.map +1 -0
- package/package.json +5 -3
- package/scripts/pi-leader-extension.ts +420 -0
- package/scripts/sync-readme.mjs +83 -2
- package/web/dist/assets/{alert-dialog-BVsVcbFy.js → alert-dialog-D6rP6d39.js} +1 -1
- package/web/dist/assets/arrow-down-DMOfFIzW.js +1 -0
- package/web/dist/assets/arrow-left-C4Ec2BUg.js +1 -0
- package/web/dist/assets/{centered-state-Djgly6Ni.js → centered-state-B0ZqrPaa.js} +12 -12
- package/web/dist/assets/chevron-right-mHJ-crvn.js +1 -0
- package/web/dist/assets/{collapsible-DZ4D-DHG.js → collapsible-DchEHgUM.js} +1 -1
- package/web/dist/assets/{commit-list-B-z1JL9G.js → commit-list-BpMim1Rv.js} +1 -1
- package/web/dist/assets/{compare-variants-CdS8r8E2.js → compare-variants-D5ItQbAq.js} +1 -1
- package/web/dist/assets/{diff-BKew9rQX.js → diff-HqqZ_WDR.js} +2 -2
- package/web/dist/assets/{diff-stat-D0mc5V6Z.js → diff-stat-NsU-3ecr.js} +1 -1
- package/web/dist/assets/{diff-view-xpIcuLIC.js → diff-view-B7c11Xux.js} +1 -1
- package/web/dist/assets/{dropdown-menu-CEQziH7X.js → dropdown-menu-CtTpnyod.js} +1 -1
- package/web/dist/assets/{editable-title-BCX3T2BY.js → editable-title-BXtuGQry.js} +1 -1
- package/web/dist/assets/{ellipsis-vertical-ByVdDc4z.js → ellipsis-vertical-MHtGZSH6.js} +1 -1
- package/web/dist/assets/{file-xBSB5hXO.js → file-DKLw5SOb.js} +1 -1
- package/web/dist/assets/{folder-CldgbZ5A.js → folder-Drw6-ufH.js} +1 -1
- package/web/dist/assets/{git-toolbar-BYlnf4qf.js → git-toolbar-BQH8bORV.js} +1 -1
- package/web/dist/assets/github-ynnpKFCJ.js +1 -0
- package/web/dist/assets/{image-preview-Cv5mZr0n.js → image-preview-DLhMN_R9.js} +1 -1
- package/web/dist/assets/index-BEsrNdXt.js +29 -0
- package/web/dist/assets/index-Da8DOBau.css +2 -0
- package/web/dist/assets/{markdown-CgEEE2ND.js → markdown-q365eN6Y.js} +1 -1
- package/web/dist/assets/{new-task-form-1r2bgsdD.js → new-task-form-CW-hNIWQ.js} +1 -1
- package/web/dist/assets/{pill-q-x22_ej.js → pill-B_TFulXa.js} +1 -1
- package/web/dist/assets/{project-router-VDN4jQM8.js → project-router-CLPc-ImZ.js} +1 -1
- package/web/dist/assets/{prompt-templates-BmZtCe-L.js → prompt-templates-CLc4RxyO.js} +1 -1
- package/web/dist/assets/{repo-git-C2On8SlI.js → repo-git-Is7R6-Tj.js} +1 -1
- package/web/dist/assets/{run-diff-vguEMJzh.js → run-diff-DHMvDlVo.js} +2 -2
- package/web/dist/assets/run-header-DvZJbQ8I.js +1 -0
- package/web/dist/assets/{search-x-QmhaAbg5.js → search-x-Ce2MMetu.js} +1 -1
- package/web/dist/assets/{skill-empty-hint-BrzUwByZ.js → skill-empty-hint-C_xCSvzB.js} +1 -1
- package/web/dist/assets/skills-CivqD4si.js +1 -0
- package/web/dist/assets/{sparkles-CP8QDOgX.js → sparkles-BCqPoj5g.js} +1 -1
- package/web/dist/assets/{square-terminal-CGypshSD.js → square-terminal-ikj5g-oK.js} +1 -1
- package/web/dist/assets/{tab-link-D4lFzzI1.js → tab-link-VsEjFV1Y.js} +1 -1
- package/web/dist/assets/{task-changes-6aopqAmV.js → task-changes-CH-E81-_.js} +1 -1
- package/web/dist/assets/{task-commits-u-dwaCyw.js → task-commits-DqfkMFxp.js} +1 -1
- package/web/dist/assets/{task-files-IMOZnIUK.js → task-files-D_nkHZ_a.js} +2 -2
- package/web/dist/assets/task-thread-bxQompxJ.js +9 -0
- package/web/dist/assets/{textarea-CyBCyKIk.js → textarea-xajuW1L_.js} +1 -1
- package/web/dist/assets/{thread-loading-Cuq2jchC.js → thread-loading-C3JQZbIE.js} +1 -1
- package/web/dist/assets/{trash-2-B6Dp3ZNs.js → trash-2-hJrBhpUD.js} +1 -1
- package/web/dist/assets/{triangle-alert-xBya8FUp.js → triangle-alert-D1u82y1a.js} +1 -1
- package/web/dist/assets/{upload-D6MXEB9p.js → upload-Bn6FBZ59.js} +1 -1
- package/web/dist/assets/{use-desktop-fLDKEJoB.js → use-desktop-DOIMfHfY.js} +1 -1
- package/web/dist/assets/{use-submit-shortcut-JTpNzU-u.js → use-submit-shortcut-Dj4DHTdE.js} +1 -1
- package/web/dist/assets/utils-YwBpOoRN.js +64 -0
- package/web/dist/assets/{workflows-BY3KTkFw.js → workflows-CTaAKz2m.js} +3 -3
- package/web/dist/assets/{zoomable-image-GTYwLtkS.js → zoomable-image-C1-6P3tg.js} +1 -1
- package/web/dist/index.html +23 -23
- package/web/dist/assets/arrow-down-CYgAlZtW.js +0 -1
- package/web/dist/assets/arrow-left-BOqE0a3k.js +0 -1
- package/web/dist/assets/chevron-right-Cc9BWnbN.js +0 -1
- package/web/dist/assets/github-CqGK62Lw.js +0 -1
- package/web/dist/assets/index-3nxVmdn5.css +0 -2
- package/web/dist/assets/index-qX7n11FX.js +0 -9
- package/web/dist/assets/refresh-cw-DcNCj2nu.js +0 -1
- package/web/dist/assets/run-header-DM7MiKj9.js +0 -1
- package/web/dist/assets/skills-Bj-uG_Hz.js +0 -1
- package/web/dist/assets/task-thread-CbMPmrWq.js +0 -9
- 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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"}
|