cool-workflow 0.1.80 → 0.1.82
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 +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +42 -2
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/agent-config.js +21 -7
- package/dist/candidate-scoring.js +62 -48
- package/dist/capability-core.js +152 -96
- package/dist/capability-registry.js +160 -174
- package/dist/cli.js +90 -100
- package/dist/collaboration.js +5 -6
- package/dist/commit.js +232 -209
- package/dist/compare.js +18 -0
- package/dist/coordinator/classify.js +45 -0
- package/dist/coordinator/paths.js +42 -0
- package/dist/coordinator/util.js +126 -0
- package/dist/coordinator.js +127 -300
- package/dist/dispatch.js +46 -3
- package/dist/drive.js +7 -7
- package/dist/error-feedback.js +8 -4
- package/dist/evidence-reasoning.js +5 -2
- package/dist/execution-backend/agent.js +294 -0
- package/dist/execution-backend/probes.js +96 -0
- package/dist/execution-backend/util.js +47 -0
- package/dist/execution-backend.js +77 -450
- package/dist/gates.js +48 -0
- package/dist/mcp-server.js +34 -173
- package/dist/multi-agent/graph.js +84 -0
- package/dist/multi-agent/helpers.js +141 -0
- package/dist/multi-agent/ids.js +20 -0
- package/dist/multi-agent/paths.js +22 -0
- package/dist/multi-agent-eval/format.js +194 -0
- package/dist/multi-agent-eval/normalize.js +51 -0
- package/dist/multi-agent-eval.js +63 -242
- package/dist/multi-agent-host.js +53 -40
- package/dist/multi-agent-operator-ux.js +2 -1
- package/dist/multi-agent-trust.js +5 -5
- package/dist/multi-agent.js +125 -314
- package/dist/node-projection.js +59 -0
- package/dist/node-snapshot.js +11 -21
- package/dist/observability/format.js +61 -0
- package/dist/observability/intake.js +98 -0
- package/dist/observability.js +14 -160
- package/dist/operator-ux/format.js +364 -0
- package/dist/operator-ux.js +22 -363
- package/dist/orchestrator/lifecycle-operations.js +22 -1
- package/dist/orchestrator/report.js +8 -0
- package/dist/orchestrator.js +41 -10
- package/dist/reclamation.js +32 -55
- package/dist/run-export.js +138 -14
- package/dist/run-registry/derive.js +172 -0
- package/dist/run-registry/format.js +124 -0
- package/dist/run-registry/gc.js +251 -0
- package/dist/run-registry/policy.js +16 -0
- package/dist/run-registry/queue.js +116 -0
- package/dist/run-registry.js +78 -593
- package/dist/run-state-schema.js +1 -0
- package/dist/sandbox-profile.js +43 -2
- package/dist/scheduler.js +34 -4
- package/dist/state-explosion/format.js +159 -0
- package/dist/state-explosion/helpers.js +82 -0
- package/dist/state-explosion.js +65 -283
- package/dist/state-node.js +19 -4
- package/dist/telemetry-attestation.js +55 -0
- package/dist/telemetry-demo.js +15 -3
- package/dist/telemetry-ledger.js +60 -15
- package/dist/topology.js +50 -12
- package/dist/triggers.js +33 -14
- package/dist/trust-audit.js +215 -71
- package/dist/validation.js +328 -0
- package/dist/version.js +1 -1
- package/dist/worker-isolation/helpers.js +51 -0
- package/dist/worker-isolation/paths.js +46 -0
- package/dist/worker-isolation.js +182 -173
- package/docs/agent-delegation-drive.7.md +14 -0
- package/docs/cli-mcp-parity.7.md +5 -0
- package/docs/contract-migration-tooling.7.md +3 -0
- package/docs/control-plane-scheduling.7.md +3 -0
- package/docs/dogfood/resume-drive-real-agent-2026-06-14.md +40 -0
- package/docs/durable-state-and-locking.7.md +5 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +3 -0
- package/docs/execution-backends.7.md +3 -0
- package/docs/index.md +1 -0
- package/docs/launch/launch-kit.md +46 -23
- package/docs/launch/pre-launch-checklist.md +14 -14
- package/docs/multi-agent-cli-mcp-surface.7.md +5 -0
- package/docs/multi-agent-eval-replay-harness.7.md +3 -0
- package/docs/multi-agent-operator-ux.7.md +3 -0
- package/docs/multi-agent-trust-policy-audit.7.md +27 -0
- package/docs/node-snapshot-diff-replay.7.md +3 -0
- package/docs/observability-cost-accounting.7.md +3 -0
- package/docs/project-index.md +23 -6
- package/docs/real-execution-backends.7.md +3 -0
- package/docs/release-and-migration.7.md +5 -0
- package/docs/release-tooling.7.md +35 -2
- package/docs/run-registry-control-plane.7.md +55 -8
- package/docs/run-retention-reclamation.7.md +5 -0
- package/docs/state-explosion-management.7.md +3 -0
- package/docs/team-collaboration.7.md +3 -0
- package/docs/trust-model.md +267 -0
- package/docs/vendor-manifest-loadability.7.md +43 -0
- package/docs/web-desktop-workbench.7.md +3 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +1 -1
- package/package.json +4 -2
- package/scripts/agents/builtin-templates.json +7 -0
- package/scripts/bump-version.js +5 -11
- package/scripts/canonical-apps-list.js +64 -0
- package/scripts/canonical-apps.js +19 -4
- package/scripts/children/batch-delegate-child.js +58 -0
- package/scripts/children/http-delegate-child.js +39 -0
- package/scripts/dogfood-release.js +1 -1
- package/scripts/golden-path.js +4 -4
- package/scripts/parity-check.js +5 -0
- package/scripts/release-check.js +5 -1
- package/scripts/release-flow.js +181 -5
- package/scripts/version-sync-check.js +5 -8
- package/dist/capability-dispatcher.js +0 -86
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Canonical StateNode projection (F9 de-triplication) — the SINGLE source of
|
|
3
|
+
// truth for which StateNode fields make up the derived, fingerprinted body.
|
|
4
|
+
//
|
|
5
|
+
// Previously this 13-field set was hand-copied in three places: node-snapshot.ts's
|
|
6
|
+
// snapshotBody, reclamation.ts's snapshotProjectionDigest, and reclamation.ts's
|
|
7
|
+
// rawNodeBody/nodeBodyDigest (whose comment literally said "Mirror
|
|
8
|
+
// node-snapshot.ts"). Any field added in one place but not the others would drift
|
|
9
|
+
// the projection — and because reclamation hashes are tombstone-chained, a drift
|
|
10
|
+
// would silently break the chain. This module collapses the list to ONE export so
|
|
11
|
+
// the field set can only change in one place.
|
|
12
|
+
//
|
|
13
|
+
// BYTE-IDENTITY [load-bearing]: the projection / digest OUTPUT here is identical to
|
|
14
|
+
// the prior in-line implementations. `normalizeValue` sorts object keys, so the
|
|
15
|
+
// literal field ORDER never affected the bytes — only the field SET ever mattered.
|
|
16
|
+
// `replayStableStringify(value) === JSON.stringify(normalizeValue(value))` and
|
|
17
|
+
// `normalizeValue` is idempotent, so the projected-body digest and the raw-body
|
|
18
|
+
// digest funnel through the same canonical bytes (node-snapshot-diff-replay +
|
|
19
|
+
// reclamation smokes verify this).
|
|
20
|
+
//
|
|
21
|
+
// Pure: no I/O, no wall-clock, no random — safe in core/state/replay logic.
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.rawNodeProjection = rawNodeProjection;
|
|
24
|
+
exports.projectNodeBody = projectNodeBody;
|
|
25
|
+
exports.nodeProjectionDigestInput = nodeProjectionDigestInput;
|
|
26
|
+
const multi_agent_eval_1 = require("./multi-agent-eval");
|
|
27
|
+
/** The raw (un-normalized) projection input: the canonical field set, copied off
|
|
28
|
+
* the StateNode with no scrubbing. `normalizeValue`/`replayStableStringify` apply
|
|
29
|
+
* the timestamp/path scrubbing downstream. This is the ONE place the field list
|
|
30
|
+
* lives — add or drop a projected field here and every consumer follows. */
|
|
31
|
+
function rawNodeProjection(node) {
|
|
32
|
+
return {
|
|
33
|
+
id: node.id,
|
|
34
|
+
kind: node.kind,
|
|
35
|
+
status: node.status,
|
|
36
|
+
loopStage: node.loopStage,
|
|
37
|
+
inputs: node.inputs,
|
|
38
|
+
outputs: node.outputs,
|
|
39
|
+
artifacts: node.artifacts,
|
|
40
|
+
evidence: node.evidence,
|
|
41
|
+
errors: node.errors,
|
|
42
|
+
parents: node.parents,
|
|
43
|
+
children: node.children,
|
|
44
|
+
contractId: node.contractId,
|
|
45
|
+
metadata: node.metadata
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** The normalized, derived projected body of one StateNode — timestamps/paths
|
|
49
|
+
* stripped via the eval normalizer, so it is byte-stable across captures of the
|
|
50
|
+
* same logical state. This is exactly node-snapshot.ts's historical snapshotBody. */
|
|
51
|
+
function projectNodeBody(node) {
|
|
52
|
+
return (0, multi_agent_eval_1.normalizeValue)(rawNodeProjection(node));
|
|
53
|
+
}
|
|
54
|
+
/** Stable digest input for the projected body: the canonical bytes that the
|
|
55
|
+
* snapshot/reconstruction digests bind. `replayStableStringify` re-normalizes, so
|
|
56
|
+
* feeding the raw projection or the already-normalized body yields identical bytes. */
|
|
57
|
+
function nodeProjectionDigestInput(node) {
|
|
58
|
+
return (0, multi_agent_eval_1.replayStableStringify)(rawNodeProjection(node));
|
|
59
|
+
}
|
package/dist/node-snapshot.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// divergence (`stale`) or a missing node/artifact (`absent`) diff/replay REFUSE
|
|
14
14
|
// with a structured error — never a silent stale replay.
|
|
15
15
|
// - REUSE, don't fork: operates on the real StateNode (getRunNode) and reuses the
|
|
16
|
-
// eval harness's normalizeValue/
|
|
16
|
+
// eval harness's normalizeValue/replayStableStringify and state-explosion's
|
|
17
17
|
// fingerprintStrings. No parallel node type, normalizer, or replay engine.
|
|
18
18
|
// - DETERMINISTIC: `now` is injected; the deterministic payload (normalized body
|
|
19
19
|
// + outputFingerprint) carries zero wall-clock, so two replays are byte-identical.
|
|
@@ -37,7 +37,9 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
37
37
|
const pipeline_runner_1 = require("./pipeline-runner");
|
|
38
38
|
const state_1 = require("./state");
|
|
39
39
|
const multi_agent_eval_1 = require("./multi-agent-eval");
|
|
40
|
+
const node_projection_1 = require("./node-projection");
|
|
40
41
|
const state_explosion_1 = require("./state-explosion");
|
|
42
|
+
const validation_1 = require("./validation");
|
|
41
43
|
exports.NODE_SNAPSHOT_SCHEMA_VERSION = 1;
|
|
42
44
|
/** Structured fail-closed error (mirrors the PipelineContractError shape). */
|
|
43
45
|
class NodeSnapshotError extends Error {
|
|
@@ -64,23 +66,11 @@ const SNAPSHOT_SECTIONS = [
|
|
|
64
66
|
"metadata"
|
|
65
67
|
];
|
|
66
68
|
/** The normalized projection of a node — timestamps/paths stripped by the eval
|
|
67
|
-
* normalizer, so it is byte-stable across captures of the same logical state.
|
|
69
|
+
* normalizer, so it is byte-stable across captures of the same logical state. The
|
|
70
|
+
* canonical field set lives in node-projection.ts (shared with reclamation.ts so
|
|
71
|
+
* the projection can never drift across the two). */
|
|
68
72
|
function snapshotBody(node) {
|
|
69
|
-
return (0,
|
|
70
|
-
id: node.id,
|
|
71
|
-
kind: node.kind,
|
|
72
|
-
status: node.status,
|
|
73
|
-
loopStage: node.loopStage,
|
|
74
|
-
inputs: node.inputs,
|
|
75
|
-
outputs: node.outputs,
|
|
76
|
-
artifacts: node.artifacts,
|
|
77
|
-
evidence: node.evidence,
|
|
78
|
-
errors: node.errors,
|
|
79
|
-
parents: node.parents,
|
|
80
|
-
children: node.children,
|
|
81
|
-
contractId: node.contractId,
|
|
82
|
-
metadata: node.metadata
|
|
83
|
-
});
|
|
73
|
+
return (0, node_projection_1.projectNodeBody)(node);
|
|
84
74
|
}
|
|
85
75
|
/** RAW fingerprint (NOT normalized): any transition (updatedAt/status) or
|
|
86
76
|
* artifact/evidence change flips it, which is how drift is detected. */
|
|
@@ -113,7 +103,7 @@ function readNodeSnapshot(run, snapshotId) {
|
|
|
113
103
|
for (const nodeDir of node_fs_1.default.readdirSync(root)) {
|
|
114
104
|
const file = node_path_1.default.join(root, nodeDir, `${snapshotId}.json`);
|
|
115
105
|
if (node_fs_1.default.existsSync(file))
|
|
116
|
-
return JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
|
|
106
|
+
return (0, validation_1.validateNodeSnapshot)(JSON.parse(node_fs_1.default.readFileSync(file, "utf8")));
|
|
117
107
|
}
|
|
118
108
|
}
|
|
119
109
|
throw new NodeSnapshotError("snapshot-not-found", `Node snapshot ${snapshotId} not found in run ${run.id}`, { freshness: "absent" });
|
|
@@ -125,7 +115,7 @@ function readNodeReplay(run, replayId) {
|
|
|
125
115
|
for (const nodeDir of node_fs_1.default.readdirSync(root)) {
|
|
126
116
|
const file = node_path_1.default.join(root, nodeDir, "replays", `${replayId}.json`);
|
|
127
117
|
if (node_fs_1.default.existsSync(file))
|
|
128
|
-
return JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
|
|
118
|
+
return (0, validation_1.validateNodeReplayRun)(JSON.parse(node_fs_1.default.readFileSync(file, "utf8")));
|
|
129
119
|
}
|
|
130
120
|
}
|
|
131
121
|
throw new NodeSnapshotError("replay-not-found", `Node replay ${replayId} not found in run ${run.id}`, { freshness: "absent" });
|
|
@@ -176,7 +166,7 @@ function diffNodeSnapshots(baseline, candidate) {
|
|
|
176
166
|
const sections = SNAPSHOT_SECTIONS.map((section) => {
|
|
177
167
|
const baselineValue = sectionValue(baseline.body, section);
|
|
178
168
|
const candidateValue = sectionValue(candidate.body, section);
|
|
179
|
-
const sameBytes = (0, multi_agent_eval_1.
|
|
169
|
+
const sameBytes = (0, multi_agent_eval_1.replayStableStringify)(baselineValue) === (0, multi_agent_eval_1.replayStableStringify)(candidateValue);
|
|
180
170
|
let change;
|
|
181
171
|
if (sameBytes)
|
|
182
172
|
change = "same";
|
|
@@ -213,7 +203,7 @@ function replayNodeSnapshot(run, snapshot, options = {}) {
|
|
|
213
203
|
throw new NodeSnapshotError(freshness === "stale" ? "snapshot-stale" : "snapshot-absent", reason || `cannot replay a ${freshness} snapshot of node ${snapshot.nodeId}`, { freshness, details: { runId: run.id, nodeId: snapshot.nodeId } });
|
|
214
204
|
}
|
|
215
205
|
const body = (0, multi_agent_eval_1.normalizeValue)(snapshot.body);
|
|
216
|
-
const outputFingerprint = (0, state_explosion_1.fingerprintStrings)([(0, multi_agent_eval_1.
|
|
206
|
+
const outputFingerprint = (0, state_explosion_1.fingerprintStrings)([(0, multi_agent_eval_1.replayStableStringify)(body)]);
|
|
217
207
|
const replay = {
|
|
218
208
|
schemaVersion: 1,
|
|
219
209
|
replayId: `replay-${snapshot.snapshotId}-${outputFingerprint.replace("sha256:", "").slice(0, 8)}`,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatMetricsReport = formatMetricsReport;
|
|
4
|
+
exports.formatMetricsSummary = formatMetricsSummary;
|
|
5
|
+
function formatRate(r) {
|
|
6
|
+
if (r.state === "n/a")
|
|
7
|
+
return `n/a (0 samples)`;
|
|
8
|
+
return `${((r.rate * 100)).toFixed(1)}% (${r.count}/${r.total})`;
|
|
9
|
+
}
|
|
10
|
+
function formatMs(ms) {
|
|
11
|
+
if (ms === null)
|
|
12
|
+
return "—";
|
|
13
|
+
if (ms < 1000)
|
|
14
|
+
return `${ms}ms`;
|
|
15
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
16
|
+
}
|
|
17
|
+
function formatCost(c) {
|
|
18
|
+
const parts = [`state=${c.state}`];
|
|
19
|
+
if (c.attestedUsd !== null)
|
|
20
|
+
parts.push(`attested=${c.currency} ${c.attestedUsd}`);
|
|
21
|
+
if (c.estimatedUsd !== null)
|
|
22
|
+
parts.push(`estimated=${c.currency} ${c.estimatedUsd}`);
|
|
23
|
+
if (c.unpricedModels.length)
|
|
24
|
+
parts.push(`unpriced-models=${c.unpricedModels.join(",")}`);
|
|
25
|
+
return parts.join(" ");
|
|
26
|
+
}
|
|
27
|
+
function formatMetricsReport(report) {
|
|
28
|
+
const lines = [];
|
|
29
|
+
lines.push(`metrics ${report.runId} [${report.freshness.status}] app=${report.scope.app || "-"}`);
|
|
30
|
+
lines.push(` time: run=${formatMs(report.time.run.wallClockMs)}${report.time.run.inFlight ? " (in-flight)" : ""} active-task=${formatMs(report.time.activeTaskMs)} in-flight-items=${report.time.inFlight}`);
|
|
31
|
+
lines.push(` failure-rate: ${formatRate(report.rates.failure)}`);
|
|
32
|
+
lines.push(` verifier-pass: ${formatRate(report.rates.verifierPass)}`);
|
|
33
|
+
lines.push(` cand-acceptance: ${formatRate(report.rates.candidateAcceptance)}`);
|
|
34
|
+
const collab = report.collaboration;
|
|
35
|
+
lines.push(` collaboration: approvals=${collab.approvals} rejections=${collab.rejections} comments=${collab.comments} handoffs=${collab.handoffs} reviewers=${collab.reviewers} approval-rate=${formatRate(collab.approvalRate)} time-to-approval=${collab.timeToApproval.meanMs === null ? "n/a" : `${Math.round(collab.timeToApproval.meanMs / 1000)}s`} (${collab.timeToApproval.samples} samples)`);
|
|
36
|
+
const cov = report.usage.coverage === null ? "n/a" : `${(report.usage.coverage * 100).toFixed(0)}%`;
|
|
37
|
+
lines.push(` usage: attested=${report.usage.attestedUnits}/${report.usage.units} units (coverage ${cov}), unreported=${report.usage.unreportedUnits}; tokens in=${report.usage.inputTokens} out=${report.usage.outputTokens} total=${report.usage.totalTokens}`);
|
|
38
|
+
lines.push(` cost: ${formatCost(report.cost)}`);
|
|
39
|
+
if (report.usage.models.length)
|
|
40
|
+
lines.push(` models: ${report.usage.models.join(", ")}`);
|
|
41
|
+
lines.push(` next: ${report.nextAction}`);
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}
|
|
44
|
+
function formatMetricsSummary(summary) {
|
|
45
|
+
const lines = [];
|
|
46
|
+
lines.push(`metrics summary scope=${summary.scope} runs=${summary.runCount}${summary.unreadableRuns ? ` (+${summary.unreadableRuns} unreadable)` : ""}`);
|
|
47
|
+
lines.push(` failure-rate: ${formatRate(summary.rates.failure)}`);
|
|
48
|
+
lines.push(` verifier-pass: ${formatRate(summary.rates.verifierPass)}`);
|
|
49
|
+
lines.push(` cand-acceptance: ${formatRate(summary.rates.candidateAcceptance)}`);
|
|
50
|
+
const cov = summary.usage.coverage === null ? "n/a" : `${(summary.usage.coverage * 100).toFixed(0)}%`;
|
|
51
|
+
lines.push(` usage: attested=${summary.usage.attestedUnits}/${summary.usage.units} units (coverage ${cov}); tokens total=${summary.usage.totalTokens}`);
|
|
52
|
+
lines.push(` cost: ${formatCost(summary.cost)}`);
|
|
53
|
+
for (const app of summary.byApp) {
|
|
54
|
+
lines.push(` app ${app.key}: runs=${app.runCount} verifier=${formatRate(app.rates.verifierPass)} cost=${formatCost(app.cost)}`);
|
|
55
|
+
}
|
|
56
|
+
for (const backend of summary.byBackend) {
|
|
57
|
+
lines.push(` backend ${backend.key}: runs=${backend.runCount} failure=${formatRate(backend.rates.failure)}`);
|
|
58
|
+
}
|
|
59
|
+
lines.push(` next: ${summary.nextAction}`);
|
|
60
|
+
return lines.join("\n");
|
|
61
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
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.loadCostPolicy = loadCostPolicy;
|
|
7
|
+
exports.parseUsageFromArgs = parseUsageFromArgs;
|
|
8
|
+
// Intake helpers for observability — POLICY as DATA and host-attested usage
|
|
9
|
+
// parsing, kept out of the kernel. Pure/IO-edge functions carved out of
|
|
10
|
+
// observability.ts (god-module carve) so the metrics module no longer bundles
|
|
11
|
+
// the CLI/MCP argument-intake layer. Re-exported from observability.ts to keep
|
|
12
|
+
// the public surface byte-unchanged.
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
const state_1 = require("../state");
|
|
16
|
+
/** Resolve a CostPolicy from CLI/MCP args. `--pricing <path>` loads a policy
|
|
17
|
+
* file; `--pricing default|bundled` loads the bundled example under
|
|
18
|
+
* manifest/pricing.policy.json. Absent ⇒ undefined ⇒ cost is `unpriced`/
|
|
19
|
+
* `unreported`, never guessed. */
|
|
20
|
+
function loadCostPolicy(args, pluginRoot) {
|
|
21
|
+
const raw = args.pricing ?? args.pricingPolicy ?? args.policy;
|
|
22
|
+
if (raw === undefined || raw === null || raw === "")
|
|
23
|
+
return undefined;
|
|
24
|
+
const value = String(raw);
|
|
25
|
+
const file = value === "default" || value === "bundled"
|
|
26
|
+
? node_path_1.default.join(pluginRoot, "manifest", "pricing.policy.json")
|
|
27
|
+
: node_path_1.default.resolve(value);
|
|
28
|
+
if (!node_fs_1.default.existsSync(file))
|
|
29
|
+
throw new Error(`Pricing policy file not found: ${file}`);
|
|
30
|
+
const parsed = (0, state_1.readJson)(file);
|
|
31
|
+
if (!parsed || parsed.schemaVersion !== 1 || !Array.isArray(parsed.models)) {
|
|
32
|
+
throw new Error(`Invalid pricing policy (expected schemaVersion 1 + models[]): ${file}`);
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
/** Parse a host-attested UsageRecord from CLI/MCP intake args. Returns undefined
|
|
37
|
+
* when NO usage was provided (⇒ `unreported`). CW never fabricates usage, so a
|
|
38
|
+
* caller that passes nothing gets nothing. */
|
|
39
|
+
function parseUsageFromArgs(args, now) {
|
|
40
|
+
const inline = args.usage;
|
|
41
|
+
if (inline && typeof inline === "object" && !Array.isArray(inline)) {
|
|
42
|
+
return normalizeUsage(inline, now);
|
|
43
|
+
}
|
|
44
|
+
const input = numeric(args.usageInputTokens ?? args["usage-input-tokens"]);
|
|
45
|
+
const output = numeric(args.usageOutputTokens ?? args["usage-output-tokens"]);
|
|
46
|
+
const model = args.usageModel ?? args["usage-model"];
|
|
47
|
+
const total = numeric(args.usageTotalTokens ?? args["usage-total-tokens"]);
|
|
48
|
+
const cacheRead = numeric(args.usageCacheReadTokens ?? args["usage-cache-read-tokens"]);
|
|
49
|
+
const cacheWrite = numeric(args.usageCacheWriteTokens ?? args["usage-cache-write-tokens"]);
|
|
50
|
+
if (input === undefined && output === undefined && total === undefined && model === undefined) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return normalizeUsage({
|
|
54
|
+
source: args.usageSource ?? args["usage-source"],
|
|
55
|
+
model,
|
|
56
|
+
inputTokens: input,
|
|
57
|
+
outputTokens: output,
|
|
58
|
+
totalTokens: total,
|
|
59
|
+
cacheReadTokens: cacheRead,
|
|
60
|
+
cacheWriteTokens: cacheWrite,
|
|
61
|
+
attestedAt: args.usageAttestedAt ?? args["usage-attested-at"],
|
|
62
|
+
note: args.usageNote ?? args["usage-note"]
|
|
63
|
+
}, now);
|
|
64
|
+
}
|
|
65
|
+
function normalizeUsage(raw, now) {
|
|
66
|
+
const source = raw.source === "operator-recorded" ? "operator-recorded" : "host-attested";
|
|
67
|
+
const usage = {
|
|
68
|
+
schemaVersion: 1,
|
|
69
|
+
source,
|
|
70
|
+
attestedAt: typeof raw.attestedAt === "string" && raw.attestedAt ? raw.attestedAt : now
|
|
71
|
+
};
|
|
72
|
+
if (raw.model !== undefined && raw.model !== null && raw.model !== "")
|
|
73
|
+
usage.model = String(raw.model);
|
|
74
|
+
const input = numeric(raw.inputTokens);
|
|
75
|
+
const output = numeric(raw.outputTokens);
|
|
76
|
+
const total = numeric(raw.totalTokens);
|
|
77
|
+
const cacheRead = numeric(raw.cacheReadTokens);
|
|
78
|
+
const cacheWrite = numeric(raw.cacheWriteTokens);
|
|
79
|
+
if (input !== undefined)
|
|
80
|
+
usage.inputTokens = input;
|
|
81
|
+
if (output !== undefined)
|
|
82
|
+
usage.outputTokens = output;
|
|
83
|
+
if (total !== undefined)
|
|
84
|
+
usage.totalTokens = total;
|
|
85
|
+
if (cacheRead !== undefined)
|
|
86
|
+
usage.cacheReadTokens = cacheRead;
|
|
87
|
+
if (cacheWrite !== undefined)
|
|
88
|
+
usage.cacheWriteTokens = cacheWrite;
|
|
89
|
+
if (raw.note !== undefined && raw.note !== null && raw.note !== "")
|
|
90
|
+
usage.note = String(raw.note);
|
|
91
|
+
return usage;
|
|
92
|
+
}
|
|
93
|
+
function numeric(value) {
|
|
94
|
+
if (value === undefined || value === null || value === "")
|
|
95
|
+
return undefined;
|
|
96
|
+
const n = Number(value);
|
|
97
|
+
return Number.isFinite(n) ? n : undefined;
|
|
98
|
+
}
|
package/dist/observability.js
CHANGED
|
@@ -34,7 +34,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
34
34
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
35
35
|
};
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.METRICS_SCHEMA_VERSION = void 0;
|
|
37
|
+
exports.formatMetricsSummary = exports.formatMetricsReport = exports.parseUsageFromArgs = exports.loadCostPolicy = exports.METRICS_SCHEMA_VERSION = void 0;
|
|
38
38
|
exports.fingerprintMetricsSource = fingerprintMetricsSource;
|
|
39
39
|
exports.deriveUsageTotals = deriveUsageTotals;
|
|
40
40
|
exports.deriveAttestationCoverage = deriveAttestationCoverage;
|
|
@@ -46,13 +46,8 @@ exports.deriveMetricsReport = deriveMetricsReport;
|
|
|
46
46
|
exports.deriveCollaborationMetrics = deriveCollaborationMetrics;
|
|
47
47
|
exports.metricsDir = metricsDir;
|
|
48
48
|
exports.loadPersistedMetricsFingerprint = loadPersistedMetricsFingerprint;
|
|
49
|
-
exports.loadPersistedMetricsReport = loadPersistedMetricsReport;
|
|
50
49
|
exports.showMetricsReport = showMetricsReport;
|
|
51
50
|
exports.deriveMetricsSummary = deriveMetricsSummary;
|
|
52
|
-
exports.loadCostPolicy = loadCostPolicy;
|
|
53
|
-
exports.parseUsageFromArgs = parseUsageFromArgs;
|
|
54
|
-
exports.formatMetricsReport = formatMetricsReport;
|
|
55
|
-
exports.formatMetricsSummary = formatMetricsSummary;
|
|
56
51
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
57
52
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
58
53
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -558,18 +553,6 @@ function loadPersistedMetricsFingerprint(run) {
|
|
|
558
553
|
return undefined;
|
|
559
554
|
}
|
|
560
555
|
}
|
|
561
|
-
/** Read the full persisted per-run report, if any (never throws). */
|
|
562
|
-
function loadPersistedMetricsReport(run) {
|
|
563
|
-
const file = metricsReportPath(run);
|
|
564
|
-
if (!node_fs_1.default.existsSync(file))
|
|
565
|
-
return undefined;
|
|
566
|
-
try {
|
|
567
|
-
return (0, state_1.readJson)(file);
|
|
568
|
-
}
|
|
569
|
-
catch {
|
|
570
|
-
return undefined;
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
556
|
/** Derive + persist the per-run report. The RETURNED payload is order- and
|
|
574
557
|
* cache-independent (freshness === "valid", persistedFingerprint === itself),
|
|
575
558
|
* so `cw metrics show --json` and `cw_metrics_show` are byte-identical. The
|
|
@@ -775,148 +758,19 @@ function deriveMetricsSummary(inputs, options) {
|
|
|
775
758
|
};
|
|
776
759
|
}
|
|
777
760
|
// ---------------------------------------------------------------------------
|
|
778
|
-
//
|
|
761
|
+
// Intake (POLICY as DATA + host-attested usage parsing) now lives in
|
|
762
|
+
// ./observability/intake.ts (god-module carve). Re-exported so importers of
|
|
763
|
+
// "./observability" keep the unchanged surface.
|
|
779
764
|
// ---------------------------------------------------------------------------
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
* `unreported`, never guessed. */
|
|
784
|
-
function loadCostPolicy(args, pluginRoot) {
|
|
785
|
-
const raw = args.pricing ?? args.pricingPolicy ?? args.policy;
|
|
786
|
-
if (raw === undefined || raw === null || raw === "")
|
|
787
|
-
return undefined;
|
|
788
|
-
const value = String(raw);
|
|
789
|
-
const file = value === "default" || value === "bundled"
|
|
790
|
-
? node_path_1.default.join(pluginRoot, "manifest", "pricing.policy.json")
|
|
791
|
-
: node_path_1.default.resolve(value);
|
|
792
|
-
if (!node_fs_1.default.existsSync(file))
|
|
793
|
-
throw new Error(`Pricing policy file not found: ${file}`);
|
|
794
|
-
const parsed = (0, state_1.readJson)(file);
|
|
795
|
-
if (!parsed || parsed.schemaVersion !== 1 || !Array.isArray(parsed.models)) {
|
|
796
|
-
throw new Error(`Invalid pricing policy (expected schemaVersion 1 + models[]): ${file}`);
|
|
797
|
-
}
|
|
798
|
-
return parsed;
|
|
799
|
-
}
|
|
800
|
-
/** Parse a host-attested UsageRecord from CLI/MCP intake args. Returns undefined
|
|
801
|
-
* when NO usage was provided (⇒ `unreported`). CW never fabricates usage, so a
|
|
802
|
-
* caller that passes nothing gets nothing. */
|
|
803
|
-
function parseUsageFromArgs(args, now) {
|
|
804
|
-
const inline = args.usage;
|
|
805
|
-
if (inline && typeof inline === "object" && !Array.isArray(inline)) {
|
|
806
|
-
return normalizeUsage(inline, now);
|
|
807
|
-
}
|
|
808
|
-
const input = numeric(args.usageInputTokens ?? args["usage-input-tokens"]);
|
|
809
|
-
const output = numeric(args.usageOutputTokens ?? args["usage-output-tokens"]);
|
|
810
|
-
const model = args.usageModel ?? args["usage-model"];
|
|
811
|
-
const total = numeric(args.usageTotalTokens ?? args["usage-total-tokens"]);
|
|
812
|
-
const cacheRead = numeric(args.usageCacheReadTokens ?? args["usage-cache-read-tokens"]);
|
|
813
|
-
const cacheWrite = numeric(args.usageCacheWriteTokens ?? args["usage-cache-write-tokens"]);
|
|
814
|
-
if (input === undefined && output === undefined && total === undefined && model === undefined) {
|
|
815
|
-
return undefined;
|
|
816
|
-
}
|
|
817
|
-
return normalizeUsage({
|
|
818
|
-
source: args.usageSource ?? args["usage-source"],
|
|
819
|
-
model,
|
|
820
|
-
inputTokens: input,
|
|
821
|
-
outputTokens: output,
|
|
822
|
-
totalTokens: total,
|
|
823
|
-
cacheReadTokens: cacheRead,
|
|
824
|
-
cacheWriteTokens: cacheWrite,
|
|
825
|
-
attestedAt: args.usageAttestedAt ?? args["usage-attested-at"],
|
|
826
|
-
note: args.usageNote ?? args["usage-note"]
|
|
827
|
-
}, now);
|
|
828
|
-
}
|
|
829
|
-
function normalizeUsage(raw, now) {
|
|
830
|
-
const source = raw.source === "operator-recorded" ? "operator-recorded" : "host-attested";
|
|
831
|
-
const usage = {
|
|
832
|
-
schemaVersion: 1,
|
|
833
|
-
source,
|
|
834
|
-
attestedAt: typeof raw.attestedAt === "string" && raw.attestedAt ? raw.attestedAt : now
|
|
835
|
-
};
|
|
836
|
-
if (raw.model !== undefined && raw.model !== null && raw.model !== "")
|
|
837
|
-
usage.model = String(raw.model);
|
|
838
|
-
const input = numeric(raw.inputTokens);
|
|
839
|
-
const output = numeric(raw.outputTokens);
|
|
840
|
-
const total = numeric(raw.totalTokens);
|
|
841
|
-
const cacheRead = numeric(raw.cacheReadTokens);
|
|
842
|
-
const cacheWrite = numeric(raw.cacheWriteTokens);
|
|
843
|
-
if (input !== undefined)
|
|
844
|
-
usage.inputTokens = input;
|
|
845
|
-
if (output !== undefined)
|
|
846
|
-
usage.outputTokens = output;
|
|
847
|
-
if (total !== undefined)
|
|
848
|
-
usage.totalTokens = total;
|
|
849
|
-
if (cacheRead !== undefined)
|
|
850
|
-
usage.cacheReadTokens = cacheRead;
|
|
851
|
-
if (cacheWrite !== undefined)
|
|
852
|
-
usage.cacheWriteTokens = cacheWrite;
|
|
853
|
-
if (raw.note !== undefined && raw.note !== null && raw.note !== "")
|
|
854
|
-
usage.note = String(raw.note);
|
|
855
|
-
return usage;
|
|
856
|
-
}
|
|
857
|
-
function numeric(value) {
|
|
858
|
-
if (value === undefined || value === null || value === "")
|
|
859
|
-
return undefined;
|
|
860
|
-
const n = Number(value);
|
|
861
|
-
return Number.isFinite(n) ? n : undefined;
|
|
862
|
-
}
|
|
765
|
+
var intake_1 = require("./observability/intake");
|
|
766
|
+
Object.defineProperty(exports, "loadCostPolicy", { enumerable: true, get: function () { return intake_1.loadCostPolicy; } });
|
|
767
|
+
Object.defineProperty(exports, "parseUsageFromArgs", { enumerable: true, get: function () { return intake_1.parseUsageFromArgs; } });
|
|
863
768
|
// ---------------------------------------------------------------------------
|
|
864
|
-
// Human formatters (CLI default text; --json emits the canonical payload)
|
|
769
|
+
// Human formatters (CLI default text; --json emits the canonical payload) now
|
|
770
|
+
// live in ./observability/format.ts (god-module carve, mirrors
|
|
771
|
+
// run-registry/format.ts). Re-exported so importers of "./observability" keep
|
|
772
|
+
// the unchanged surface.
|
|
865
773
|
// ---------------------------------------------------------------------------
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
return `${((r.rate * 100)).toFixed(1)}% (${r.count}/${r.total})`;
|
|
870
|
-
}
|
|
871
|
-
function formatMs(ms) {
|
|
872
|
-
if (ms === null)
|
|
873
|
-
return "—";
|
|
874
|
-
if (ms < 1000)
|
|
875
|
-
return `${ms}ms`;
|
|
876
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
877
|
-
}
|
|
878
|
-
function formatCost(c) {
|
|
879
|
-
const parts = [`state=${c.state}`];
|
|
880
|
-
if (c.attestedUsd !== null)
|
|
881
|
-
parts.push(`attested=${c.currency} ${c.attestedUsd}`);
|
|
882
|
-
if (c.estimatedUsd !== null)
|
|
883
|
-
parts.push(`estimated=${c.currency} ${c.estimatedUsd}`);
|
|
884
|
-
if (c.unpricedModels.length)
|
|
885
|
-
parts.push(`unpriced-models=${c.unpricedModels.join(",")}`);
|
|
886
|
-
return parts.join(" ");
|
|
887
|
-
}
|
|
888
|
-
function formatMetricsReport(report) {
|
|
889
|
-
const lines = [];
|
|
890
|
-
lines.push(`metrics ${report.runId} [${report.freshness.status}] app=${report.scope.app || "-"}`);
|
|
891
|
-
lines.push(` time: run=${formatMs(report.time.run.wallClockMs)}${report.time.run.inFlight ? " (in-flight)" : ""} active-task=${formatMs(report.time.activeTaskMs)} in-flight-items=${report.time.inFlight}`);
|
|
892
|
-
lines.push(` failure-rate: ${formatRate(report.rates.failure)}`);
|
|
893
|
-
lines.push(` verifier-pass: ${formatRate(report.rates.verifierPass)}`);
|
|
894
|
-
lines.push(` cand-acceptance: ${formatRate(report.rates.candidateAcceptance)}`);
|
|
895
|
-
const collab = report.collaboration;
|
|
896
|
-
lines.push(` collaboration: approvals=${collab.approvals} rejections=${collab.rejections} comments=${collab.comments} handoffs=${collab.handoffs} reviewers=${collab.reviewers} approval-rate=${formatRate(collab.approvalRate)} time-to-approval=${collab.timeToApproval.meanMs === null ? "n/a" : `${Math.round(collab.timeToApproval.meanMs / 1000)}s`} (${collab.timeToApproval.samples} samples)`);
|
|
897
|
-
const cov = report.usage.coverage === null ? "n/a" : `${(report.usage.coverage * 100).toFixed(0)}%`;
|
|
898
|
-
lines.push(` usage: attested=${report.usage.attestedUnits}/${report.usage.units} units (coverage ${cov}), unreported=${report.usage.unreportedUnits}; tokens in=${report.usage.inputTokens} out=${report.usage.outputTokens} total=${report.usage.totalTokens}`);
|
|
899
|
-
lines.push(` cost: ${formatCost(report.cost)}`);
|
|
900
|
-
if (report.usage.models.length)
|
|
901
|
-
lines.push(` models: ${report.usage.models.join(", ")}`);
|
|
902
|
-
lines.push(` next: ${report.nextAction}`);
|
|
903
|
-
return lines.join("\n");
|
|
904
|
-
}
|
|
905
|
-
function formatMetricsSummary(summary) {
|
|
906
|
-
const lines = [];
|
|
907
|
-
lines.push(`metrics summary scope=${summary.scope} runs=${summary.runCount}${summary.unreadableRuns ? ` (+${summary.unreadableRuns} unreadable)` : ""}`);
|
|
908
|
-
lines.push(` failure-rate: ${formatRate(summary.rates.failure)}`);
|
|
909
|
-
lines.push(` verifier-pass: ${formatRate(summary.rates.verifierPass)}`);
|
|
910
|
-
lines.push(` cand-acceptance: ${formatRate(summary.rates.candidateAcceptance)}`);
|
|
911
|
-
const cov = summary.usage.coverage === null ? "n/a" : `${(summary.usage.coverage * 100).toFixed(0)}%`;
|
|
912
|
-
lines.push(` usage: attested=${summary.usage.attestedUnits}/${summary.usage.units} units (coverage ${cov}); tokens total=${summary.usage.totalTokens}`);
|
|
913
|
-
lines.push(` cost: ${formatCost(summary.cost)}`);
|
|
914
|
-
for (const app of summary.byApp) {
|
|
915
|
-
lines.push(` app ${app.key}: runs=${app.runCount} verifier=${formatRate(app.rates.verifierPass)} cost=${formatCost(app.cost)}`);
|
|
916
|
-
}
|
|
917
|
-
for (const backend of summary.byBackend) {
|
|
918
|
-
lines.push(` backend ${backend.key}: runs=${backend.runCount} failure=${formatRate(backend.rates.failure)}`);
|
|
919
|
-
}
|
|
920
|
-
lines.push(` next: ${summary.nextAction}`);
|
|
921
|
-
return lines.join("\n");
|
|
922
|
-
}
|
|
774
|
+
var format_1 = require("./observability/format");
|
|
775
|
+
Object.defineProperty(exports, "formatMetricsReport", { enumerable: true, get: function () { return format_1.formatMetricsReport; } });
|
|
776
|
+
Object.defineProperty(exports, "formatMetricsSummary", { enumerable: true, get: function () { return format_1.formatMetricsSummary; } });
|