cool-workflow 0.1.78
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/.claude-plugin/plugin.json +20 -0
- package/.codex-plugin/mcp.json +10 -0
- package/.codex-plugin/plugin.json +38 -0
- package/.mcp.json +10 -0
- package/LICENSE +24 -0
- package/README.md +638 -0
- package/apps/architecture-review/app.json +51 -0
- package/apps/architecture-review/workflow.js +116 -0
- package/apps/end-to-end-golden-path/app.json +30 -0
- package/apps/end-to-end-golden-path/workflow.js +33 -0
- package/apps/pr-review-fix-ci/app.json +59 -0
- package/apps/pr-review-fix-ci/workflow.js +90 -0
- package/apps/release-cut/app.json +54 -0
- package/apps/release-cut/workflow.js +82 -0
- package/apps/research-synthesis/app.json +50 -0
- package/apps/research-synthesis/workflow.js +76 -0
- package/apps/workflow-app-framework-demo/app.json +29 -0
- package/apps/workflow-app-framework-demo/workflow.js +44 -0
- package/dist/agent-config.js +223 -0
- package/dist/candidate-scoring.js +715 -0
- package/dist/capability-core.js +630 -0
- package/dist/capability-dispatcher.js +86 -0
- package/dist/capability-registry.js +523 -0
- package/dist/cli.js +1276 -0
- package/dist/collaboration.js +727 -0
- package/dist/commit.js +570 -0
- package/dist/contract-migration.js +234 -0
- package/dist/coordinator.js +1163 -0
- package/dist/daemon.js +44 -0
- package/dist/dispatch.js +201 -0
- package/dist/drive.js +503 -0
- package/dist/error-feedback.js +415 -0
- package/dist/evidence-grounding.js +179 -0
- package/dist/evidence-reasoning.js +733 -0
- package/dist/execution-backend.js +1279 -0
- package/dist/harness.js +61 -0
- package/dist/mcp-server.js +1615 -0
- package/dist/multi-agent-eval.js +857 -0
- package/dist/multi-agent-host.js +764 -0
- package/dist/multi-agent-operator-ux.js +537 -0
- package/dist/multi-agent-trust.js +366 -0
- package/dist/multi-agent.js +1173 -0
- package/dist/node-snapshot.js +270 -0
- package/dist/observability.js +922 -0
- package/dist/operator-ux.js +971 -0
- package/dist/orchestrator/audit-operations.js +182 -0
- package/dist/orchestrator/candidate-operations.js +117 -0
- package/dist/orchestrator/cli-options.js +288 -0
- package/dist/orchestrator/collaboration-operations.js +86 -0
- package/dist/orchestrator/feedback-operations.js +81 -0
- package/dist/orchestrator/host-operations.js +78 -0
- package/dist/orchestrator/lifecycle-operations.js +462 -0
- package/dist/orchestrator/migration-operations.js +44 -0
- package/dist/orchestrator/multi-agent-operations.js +362 -0
- package/dist/orchestrator/report.js +369 -0
- package/dist/orchestrator/topology-operations.js +84 -0
- package/dist/orchestrator.js +874 -0
- package/dist/pipeline-contract.js +92 -0
- package/dist/pipeline-runner.js +285 -0
- package/dist/reclamation.js +882 -0
- package/dist/result-normalize.js +194 -0
- package/dist/run-export.js +64 -0
- package/dist/run-registry.js +1347 -0
- package/dist/run-state-schema.js +67 -0
- package/dist/sandbox-profile.js +471 -0
- package/dist/scheduler.js +266 -0
- package/dist/scheduling.js +184 -0
- package/dist/schema-validate.js +98 -0
- package/dist/state-explosion.js +1213 -0
- package/dist/state-migrations.js +463 -0
- package/dist/state-node.js +301 -0
- package/dist/state.js +308 -0
- package/dist/telemetry-attestation.js +156 -0
- package/dist/telemetry-ledger.js +145 -0
- package/dist/topology.js +527 -0
- package/dist/triggers.js +159 -0
- package/dist/trust-audit.js +475 -0
- package/dist/types/blackboard.js +2 -0
- package/dist/types/boundary.js +29 -0
- package/dist/types/candidate.js +2 -0
- package/dist/types/collaboration.js +2 -0
- package/dist/types/core.js +2 -0
- package/dist/types/drive.js +10 -0
- package/dist/types/error-feedback.js +2 -0
- package/dist/types/evidence-reasoning.js +2 -0
- package/dist/types/execution-backend.js +2 -0
- package/dist/types/multi-agent.js +2 -0
- package/dist/types/observability.js +2 -0
- package/dist/types/pipeline.js +2 -0
- package/dist/types/reclamation.js +8 -0
- package/dist/types/result.js +2 -0
- package/dist/types/run-registry.js +2 -0
- package/dist/types/run.js +2 -0
- package/dist/types/sandbox.js +2 -0
- package/dist/types/schedule.js +2 -0
- package/dist/types/state-node.js +2 -0
- package/dist/types/topology.js +2 -0
- package/dist/types/trust.js +2 -0
- package/dist/types/workbench.js +2 -0
- package/dist/types/worker.js +2 -0
- package/dist/types/workflow-app.js +2 -0
- package/dist/types.js +43 -0
- package/dist/verifier-registry.js +46 -0
- package/dist/verifier.js +78 -0
- package/dist/version.js +8 -0
- package/dist/workbench-host.js +172 -0
- package/dist/workbench.js +190 -0
- package/dist/worker-isolation.js +1028 -0
- package/dist/workflow-api.js +98 -0
- package/dist/workflow-app-framework.js +626 -0
- package/docs/agent-delegation-drive.7.md +190 -0
- package/docs/agent-framework.md +176 -0
- package/docs/candidate-scoring.7.md +106 -0
- package/docs/canonical-workflow-apps.7.md +137 -0
- package/docs/capability-topology-registry.7.md +168 -0
- package/docs/cli-mcp-parity.7.md +373 -0
- package/docs/contract-migration-tooling.7.md +123 -0
- package/docs/control-plane-scheduling.7.md +110 -0
- package/docs/coordinator-blackboard.7.md +183 -0
- package/docs/dogfood/architecture-review-cool-workflow.md +16 -0
- package/docs/dogfood-one-real-repo.7.md +168 -0
- package/docs/durable-state-and-locking.7.md +107 -0
- package/docs/end-to-end-golden-path.7.md +117 -0
- package/docs/error-feedback.7.md +153 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +270 -0
- package/docs/execution-backends.7.md +300 -0
- package/docs/getting-started.md +99 -0
- package/docs/index.md +41 -0
- package/docs/mcp-app-surface.7.md +235 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +265 -0
- package/docs/multi-agent-eval-replay-harness.7.md +302 -0
- package/docs/multi-agent-operator-ux.7.md +314 -0
- package/docs/multi-agent-runtime-core.7.md +231 -0
- package/docs/multi-agent-topologies.7.md +103 -0
- package/docs/multi-agent-trust-policy-audit.7.md +154 -0
- package/docs/node-snapshot-diff-replay.7.md +135 -0
- package/docs/observability-cost-accounting.7.md +194 -0
- package/docs/operator-ux.7.md +180 -0
- package/docs/pipeline-runner.7.md +136 -0
- package/docs/project-index.md +261 -0
- package/docs/real-execution-backends.7.md +142 -0
- package/docs/release-and-migration.7.md +280 -0
- package/docs/release-tooling.7.md +159 -0
- package/docs/routines.md +48 -0
- package/docs/run-registry-control-plane.7.md +312 -0
- package/docs/run-retention-reclamation.7.md +191 -0
- package/docs/sandbox-profiles.7.md +137 -0
- package/docs/scheduled-tasks.md +80 -0
- package/docs/security-trust-hardening.7.md +117 -0
- package/docs/state-explosion-management.7.md +264 -0
- package/docs/state-node.7.md +96 -0
- package/docs/team-collaboration.7.md +207 -0
- package/docs/unix-principles.md +192 -0
- package/docs/verifier-gated-commit.7.md +140 -0
- package/docs/web-desktop-workbench.7.md +215 -0
- package/docs/worker-isolation.7.md +167 -0
- package/docs/workflow-app-framework.7.md +274 -0
- package/manifest/README.md +43 -0
- package/manifest/plugin.manifest.json +316 -0
- package/manifest/pricing.policy.json +14 -0
- package/package.json +79 -0
- package/scripts/agents/claude-p-agent.js +104 -0
- package/scripts/agents/claude-p-agent.sh +9 -0
- package/scripts/agents/cw-attest-keygen.js +55 -0
- package/scripts/agents/cw-attest-wrap.js +143 -0
- package/scripts/block-unapproved-tag.sh +39 -0
- package/scripts/bump-version.js +249 -0
- package/scripts/canonical-apps.js +171 -0
- package/scripts/cw.js +4 -0
- package/scripts/dist-drift-check.js +79 -0
- package/scripts/dogfood-architecture-review.js +237 -0
- package/scripts/dogfood-release.js +624 -0
- package/scripts/forward-ref-docs.js +73 -0
- package/scripts/gen-manifests.js +232 -0
- package/scripts/golden-path.js +300 -0
- package/scripts/mcp-server.js +4 -0
- package/scripts/new-feature.js +121 -0
- package/scripts/parity-check.js +213 -0
- package/scripts/release-check.js +118 -0
- package/scripts/release-flow.js +272 -0
- package/scripts/release-gate.sh +85 -0
- package/scripts/sync-project-index.js +387 -0
- package/scripts/validate-run-state-schema.js +126 -0
- package/scripts/verify-container-selfref.js +64 -0
- package/scripts/version-sync-check.js +237 -0
- package/skills/cool-workflow/SKILL.md +162 -0
- package/skills/cool-workflow/references/commands.md +282 -0
- package/tsconfig.json +16 -0
- package/ui/workbench/app.css +76 -0
- package/ui/workbench/app.js +159 -0
- package/ui/workbench/index.html +32 -0
- package/workflows/architecture-review.workflow.js +84 -0
- package/workflows/research-synthesis.workflow.js +47 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ERROR_FEEDBACK_SCHEMA_VERSION = void 0;
|
|
7
|
+
exports.createErrorFeedbackLoop = createErrorFeedbackLoop;
|
|
8
|
+
exports.collectRunErrors = collectRunErrors;
|
|
9
|
+
exports.recordFeedback = recordFeedback;
|
|
10
|
+
exports.classifyFeedback = classifyFeedback;
|
|
11
|
+
exports.createCorrectionTask = createCorrectionTask;
|
|
12
|
+
exports.resolveFeedback = resolveFeedback;
|
|
13
|
+
exports.listFeedback = listFeedback;
|
|
14
|
+
exports.getFeedback = getFeedback;
|
|
15
|
+
exports.summarizeFeedback = summarizeFeedback;
|
|
16
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
17
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
const state_1 = require("./state");
|
|
19
|
+
const state_node_1 = require("./state-node");
|
|
20
|
+
exports.ERROR_FEEDBACK_SCHEMA_VERSION = 1;
|
|
21
|
+
function createErrorFeedbackLoop(options = {}) {
|
|
22
|
+
return {
|
|
23
|
+
collectRunErrors: (run, collectOptions) => collectRunErrors(run, mergeOptions(options, collectOptions)),
|
|
24
|
+
recordFeedback: (run, input) => recordFeedback(run, input, options),
|
|
25
|
+
classifyFeedback,
|
|
26
|
+
createCorrectionTask: (run, feedbackId, taskOptions) => createCorrectionTask(run, feedbackId, taskOptions),
|
|
27
|
+
resolveFeedback: (run, feedbackId, result) => resolveFeedback(run, feedbackId, result),
|
|
28
|
+
listFeedback: (run, listOptions) => listFeedback(run, listOptions),
|
|
29
|
+
getFeedback,
|
|
30
|
+
summarizeFeedback
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function collectRunErrors(run, options = {}) {
|
|
34
|
+
const records = [];
|
|
35
|
+
const existing = new Set((run.feedback || []).map(feedbackKey));
|
|
36
|
+
for (const node of run.nodes || []) {
|
|
37
|
+
if (node.status !== "failed" && !node.errors.length)
|
|
38
|
+
continue;
|
|
39
|
+
for (const error of node.errors) {
|
|
40
|
+
const key = feedbackKey({
|
|
41
|
+
runId: run.id,
|
|
42
|
+
code: error.code,
|
|
43
|
+
message: error.message,
|
|
44
|
+
nodeId: node.id,
|
|
45
|
+
stageId: stringMetadata(node.metadata, "pipelineStage"),
|
|
46
|
+
contractId: node.contractId,
|
|
47
|
+
path: error.path
|
|
48
|
+
});
|
|
49
|
+
if (existing.has(key))
|
|
50
|
+
continue;
|
|
51
|
+
const record = recordFeedback(run, {
|
|
52
|
+
source: "state-node",
|
|
53
|
+
error,
|
|
54
|
+
nodeId: node.id,
|
|
55
|
+
stageId: stringMetadata(node.metadata, "pipelineStage"),
|
|
56
|
+
contractId: node.contractId,
|
|
57
|
+
taskId: stringMetadata(node.metadata, "taskId"),
|
|
58
|
+
path: error.path,
|
|
59
|
+
retryable: error.retryable,
|
|
60
|
+
evidence: node.evidence,
|
|
61
|
+
artifacts: node.artifacts,
|
|
62
|
+
metadata: {
|
|
63
|
+
collectedFromNodeId: node.id,
|
|
64
|
+
errorAt: error.at,
|
|
65
|
+
details: error.details
|
|
66
|
+
}
|
|
67
|
+
}, options);
|
|
68
|
+
records.push(record);
|
|
69
|
+
existing.add(feedbackKey(record));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (options.persist !== false && records.length)
|
|
73
|
+
(0, state_1.saveCheckpoint)(run);
|
|
74
|
+
return records;
|
|
75
|
+
}
|
|
76
|
+
function recordFeedback(run, input, options = {}) {
|
|
77
|
+
ensureFeedbackState(run);
|
|
78
|
+
const error = normalizeError(input.error);
|
|
79
|
+
const nodeId = input.nodeId || error.nodeId;
|
|
80
|
+
const stageId = input.stageId;
|
|
81
|
+
const contractId = input.contractId;
|
|
82
|
+
const existing = (run.feedback || []).find((record) => record.status !== "resolved" &&
|
|
83
|
+
record.code === error.code &&
|
|
84
|
+
record.message === error.message &&
|
|
85
|
+
record.nodeId === nodeId &&
|
|
86
|
+
record.stageId === stageId &&
|
|
87
|
+
record.contractId === contractId &&
|
|
88
|
+
record.path === (input.path || error.path));
|
|
89
|
+
if (existing)
|
|
90
|
+
return existing;
|
|
91
|
+
const classification = classifyFeedback(error, {
|
|
92
|
+
source: input.source || options.source,
|
|
93
|
+
stageId,
|
|
94
|
+
contractId,
|
|
95
|
+
metadata: input.metadata
|
|
96
|
+
});
|
|
97
|
+
const now = new Date().toISOString();
|
|
98
|
+
const record = {
|
|
99
|
+
schemaVersion: exports.ERROR_FEEDBACK_SCHEMA_VERSION,
|
|
100
|
+
id: createFeedbackId(classification),
|
|
101
|
+
runId: run.id,
|
|
102
|
+
createdAt: now,
|
|
103
|
+
updatedAt: now,
|
|
104
|
+
status: "open",
|
|
105
|
+
severity: severityFor(classification, error),
|
|
106
|
+
classification,
|
|
107
|
+
source: input.source || options.source || sourceFor(classification),
|
|
108
|
+
code: error.code,
|
|
109
|
+
message: error.message,
|
|
110
|
+
nodeId,
|
|
111
|
+
stageId,
|
|
112
|
+
contractId,
|
|
113
|
+
taskId: input.taskId,
|
|
114
|
+
path: input.path || error.path,
|
|
115
|
+
retryable: input.retryable ?? error.retryable ?? options.policy?.retryableByDefault ?? false,
|
|
116
|
+
evidence: input.evidence || [],
|
|
117
|
+
artifacts: input.artifacts || [],
|
|
118
|
+
metadata: compactMetadata({
|
|
119
|
+
...input.metadata,
|
|
120
|
+
details: input.metadata?.details || error.details
|
|
121
|
+
})
|
|
122
|
+
};
|
|
123
|
+
run.feedback = [...(run.feedback || []), record];
|
|
124
|
+
writeFeedback(run, record);
|
|
125
|
+
writeFeedbackIndex(run);
|
|
126
|
+
if (options.persist !== false)
|
|
127
|
+
(0, state_1.saveCheckpoint)(run);
|
|
128
|
+
return record;
|
|
129
|
+
}
|
|
130
|
+
function classifyFeedback(error, context = {}) {
|
|
131
|
+
const normalized = normalizeError(error);
|
|
132
|
+
const code = normalized.code.toLowerCase();
|
|
133
|
+
if (code.includes("missing-artifact") || code.includes("artifact-path"))
|
|
134
|
+
return "missing-artifact";
|
|
135
|
+
if (code.includes("missing-required-evidence") || code.includes("missing-evidence"))
|
|
136
|
+
return "missing-evidence";
|
|
137
|
+
if (code.includes("verifier") || context.stageId === "verify" || context.source === "verifier")
|
|
138
|
+
return "verifier-failure";
|
|
139
|
+
if (code.includes("illegal-transition") || code.includes("state-transition"))
|
|
140
|
+
return "state-transition";
|
|
141
|
+
if (code.includes("contract") || code.includes("unexpected-node") || context.contractId)
|
|
142
|
+
return "contract-violation";
|
|
143
|
+
if (code.startsWith("sandbox-"))
|
|
144
|
+
return "sandbox-policy";
|
|
145
|
+
if (code.includes("parse") || code.includes("json"))
|
|
146
|
+
return "parse-error";
|
|
147
|
+
if (code.includes("pipeline"))
|
|
148
|
+
return "pipeline-failure";
|
|
149
|
+
if (normalized.code === "runtime-error")
|
|
150
|
+
return "runtime-error";
|
|
151
|
+
return "unknown";
|
|
152
|
+
}
|
|
153
|
+
function createCorrectionTask(run, feedbackId, options = {}) {
|
|
154
|
+
const record = requireFeedback(run, feedbackId);
|
|
155
|
+
if (record.correctionTaskId)
|
|
156
|
+
return record;
|
|
157
|
+
const taskId = `feedback:${(0, state_1.safeFileName)(record.id)}`;
|
|
158
|
+
const taskPath = node_path_1.default.join(run.paths.tasksDir, `${(0, state_1.safeFileName)(taskId)}.md`);
|
|
159
|
+
const body = renderCorrectionTask(record, options);
|
|
160
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(taskPath), { recursive: true });
|
|
161
|
+
node_fs_1.default.writeFileSync(taskPath, body, "utf8");
|
|
162
|
+
const node = (0, state_node_1.appendRunNode)(run, (0, state_node_1.createStateNode)({
|
|
163
|
+
id: `${run.id}:task:${taskId}`,
|
|
164
|
+
kind: "task",
|
|
165
|
+
status: "pending",
|
|
166
|
+
loopStage: "adjust",
|
|
167
|
+
inputs: { feedbackId: record.id, nodeId: record.nodeId, stageId: record.stageId, contractId: record.contractId },
|
|
168
|
+
artifacts: [{ id: "task", kind: "markdown", path: taskPath }],
|
|
169
|
+
parents: record.nodeId ? [record.nodeId] : [],
|
|
170
|
+
contractId: record.contractId,
|
|
171
|
+
metadata: { feedbackId: record.id, correctionTask: true, retryable: record.retryable }
|
|
172
|
+
}));
|
|
173
|
+
updateFeedback(run, {
|
|
174
|
+
...record,
|
|
175
|
+
updatedAt: new Date().toISOString(),
|
|
176
|
+
status: "tasked",
|
|
177
|
+
correctionTaskId: taskId,
|
|
178
|
+
metadata: {
|
|
179
|
+
...(record.metadata || {}),
|
|
180
|
+
correctionTaskPath: taskPath,
|
|
181
|
+
correctionTaskNodeId: node.id,
|
|
182
|
+
verifierCommand: options.verifierCommand
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
(0, state_1.saveCheckpoint)(run);
|
|
186
|
+
return requireFeedback(run, feedbackId);
|
|
187
|
+
}
|
|
188
|
+
function resolveFeedback(run, feedbackId, result) {
|
|
189
|
+
const record = requireFeedback(run, feedbackId);
|
|
190
|
+
if (result.status === "resolved" && !result.nodeId) {
|
|
191
|
+
throw new Error(`Feedback ${feedbackId} cannot resolve without a verified node id`);
|
|
192
|
+
}
|
|
193
|
+
if (result.status === "resolved") {
|
|
194
|
+
const node = (run.nodes || []).find((candidate) => candidate.id === result.nodeId);
|
|
195
|
+
if (!node)
|
|
196
|
+
throw new Error(`Feedback ${feedbackId} resolution node not found: ${result.nodeId}`);
|
|
197
|
+
if (node.status !== "verified" && node.status !== "committed") {
|
|
198
|
+
throw new Error(`Feedback ${feedbackId} resolution node must be verified or committed`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const nextStatus = result.status === "resolved" ? "resolved" : "rejected";
|
|
202
|
+
updateFeedback(run, {
|
|
203
|
+
...record,
|
|
204
|
+
updatedAt: new Date().toISOString(),
|
|
205
|
+
status: nextStatus,
|
|
206
|
+
resolvedByNodeId: result.nodeId,
|
|
207
|
+
resolvedAt: nextStatus === "resolved" ? new Date().toISOString() : record.resolvedAt,
|
|
208
|
+
resolutionNote: result.message || record.resolutionNote,
|
|
209
|
+
evidence: mergeById(record.evidence, result.evidence || []),
|
|
210
|
+
artifacts: mergeById(record.artifacts, result.artifacts || []),
|
|
211
|
+
metadata: compactMetadata({
|
|
212
|
+
...(record.metadata || {}),
|
|
213
|
+
resolutionMessage: result.message,
|
|
214
|
+
resolution: result.metadata
|
|
215
|
+
})
|
|
216
|
+
});
|
|
217
|
+
(0, state_1.saveCheckpoint)(run);
|
|
218
|
+
return requireFeedback(run, feedbackId);
|
|
219
|
+
}
|
|
220
|
+
function listFeedback(run, options = {}) {
|
|
221
|
+
ensureFeedbackState(run);
|
|
222
|
+
return (run.feedback || []).filter((record) => {
|
|
223
|
+
if (options.status && record.status !== options.status)
|
|
224
|
+
return false;
|
|
225
|
+
if (options.severity && record.severity !== options.severity)
|
|
226
|
+
return false;
|
|
227
|
+
if (options.classification && record.classification !== options.classification)
|
|
228
|
+
return false;
|
|
229
|
+
return true;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function getFeedback(run, feedbackId) {
|
|
233
|
+
ensureFeedbackState(run);
|
|
234
|
+
return (run.feedback || []).find((record) => record.id === feedbackId);
|
|
235
|
+
}
|
|
236
|
+
function summarizeFeedback(run) {
|
|
237
|
+
ensureFeedbackState(run);
|
|
238
|
+
const records = run.feedback || [];
|
|
239
|
+
return {
|
|
240
|
+
total: records.length,
|
|
241
|
+
byStatus: countBy(records, (record) => record.status),
|
|
242
|
+
bySeverity: countBy(records, (record) => record.severity),
|
|
243
|
+
byClassification: countBy(records, (record) => record.classification),
|
|
244
|
+
artifacts: records.map((record) => feedbackPath(run, record.id))
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function ensureFeedbackState(run) {
|
|
248
|
+
run.paths.feedbackDir = run.paths.feedbackDir || node_path_1.default.join(run.paths.runDir, "feedback");
|
|
249
|
+
node_fs_1.default.mkdirSync(run.paths.feedbackDir, { recursive: true });
|
|
250
|
+
run.feedback = run.feedback || [];
|
|
251
|
+
}
|
|
252
|
+
function normalizeError(error) {
|
|
253
|
+
if (typeof error === "string") {
|
|
254
|
+
return { code: "runtime-error", message: error, at: new Date().toISOString() };
|
|
255
|
+
}
|
|
256
|
+
if (error instanceof Error) {
|
|
257
|
+
return { code: codeFromError(error), message: error.message, at: new Date().toISOString() };
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
...error,
|
|
261
|
+
code: error.code || "runtime-error",
|
|
262
|
+
message: error.message || "Unknown error",
|
|
263
|
+
at: error.at || new Date().toISOString()
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function codeFromError(error) {
|
|
267
|
+
if (/Invalid cw:result JSON/i.test(error.message))
|
|
268
|
+
return "result-parse-error";
|
|
269
|
+
if (/requires cw:result evidence/i.test(error.message))
|
|
270
|
+
return "missing-required-evidence";
|
|
271
|
+
if (/requires evidence/i.test(error.message))
|
|
272
|
+
return "missing-required-evidence";
|
|
273
|
+
if (/Phase gate blocked/i.test(error.message))
|
|
274
|
+
return "phase-gate-blocked";
|
|
275
|
+
return "runtime-error";
|
|
276
|
+
}
|
|
277
|
+
function severityFor(classification, error) {
|
|
278
|
+
if (classification === "verifier-failure" || classification === "contract-violation")
|
|
279
|
+
return "high";
|
|
280
|
+
if (classification === "sandbox-policy")
|
|
281
|
+
return "medium";
|
|
282
|
+
if (classification === "state-transition" || classification === "missing-evidence")
|
|
283
|
+
return "medium";
|
|
284
|
+
if (classification === "missing-artifact" || classification === "parse-error" || classification === "pipeline-failure") {
|
|
285
|
+
return error.retryable ? "medium" : "low";
|
|
286
|
+
}
|
|
287
|
+
return "low";
|
|
288
|
+
}
|
|
289
|
+
function sourceFor(classification) {
|
|
290
|
+
if (classification === "contract-violation")
|
|
291
|
+
return "contract";
|
|
292
|
+
if (classification === "verifier-failure" || classification === "missing-evidence")
|
|
293
|
+
return "verifier";
|
|
294
|
+
if (classification === "pipeline-failure")
|
|
295
|
+
return "pipeline-runner";
|
|
296
|
+
if (classification === "sandbox-policy")
|
|
297
|
+
return "contract";
|
|
298
|
+
return "manual";
|
|
299
|
+
}
|
|
300
|
+
function writeFeedback(run, record) {
|
|
301
|
+
(0, state_1.writeJson)(feedbackPath(run, record.id), record);
|
|
302
|
+
}
|
|
303
|
+
function writeFeedbackIndex(run) {
|
|
304
|
+
ensureFeedbackState(run);
|
|
305
|
+
(0, state_1.writeJson)(node_path_1.default.join(run.paths.feedbackDir, "index.json"), run.feedback || []);
|
|
306
|
+
}
|
|
307
|
+
function feedbackPath(run, feedbackId) {
|
|
308
|
+
ensureFeedbackState(run);
|
|
309
|
+
return node_path_1.default.join(run.paths.feedbackDir, `${(0, state_1.safeFileName)(feedbackId)}.json`);
|
|
310
|
+
}
|
|
311
|
+
function updateFeedback(run, record) {
|
|
312
|
+
ensureFeedbackState(run);
|
|
313
|
+
run.feedback = (run.feedback || []).map((candidate) => (candidate.id === record.id ? record : candidate));
|
|
314
|
+
writeFeedback(run, record);
|
|
315
|
+
writeFeedbackIndex(run);
|
|
316
|
+
}
|
|
317
|
+
function requireFeedback(run, feedbackId) {
|
|
318
|
+
const record = getFeedback(run, feedbackId);
|
|
319
|
+
if (!record)
|
|
320
|
+
throw new Error(`Unknown feedback id for run ${run.id}: ${feedbackId}`);
|
|
321
|
+
return record;
|
|
322
|
+
}
|
|
323
|
+
function renderCorrectionTask(record, options) {
|
|
324
|
+
const verifier = options.verifierCommand || "Run the relevant verifier or smoke test and record the verified StateNode id.";
|
|
325
|
+
const guidance = options.guidance || (record.retryable ? "Retry only after explicit correction input." : "Do not retry blindly.");
|
|
326
|
+
return [
|
|
327
|
+
`# Correction Task: ${record.id}`,
|
|
328
|
+
"",
|
|
329
|
+
`- Status: ${record.status}`,
|
|
330
|
+
`- Severity: ${record.severity}`,
|
|
331
|
+
`- Classification: ${record.classification}`,
|
|
332
|
+
`- Source: ${record.source}`,
|
|
333
|
+
`- Code: ${record.code}`,
|
|
334
|
+
`- Message: ${record.message}`,
|
|
335
|
+
`- Node: ${record.nodeId || ""}`,
|
|
336
|
+
`- Stage: ${record.stageId || ""}`,
|
|
337
|
+
`- Contract: ${record.contractId || ""}`,
|
|
338
|
+
`- Path: ${record.path || ""}`,
|
|
339
|
+
`- Retryable: ${record.retryable ? "yes" : "no"}`,
|
|
340
|
+
"",
|
|
341
|
+
"## Evidence",
|
|
342
|
+
"",
|
|
343
|
+
...formatEvidence(record.evidence),
|
|
344
|
+
"",
|
|
345
|
+
"## Expected Verification",
|
|
346
|
+
"",
|
|
347
|
+
verifier,
|
|
348
|
+
"",
|
|
349
|
+
"## Guidance",
|
|
350
|
+
"",
|
|
351
|
+
guidance,
|
|
352
|
+
""
|
|
353
|
+
].join("\n");
|
|
354
|
+
}
|
|
355
|
+
function formatEvidence(evidence) {
|
|
356
|
+
if (!evidence.length)
|
|
357
|
+
return ["No evidence recorded."];
|
|
358
|
+
return evidence.map((entry) => `- ${entry.id}: ${entry.locator || entry.path || entry.summary || entry.source || ""}`);
|
|
359
|
+
}
|
|
360
|
+
function createFeedbackId(classification) {
|
|
361
|
+
const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(/\..+/, "Z");
|
|
362
|
+
return `feedback-${classification}-${stamp}-${Math.random().toString(36).slice(2, 8)}`;
|
|
363
|
+
}
|
|
364
|
+
function feedbackKey(value) {
|
|
365
|
+
return [
|
|
366
|
+
value.runId || "",
|
|
367
|
+
value.code || "",
|
|
368
|
+
value.message || "",
|
|
369
|
+
value.nodeId || "",
|
|
370
|
+
value.stageId || "",
|
|
371
|
+
value.contractId || "",
|
|
372
|
+
value.path || ""
|
|
373
|
+
].join("\u001f");
|
|
374
|
+
}
|
|
375
|
+
function stringMetadata(metadata, key) {
|
|
376
|
+
const value = metadata?.[key];
|
|
377
|
+
return typeof value === "string" ? value : undefined;
|
|
378
|
+
}
|
|
379
|
+
function compactMetadata(metadata) {
|
|
380
|
+
const compacted = {};
|
|
381
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
382
|
+
if (value !== undefined)
|
|
383
|
+
compacted[key] = value;
|
|
384
|
+
}
|
|
385
|
+
return Object.keys(compacted).length ? compacted : undefined;
|
|
386
|
+
}
|
|
387
|
+
function mergeById(existing, next) {
|
|
388
|
+
const values = [...existing];
|
|
389
|
+
for (const item of next) {
|
|
390
|
+
const index = values.findIndex((candidate) => candidate.id === item.id);
|
|
391
|
+
if (index >= 0)
|
|
392
|
+
values[index] = item;
|
|
393
|
+
else
|
|
394
|
+
values.push(item);
|
|
395
|
+
}
|
|
396
|
+
return values;
|
|
397
|
+
}
|
|
398
|
+
function countBy(values, key) {
|
|
399
|
+
const counts = {};
|
|
400
|
+
for (const value of values) {
|
|
401
|
+
const bucket = key(value);
|
|
402
|
+
counts[bucket] = (counts[bucket] || 0) + 1;
|
|
403
|
+
}
|
|
404
|
+
return counts;
|
|
405
|
+
}
|
|
406
|
+
function mergeOptions(base, next = {}) {
|
|
407
|
+
return {
|
|
408
|
+
...base,
|
|
409
|
+
...next,
|
|
410
|
+
policy: {
|
|
411
|
+
...(base.policy || {}),
|
|
412
|
+
...(next.policy || {})
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isGroundedEvidence = isGroundedEvidence;
|
|
7
|
+
exports.hasGroundedEvidence = hasGroundedEvidence;
|
|
8
|
+
exports.requireResolvableEvidence = requireResolvableEvidence;
|
|
9
|
+
exports.requireUrlReachability = requireUrlReachability;
|
|
10
|
+
exports.resolveEvidenceLocator = resolveEvidenceLocator;
|
|
11
|
+
exports.unresolvedFileEvidence = unresolvedFileEvidence;
|
|
12
|
+
exports.computeEvidenceConfidence = computeEvidenceConfidence;
|
|
13
|
+
exports.computeEvidenceConfidenceTiers = computeEvidenceConfidenceTiers;
|
|
14
|
+
exports.maxEvidenceConfidence = maxEvidenceConfidence;
|
|
15
|
+
exports.extractEvidenceContent = extractEvidenceContent;
|
|
16
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
17
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Evidence grounding (v0.1.40 self-audit P1).
|
|
20
|
+
//
|
|
21
|
+
// The flagship "evidence-gated commit" used to accept ANY non-empty string as
|
|
22
|
+
// evidence (`verifier.ts` checked only `.some(entry => entry.trim())`). That made
|
|
23
|
+
// the gate trust-on-self-report: an agent could pass it with `evidence: ["x"]`.
|
|
24
|
+
//
|
|
25
|
+
// CW's evidence is a deliberately free-form LOCATOR namespace — `file:line`,
|
|
26
|
+
// URLs, and machine tokens like `exitCode:0`, `stdoutSha256:<hash>`, `refused:<why>`
|
|
27
|
+
// are all legitimate — so we cannot require "must be a file that exists" by
|
|
28
|
+
// default without breaking cross-repo, URL, and runtime-token evidence. Instead:
|
|
29
|
+
//
|
|
30
|
+
// 1. DEFAULT (pure, deterministic): require evidence to be GROUNDED — a URL, a
|
|
31
|
+
// path-like locator, or a `namespace:value` token. This rejects bare prose
|
|
32
|
+
// ("x", "anything", "HIGH severity") while accepting every shape CW itself
|
|
33
|
+
// emits. Being a pure function of the string, it is replay-safe.
|
|
34
|
+
//
|
|
35
|
+
// 2. OPT-IN (CW_REQUIRE_RESOLVABLE_EVIDENCE=1): additionally resolve path-like
|
|
36
|
+
// locators against the run's base dirs and fail closed if a file locator does
|
|
37
|
+
// not exist on disk. Off by default because the resolving cwd is context-
|
|
38
|
+
// dependent; on for self-audit / compliance where evidence MUST be checkable.
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
const URL_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
41
|
+
const PATH_SEP_RE = /[\\/]/;
|
|
42
|
+
const FILE_EXT_RE = /\.[A-Za-z0-9]{1,12}(?::\d+(?:-\d+)?)?$/;
|
|
43
|
+
const NAMESPACE_TOKEN_RE = /^[A-Za-z][A-Za-z0-9_.-]*:\S/;
|
|
44
|
+
const LINE_SUFFIX_RE = /:(\d+(?:-\d+)?)$/;
|
|
45
|
+
/** A single evidence string is "grounded" if it is machine-shaped — a URL, a
|
|
46
|
+
* path-like locator, or a `namespace:value` token — rather than free prose. */
|
|
47
|
+
function isGroundedEvidence(raw) {
|
|
48
|
+
const value = String(raw ?? "").trim();
|
|
49
|
+
if (!value)
|
|
50
|
+
return false;
|
|
51
|
+
if (URL_RE.test(value))
|
|
52
|
+
return true;
|
|
53
|
+
if (PATH_SEP_RE.test(value))
|
|
54
|
+
return true;
|
|
55
|
+
if (FILE_EXT_RE.test(value))
|
|
56
|
+
return true;
|
|
57
|
+
if (NAMESPACE_TOKEN_RE.test(value))
|
|
58
|
+
return true;
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
/** An evidence array passes the gate if at least one entry is grounded. */
|
|
62
|
+
function hasGroundedEvidence(evidence) {
|
|
63
|
+
return Array.isArray(evidence) && evidence.some((entry) => isGroundedEvidence(entry));
|
|
64
|
+
}
|
|
65
|
+
/** Whether opt-in strict resolution is requested via the environment.
|
|
66
|
+
* Set CW_REQUIRE_RESOLVABLE_EVIDENCE=1 for file resolution, =url for
|
|
67
|
+
* URL reachability checks too (v0.1.63). */
|
|
68
|
+
function requireResolvableEvidence() {
|
|
69
|
+
return /^(1|true|yes|on)$/i.test(process.env.CW_REQUIRE_RESOLVABLE_EVIDENCE || "");
|
|
70
|
+
}
|
|
71
|
+
/** Whether URL reachability checks are enabled in strict mode (v0.1.63). */
|
|
72
|
+
function requireUrlReachability() {
|
|
73
|
+
return requireResolvableEvidence() || /url/i.test(process.env.CW_REQUIRE_RESOLVABLE_EVIDENCE || "");
|
|
74
|
+
}
|
|
75
|
+
function classify(raw) {
|
|
76
|
+
const value = raw.trim();
|
|
77
|
+
if (!value)
|
|
78
|
+
return { kind: "opaque" };
|
|
79
|
+
if (URL_RE.test(value))
|
|
80
|
+
return { kind: "url" };
|
|
81
|
+
const line = value.match(LINE_SUFFIX_RE);
|
|
82
|
+
const pathPart = line ? value.slice(0, value.length - line[0].length) : value;
|
|
83
|
+
const looksFile = (PATH_SEP_RE.test(pathPart) || FILE_EXT_RE.test(value)) && !/\s/.test(pathPart);
|
|
84
|
+
return looksFile ? { kind: "file", pathPart } : { kind: "opaque" };
|
|
85
|
+
}
|
|
86
|
+
/** Resolve one locator against base dirs (used only in strict mode). */
|
|
87
|
+
function resolveEvidenceLocator(raw, baseDirs) {
|
|
88
|
+
const shape = classify(raw);
|
|
89
|
+
if (shape.kind === "url")
|
|
90
|
+
return "external";
|
|
91
|
+
if (shape.kind === "opaque" || !shape.pathPart)
|
|
92
|
+
return "opaque";
|
|
93
|
+
const candidates = node_path_1.default.isAbsolute(shape.pathPart)
|
|
94
|
+
? [shape.pathPart]
|
|
95
|
+
: baseDirs.filter(Boolean).map((base) => node_path_1.default.resolve(base, shape.pathPart));
|
|
96
|
+
for (const candidate of candidates) {
|
|
97
|
+
try {
|
|
98
|
+
node_fs_1.default.statSync(candidate);
|
|
99
|
+
return "resolved";
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/* try next base */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return "unresolved";
|
|
106
|
+
}
|
|
107
|
+
/** In strict mode, the file-style locators that could NOT be resolved on disk.
|
|
108
|
+
* Returns [] when strict mode is off or all file locators resolve. */
|
|
109
|
+
function unresolvedFileEvidence(evidence, baseDirs) {
|
|
110
|
+
if (!requireResolvableEvidence() || !Array.isArray(evidence))
|
|
111
|
+
return [];
|
|
112
|
+
return evidence
|
|
113
|
+
.map((entry) => String(entry))
|
|
114
|
+
.filter((entry) => resolveEvidenceLocator(entry, baseDirs) === "unresolved");
|
|
115
|
+
}
|
|
116
|
+
/** Compute the confidence tier for a single evidence string. Deterministic:
|
|
117
|
+
* pure function of the string and optional base dirs for resolution. */
|
|
118
|
+
function computeEvidenceConfidence(raw, baseDirs) {
|
|
119
|
+
if (!isGroundedEvidence(raw))
|
|
120
|
+
return "ungrounded";
|
|
121
|
+
if (!baseDirs || !baseDirs.length || !requireResolvableEvidence())
|
|
122
|
+
return "grounded";
|
|
123
|
+
const value = String(raw).trim();
|
|
124
|
+
const shape = classify(value);
|
|
125
|
+
if (shape.kind === "url")
|
|
126
|
+
return "grounded"; // URLs not resolved yet
|
|
127
|
+
if (shape.kind === "opaque")
|
|
128
|
+
return "grounded"; // namespace:value tokens are grounded
|
|
129
|
+
// File-style: try resolution
|
|
130
|
+
const resolution = resolveEvidenceLocator(value, baseDirs);
|
|
131
|
+
return resolution === "resolved" ? "resolvable" : "grounded";
|
|
132
|
+
}
|
|
133
|
+
/** Compute confidence tiers for an array of evidence entries. */
|
|
134
|
+
function computeEvidenceConfidenceTiers(evidence, baseDirs) {
|
|
135
|
+
if (!Array.isArray(evidence))
|
|
136
|
+
return [];
|
|
137
|
+
return evidence.map((entry) => computeEvidenceConfidence(entry, baseDirs));
|
|
138
|
+
}
|
|
139
|
+
/** The highest confidence tier in an evidence array. Used for gate decisions. */
|
|
140
|
+
function maxEvidenceConfidence(evidence, baseDirs) {
|
|
141
|
+
const tiers = computeEvidenceConfidenceTiers(evidence, baseDirs);
|
|
142
|
+
if (!tiers.length)
|
|
143
|
+
return "ungrounded";
|
|
144
|
+
const order = ["ungrounded", "grounded", "resolvable", "verified"];
|
|
145
|
+
let max = "ungrounded";
|
|
146
|
+
for (const tier of tiers) {
|
|
147
|
+
if (order.indexOf(tier) > order.indexOf(max))
|
|
148
|
+
max = tier;
|
|
149
|
+
}
|
|
150
|
+
return max;
|
|
151
|
+
}
|
|
152
|
+
/** Extract actual content from a file-style evidence locator (v0.1.74).
|
|
153
|
+
* For `file.ts:42`, reads the file and returns line 42's content.
|
|
154
|
+
* Never fabricates — returns undefined when the file doesn't exist or
|
|
155
|
+
* the locator is not file-style. Lines are 1-indexed. */
|
|
156
|
+
function extractEvidenceContent(locator, baseDirs) {
|
|
157
|
+
const shape = classify(locator);
|
|
158
|
+
if (shape.kind !== "file" || !shape.pathPart)
|
|
159
|
+
return undefined;
|
|
160
|
+
const lineMatch = locator.match(/:(\d+)$/);
|
|
161
|
+
const lineNum = lineMatch ? Number(lineMatch[1]) : undefined;
|
|
162
|
+
const candidatePath = node_path_1.default.isAbsolute(shape.pathPart)
|
|
163
|
+
? shape.pathPart
|
|
164
|
+
: baseDirs.filter(Boolean).map((base) => node_path_1.default.resolve(base, shape.pathPart)).find((p) => node_fs_1.default.existsSync(p));
|
|
165
|
+
if (!candidatePath)
|
|
166
|
+
return undefined;
|
|
167
|
+
try {
|
|
168
|
+
const content = node_fs_1.default.readFileSync(candidatePath, "utf8");
|
|
169
|
+
if (lineNum && lineNum > 0) {
|
|
170
|
+
const lines = content.split("\n");
|
|
171
|
+
return lines[lineNum - 1] || undefined;
|
|
172
|
+
}
|
|
173
|
+
// No line number: return first 200 chars as preview
|
|
174
|
+
return content.slice(0, 200);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
}
|