@claudexor/cli 3.3.15 → 3.4.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/dist/acp-surface-runner.d.ts +3 -2
- package/dist/acp-surface-runner.d.ts.map +1 -1
- package/dist/acp-surface-runner.js +20 -17
- package/dist/acp-surface-runner.js.map +1 -1
- package/dist/claudexord-entry.d.ts +7 -0
- package/dist/claudexord-entry.d.ts.map +1 -1
- package/dist/claudexord-entry.js +14 -0
- package/dist/claudexord-entry.js.map +1 -1
- package/dist/claudexord.d.ts +0 -10
- package/dist/claudexord.d.ts.map +1 -1
- package/dist/claudexord.js +95 -100
- package/dist/claudexord.js.map +1 -1
- package/dist/cli.js +10 -12
- package/dist/cli.js.map +1 -1
- package/dist/control-services.d.ts +4 -1
- package/dist/control-services.d.ts.map +1 -1
- package/dist/control-services.js +22 -21
- package/dist/control-services.js.map +1 -1
- package/dist/daemon-admission-runtime.d.ts +63 -0
- package/dist/daemon-admission-runtime.d.ts.map +1 -0
- package/dist/daemon-admission-runtime.js +180 -0
- package/dist/daemon-admission-runtime.js.map +1 -0
- package/dist/daemon-launch.d.ts +53 -0
- package/dist/daemon-launch.d.ts.map +1 -0
- package/dist/daemon-launch.js +211 -0
- package/dist/daemon-launch.js.map +1 -0
- package/dist/daemon-lifecycle.d.ts +14 -2
- package/dist/daemon-lifecycle.d.ts.map +1 -1
- package/dist/daemon-lifecycle.js +53 -10
- package/dist/daemon-lifecycle.js.map +1 -1
- package/dist/daemon-run.d.ts +7 -1
- package/dist/daemon-run.d.ts.map +1 -1
- package/dist/daemon-run.js +76 -20
- package/dist/daemon-run.js.map +1 -1
- package/dist/daemon-start-report.d.ts +8 -0
- package/dist/daemon-start-report.d.ts.map +1 -0
- package/dist/daemon-start-report.js +24 -0
- package/dist/daemon-start-report.js.map +1 -0
- package/dist/daemon-startup.d.ts +90 -0
- package/dist/daemon-startup.d.ts.map +1 -0
- package/dist/daemon-startup.js +156 -0
- package/dist/daemon-startup.js.map +1 -0
- package/dist/delegation-belt-descriptor.d.ts +1 -1
- package/dist/delegation-belt-descriptor.js +1 -1
- package/dist/engine-skew.d.ts +4 -0
- package/dist/engine-skew.d.ts.map +1 -1
- package/dist/engine-skew.js +10 -3
- package/dist/engine-skew.js.map +1 -1
- package/dist/mcp-run-projections.d.ts +78 -1
- package/dist/mcp-run-projections.d.ts.map +1 -1
- package/dist/mcp-run-projections.js +57 -3
- package/dist/mcp-run-projections.js.map +1 -1
- package/dist/mcp-runner.d.ts.map +1 -1
- package/dist/mcp-runner.js +41 -37
- package/dist/mcp-runner.js.map +1 -1
- package/dist/ops-commands.d.ts +9 -0
- package/dist/ops-commands.d.ts.map +1 -1
- package/dist/ops-commands.js +62 -67
- package/dist/ops-commands.js.map +1 -1
- package/dist/ops-harness-selection.d.ts +6 -0
- package/dist/ops-harness-selection.d.ts.map +1 -0
- package/dist/ops-harness-selection.js +29 -0
- package/dist/ops-harness-selection.js.map +1 -0
- package/dist/orphan-sweeper.js +13 -3
- package/dist/orphan-sweeper.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +1 -0
- package/dist/registry.js.map +1 -1
- package/dist/remote-command.d.ts +10 -1
- package/dist/remote-command.d.ts.map +1 -1
- package/dist/remote-command.js +34 -10
- package/dist/remote-command.js.map +1 -1
- package/dist/runtime-replacement-stop.d.ts +13 -2
- package/dist/runtime-replacement-stop.d.ts.map +1 -1
- package/dist/runtime-replacement-stop.js +46 -11
- package/dist/runtime-replacement-stop.js.map +1 -1
- package/dist/setup-lifecycle-binding.d.ts +7 -1
- package/dist/setup-lifecycle-binding.d.ts.map +1 -1
- package/dist/setup-lifecycle-binding.js +27 -17
- package/dist/setup-lifecycle-binding.js.map +1 -1
- package/dist/startup-diagnostics.d.ts +61 -0
- package/dist/startup-diagnostics.d.ts.map +1 -0
- package/dist/startup-diagnostics.js +416 -0
- package/dist/startup-diagnostics.js.map +1 -0
- package/dist/thread-turn-services.d.ts +1 -1
- package/dist/thread-turn-services.d.ts.map +1 -1
- package/dist/thread-turn-services.js.map +1 -1
- package/package.json +23 -23
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two-stage daemon startup admission (issue #165 D5).
|
|
3
|
+
*
|
|
4
|
+
* Stage order enforced by main(): (1) acquire the root authority barrier with
|
|
5
|
+
* its epoch/floor refusals; (2) read-only prepare + validate the global AND
|
|
6
|
+
* every registered project partition — zero recovery writes; (3) bind the
|
|
7
|
+
* REAL transport (socket + control API) with product admission CLOSED and
|
|
8
|
+
* prove self-health/exact identity through it; (4) only then advance the
|
|
9
|
+
* semantic floor, run destructive recovery (crash-GC, prepared activation,
|
|
10
|
+
* recoverAfterStartup terminalization) and open normal admission. A
|
|
11
|
+
* recovery-needed partition leaves the recovery plane online with the floor
|
|
12
|
+
* UNCHANGED and cleanup OFF.
|
|
13
|
+
*/
|
|
14
|
+
import { appendFileSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { JournalRecoveryRequiredError } from "@claudexor/journal";
|
|
17
|
+
import { CONTROL_PROTOCOL_MAJOR } from "@claudexor/schema";
|
|
18
|
+
/** Single canonical admission snapshot; both transports read the SAME fact. */
|
|
19
|
+
export class DaemonStartupAdmission {
|
|
20
|
+
mode = "recovery_only";
|
|
21
|
+
snapshot = () => this.mode;
|
|
22
|
+
openNormal() {
|
|
23
|
+
this.mode = "normal";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Stage-2 verdict: every partition that blocks destructive recovery. The
|
|
27
|
+
* loose Picks let the C6 in-process reopen feed LIVE inspections through the
|
|
28
|
+
* same verdict the frozen stage-2 receipts use. */
|
|
29
|
+
export function recoveryBlockedPartitions(input) {
|
|
30
|
+
const blocked = [];
|
|
31
|
+
if (input.globalPreparation.inspection.status !== "ready")
|
|
32
|
+
blocked.push("global");
|
|
33
|
+
if (input.partitionsPreparation.coverage !== "complete") {
|
|
34
|
+
blocked.push("project-registry");
|
|
35
|
+
}
|
|
36
|
+
blocked.push(...input.partitionsPreparation.recoveryRequiredPartitions);
|
|
37
|
+
return blocked;
|
|
38
|
+
}
|
|
39
|
+
/** C6a: whether the control API binds this startup. CLAUDEXOR_NO_CONTROL_API=1
|
|
40
|
+
* is honored only on a clean verdict — the recovery surface IS the point of
|
|
41
|
+
* the recovery plane, so a recovery-required verdict overrides it (logged). */
|
|
42
|
+
export function controlApiEnabledForStartup(input) {
|
|
43
|
+
if (!input.disabledByEnv)
|
|
44
|
+
return true;
|
|
45
|
+
if (input.blockedPartitions.length === 0)
|
|
46
|
+
return false;
|
|
47
|
+
input.log("recovery-required startup verdict overrides CLAUDEXOR_NO_CONTROL_API=1: binding the control API to serve the recovery surface");
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
/** Stage 3 bind: start the REAL socket + control transports (product
|
|
51
|
+
* admission still closed) and publish their addresses. Returns the control
|
|
52
|
+
* address, or null when the control API is disabled or shutdown began. */
|
|
53
|
+
export async function bindRecoveryTransport(input) {
|
|
54
|
+
const stamp = () => `[${new Date().toISOString()}]`;
|
|
55
|
+
if (!input.requested())
|
|
56
|
+
await input.server.start();
|
|
57
|
+
if (!input.requested()) {
|
|
58
|
+
appendFileSync(input.logPath, `${stamp()} claudexord listening on ${input.socketPath} (recovery-only admission)\n`);
|
|
59
|
+
}
|
|
60
|
+
if (!input.control) {
|
|
61
|
+
if (!input.requested()) {
|
|
62
|
+
appendFileSync(input.logPath, `${stamp()} claudexor control-api disabled by CLAUDEXOR_NO_CONTROL_API=1\n`);
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (input.requested())
|
|
67
|
+
return null;
|
|
68
|
+
const controlAddr = await input.control.start();
|
|
69
|
+
if (input.requested())
|
|
70
|
+
return null;
|
|
71
|
+
writeFileSync(join(input.daemonDir, "control-api.json"), `${JSON.stringify({ ...controlAddr, tokenPath: join(input.daemonDir, "token") }, null, 2)}\n`, { mode: 0o600 });
|
|
72
|
+
appendFileSync(input.logPath, `${stamp()} claudexor control-api listening on http://${controlAddr.host}:${controlAddr.port}\n`);
|
|
73
|
+
return controlAddr;
|
|
74
|
+
}
|
|
75
|
+
/** Stage 3 proof: prove self-health and exact identity through the REAL
|
|
76
|
+
* transport while product admission is still closed. */
|
|
77
|
+
export async function proveRecoveryTransport(input) {
|
|
78
|
+
const health = (await input.socket.health());
|
|
79
|
+
if (health?.ok !== true || health.servingMode !== "recovery_only") {
|
|
80
|
+
throw new Error("daemon transport proof failed: socket health did not report a recovery-only serving daemon");
|
|
81
|
+
}
|
|
82
|
+
if (!input.control)
|
|
83
|
+
return;
|
|
84
|
+
const response = await fetch(`http://${input.control.host}:${input.control.port}/v2/handshake`, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: {
|
|
87
|
+
authorization: `Bearer ${input.token}`,
|
|
88
|
+
"content-type": "application/json",
|
|
89
|
+
},
|
|
90
|
+
body: JSON.stringify({ protocolMajor: CONTROL_PROTOCOL_MAJOR, client: "claudexord-startup" }),
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
throw new Error(`daemon transport proof failed: control handshake HTTP ${response.status}`);
|
|
94
|
+
}
|
|
95
|
+
const body = (await response.json());
|
|
96
|
+
if (body?.engine?.version !== input.identity.version ||
|
|
97
|
+
body.engine.sha !== input.identity.sha ||
|
|
98
|
+
body.servingMode !== "recovery_only") {
|
|
99
|
+
throw new Error("daemon transport proof failed: control handshake did not prove this exact recovery-only runtime");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Stage 4: revalidate every read-only preparation FIRST; only on all-green
|
|
103
|
+
* advance the floor, run destructive recovery and open normal admission — or
|
|
104
|
+
* the protected recovery-only outcome (floor unchanged, cleanup off). */
|
|
105
|
+
export async function completeStartupAdmission(input) {
|
|
106
|
+
if (input.blockedPartitions.length > 0) {
|
|
107
|
+
input.log(`startup admission: serving recovery only — floor unchanged, destructive recovery and cleanup OFF (recovery required: ${input.blockedPartitions.join(", ")})`);
|
|
108
|
+
return "recovery_only";
|
|
109
|
+
}
|
|
110
|
+
// C3: prove the filesystem still matches every stage-2 preparation BEFORE
|
|
111
|
+
// the first irreversible act. Any revalidation failure leaves the floor
|
|
112
|
+
// UNCHANGED and zero destructive work done.
|
|
113
|
+
try {
|
|
114
|
+
input.global.revalidatePreparation();
|
|
115
|
+
input.partitions.revalidatePreparation();
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
if (error instanceof JournalRecoveryRequiredError) {
|
|
119
|
+
input.log(`startup admission: preparation revalidation failed; serving recovery only with floor unchanged (${error.message})`);
|
|
120
|
+
return "recovery_only";
|
|
121
|
+
}
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
input.grant.advanceFloor();
|
|
125
|
+
await input.crashGc();
|
|
126
|
+
try {
|
|
127
|
+
input.global.activatePrepared();
|
|
128
|
+
input.partitions.activatePrepared();
|
|
129
|
+
input.global.recoverAfterStartup();
|
|
130
|
+
input.partitions.recoverAfterStartup();
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
if (error instanceof JournalRecoveryRequiredError) {
|
|
134
|
+
// Activation re-revalidates and raced a journal mutation: stay on the
|
|
135
|
+
// recovery plane instead of serving a partition we could not activate.
|
|
136
|
+
input.log(`startup admission: prepared activation entered recovery; serving recovery only (${error.message})`);
|
|
137
|
+
return "recovery_only";
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
input.admission.openNormal();
|
|
142
|
+
input.log("startup admission: normal product admission open");
|
|
143
|
+
return "normal";
|
|
144
|
+
}
|
|
145
|
+
/** Post-admission ghost-project quarantine (F2), normal admission only. */
|
|
146
|
+
export function quarantineGhostProjectsAtStartup(threads, log) {
|
|
147
|
+
try {
|
|
148
|
+
for (const ghost of threads.quarantineGhostProjects()) {
|
|
149
|
+
log(`projects: quarantined ghost ${ghost.projectId} (${ghost.reason}): ${ghost.root}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
log(`projects: ghost sweep failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=daemon-startup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-startup.js","sourceRoot":"","sources":["../src/daemon-startup.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@ import type { ExtraMcpServer, PaidBudget } from "@claudexor/schema";
|
|
|
6
6
|
* for credential isolation. Left to default, the belt's `defaultSocketPath()`
|
|
7
7
|
* and token lookup both re-derive under that scoped HOME → a socket/token that
|
|
8
8
|
* does not exist → the belt tries to AUTO-START a fresh daemon there, fails
|
|
9
|
-
*
|
|
9
|
+
* after its bounded readiness wait, and every belt run tool errors. Pin
|
|
10
10
|
* the belt to the real daemon by injecting the ACTUAL config root (so the token
|
|
11
11
|
* and daemon dir resolve to this daemon's) and the exact socket path — both
|
|
12
12
|
* captured from the live daemon env at descriptor-build time, so this is correct
|
|
@@ -11,7 +11,7 @@ import { userConfigDir } from "@claudexor/util";
|
|
|
11
11
|
* for credential isolation. Left to default, the belt's `defaultSocketPath()`
|
|
12
12
|
* and token lookup both re-derive under that scoped HOME → a socket/token that
|
|
13
13
|
* does not exist → the belt tries to AUTO-START a fresh daemon there, fails
|
|
14
|
-
*
|
|
14
|
+
* after its bounded readiness wait, and every belt run tool errors. Pin
|
|
15
15
|
* the belt to the real daemon by injecting the ACTUAL config root (so the token
|
|
16
16
|
* and daemon dir resolve to this daemon's) and the exact socket path — both
|
|
17
17
|
* captured from the live daemon env at descriptor-build time, so this is correct
|
package/dist/engine-skew.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ export interface EngineSkew {
|
|
|
12
12
|
export interface EngineIdentity {
|
|
13
13
|
engineVersion: string | null;
|
|
14
14
|
engineBuildSha: string | null;
|
|
15
|
+
/** Product admission at handshake time (issue #165 D5). A pre-#165 daemon
|
|
16
|
+
* reports nothing and is always serving normally, so absent maps to
|
|
17
|
+
* `normal`; `recovery_only` means product routes are typed-closed. */
|
|
18
|
+
servingMode: "normal" | "recovery_only";
|
|
15
19
|
}
|
|
16
20
|
/** The ONE remedy wording — stderr advisory and typed requiredActions speak
|
|
17
21
|
* with the same voice, so it must stay CALLER-NEUTRAL: acting commands
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine-skew.d.ts","sourceRoot":"","sources":["../src/engine-skew.ts"],"names":[],"mappings":"AA0BA,gFAAgF;AAChF,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;kFAGkF;AAClF,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"engine-skew.d.ts","sourceRoot":"","sources":["../src/engine-skew.ts"],"names":[],"mappings":"AA0BA,gFAAgF;AAChF,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;kFAGkF;AAClF,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;0EAEsE;IACtE,WAAW,EAAE,QAAQ,GAAG,eAAe,CAAC;CACzC;AAED;;;;;;sEAMsE;AACtE,eAAO,MAAM,kBAAkB,uJACkH,CAAC;AAIlJ;;sEAEsE;AACtE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAE9D;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,IAAI,UAAU,GAAG,IAAI,CAEtD;AAQD,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,MAAM,EAAE,cAAc,CAAC;IACvB;uEACmE;IACnE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,iBAAiB,CAgCzE;AAED;;2DAE2D;AAC3D,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,mBAAmB,EAC3B,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAC/B,mBAAmB,CAYrB"}
|
package/dist/engine-skew.js
CHANGED
|
@@ -58,9 +58,13 @@ export function consumeHandshakeIdentity(body) {
|
|
|
58
58
|
if (!parsed.success) {
|
|
59
59
|
// Identity is advisory; a malformed body never fails the handshake.
|
|
60
60
|
recordEngineSkew(null);
|
|
61
|
-
return {
|
|
61
|
+
return {
|
|
62
|
+
engine: { engineVersion: null, engineBuildSha: null, servingMode: "normal" },
|
|
63
|
+
skewAdvisory: null,
|
|
64
|
+
};
|
|
62
65
|
}
|
|
63
66
|
const { version, sha } = parsed.data.engine;
|
|
67
|
+
const servingMode = parsed.data.servingMode ?? "normal";
|
|
64
68
|
const daemonVersion = ENGINE_VERSION_RE.test(version) ? version : null;
|
|
65
69
|
const engineBuildSha = sha === "unknown" || BUILD_SHA_RE.test(sha) ? sha : null;
|
|
66
70
|
if (daemonVersion && daemonVersion !== CLAUDEXOR_VERSION) {
|
|
@@ -70,13 +74,16 @@ export function consumeHandshakeIdentity(body) {
|
|
|
70
74
|
cliVersion: CLAUDEXOR_VERSION,
|
|
71
75
|
});
|
|
72
76
|
return {
|
|
73
|
-
engine: { engineVersion: daemonVersion, engineBuildSha },
|
|
77
|
+
engine: { engineVersion: daemonVersion, engineBuildSha, servingMode },
|
|
74
78
|
skewAdvisory: `claudexor: daemon is engine ${daemonVersion} but this CLI is ${CLAUDEXOR_VERSION}; ` +
|
|
75
79
|
`${ENGINE_STOP_REMEDY}\n`,
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
recordEngineSkew(null);
|
|
79
|
-
return {
|
|
83
|
+
return {
|
|
84
|
+
engine: { engineVersion: daemonVersion, engineBuildSha, servingMode },
|
|
85
|
+
skewAdvisory: null,
|
|
86
|
+
};
|
|
80
87
|
}
|
|
81
88
|
/**
|
|
82
89
|
* Shape a typed problem's fields at the controlProblemError choke point:
|
package/dist/engine-skew.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine-skew.js","sourceRoot":"","sources":["../src/engine-skew.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"engine-skew.js","sourceRoot":"","sources":["../src/engine-skew.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAuBpD;;;;;;sEAMsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAC7B,+IAA+I,CAAC;AAElJ,IAAI,YAAY,GAAsB,IAAI,CAAC;AAE3C;;sEAEsE;AACtE,MAAM,UAAU,gBAAgB,CAAC,IAAuB;IACtD,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,kBAAkB;IAChC,OAAO,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,CAAC;AAED,2EAA2E;AAC3E,6EAA6E;AAC7E,qCAAqC;AACrC,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAC5C,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAUtC;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAa;IACpD,MAAM,MAAM,GAAG,wBAAwB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,oEAAoE;QACpE,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACvB,OAAO;YACL,MAAM,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE;YAC5E,YAAY,EAAE,IAAI;SACnB,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;IAC5C,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,QAAQ,CAAC;IACxD,MAAM,aAAa,GAAG,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACvE,MAAM,cAAc,GAAG,GAAG,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAChF,IAAI,aAAa,IAAI,aAAa,KAAK,iBAAiB,EAAE,CAAC;QACzD,gBAAgB,CAAC;YACf,aAAa;YACb,GAAG,CAAC,cAAc,IAAI,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxF,UAAU,EAAE,iBAAiB;SAC9B,CAAC,CAAC;QACH,OAAO;YACL,MAAM,EAAE,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE;YACrE,YAAY,EACV,+BAA+B,aAAa,oBAAoB,iBAAiB,IAAI;gBACrF,GAAG,kBAAkB,IAAI;SAC5B,CAAC;IACJ,CAAC;IACD,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO;QACL,MAAM,EAAE,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE;QACrE,YAAY,EAAE,IAAI;KACnB,CAAC;AACJ,CAAC;AAcD;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA2B,EAC3B,qBAAgC;IAEhC,MAAM,IAAI,GAAG,YAAY,CAAC;IAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC;IAC9C,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,qBAAqB,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAC/F,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,CACpF,CAAC;IACF,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAClD,OAAO;QACL,GAAG,MAAM;QACT,eAAe,EAAE,CAAC,GAAG,QAAQ,EAAE,GAAG,QAAQ,CAAC;QAC3C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrF,CAAC;AACJ,CAAC"}
|
|
@@ -1,7 +1,80 @@
|
|
|
1
|
-
|
|
1
|
+
import { type ExpectedRunFactsIdentity } from "./run-facts-projection.js";
|
|
2
|
+
export declare function projectImmediateRunDetail(detail: Record<string, unknown> | null, expected?: ExpectedRunFactsIdentity): {
|
|
2
3
|
parentRunId: string | null;
|
|
3
4
|
delegatedFromRunId: string | null;
|
|
4
5
|
delegation: Record<string, unknown> | null;
|
|
6
|
+
runFacts: {
|
|
7
|
+
schema_version: 2;
|
|
8
|
+
run_id: string;
|
|
9
|
+
task_id: string;
|
|
10
|
+
mode: "agent" | "ask" | "plan";
|
|
11
|
+
outcome: {
|
|
12
|
+
lifecycle: "cancelled" | "failed" | "interrupted" | "succeeded";
|
|
13
|
+
noChanges: boolean;
|
|
14
|
+
checks: "failed" | "not_configured" | "passed";
|
|
15
|
+
review: "approved" | "blocked" | "not_run";
|
|
16
|
+
reason: "budget_exhausted" | "budget_overshoot" | "checks_failed" | "context_capacity_exhausted" | "cost_unverifiable" | "crash_interrupted" | "harness_failed" | "input_required" | "no_changes" | "not_converged" | "review_blocked" | "stuck_no_progress" | "user_cancelled" | "wall_clock_exceeded" | "work_incomplete" | "work_report_contract" | "workspace_unavailable" | null;
|
|
17
|
+
work_state?: {
|
|
18
|
+
state: "completed" | "incomplete" | "needs_input" | "unverified";
|
|
19
|
+
source: "absent" | "constrained" | "validated";
|
|
20
|
+
required_inputs?: {
|
|
21
|
+
kind: "context" | "credential" | "decision" | "external_dependency" | "file" | "permission";
|
|
22
|
+
locator: string | null;
|
|
23
|
+
description: string;
|
|
24
|
+
}[] | undefined;
|
|
25
|
+
} | undefined;
|
|
26
|
+
};
|
|
27
|
+
deliverable: {
|
|
28
|
+
present: boolean;
|
|
29
|
+
kind: "answer" | "patch" | "plan" | "report" | "structured_output" | null;
|
|
30
|
+
path: string | null;
|
|
31
|
+
producer_attempt_id: string | null;
|
|
32
|
+
};
|
|
33
|
+
presentation?: {
|
|
34
|
+
state: "diagnostic" | "ready";
|
|
35
|
+
primary: {
|
|
36
|
+
kind: "answer" | "diagnostic" | "patch" | "plan" | "report" | "structured_output";
|
|
37
|
+
path: string;
|
|
38
|
+
} | null;
|
|
39
|
+
} | undefined;
|
|
40
|
+
participants: {
|
|
41
|
+
planners: number;
|
|
42
|
+
attempts: {
|
|
43
|
+
attempt_id: string | null;
|
|
44
|
+
harness_id: string;
|
|
45
|
+
role: "candidate" | "merge" | "planner" | "reducer" | "reviewer" | "scout";
|
|
46
|
+
deliverable_present: boolean;
|
|
47
|
+
status: "blocked" | "failed" | "success" | "success_with_warnings" | null;
|
|
48
|
+
}[];
|
|
49
|
+
};
|
|
50
|
+
gates: {
|
|
51
|
+
configured: boolean;
|
|
52
|
+
required: number;
|
|
53
|
+
total: number;
|
|
54
|
+
executed: boolean;
|
|
55
|
+
state: "failed" | "not_configured" | "passed" | "skipped";
|
|
56
|
+
receipt_attempt_id: string | null;
|
|
57
|
+
};
|
|
58
|
+
review: {
|
|
59
|
+
state: "approved" | "blocked" | "not_run";
|
|
60
|
+
blocker_ids: string[];
|
|
61
|
+
blockers: number;
|
|
62
|
+
};
|
|
63
|
+
apply: {
|
|
64
|
+
eligibility: {
|
|
65
|
+
eligible: boolean;
|
|
66
|
+
state: string | null;
|
|
67
|
+
reason: string | null;
|
|
68
|
+
requiredAction: string | null;
|
|
69
|
+
} | null;
|
|
70
|
+
operator_decision_present: boolean;
|
|
71
|
+
};
|
|
72
|
+
required_actions: {
|
|
73
|
+
id: "complete_incomplete_work" | "fix_failed_checks" | "provide_required_input" | "record_operator_decision" | "resolve_review_block";
|
|
74
|
+
detail: string;
|
|
75
|
+
}[];
|
|
76
|
+
generated_at: string;
|
|
77
|
+
} | null;
|
|
5
78
|
outcomeFacts: {
|
|
6
79
|
lifecycle: "cancelled" | "failed" | "interrupted" | "succeeded";
|
|
7
80
|
noChanges: boolean;
|
|
@@ -40,4 +113,8 @@ export declare function projectImmediateRunDetail(detail: Record<string, unknown
|
|
|
40
113
|
};
|
|
41
114
|
/** One typed read projection for inspect/status/result from a single detail snapshot. */
|
|
42
115
|
export declare function projectRecoveryRunDetail(mode: string, runId: string, detail: Record<string, unknown>, delegationParentRunId?: string): Record<string, unknown>;
|
|
116
|
+
/** Only canonical detail-integrity failures preserve a public recovery handle. */
|
|
117
|
+
export declare function isRecoverableRunDetailIntegrityProblem(error: unknown): boolean;
|
|
118
|
+
/** Minimal schema-valid handle when public recovery cannot trust run detail. */
|
|
119
|
+
export declare function projectDegradedRecoveryRunDetail(runId: string, error: unknown): Record<string, unknown>;
|
|
43
120
|
//# sourceMappingURL=mcp-run-projections.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-run-projections.d.ts","sourceRoot":"","sources":["../src/mcp-run-projections.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mcp-run-projections.d.ts","sourceRoot":"","sources":["../src/mcp-run-projections.ts"],"names":[],"mappings":"AAcA,OAAO,EAAmB,KAAK,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAE3F,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACtC,QAAQ,GAAE,wBAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBxC;AAED,yFAAyF;AACzF,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,qBAAqB,CAAC,EAAE,MAAM,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+EzB;AAED,kFAAkF;AAClF,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQ9E;AAED,gFAAgF;AAChF,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,OAAO,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBzB"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isTerminalLifecycle, RunFactsInvalidError } from "@claudexor/schema";
|
|
2
|
+
import { CliError } from "./cli-error.js";
|
|
3
|
+
import { describeRunDetailProblem, projectApplyEligibility, projectOutcomeBanner, presentRunPrimaryOutput, projectRunFailure, projectRunCouncil, projectRunLineage, projectRunPrimaryOutput, projectRunSpendUsd, } from "./run-detail-projections.js";
|
|
2
4
|
import { projectRunOutcomeFacts } from "./daemon-outcome.js";
|
|
3
|
-
|
|
5
|
+
import { projectRunFacts } from "./run-facts-projection.js";
|
|
6
|
+
export function projectImmediateRunDetail(detail, expected = {}) {
|
|
4
7
|
return {
|
|
8
|
+
runFacts: projectRunFacts(detail, expected),
|
|
5
9
|
outcomeFacts: projectRunOutcomeFacts(detail),
|
|
6
10
|
applyEligibility: projectApplyEligibility(detail),
|
|
7
11
|
spendUsd: projectRunSpendUsd(detail),
|
|
@@ -17,7 +21,14 @@ export function projectImmediateRunDetail(detail) {
|
|
|
17
21
|
/** One typed read projection for inspect/status/result from a single detail snapshot. */
|
|
18
22
|
export function projectRecoveryRunDetail(mode, runId, detail, delegationParentRunId) {
|
|
19
23
|
const summary = (detail["summary"] ?? {});
|
|
20
|
-
if (
|
|
24
|
+
if (summary["runId"] !== runId) {
|
|
25
|
+
throw new CliError("operational", `run detail identity does not match requested run ${runId}`, {
|
|
26
|
+
code: "invalid_service_response",
|
|
27
|
+
retryable: true,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (delegationParentRunId !== undefined &&
|
|
31
|
+
summary["delegatedFromRunId"] !== delegationParentRunId) {
|
|
21
32
|
throw Object.assign(new Error(`run ${runId} is not a child of this delegation parent`), {
|
|
22
33
|
code: "delegation_child_scope_violation",
|
|
23
34
|
status: 403,
|
|
@@ -27,10 +38,19 @@ export function projectRecoveryRunDetail(mode, runId, detail, delegationParentRu
|
|
|
27
38
|
const status = (typeof summary["state"] === "string" ? summary["state"] : null) ??
|
|
28
39
|
(typeof summary["status"] === "string" ? summary["status"] : null) ??
|
|
29
40
|
null;
|
|
41
|
+
const runFacts = status === "queued" || status === "running"
|
|
42
|
+
? null
|
|
43
|
+
: projectRunFacts(detail, {
|
|
44
|
+
runId,
|
|
45
|
+
...(status && isTerminalLifecycle(status)
|
|
46
|
+
? { lifecycle: status }
|
|
47
|
+
: {}),
|
|
48
|
+
});
|
|
30
49
|
const base = {
|
|
31
50
|
runId,
|
|
32
51
|
runDir: typeof summary["runDir"] === "string" ? summary["runDir"] : null,
|
|
33
52
|
status,
|
|
53
|
+
runFacts,
|
|
34
54
|
decisionStatus: decision ? (decision["status"] ?? null) : null,
|
|
35
55
|
pendingInteractions: Array.isArray(detail["pendingInteractions"])
|
|
36
56
|
? detail["pendingInteractions"].length
|
|
@@ -71,4 +91,38 @@ export function projectRecoveryRunDetail(mode, runId, detail, delegationParentRu
|
|
|
71
91
|
: `run ${runId}: ${String(status ?? "unknown")}`;
|
|
72
92
|
return { summary: terminalSummary, ...base };
|
|
73
93
|
}
|
|
94
|
+
/** Only canonical detail-integrity failures preserve a public recovery handle. */
|
|
95
|
+
export function isRecoverableRunDetailIntegrityProblem(error) {
|
|
96
|
+
const record = error && typeof error === "object" ? error : {};
|
|
97
|
+
const code = record["code"];
|
|
98
|
+
if (code !== "run_facts_invalid" && code !== "invalid_service_response")
|
|
99
|
+
return false;
|
|
100
|
+
if (typeof record["mcpRecoveryTypedControlProblem"] === "boolean") {
|
|
101
|
+
return record["mcpRecoveryTypedControlProblem"] === true;
|
|
102
|
+
}
|
|
103
|
+
return error instanceof RunFactsInvalidError || error instanceof CliError;
|
|
104
|
+
}
|
|
105
|
+
/** Minimal schema-valid handle when public recovery cannot trust run detail. */
|
|
106
|
+
export function projectDegradedRecoveryRunDetail(runId, error) {
|
|
107
|
+
return {
|
|
108
|
+
summary: `run ${runId}: detail unavailable`,
|
|
109
|
+
runId,
|
|
110
|
+
runDir: null,
|
|
111
|
+
status: null,
|
|
112
|
+
runFacts: null,
|
|
113
|
+
decisionStatus: null,
|
|
114
|
+
pendingInteractions: null,
|
|
115
|
+
outcomeFacts: null,
|
|
116
|
+
failure: null,
|
|
117
|
+
outcomeBanner: null,
|
|
118
|
+
applyEligibility: null,
|
|
119
|
+
planReadiness: null,
|
|
120
|
+
council: null,
|
|
121
|
+
budget: null,
|
|
122
|
+
parentRunId: null,
|
|
123
|
+
delegatedFromRunId: null,
|
|
124
|
+
delegation: null,
|
|
125
|
+
detailProblem: describeRunDetailProblem(error),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
74
128
|
//# sourceMappingURL=mcp-run-projections.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-run-projections.js","sourceRoot":"","sources":["../src/mcp-run-projections.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp-run-projections.js","sourceRoot":"","sources":["../src/mcp-run-projections.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp-runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-runner.d.ts","sourceRoot":"","sources":["../src/mcp-runner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mcp-runner.d.ts","sourceRoot":"","sources":["../src/mcp-runner.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAmB,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAUpE,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC;8DAC0D;IAC1D,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;oFACgF;IAChF,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC;qFACiF;IACjF,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC;;0CAEsC;IACtC,eAAe,CAAC,EAAE,CAChB,KAAK,EAAE,GAAG,EACV,KAAK,EAAE,kBAAkB,GAAG,SAAS,EACrC,OAAO,EAAE;QACP,MAAM,EAAE,OAAO,gBAAgB,CAAC;QAChC,YAAY,EAAE,OAAO,qBAAqB,CAAC;KAC5C,KACE,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,OACnD,GAAG,UAAU,kBAAkB,sBA6KjD;AAoPD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,WAAW,GAClB,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAqB5C;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,iBAAiB,EACvB,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,GAC/C,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CA0E5C"}
|
package/dist/mcp-runner.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { isTerminalLifecycle, ModeKind } from "@claudexor/schema";
|
|
1
|
+
import { ControlProblem, isTerminalLifecycle, ModeKind, } from "@claudexor/schema";
|
|
2
2
|
import { connectDaemonIfRunning, daemonOutcomeSummary, ensureDaemon, enqueueAndAwait, fetchRunDetail, } from "./daemon-run.js";
|
|
3
3
|
import { describeRunDetailProblem, presentRunPrimaryOutput, projectRunFailure, projectRunPrimaryOutput, } from "./run-detail-projections.js";
|
|
4
4
|
import { primaryOutputForCli } from "./primary-output.js";
|
|
5
|
+
import { controlProblemError } from "./cli-error.js";
|
|
5
6
|
import { controlApiFetch } from "./live.js";
|
|
6
7
|
import { absentDaemonRecovery, BELT_DAEMON_LOST } from "./mcp-daemon-unavailable.js";
|
|
7
|
-
import { projectImmediateRunDetail, projectRecoveryRunDetail } from "./mcp-run-projections.js";
|
|
8
|
+
import { isRecoverableRunDetailIntegrityProblem, projectDegradedRecoveryRunDetail, projectImmediateRunDetail, projectRecoveryRunDetail, } from "./mcp-run-projections.js";
|
|
9
|
+
import { readRunDetailResponse } from "./run-detail-response.js";
|
|
8
10
|
/**
|
|
9
11
|
* Shared MCP/ACP runner. All product modes cross the daemon's /v2 boundary;
|
|
10
12
|
* interactive questions bridge through pendingInteractions and typed answers.
|
|
@@ -112,30 +114,28 @@ export function mcpSurfaceRunner(options = {}) {
|
|
|
112
114
|
...(onPollTick ? { onPollTick } : {}),
|
|
113
115
|
});
|
|
114
116
|
try {
|
|
115
|
-
// The derived apply verdict rides the result (single producer: the run
|
|
116
|
-
// detail endpoint). The post-terminal detail read DEGRADES: a missing/
|
|
117
|
-
// legacy detail projects null fields, and a raised typed problem —
|
|
118
|
-
// especially 500 run_facts_invalid, the server's verdict that the run's
|
|
119
|
-
// receipt cannot be trusted — rides the result as `detailProblem` with
|
|
120
|
-
// the runId preserved instead of erasing the finished run's outcome.
|
|
121
|
-
// A deferred MCP call normally returns while the run is still live, but
|
|
122
|
-
// the bind/status race may already observe a terminal. Project detail
|
|
123
|
-
// from ACTUAL lifecycle truth; never delay an ordinary running handle.
|
|
124
117
|
let detail = null;
|
|
118
|
+
let canonicalPrimary = null;
|
|
119
|
+
let detailProjection = projectImmediateRunDetail(null, { runId: out.runId });
|
|
125
120
|
let detailProblem = null;
|
|
126
121
|
if (p?.deferred !== true || isTerminalLifecycle(out.status)) {
|
|
127
122
|
try {
|
|
128
123
|
detail = await fetchRunDetail(addr, out.runId);
|
|
124
|
+
detailProjection = projectImmediateRunDetail(detail, {
|
|
125
|
+
runId: out.runId,
|
|
126
|
+
...(isTerminalLifecycle(out.status)
|
|
127
|
+
? { lifecycle: out.status }
|
|
128
|
+
: {}),
|
|
129
|
+
});
|
|
130
|
+
canonicalPrimary = projectRunPrimaryOutput(detail);
|
|
129
131
|
}
|
|
130
132
|
catch (error) {
|
|
133
|
+
detail = null;
|
|
131
134
|
detailProblem = describeRunDetailProblem(error);
|
|
135
|
+
detailProjection = projectImmediateRunDetail(null, { runId: out.runId });
|
|
132
136
|
}
|
|
133
137
|
}
|
|
134
|
-
//
|
|
135
|
-
// mode-aware). Read local artifacts only when detail was attempted but
|
|
136
|
-
// unavailable; this preserves a useful terminal result without creating a
|
|
137
|
-
// second normal-path artifact owner.
|
|
138
|
-
const canonicalPrimary = projectRunPrimaryOutput(detail);
|
|
138
|
+
// Local artifacts remain only the established soft-absence fallback.
|
|
139
139
|
const localFallback = (p?.deferred !== true || isTerminalLifecycle(out.status)) &&
|
|
140
140
|
!detail &&
|
|
141
141
|
!detailProblem &&
|
|
@@ -149,7 +149,6 @@ export function mcpSurfaceRunner(options = {}) {
|
|
|
149
149
|
: null;
|
|
150
150
|
const primary = canonicalPrimary ?? localFallback;
|
|
151
151
|
const presented = presentRunPrimaryOutput(primary);
|
|
152
|
-
const detailProjection = projectImmediateRunDetail(detail);
|
|
153
152
|
const reason = daemonOutcomeSummary({
|
|
154
153
|
...out,
|
|
155
154
|
outcomeFacts: detailProjection.outcomeFacts ?? undefined,
|
|
@@ -158,14 +157,6 @@ export function mcpSurfaceRunner(options = {}) {
|
|
|
158
157
|
detailProjection.outcomeBanner ??
|
|
159
158
|
reason ??
|
|
160
159
|
(primary?.kind === "patch" ? "patch produced (see artifacts)" : `run ${out.status}`);
|
|
161
|
-
// The sub-run's real settled spend rides the result so the delegation belt
|
|
162
|
-
// can reconcile its budget reservation against the actual drawn amount
|
|
163
|
-
// (single producer: the run-detail budget projection). Deferred calls return
|
|
164
|
-
// before terminal, so spend is not yet settled — null (the belt then keeps
|
|
165
|
-
// its reservation committed fail-closed rather than seeing spend 0).
|
|
166
|
-
// Council membership + merge disclosure (QA-023b) rides the result so an MCP
|
|
167
|
-
// host that asked for `--council` can machine-verify it was really N/N and
|
|
168
|
-
// who merged. Terminal projection — null on a deferred (still-live) handle.
|
|
169
160
|
return {
|
|
170
161
|
runId: out.runId,
|
|
171
162
|
runDir: out.runDir,
|
|
@@ -220,18 +211,21 @@ async function recoveryQuery(mode, runId, input = {}, context = {}) {
|
|
|
220
211
|
if (!conn)
|
|
221
212
|
return absentDaemonRecovery(mode, context.beltContext);
|
|
222
213
|
const { addr } = conn;
|
|
223
|
-
const get = async (path) => {
|
|
214
|
+
const get = async (path, runDetail = false) => {
|
|
224
215
|
const res = await controlApiFetch(addr, path, {
|
|
225
216
|
headers: { authorization: `Bearer ${addr.token}` },
|
|
226
217
|
});
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
218
|
+
if (res.ok) {
|
|
219
|
+
return runDetail
|
|
220
|
+
? readRunDetailResponse(res)
|
|
221
|
+
: (await res.json());
|
|
222
|
+
}
|
|
223
|
+
const body = await res.json().catch(() => ({}));
|
|
224
|
+
const error = controlProblemError(res.status, body, `HTTP ${res.status} for ${path}`);
|
|
225
|
+
Object.assign(error, {
|
|
226
|
+
mcpRecoveryTypedControlProblem: ControlProblem.safeParse(body).success,
|
|
227
|
+
});
|
|
228
|
+
throw error;
|
|
235
229
|
};
|
|
236
230
|
if (mode === "__runs_list") {
|
|
237
231
|
// GET /runs is a BOUNDED keyset page (QA-052): a single read undercounts the
|
|
@@ -285,10 +279,20 @@ async function recoveryQuery(mode, runId, input = {}, context = {}) {
|
|
|
285
279
|
if (!runId)
|
|
286
280
|
throw new Error("runId is required");
|
|
287
281
|
if (mode === "__run_inspect" || mode === "__run_status" || mode === "__run_result") {
|
|
288
|
-
const
|
|
289
|
-
return projectRecoveryRunDetail(mode, runId, detail, typeof input["delegatedFromRunId"] === "string"
|
|
282
|
+
const parent = typeof input["delegatedFromRunId"] === "string"
|
|
290
283
|
? input["delegatedFromRunId"]
|
|
291
|
-
: undefined
|
|
284
|
+
: undefined;
|
|
285
|
+
const scopedRecovery = context.beltContext || parent !== undefined;
|
|
286
|
+
try {
|
|
287
|
+
const detail = await get(`/runs/${encodeURIComponent(runId)}`, true);
|
|
288
|
+
return projectRecoveryRunDetail(mode, runId, detail, parent);
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (scopedRecovery || !isRecoverableRunDetailIntegrityProblem(error)) {
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
return projectDegradedRecoveryRunDetail(runId, error);
|
|
295
|
+
}
|
|
292
296
|
}
|
|
293
297
|
if (mode === "__run_cancel") {
|
|
294
298
|
const res = await controlApiFetch(addr, `/runs/${encodeURIComponent(runId)}/control`, {
|