clinkx 1.1.1 → 1.2.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/CHANGELOG.md +60 -0
- package/README.md +97 -13
- package/clinkx-workflows/dist/allowed-roots.js +2 -0
- package/clinkx-workflows/dist/allowed-roots.js.map +1 -1
- package/clinkx-workflows/dist/architecture-comparison/admission.d.ts +5 -0
- package/clinkx-workflows/dist/architecture-comparison/admission.js +50 -0
- package/clinkx-workflows/dist/architecture-comparison/admission.js.map +1 -0
- package/clinkx-workflows/dist/architecture-comparison/contract.d.ts +995 -0
- package/clinkx-workflows/dist/architecture-comparison/contract.js +149 -0
- package/clinkx-workflows/dist/architecture-comparison/contract.js.map +1 -0
- package/clinkx-workflows/dist/architecture-comparison/runtime.d.ts +29 -0
- package/clinkx-workflows/dist/architecture-comparison/runtime.js +387 -0
- package/clinkx-workflows/dist/architecture-comparison/runtime.js.map +1 -0
- package/clinkx-workflows/dist/child-env.d.ts +3 -1
- package/clinkx-workflows/dist/child-env.js +6 -2
- package/clinkx-workflows/dist/child-env.js.map +1 -1
- package/clinkx-workflows/dist/config.d.ts +28 -5
- package/clinkx-workflows/dist/config.js +137 -20
- package/clinkx-workflows/dist/config.js.map +1 -1
- package/clinkx-workflows/dist/contained-file.js +10 -1
- package/clinkx-workflows/dist/contained-file.js.map +1 -1
- package/clinkx-workflows/dist/council-execution-policy.d.ts +4 -2
- package/clinkx-workflows/dist/council-execution-policy.js +5 -1
- package/clinkx-workflows/dist/council-execution-policy.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/activity-contract.d.ts +72 -72
- package/clinkx-workflows/dist/dashboard/contracts.d.ts +1564 -122
- package/clinkx-workflows/dist/dashboard/contracts.js +4 -0
- package/clinkx-workflows/dist/dashboard/contracts.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/progress-report-contract.d.ts +12 -12
- package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.d.ts +8 -8
- package/clinkx-workflows/dist/dashboard/scanner.d.ts +3 -0
- package/clinkx-workflows/dist/dashboard/scanner.js +27 -0
- package/clinkx-workflows/dist/dashboard/scanner.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/session-log-contract.d.ts +48 -48
- package/clinkx-workflows/dist/dashboard/session-log-identity.js +2 -1
- package/clinkx-workflows/dist/dashboard/session-log-identity.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/session-log-redaction.d.ts +2 -0
- package/clinkx-workflows/dist/dashboard/session-log-redaction.js +4 -0
- package/clinkx-workflows/dist/dashboard/session-log-redaction.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/verified-repair-contract.d.ts +191 -0
- package/clinkx-workflows/dist/dashboard/verified-repair-contract.js +28 -0
- package/clinkx-workflows/dist/dashboard/verified-repair-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/index-BdpmbPps.js +115 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/style-Im5i1iAs.css +1 -0
- package/clinkx-workflows/dist/dashboard-ui/index.html +2 -2
- package/clinkx-workflows/dist/engine.d.ts +6 -0
- package/clinkx-workflows/dist/engine.js +78 -7
- package/clinkx-workflows/dist/engine.js.map +1 -1
- package/clinkx-workflows/dist/index.d.ts +3 -0
- package/clinkx-workflows/dist/index.js +3 -2
- package/clinkx-workflows/dist/index.js.map +1 -1
- package/clinkx-workflows/dist/reducers/index.d.ts +9 -1
- package/clinkx-workflows/dist/reducers/index.js +22 -0
- package/clinkx-workflows/dist/reducers/index.js.map +1 -1
- package/clinkx-workflows/dist/root-path-validation.d.ts +12 -0
- package/clinkx-workflows/dist/root-path-validation.js +63 -0
- package/clinkx-workflows/dist/root-path-validation.js.map +1 -0
- package/clinkx-workflows/dist/rosters.d.ts +1 -0
- package/clinkx-workflows/dist/rosters.js +16 -6
- package/clinkx-workflows/dist/rosters.js.map +1 -1
- package/clinkx-workflows/dist/server.js +5 -2
- package/clinkx-workflows/dist/server.js.map +1 -1
- package/clinkx-workflows/dist/state-schema.d.ts +13 -0
- package/clinkx-workflows/dist/state.d.ts +7 -1
- package/clinkx-workflows/dist/state.js +90 -19
- package/clinkx-workflows/dist/state.js.map +1 -1
- package/clinkx-workflows/dist/validators/architecture-comparison.d.ts +2 -0
- package/clinkx-workflows/dist/validators/architecture-comparison.js +12 -0
- package/clinkx-workflows/dist/validators/architecture-comparison.js.map +1 -0
- package/clinkx-workflows/dist/validators/index.js +4 -0
- package/clinkx-workflows/dist/validators/index.js.map +1 -1
- package/clinkx-workflows/dist/validators/verified-repair.d.ts +2 -0
- package/clinkx-workflows/dist/validators/verified-repair.js +12 -0
- package/clinkx-workflows/dist/validators/verified-repair.js.map +1 -0
- package/clinkx-workflows/dist/verification-proof.d.ts +254 -0
- package/clinkx-workflows/dist/verification-proof.js +195 -0
- package/clinkx-workflows/dist/verification-proof.js.map +1 -0
- package/clinkx-workflows/dist/verified-repair/admission.d.ts +72 -0
- package/clinkx-workflows/dist/verified-repair/admission.js +63 -0
- package/clinkx-workflows/dist/verified-repair/admission.js.map +1 -0
- package/clinkx-workflows/dist/verified-repair/contract.d.ts +1910 -0
- package/clinkx-workflows/dist/verified-repair/contract.js +127 -0
- package/clinkx-workflows/dist/verified-repair/contract.js.map +1 -0
- package/clinkx-workflows/dist/verified-repair/host.d.ts +32 -0
- package/clinkx-workflows/dist/verified-repair/host.js +2 -0
- package/clinkx-workflows/dist/verified-repair/host.js.map +1 -0
- package/clinkx-workflows/dist/verified-repair/prompts.d.ts +12 -0
- package/clinkx-workflows/dist/verified-repair/prompts.js +58 -0
- package/clinkx-workflows/dist/verified-repair/prompts.js.map +1 -0
- package/clinkx-workflows/dist/verified-repair/result.d.ts +16 -0
- package/clinkx-workflows/dist/verified-repair/result.js +102 -0
- package/clinkx-workflows/dist/verified-repair/result.js.map +1 -0
- package/clinkx-workflows/dist/verified-repair/runtime.d.ts +28 -0
- package/clinkx-workflows/dist/verified-repair/runtime.js +186 -0
- package/clinkx-workflows/dist/verified-repair/runtime.js.map +1 -0
- package/clinkx-workflows/dist/workflow-completion.d.ts +83 -0
- package/clinkx-workflows/dist/workflow-completion.js +250 -0
- package/clinkx-workflows/dist/workflow-completion.js.map +1 -0
- package/clinkx-workflows/dist/workflow-tools.d.ts +7 -3
- package/clinkx-workflows/dist/workflow-tools.js +487 -46
- package/clinkx-workflows/dist/workflow-tools.js.map +1 -1
- package/clinkx-workflows/dist/workflow-verification-contract.d.ts +539 -0
- package/clinkx-workflows/dist/workflow-verification-contract.js +52 -0
- package/clinkx-workflows/dist/workflow-verification-contract.js.map +1 -0
- package/clinkx-workflows/dist/workflow-verification.d.ts +15 -0
- package/clinkx-workflows/dist/workflow-verification.js +218 -0
- package/clinkx-workflows/dist/workflow-verification.js.map +1 -0
- package/clinkx-workflows/templates/architecture-comparison/candidate.txt +29 -0
- package/clinkx-workflows/templates/architecture-comparison/judge.txt +31 -0
- package/clinkx-workflows/templates/verified-repair/investigate.txt +20 -0
- package/clinkx-workflows/templates/verified-repair/repair.txt +11 -0
- package/clinkx-workflows/templates/verified-repair/review.txt +18 -0
- package/clinkx-workflows/workflows/architecture-comparison.yaml +133 -0
- package/clinkx-workflows/workflows/verified-repair.yaml +104 -0
- package/dist/allowed-roots.d.ts +1 -1
- package/dist/allowed-roots.js +128 -15
- package/dist/allowed-roots.js.map +1 -1
- package/dist/codex/app-server-client.d.ts +1 -0
- package/dist/codex/app-server-client.js +3 -2
- package/dist/codex/app-server-client.js.map +1 -1
- package/dist/context-builder/config.d.ts +1 -0
- package/dist/context-builder/config.js +21 -9
- package/dist/context-builder/config.js.map +1 -1
- package/dist/context-builder/diagnostics.d.ts +1 -0
- package/dist/context-builder/diagnostics.js +1 -1
- package/dist/context-builder/diagnostics.js.map +1 -1
- package/dist/context-builder/discovery.d.ts +18 -1
- package/dist/context-builder/discovery.js +69 -1
- package/dist/context-builder/discovery.js.map +1 -1
- package/dist/context-builder/handler.js +8 -1
- package/dist/context-builder/handler.js.map +1 -1
- package/dist/context-builder/lifecycle-contract.d.ts +24 -24
- package/dist/context-builder/mutation-lock.d.ts +4 -0
- package/dist/context-builder/mutation-lock.js +7 -1
- package/dist/context-builder/mutation-lock.js.map +1 -1
- package/dist/context-builder/refine-handler.js +16 -1
- package/dist/context-builder/refine-handler.js.map +1 -1
- package/dist/context-builder/secrets.d.ts +11 -0
- package/dist/context-builder/secrets.js +75 -0
- package/dist/context-builder/secrets.js.map +1 -1
- package/dist/context-builder/storage.d.ts +10 -2
- package/dist/context-builder/storage.js +213 -16
- package/dist/context-builder/storage.js.map +1 -1
- package/dist/context-builder/versions.d.ts +5 -2
- package/dist/context-builder/versions.js +5 -2
- package/dist/context-builder/versions.js.map +1 -1
- package/dist/dashboard-cli.d.ts +6 -0
- package/dist/dashboard-cli.js +18 -32
- package/dist/dashboard-cli.js.map +1 -1
- package/dist/dashboard-gateway/main.js +8 -3
- package/dist/dashboard-gateway/main.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/project-verification/contract.d.ts +537 -0
- package/dist/project-verification/contract.js +40 -0
- package/dist/project-verification/contract.js.map +1 -0
- package/dist/project-verification/files.d.ts +23 -0
- package/dist/project-verification/files.js +80 -0
- package/dist/project-verification/files.js.map +1 -0
- package/dist/project-verification/process.d.ts +7 -0
- package/dist/project-verification/process.js +115 -0
- package/dist/project-verification/process.js.map +1 -0
- package/dist/project-verification/runner.d.ts +35 -0
- package/dist/project-verification/runner.js +185 -0
- package/dist/project-verification/runner.js.map +1 -0
- package/dist/project-verification/service.d.ts +37 -0
- package/dist/project-verification/service.js +151 -0
- package/dist/project-verification/service.js.map +1 -0
- package/dist/provider-doctor.d.ts +22 -0
- package/dist/provider-doctor.js +22 -0
- package/dist/provider-doctor.js.map +1 -0
- package/dist/registry.d.ts +1 -1
- package/dist/registry.js +5 -1
- package/dist/registry.js.map +1 -1
- package/dist/server.js +41 -6
- package/dist/server.js.map +1 -1
- package/dist/verification-tools.d.ts +90 -0
- package/dist/verification-tools.js +120 -0
- package/dist/verification-tools.js.map +1 -0
- package/dist/verified-repair/files.d.ts +22 -0
- package/dist/verified-repair/files.js +114 -0
- package/dist/verified-repair/files.js.map +1 -0
- package/dist/verified-repair/host.d.ts +14 -0
- package/dist/verified-repair/host.js +612 -0
- package/dist/verified-repair/host.js.map +1 -0
- package/docs/configuration.md +224 -12
- package/docs/engineering-recipes.md +415 -0
- package/docs/security.md +3 -0
- package/docs/verification/README.md +59 -0
- package/docs/verification/dashboard-real-disk.md +436 -0
- package/docs/verification/project-checks.example.json +14 -0
- package/docs/verification/project-checks.md +444 -0
- package/docs/verification/workflow-handoff.md +113 -0
- package/package.json +10 -2
- package/scripts/provider-doctor-core.mjs +380 -0
- package/scripts/provider-doctor-probe.mjs +34 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/index-JPHQVtn9.js +0 -106
- package/clinkx-workflows/dist/dashboard-ui/assets/style-DOMHk-j6.css +0 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { SchemaValidationError, OutputSizeError } from "../artifacts.js";
|
|
2
|
+
import { relative } from "node:path";
|
|
3
|
+
import { readContainedRelativeFile } from "../contained-file.js";
|
|
4
|
+
import { FixProposalSchema, InvestigationSchema, MAX_REPAIR_PACKET_BYTES, MAX_REPAIR_PROPOSAL_BYTES, RepairPacketSchema, RepairRecordSchema, RepairReviewSchema, REPAIR_REFS, assertRepairBounded, } from "./contract.js";
|
|
5
|
+
import { bytesDigest } from "../architecture-comparison/contract.js";
|
|
6
|
+
import { publishRepairRecord } from "./result.js";
|
|
7
|
+
import { hasCredentialTokenShape } from "../dashboard/session-log-redaction.js";
|
|
8
|
+
import { WorkflowCancellationError } from "../errors.js";
|
|
9
|
+
const PROVIDER_PHASES = {
|
|
10
|
+
[REPAIR_REFS.investigate]: { phase: "investigate", packet: "capture" },
|
|
11
|
+
[REPAIR_REFS.repair]: { phase: "repair", packet: "reproduce" },
|
|
12
|
+
[REPAIR_REFS.review]: { phase: "review", packet: "fixed" },
|
|
13
|
+
};
|
|
14
|
+
/** Fixed phase policy; the existing engine remains the only dispatcher. */
|
|
15
|
+
export class VerifiedRepairRuntime {
|
|
16
|
+
host;
|
|
17
|
+
runId;
|
|
18
|
+
runDir;
|
|
19
|
+
packet;
|
|
20
|
+
providerPackets = new Map();
|
|
21
|
+
retained = new Map();
|
|
22
|
+
finalization;
|
|
23
|
+
invalidArtifact;
|
|
24
|
+
constructor(host, runId, runDir) {
|
|
25
|
+
this.host = host;
|
|
26
|
+
this.runId = runId;
|
|
27
|
+
this.runDir = runDir;
|
|
28
|
+
}
|
|
29
|
+
async prepareProvider(reference, signal) {
|
|
30
|
+
if (this.invalidArtifact !== undefined)
|
|
31
|
+
throw new Error(`invalid repair provider artifact: ${this.invalidArtifact.phase}`);
|
|
32
|
+
const binding = PROVIDER_PHASES[reference];
|
|
33
|
+
if (binding === undefined)
|
|
34
|
+
throw new Error("unexpected repair provider call");
|
|
35
|
+
if (this.packet?.phase !== binding.packet)
|
|
36
|
+
throw new Error(`repair prerequisite unavailable for ${binding.phase}`);
|
|
37
|
+
await this.verifyRetained();
|
|
38
|
+
await this.host.assertProviderPhase(binding.phase, signal);
|
|
39
|
+
assertRepairBounded(this.packet, MAX_REPAIR_PACKET_BYTES);
|
|
40
|
+
this.providerPackets.set(reference, JSON.stringify(this.packet));
|
|
41
|
+
}
|
|
42
|
+
providerPacket(reference) {
|
|
43
|
+
const packet = this.providerPackets.get(reference);
|
|
44
|
+
if (packet === undefined)
|
|
45
|
+
throw new Error("repair provider packet was not prepared");
|
|
46
|
+
return packet;
|
|
47
|
+
}
|
|
48
|
+
async beforeDispatch(reference, input, signal) {
|
|
49
|
+
const binding = PROVIDER_PHASES[reference];
|
|
50
|
+
if (binding === undefined || input["continuation_id"] !== undefined ||
|
|
51
|
+
typeof input["prompt"] !== "string" || !input["prompt"].endsWith(`\n\nFROZEN REPAIR PACKET\n${this.providerPacket(reference)}`)) {
|
|
52
|
+
throw new Error("repair dispatch is not the fresh frozen packet");
|
|
53
|
+
}
|
|
54
|
+
await this.verifyRetained();
|
|
55
|
+
await this.host.assertProviderPhase(binding.phase, signal);
|
|
56
|
+
}
|
|
57
|
+
validateOutput(reference, value) {
|
|
58
|
+
const schema = reference === REPAIR_REFS.investigate ? InvestigationSchema
|
|
59
|
+
: reference === REPAIR_REFS.repair ? FixProposalSchema
|
|
60
|
+
: reference === REPAIR_REFS.review ? RepairReviewSchema
|
|
61
|
+
: reference === REPAIR_REFS.record ? RepairRecordSchema : RepairPacketSchema;
|
|
62
|
+
const parsed = schema.parse(value);
|
|
63
|
+
assertRepairBounded(parsed, MAX_REPAIR_PROPOSAL_BYTES);
|
|
64
|
+
if (hasCredentialTokenShape(JSON.stringify(parsed)))
|
|
65
|
+
throw new Error("repair artifact contains credential-like material; publication is rejected");
|
|
66
|
+
this.host.validateProviderOutput?.(parsed);
|
|
67
|
+
if (this.packet !== undefined && parsed.frozen_digest !== this.packet.frozen_digest)
|
|
68
|
+
throw new Error("repair artifact frozen identity mismatch");
|
|
69
|
+
}
|
|
70
|
+
noteInvalidArtifact(reference, error) {
|
|
71
|
+
if (!(reference in PROVIDER_PHASES))
|
|
72
|
+
return;
|
|
73
|
+
// JSON parser errors can include excerpts of untrusted provider text.
|
|
74
|
+
// Preserve the phase and failure class without copying those excerpts.
|
|
75
|
+
const message = error instanceof SchemaValidationError ? "provider artifact failed strict JSON/schema validation"
|
|
76
|
+
: error instanceof OutputSizeError ? "provider artifact exceeded repair byte limits"
|
|
77
|
+
: "provider artifact identity or semantic contract is invalid";
|
|
78
|
+
this.invalidArtifact ??= { kind: "invalid_artifact", phase: reference, message };
|
|
79
|
+
}
|
|
80
|
+
recordWritten(artifact) {
|
|
81
|
+
this.retained.set(artifact.reference, {
|
|
82
|
+
path: relative(this.runDir, artifact.filePath),
|
|
83
|
+
sha256: bytesDigest(`${JSON.stringify(artifact.parsed, null, 2)}\n`),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async reduce(operation, artifacts, signal) {
|
|
87
|
+
if (operation !== "record" && this.invalidArtifact !== undefined)
|
|
88
|
+
throw new Error(`invalid repair provider artifact: ${this.invalidArtifact.phase}`);
|
|
89
|
+
await this.verifyRetained();
|
|
90
|
+
const proposal = (reference) => {
|
|
91
|
+
const artifact = artifacts.find((entry) => entry.reference === reference);
|
|
92
|
+
if (artifact === undefined || !this.retained.has(reference))
|
|
93
|
+
throw new Error(`repair prerequisite unavailable: ${reference}`);
|
|
94
|
+
this.validateOutput(reference, artifact.parsed);
|
|
95
|
+
return artifact.parsed;
|
|
96
|
+
};
|
|
97
|
+
if (operation === "record") {
|
|
98
|
+
// The run owner publishes again after cleanup with actual terminal state.
|
|
99
|
+
// A final reducer is never the interruption publication boundary.
|
|
100
|
+
return JSON.stringify(RepairRecordSchema.parse(await this.host.record("completed")));
|
|
101
|
+
}
|
|
102
|
+
const packet = operation === "capture" ? await this.host.capture(signal)
|
|
103
|
+
: operation === "reproduce" ? await this.host.reproduce(proposal(REPAIR_REFS.investigate), signal)
|
|
104
|
+
: operation === "fixed" ? await this.host.fixed(proposal(REPAIR_REFS.repair), signal)
|
|
105
|
+
: await this.host.independently(proposal(REPAIR_REFS.review), signal);
|
|
106
|
+
const parsed = RepairPacketSchema.parse(packet);
|
|
107
|
+
if (parsed.phase !== operation || (this.packet !== undefined && parsed.frozen_digest !== this.packet.frozen_digest))
|
|
108
|
+
throw new Error("repair host packet phase or frozen identity mismatch");
|
|
109
|
+
assertRepairBounded(parsed, MAX_REPAIR_PACKET_BYTES);
|
|
110
|
+
this.packet = parsed;
|
|
111
|
+
return JSON.stringify(this.packet);
|
|
112
|
+
}
|
|
113
|
+
/** Await all owned cleanup before publication, even with an aborted run signal. */
|
|
114
|
+
finalize(status, reason, signal) {
|
|
115
|
+
this.finalization ??= this.finalizeOnce(status, reason, signal);
|
|
116
|
+
return this.finalization;
|
|
117
|
+
}
|
|
118
|
+
async cleanupUnadmitted(reason) {
|
|
119
|
+
try {
|
|
120
|
+
await this.host.abort(reason);
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await this.host.cleanup();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async finalizeOnce(status, reason, signal) {
|
|
127
|
+
const requestedCompletion = status === "completed";
|
|
128
|
+
const finalizationErrors = [];
|
|
129
|
+
const abort = async (error) => {
|
|
130
|
+
try {
|
|
131
|
+
await this.host.abort(error);
|
|
132
|
+
}
|
|
133
|
+
catch (abortError) {
|
|
134
|
+
finalizationErrors.push(abortError);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
if (this.invalidArtifact !== undefined)
|
|
138
|
+
await abort(this.invalidArtifact);
|
|
139
|
+
if (status !== "completed")
|
|
140
|
+
await abort(reason ?? status);
|
|
141
|
+
try {
|
|
142
|
+
await this.host.cleanup();
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
finalizationErrors.push(error);
|
|
146
|
+
status = status === "completed" ? "failed" : status;
|
|
147
|
+
await abort(error);
|
|
148
|
+
}
|
|
149
|
+
if (status === "completed" && finalizationErrors.length > 0)
|
|
150
|
+
status = "failed";
|
|
151
|
+
if (requestedCompletion && signal?.aborted) {
|
|
152
|
+
status = "cancelled";
|
|
153
|
+
await abort(signal.reason ?? "cancelled during finalization");
|
|
154
|
+
}
|
|
155
|
+
let record = RepairRecordSchema.parse(await this.host.record(status));
|
|
156
|
+
if (record.run_id !== this.runId || record.execution_status !== status)
|
|
157
|
+
throw new Error("repair terminal record identity mismatch");
|
|
158
|
+
await publishRepairRecord(this.runDir, record);
|
|
159
|
+
// Cancellation can arrive while the host projection or atomic publication
|
|
160
|
+
// awaits I/O. Reproject partial facts before the owner commits terminal state.
|
|
161
|
+
if (requestedCompletion && signal?.aborted && status !== "cancelled") {
|
|
162
|
+
await abort(signal.reason ?? "cancelled during publication");
|
|
163
|
+
status = "cancelled";
|
|
164
|
+
record = RepairRecordSchema.parse(await this.host.record(status));
|
|
165
|
+
if (record.run_id !== this.runId || record.execution_status !== status)
|
|
166
|
+
throw new Error("repair terminal record identity mismatch");
|
|
167
|
+
await publishRepairRecord(this.runDir, record);
|
|
168
|
+
}
|
|
169
|
+
if (requestedCompletion && status === "cancelled")
|
|
170
|
+
throw new WorkflowCancellationError();
|
|
171
|
+
if (finalizationErrors.length > 0)
|
|
172
|
+
throw new AggregateError(finalizationErrors, "repair finalization failed after retaining terminal facts");
|
|
173
|
+
return JSON.stringify(record);
|
|
174
|
+
}
|
|
175
|
+
async verifyRetained() {
|
|
176
|
+
for (const [reference, fact] of this.retained) {
|
|
177
|
+
const read = await readContainedRelativeFile(this.runDir, fact.path, {
|
|
178
|
+
wantContent: true, requireRegular: true, requireExactRequestedPath: true,
|
|
179
|
+
maxBytes: MAX_REPAIR_PROPOSAL_BYTES,
|
|
180
|
+
});
|
|
181
|
+
if (bytesDigest(read.content) !== fact.sha256)
|
|
182
|
+
throw new Error(`retained repair artifact changed: ${reference}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/verified-repair/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export declare const WORKFLOW_WAIT_BUDGET_MS = 30000;
|
|
2
|
+
export declare const SETTLED_LIMIT = 128;
|
|
3
|
+
export type IdentityFailure = "identity_changed" | "identity_unavailable";
|
|
4
|
+
export interface PublicationIdentity {
|
|
5
|
+
readonly generation: string;
|
|
6
|
+
readonly fingerprint: string;
|
|
7
|
+
readonly terminal: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Read-only view; every transition goes through WorkflowCompletionRegistry. */
|
|
10
|
+
export interface WorkflowCompletion {
|
|
11
|
+
readonly executionId: string;
|
|
12
|
+
readonly runId: string;
|
|
13
|
+
readonly runDir: string;
|
|
14
|
+
readonly settled: boolean;
|
|
15
|
+
/** state.json carries executionId; set once the owner's stamp write lands. */
|
|
16
|
+
readonly stamped: boolean;
|
|
17
|
+
readonly observers: number;
|
|
18
|
+
readonly listenerCount: number;
|
|
19
|
+
}
|
|
20
|
+
/** One run owner's lazy binding to the store it created or resumed. */
|
|
21
|
+
export interface CompletionOwner {
|
|
22
|
+
readonly entry: WorkflowCompletion | undefined;
|
|
23
|
+
/** Idempotent for the same store; a different store is a programming error. */
|
|
24
|
+
bind(store: {
|
|
25
|
+
readonly sessionLogGeneration: string | undefined;
|
|
26
|
+
}): WorkflowCompletion;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Provenance only: result bodies and unfinished promise callbacks are never cached.
|
|
30
|
+
* An execution exists here only once its owner has bound a created/resumed
|
|
31
|
+
* store, so a request rejected during admission can never displace one.
|
|
32
|
+
*/
|
|
33
|
+
export declare class WorkflowCompletionRegistry {
|
|
34
|
+
private readonly latest;
|
|
35
|
+
private readonly byId;
|
|
36
|
+
private readonly minted;
|
|
37
|
+
private readonly settledIds;
|
|
38
|
+
bind(runId: string, runDir: string, generation: string | undefined): WorkflowCompletion;
|
|
39
|
+
owner(runId: string, runDir: string): CompletionOwner;
|
|
40
|
+
/**
|
|
41
|
+
* A pin yields `identity_unavailable` only when this registry retains no
|
|
42
|
+
* record for it (bound by another process, or evicted); callers may then
|
|
43
|
+
* consult the identity persisted in state.json.
|
|
44
|
+
*/
|
|
45
|
+
select(runDir: string): WorkflowCompletion | undefined;
|
|
46
|
+
select(runDir: string, expected?: string): WorkflowCompletion | IdentityFailure | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Capture the terminal publication while ownership still prevents local
|
|
49
|
+
* re-admission. Only a terminal state of the bound directory incarnation counts.
|
|
50
|
+
*/
|
|
51
|
+
recordPublication(entry: WorkflowCompletion): Promise<void>;
|
|
52
|
+
markStamped(entry: WorkflowCompletion): void;
|
|
53
|
+
settle(entry: WorkflowCompletion): void;
|
|
54
|
+
/** Protect provenance through the handler's final filesystem rereads. */
|
|
55
|
+
retain(entry: WorkflowCompletion): () => void;
|
|
56
|
+
/**
|
|
57
|
+
* Execution fence around a projection. `observedDigest` is the fingerprint of
|
|
58
|
+
* the state bytes the projection actually parsed (null: unreadable); pass it
|
|
59
|
+
* only when the entry was already settled before that projection began.
|
|
60
|
+
* Without it, only provenance and directory incarnation are checked, which
|
|
61
|
+
* is correct for an active execution that legitimately republishes state.
|
|
62
|
+
*/
|
|
63
|
+
check(entry: WorkflowCompletion, observedDigest?: string | null): Promise<IdentityFailure | undefined>;
|
|
64
|
+
wait(entry: WorkflowCompletion, signal: AbortSignal): Promise<"settled" | "budget_elapsed">;
|
|
65
|
+
private selectionFailure;
|
|
66
|
+
private own;
|
|
67
|
+
private prune;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Persisted provenance for a pin this process retains no record for. Only a
|
|
71
|
+
* terminal state stamped with exactly that id, whose run.lock is released
|
|
72
|
+
* (`stale_or_absent`), counts as its settlement: a live or unknown lock means
|
|
73
|
+
* an owner may still be inside its cleanup fence, and a non-terminal match has
|
|
74
|
+
* no local owner whose fence could be witnessed.
|
|
75
|
+
*/
|
|
76
|
+
export declare function persistedIdentityFailure(state: {
|
|
77
|
+
readonly runId?: unknown;
|
|
78
|
+
readonly status?: unknown;
|
|
79
|
+
readonly executionId?: unknown;
|
|
80
|
+
readonly completedAt?: unknown;
|
|
81
|
+
readonly executionCompletedAt?: unknown;
|
|
82
|
+
}, lock: "held" | "stale_or_absent" | "unknown", runId: string, expected: string): IdentityFailure | undefined;
|
|
83
|
+
export declare function inspectPublication(runDir: string): Promise<PublicationIdentity | undefined>;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { readContainedRelativeFile } from "./contained-file.js";
|
|
4
|
+
import { DEFAULT_DASHBOARD_MAX_STATE_BYTES } from "./config.js";
|
|
5
|
+
import { readSessionLogGeneration } from "./dashboard/session-log-identity.js";
|
|
6
|
+
import { WorkflowCancellationError } from "./errors.js";
|
|
7
|
+
import { logger } from "./logger.js";
|
|
8
|
+
export const WORKFLOW_WAIT_BUDGET_MS = 30_000;
|
|
9
|
+
export const SETTLED_LIMIT = 128;
|
|
10
|
+
/** sha256 over the exact bytes read from state.json (never a decoded string). */
|
|
11
|
+
function fingerprintStateBytes(bytes) {
|
|
12
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Provenance only: result bodies and unfinished promise callbacks are never cached.
|
|
16
|
+
* An execution exists here only once its owner has bound a created/resumed
|
|
17
|
+
* store, so a request rejected during admission can never displace one.
|
|
18
|
+
*/
|
|
19
|
+
export class WorkflowCompletionRegistry {
|
|
20
|
+
latest = new Map();
|
|
21
|
+
byId = new Map();
|
|
22
|
+
minted = new WeakSet();
|
|
23
|
+
settledIds = [];
|
|
24
|
+
bind(runId, runDir, generation) {
|
|
25
|
+
const listeners = new Set();
|
|
26
|
+
const record = {
|
|
27
|
+
executionId: randomUUID(),
|
|
28
|
+
runId,
|
|
29
|
+
runDir: resolve(runDir),
|
|
30
|
+
generation,
|
|
31
|
+
publication: undefined,
|
|
32
|
+
settled: false,
|
|
33
|
+
stamped: false,
|
|
34
|
+
observers: 0,
|
|
35
|
+
listeners,
|
|
36
|
+
get listenerCount() { return listeners.size; },
|
|
37
|
+
};
|
|
38
|
+
this.minted.add(record);
|
|
39
|
+
this.byId.set(record.executionId, record);
|
|
40
|
+
this.latest.set(record.runDir, record);
|
|
41
|
+
return record;
|
|
42
|
+
}
|
|
43
|
+
owner(runId, runDir) {
|
|
44
|
+
let entry;
|
|
45
|
+
let boundStore;
|
|
46
|
+
return {
|
|
47
|
+
get entry() { return entry; },
|
|
48
|
+
bind: (store) => {
|
|
49
|
+
if (entry !== undefined) {
|
|
50
|
+
if (boundStore !== store)
|
|
51
|
+
throw new Error(`workflow run ${runId} is already bound to a different state store`);
|
|
52
|
+
return entry;
|
|
53
|
+
}
|
|
54
|
+
entry = this.bind(runId, runDir, store.sessionLogGeneration);
|
|
55
|
+
boundStore = store;
|
|
56
|
+
return entry;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
select(runDir, expected) {
|
|
61
|
+
const latest = this.latest.get(resolve(runDir));
|
|
62
|
+
if (expected === undefined)
|
|
63
|
+
return latest;
|
|
64
|
+
const entry = this.byId.get(expected);
|
|
65
|
+
if (entry === undefined)
|
|
66
|
+
return "identity_unavailable";
|
|
67
|
+
if (entry.runDir !== resolve(runDir) || latest !== entry)
|
|
68
|
+
return "identity_changed";
|
|
69
|
+
return entry;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Capture the terminal publication while ownership still prevents local
|
|
73
|
+
* re-admission. Only a terminal state of the bound directory incarnation counts.
|
|
74
|
+
*/
|
|
75
|
+
async recordPublication(entry) {
|
|
76
|
+
const record = this.own(entry);
|
|
77
|
+
if (record.settled || record.generation === undefined)
|
|
78
|
+
return;
|
|
79
|
+
const publication = await inspectPublication(record.runDir);
|
|
80
|
+
if (publication?.generation === record.generation && publication.terminal) {
|
|
81
|
+
record.publication = publication;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
markStamped(entry) {
|
|
85
|
+
this.own(entry).stamped = true;
|
|
86
|
+
}
|
|
87
|
+
settle(entry) {
|
|
88
|
+
const record = this.own(entry);
|
|
89
|
+
if (record.settled)
|
|
90
|
+
return;
|
|
91
|
+
record.settled = true;
|
|
92
|
+
this.settledIds.push(record.executionId);
|
|
93
|
+
this.prune();
|
|
94
|
+
for (const listener of [...record.listeners])
|
|
95
|
+
listener();
|
|
96
|
+
}
|
|
97
|
+
/** Protect provenance through the handler's final filesystem rereads. */
|
|
98
|
+
retain(entry) {
|
|
99
|
+
const record = this.own(entry);
|
|
100
|
+
record.observers += 1;
|
|
101
|
+
return () => {
|
|
102
|
+
record.observers -= 1;
|
|
103
|
+
this.prune();
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Execution fence around a projection. `observedDigest` is the fingerprint of
|
|
108
|
+
* the state bytes the projection actually parsed (null: unreadable); pass it
|
|
109
|
+
* only when the entry was already settled before that projection began.
|
|
110
|
+
* Without it, only provenance and directory incarnation are checked, which
|
|
111
|
+
* is correct for an active execution that legitimately republishes state.
|
|
112
|
+
*/
|
|
113
|
+
async check(entry, observedDigest) {
|
|
114
|
+
const record = this.own(entry);
|
|
115
|
+
const before = this.selectionFailure(record);
|
|
116
|
+
if (before !== undefined)
|
|
117
|
+
return before;
|
|
118
|
+
if (record.generation === undefined)
|
|
119
|
+
return "identity_unavailable";
|
|
120
|
+
const generation = await readSessionLogGeneration(record.runDir);
|
|
121
|
+
// Recheck after the I/O: a replacement may bind while the read is pending.
|
|
122
|
+
const after = this.selectionFailure(record);
|
|
123
|
+
if (after !== undefined)
|
|
124
|
+
return after;
|
|
125
|
+
if (generation.kind !== "available")
|
|
126
|
+
return "identity_unavailable";
|
|
127
|
+
if (generation.sessionLogGeneration !== record.generation)
|
|
128
|
+
return "identity_changed";
|
|
129
|
+
if (record.settled && record.publication === undefined)
|
|
130
|
+
return "identity_unavailable";
|
|
131
|
+
if (observedDigest !== undefined) {
|
|
132
|
+
if (record.publication === undefined || observedDigest === null)
|
|
133
|
+
return "identity_unavailable";
|
|
134
|
+
if (observedDigest !== record.publication.fingerprint)
|
|
135
|
+
return "identity_changed";
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
wait(entry, signal) {
|
|
140
|
+
const record = this.own(entry);
|
|
141
|
+
if (signal.aborted)
|
|
142
|
+
return Promise.reject(new WorkflowCancellationError());
|
|
143
|
+
if (record.settled)
|
|
144
|
+
return Promise.resolve("settled");
|
|
145
|
+
return new Promise((resolveWait, reject) => {
|
|
146
|
+
let timer;
|
|
147
|
+
const cleanup = () => {
|
|
148
|
+
if (timer !== undefined)
|
|
149
|
+
clearTimeout(timer);
|
|
150
|
+
record.listeners.delete(onComplete);
|
|
151
|
+
signal.removeEventListener("abort", onAbort);
|
|
152
|
+
};
|
|
153
|
+
const onComplete = () => {
|
|
154
|
+
cleanup();
|
|
155
|
+
resolveWait("settled");
|
|
156
|
+
};
|
|
157
|
+
const onAbort = () => {
|
|
158
|
+
cleanup();
|
|
159
|
+
reject(new WorkflowCancellationError());
|
|
160
|
+
};
|
|
161
|
+
record.listeners.add(onComplete);
|
|
162
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
163
|
+
timer = setTimeout(() => {
|
|
164
|
+
cleanup();
|
|
165
|
+
resolveWait("budget_elapsed");
|
|
166
|
+
}, WORKFLOW_WAIT_BUDGET_MS);
|
|
167
|
+
// A pending observation must not keep an otherwise idle process alive.
|
|
168
|
+
timer.unref?.();
|
|
169
|
+
// Inspect after registration, so a fast completion cannot strand a waiter.
|
|
170
|
+
if (record.settled)
|
|
171
|
+
onComplete();
|
|
172
|
+
else if (signal.aborted)
|
|
173
|
+
onAbort();
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
selectionFailure(record) {
|
|
177
|
+
const selected = this.select(record.runDir, record.executionId);
|
|
178
|
+
if (typeof selected === "string")
|
|
179
|
+
return selected;
|
|
180
|
+
return selected === record ? undefined : "identity_unavailable";
|
|
181
|
+
}
|
|
182
|
+
own(entry) {
|
|
183
|
+
if (!this.minted.has(entry))
|
|
184
|
+
throw new Error("workflow completion was not bound by this registry");
|
|
185
|
+
return entry;
|
|
186
|
+
}
|
|
187
|
+
prune() {
|
|
188
|
+
while (this.settledIds.length > SETTLED_LIMIT) {
|
|
189
|
+
const index = this.settledIds.findIndex((id) => this.byId.get(id)?.observers === 0);
|
|
190
|
+
if (index === -1)
|
|
191
|
+
return;
|
|
192
|
+
const id = this.settledIds.splice(index, 1)[0];
|
|
193
|
+
const retired = this.byId.get(id);
|
|
194
|
+
this.byId.delete(id);
|
|
195
|
+
if (retired !== undefined && this.latest.get(retired.runDir) === retired) {
|
|
196
|
+
this.latest.delete(retired.runDir);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function isTerminalStatus(status) {
|
|
202
|
+
return status === "succeeded" || status === "failed" || status === "cancelled";
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Persisted provenance for a pin this process retains no record for. Only a
|
|
206
|
+
* terminal state stamped with exactly that id, whose run.lock is released
|
|
207
|
+
* (`stale_or_absent`), counts as its settlement: a live or unknown lock means
|
|
208
|
+
* an owner may still be inside its cleanup fence, and a non-terminal match has
|
|
209
|
+
* no local owner whose fence could be witnessed.
|
|
210
|
+
*/
|
|
211
|
+
export function persistedIdentityFailure(state, lock, runId, expected) {
|
|
212
|
+
if (typeof state.executionId !== "string" || state.runId !== runId)
|
|
213
|
+
return "identity_unavailable";
|
|
214
|
+
if (state.executionId !== expected)
|
|
215
|
+
return "identity_changed";
|
|
216
|
+
if (!isTerminalStatus(state.status) || lock !== "stale_or_absent")
|
|
217
|
+
return "identity_unavailable";
|
|
218
|
+
return typeof state.completedAt === "string" && state.executionCompletedAt === state.completedAt
|
|
219
|
+
? undefined
|
|
220
|
+
: "identity_unavailable";
|
|
221
|
+
}
|
|
222
|
+
export async function inspectPublication(runDir) {
|
|
223
|
+
try {
|
|
224
|
+
const before = await readSessionLogGeneration(runDir);
|
|
225
|
+
if (before.kind !== "available")
|
|
226
|
+
return undefined;
|
|
227
|
+
const file = await readContainedRelativeFile(runDir, "state.json", {
|
|
228
|
+
wantBytes: true,
|
|
229
|
+
requireRegular: true,
|
|
230
|
+
requireExactRequestedPath: true,
|
|
231
|
+
maxBytes: DEFAULT_DASHBOARD_MAX_STATE_BYTES,
|
|
232
|
+
});
|
|
233
|
+
const state = JSON.parse(file.bytes.toString("utf-8"));
|
|
234
|
+
const after = await readSessionLogGeneration(runDir);
|
|
235
|
+
if (after.kind !== "available" || after.sessionLogGeneration !== before.sessionLogGeneration) {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
return {
|
|
239
|
+
generation: before.sessionLogGeneration,
|
|
240
|
+
fingerprint: fingerprintStateBytes(file.bytes),
|
|
241
|
+
terminal: isTerminalStatus(state.status),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
// Class only: messages can carry paths or state values.
|
|
246
|
+
logger.debug({ errClass: error instanceof Error ? error.constructor.name : typeof error, runDir }, "workflow publication identity unavailable");
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=workflow-completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-completion.js","sourceRoot":"","sources":["../src/workflow-completion.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* - Standalone STDIO server (ClinkSession child process backend)
|
|
6
6
|
* - In-process integration (LocalClinkBackend via root ClinkX)
|
|
7
7
|
*
|
|
8
|
-
* The `registerWorkflowTools()` function registers
|
|
8
|
+
* The `registerWorkflowTools()` function registers six tools on a Server:
|
|
9
9
|
* - run_workflow: start a new workflow execution
|
|
10
10
|
* - resume_workflow: resume a failed/cancelled run
|
|
11
11
|
* - list_workflows: list available workflow definitions
|
|
12
12
|
* - workflow_run_status: read workflow run progress/result
|
|
13
|
+
* - workflow_run_wait: observe local execution settlement
|
|
13
14
|
* - cancel_workflow: cancel a live or orphaned workflow run
|
|
14
15
|
*
|
|
15
16
|
* The caller provides a `createBackend` factory that determines how clink
|
|
@@ -26,7 +27,10 @@ import type { Variable } from "./schema.js";
|
|
|
26
27
|
import { type WorkflowContextRequestedMode, type WorkflowContextPolicy } from "./context-mode-resolver.js";
|
|
27
28
|
import { type AvailableCliCapability, type ResolvedRoster } from "./roster-contract.js";
|
|
28
29
|
import type { RosterLoader } from "./rosters.js";
|
|
30
|
+
import type { VerifiedRepairHost } from "./verified-repair/host.js";
|
|
29
31
|
export interface WorkflowToolsOptions {
|
|
32
|
+
/** Root-owned contract capture, scoped edits, and native verification. */
|
|
33
|
+
readonly verifiedRepairHost?: VerifiedRepairHost | undefined;
|
|
30
34
|
/**
|
|
31
35
|
* Factory that creates a ClinkBackend for a given run.
|
|
32
36
|
* For standalone server: `(runId) => new ClinkSession({ runId })`
|
|
@@ -191,8 +195,8 @@ export declare function handleWorkflowToolCall(server: Server, options: Workflow
|
|
|
191
195
|
* Register workflow tools on an MCP Server instance.
|
|
192
196
|
*
|
|
193
197
|
* This is the reusable core extracted from createWorkflowServer().
|
|
194
|
-
* It registers
|
|
195
|
-
* workflow_run_status, cancel_workflow.
|
|
198
|
+
* It registers six tools: run_workflow, resume_workflow, list_workflows,
|
|
199
|
+
* workflow_run_status, workflow_run_wait, cancel_workflow.
|
|
196
200
|
*
|
|
197
201
|
* The `options.createBackend` factory determines how clink calls are
|
|
198
202
|
* dispatched — callers provide either a child-process or in-process backend.
|