open-multi-agent-kit 0.90.6 → 0.90.8
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 +37 -1
- package/README.md +11 -10
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts +3 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +26 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/command-safety.d.ts.map +1 -1
- package/dist/core/command-safety.js +7 -0
- package/dist/core/command-safety.js.map +1 -1
- package/dist/core/context-budget-headroom.d.ts.map +1 -1
- package/dist/core/context-budget-headroom.js +5 -1
- package/dist/core/context-budget-headroom.js.map +1 -1
- package/dist/core/context-budget-v2-cache-provider.d.ts +4 -2
- package/dist/core/context-budget-v2-cache-provider.d.ts.map +1 -1
- package/dist/core/context-budget-v2-cache-provider.js +9 -5
- package/dist/core/context-budget-v2-cache-provider.js.map +1 -1
- package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
- package/dist/core/context-budget-v2-selection.js +9 -0
- package/dist/core/context-budget-v2-selection.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +4 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/loop-gates.d.ts.map +1 -1
- package/dist/core/loop-gates.js +25 -1
- package/dist/core/loop-gates.js.map +1 -1
- package/dist/core/pi-package-intake.d.ts +1 -0
- package/dist/core/pi-package-intake.d.ts.map +1 -1
- package/dist/core/pi-package-intake.js +7 -0
- package/dist/core/pi-package-intake.js.map +1 -1
- package/dist/core/reasoning-router-v4-normalize.d.ts +11 -0
- package/dist/core/reasoning-router-v4-normalize.d.ts.map +1 -0
- package/dist/core/reasoning-router-v4-normalize.js +224 -0
- package/dist/core/reasoning-router-v4-normalize.js.map +1 -0
- package/dist/core/reasoning-router-v4-weights.d.ts +13 -5
- package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4-weights.js +8 -4
- package/dist/core/reasoning-router-v4-weights.js.map +1 -1
- package/dist/core/reasoning-router-v4.d.ts +3 -3
- package/dist/core/reasoning-router-v4.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4.js +394 -58
- package/dist/core/reasoning-router-v4.js.map +1 -1
- package/dist/core/router-feedback-collector.d.ts +1 -1
- package/dist/core/router-feedback-collector.d.ts.map +1 -1
- package/dist/core/router-feedback-collector.js +9 -1
- package/dist/core/router-feedback-collector.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +15 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/guardrails/evidence-system.d.ts +8 -1
- package/dist/guardrails/evidence-system.d.ts.map +1 -1
- package/dist/guardrails/evidence-system.js +154 -16
- package/dist/guardrails/evidence-system.js.map +1 -1
- package/dist/modes/interactive/components/control-panel-layout.d.ts.map +1 -1
- package/dist/modes/interactive/components/control-panel-layout.js +1 -1
- package/dist/modes/interactive/components/control-panel-layout.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +1 -0
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +2 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/types/evidence.d.ts +4 -0
- package/dist/types/evidence.d.ts.map +1 -1
- package/dist/types/evidence.js.map +1 -1
- package/docs/adaptorch-preview.md +84 -0
- package/docs/compaction.md +6 -0
- package/docs/loadout-domains/frontend-ui.md +5 -1
- package/docs/providers.md +6 -0
- package/docs/settings.md +17 -2
- package/examples/extensions/correctness-wall/README.md +6 -6
- package/examples/extensions/correctness-wall/index.ts +37 -11
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/preset.ts +2 -2
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// allow: SIZE_OK -
|
|
1
|
+
// allow: SIZE_OK - evidence ledger guardrails: tamper-evident replay chain + fail-closed parsing.
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
@@ -66,26 +66,132 @@ export class TaskContractBuilder {
|
|
|
66
66
|
this.updatedAt = new Date().toISOString();
|
|
67
67
|
}
|
|
68
68
|
build() {
|
|
69
|
-
return {
|
|
69
|
+
return structuredClone({
|
|
70
70
|
goalId: this.goalId,
|
|
71
71
|
completionClaim: this.completionClaim,
|
|
72
|
-
requiredEvidence:
|
|
72
|
+
requiredEvidence: this.requiredEvidence,
|
|
73
73
|
finalRisk: this.finalRisk,
|
|
74
74
|
verdict: this.verdict,
|
|
75
75
|
createdAt: this.createdAt,
|
|
76
76
|
updatedAt: this.updatedAt,
|
|
77
|
-
};
|
|
77
|
+
});
|
|
78
78
|
}
|
|
79
|
+
/** Parse and validate a serialized TaskContract. Fails closed on any shape violation. */
|
|
79
80
|
static fromJSON(json) {
|
|
80
|
-
|
|
81
|
+
const raw = JSON.parse(json);
|
|
82
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
83
|
+
throw new Error("TaskContract JSON must be an object");
|
|
84
|
+
}
|
|
85
|
+
const { goalId, completionClaim, finalRisk, verdict, createdAt, updatedAt, requiredEvidence } = raw;
|
|
86
|
+
if (typeof goalId !== "string" ||
|
|
87
|
+
typeof completionClaim !== "string" ||
|
|
88
|
+
typeof finalRisk !== "string" ||
|
|
89
|
+
typeof createdAt !== "string" ||
|
|
90
|
+
typeof updatedAt !== "string") {
|
|
91
|
+
throw new Error("TaskContract JSON has missing or mistyped string fields");
|
|
92
|
+
}
|
|
93
|
+
if (verdict !== "pass" && verdict !== "fail" && verdict !== "conditional") {
|
|
94
|
+
throw new Error(`TaskContract verdict must be pass|fail|conditional, got ${String(verdict)}`);
|
|
95
|
+
}
|
|
96
|
+
if (!Array.isArray(requiredEvidence)) {
|
|
97
|
+
throw new Error("TaskContract requiredEvidence must be an array");
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
goalId,
|
|
101
|
+
completionClaim,
|
|
102
|
+
finalRisk,
|
|
103
|
+
verdict,
|
|
104
|
+
createdAt,
|
|
105
|
+
updatedAt,
|
|
106
|
+
requiredEvidence: requiredEvidence.map((item, index) => parseEvidenceItem(item, index)),
|
|
107
|
+
};
|
|
81
108
|
}
|
|
82
109
|
static toJSON(contract) {
|
|
83
110
|
return JSON.stringify(contract, null, 2);
|
|
84
111
|
}
|
|
85
112
|
}
|
|
113
|
+
const EVIDENCE_STATUSES = new Set(["pending", "gathering", "satisfied", "failed", "waived"]);
|
|
114
|
+
function optionalStringField(value, field, index) {
|
|
115
|
+
if (value !== undefined && typeof value !== "string") {
|
|
116
|
+
throw new Error(`TaskContract evidence[${index}].${field} must be a string when present`);
|
|
117
|
+
}
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
function parseEvidenceItem(raw, index) {
|
|
121
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
122
|
+
throw new Error(`TaskContract evidence[${index}] must be an object`);
|
|
123
|
+
}
|
|
124
|
+
const item = raw;
|
|
125
|
+
const { claim, category, timestamp, status } = item;
|
|
126
|
+
if (typeof claim !== "string" || typeof category !== "string" || typeof timestamp !== "string") {
|
|
127
|
+
throw new Error(`TaskContract evidence[${index}] has missing or mistyped fields`);
|
|
128
|
+
}
|
|
129
|
+
if (typeof status !== "string" || !EVIDENCE_STATUSES.has(status)) {
|
|
130
|
+
throw new Error(`TaskContract evidence[${index}] has invalid status ${String(status)}`);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
claim,
|
|
134
|
+
category: category,
|
|
135
|
+
timestamp,
|
|
136
|
+
status: status,
|
|
137
|
+
artifactPath: optionalStringField(item.artifactPath, "artifactPath", index),
|
|
138
|
+
verificationCommand: optionalStringField(item.verificationCommand, "verificationCommand", index),
|
|
139
|
+
hash: optionalStringField(item.hash, "hash", index),
|
|
140
|
+
gapReason: optionalStringField(item.gapReason, "gapReason", index),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
86
143
|
// ============================================================================
|
|
87
144
|
// ReplayLedger
|
|
88
145
|
// ============================================================================
|
|
146
|
+
/** prevHash sentinel for the first event in a replay chain. */
|
|
147
|
+
const GENESIS_HASH = "genesis";
|
|
148
|
+
function computeEventHash(event) {
|
|
149
|
+
return sha256(JSON.stringify([
|
|
150
|
+
event.seq,
|
|
151
|
+
event.type,
|
|
152
|
+
event.timestamp,
|
|
153
|
+
event.goalId,
|
|
154
|
+
event.laneId ?? null,
|
|
155
|
+
event.payloadHash,
|
|
156
|
+
event.prevHash,
|
|
157
|
+
]));
|
|
158
|
+
}
|
|
159
|
+
function parseReplayEventLine(line, lineNumber) {
|
|
160
|
+
let raw;
|
|
161
|
+
try {
|
|
162
|
+
raw = JSON.parse(line);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
throw new Error(`Replay ledger corrupted at line ${lineNumber}: invalid JSON`);
|
|
166
|
+
}
|
|
167
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
168
|
+
throw new Error(`Replay ledger corrupted at line ${lineNumber}: event must be an object`);
|
|
169
|
+
}
|
|
170
|
+
const event = raw;
|
|
171
|
+
const { seq, type, timestamp, goalId, laneId, payloadHash, prevHash, eventHash } = event;
|
|
172
|
+
if (typeof seq !== "number" ||
|
|
173
|
+
typeof type !== "string" ||
|
|
174
|
+
typeof timestamp !== "string" ||
|
|
175
|
+
typeof goalId !== "string" ||
|
|
176
|
+
typeof payloadHash !== "string" ||
|
|
177
|
+
typeof prevHash !== "string" ||
|
|
178
|
+
typeof eventHash !== "string" ||
|
|
179
|
+
(laneId !== undefined && typeof laneId !== "string") ||
|
|
180
|
+
!("payload" in event)) {
|
|
181
|
+
throw new Error(`Replay ledger corrupted at line ${lineNumber}: event fields are missing or mistyped`);
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
seq,
|
|
185
|
+
type: type,
|
|
186
|
+
timestamp,
|
|
187
|
+
goalId,
|
|
188
|
+
laneId,
|
|
189
|
+
payload: event.payload,
|
|
190
|
+
payloadHash,
|
|
191
|
+
prevHash,
|
|
192
|
+
eventHash,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
89
195
|
export class ReplayLedgerManager {
|
|
90
196
|
ledger;
|
|
91
197
|
nextSeq;
|
|
@@ -104,12 +210,15 @@ export class ReplayLedgerManager {
|
|
|
104
210
|
}
|
|
105
211
|
append(event) {
|
|
106
212
|
const payloadStr = JSON.stringify(event.payload);
|
|
107
|
-
const
|
|
213
|
+
const previous = this.ledger.events[this.ledger.events.length - 1];
|
|
214
|
+
const chained = {
|
|
108
215
|
...event,
|
|
109
216
|
seq: this.nextSeq++,
|
|
110
217
|
timestamp: new Date().toISOString(),
|
|
111
218
|
payloadHash: sha256(payloadStr),
|
|
219
|
+
prevHash: previous ? previous.eventHash : GENESIS_HASH,
|
|
112
220
|
};
|
|
221
|
+
const fullEvent = { ...chained, eventHash: computeEventHash(chained) };
|
|
113
222
|
this.ledger.events.push(fullEvent);
|
|
114
223
|
return fullEvent;
|
|
115
224
|
}
|
|
@@ -124,23 +233,45 @@ export class ReplayLedgerManager {
|
|
|
124
233
|
appendFileSync(this.ledger.ledgerPath, lines, "utf-8");
|
|
125
234
|
this.ledger.lastPersistedSeq = last.seq;
|
|
126
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Load and verify the persisted ledger. Fails closed: any schema violation,
|
|
238
|
+
* sequence gap, payload-hash mismatch, or broken event-hash chain throws, so
|
|
239
|
+
* a tampered ledger (edited, deleted, inserted, or reordered lines) is never
|
|
240
|
+
* loaded silently.
|
|
241
|
+
*/
|
|
127
242
|
load() {
|
|
128
243
|
if (!existsSync(this.ledger.ledgerPath))
|
|
129
244
|
return;
|
|
130
245
|
const content = readFileSync(this.ledger.ledgerPath, "utf-8");
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
246
|
+
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
247
|
+
const events = [];
|
|
248
|
+
let prevHash = GENESIS_HASH;
|
|
249
|
+
for (let index = 0; index < lines.length; index++) {
|
|
250
|
+
const event = parseReplayEventLine(lines[index], index + 1);
|
|
251
|
+
if (event.seq !== index + 1) {
|
|
252
|
+
throw new Error(`Replay ledger corrupted at line ${index + 1}: expected seq ${index + 1}, got ${event.seq}`);
|
|
253
|
+
}
|
|
254
|
+
if (event.prevHash !== prevHash) {
|
|
255
|
+
throw new Error(`Replay ledger chain broken at line ${index + 1}: an event was inserted, deleted, or reordered`);
|
|
256
|
+
}
|
|
257
|
+
if (sha256(JSON.stringify(event.payload)) !== event.payloadHash) {
|
|
258
|
+
throw new Error(`Replay ledger payload tampered at line ${index + 1} (seq ${event.seq})`);
|
|
259
|
+
}
|
|
260
|
+
if (computeEventHash(event) !== event.eventHash) {
|
|
261
|
+
throw new Error(`Replay ledger event hash mismatch at line ${index + 1} (seq ${event.seq})`);
|
|
262
|
+
}
|
|
263
|
+
prevHash = event.eventHash;
|
|
264
|
+
events.push(event);
|
|
265
|
+
}
|
|
135
266
|
this.ledger.events = events;
|
|
136
267
|
this.ledger.lastPersistedSeq = events.length > 0 ? events[events.length - 1].seq : 0;
|
|
137
268
|
this.nextSeq = this.ledger.lastPersistedSeq + 1;
|
|
138
269
|
}
|
|
139
270
|
getEvents() {
|
|
140
|
-
return this.ledger.events;
|
|
271
|
+
return structuredClone(this.ledger.events);
|
|
141
272
|
}
|
|
142
273
|
getLedger() {
|
|
143
|
-
return
|
|
274
|
+
return structuredClone(this.ledger);
|
|
144
275
|
}
|
|
145
276
|
replay(handler) {
|
|
146
277
|
const results = [];
|
|
@@ -260,6 +391,13 @@ export class FailClosedMergeGate {
|
|
|
260
391
|
};
|
|
261
392
|
}
|
|
262
393
|
}
|
|
394
|
+
// ============================================================================
|
|
395
|
+
// Verify Reporter v2
|
|
396
|
+
// ============================================================================
|
|
397
|
+
/** Escape pipes/newlines so untrusted claim or command text cannot restructure the report table. */
|
|
398
|
+
function escapeTableCell(value) {
|
|
399
|
+
return value.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
400
|
+
}
|
|
263
401
|
export class VerifyReporterV2 {
|
|
264
402
|
options;
|
|
265
403
|
constructor(options) {
|
|
@@ -273,9 +411,9 @@ export class VerifyReporterV2 {
|
|
|
273
411
|
"",
|
|
274
412
|
`| Field | Value |`,
|
|
275
413
|
`|-------|-------|`,
|
|
276
|
-
`| Claim | ${contract.completionClaim} |`,
|
|
414
|
+
`| Claim | ${escapeTableCell(contract.completionClaim)} |`,
|
|
277
415
|
`| Verdict | ${contract.verdict} |`,
|
|
278
|
-
`| Final Risk | ${contract.finalRisk} |`,
|
|
416
|
+
`| Final Risk | ${escapeTableCell(contract.finalRisk)} |`,
|
|
279
417
|
"",
|
|
280
418
|
"### Evidence",
|
|
281
419
|
"",
|
|
@@ -283,9 +421,9 @@ export class VerifyReporterV2 {
|
|
|
283
421
|
`|-------|--------|----------|----------|`,
|
|
284
422
|
];
|
|
285
423
|
for (const ev of contract.requiredEvidence) {
|
|
286
|
-
lines.push(`| ${ev.claim} | ${ev.status} | ${ev.artifactPath ?? "—"} | ${ev.verificationCommand ?? "—"} |`);
|
|
424
|
+
lines.push(`| ${escapeTableCell(ev.claim)} | ${ev.status} | ${escapeTableCell(ev.artifactPath ?? "—")} | ${escapeTableCell(ev.verificationCommand ?? "—")} |`);
|
|
287
425
|
}
|
|
288
|
-
lines.push("", "### Merge Gate", "", `| Gate | Status | Reason |`, `|------|--------|--------|`, `| ${mergeGate.gateId} | ${mergeGate.status} | ${mergeGate.reason} |`, "");
|
|
426
|
+
lines.push("", "### Merge Gate", "", `| Gate | Status | Reason |`, `|------|--------|--------|`, `| ${mergeGate.gateId} | ${mergeGate.status} | ${escapeTableCell(mergeGate.reason)} |`, "");
|
|
289
427
|
if (mergeGate.suggestion) {
|
|
290
428
|
lines.push(`**Suggestion:** ${mergeGate.suggestion}`, "");
|
|
291
429
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evidence-system.js","sourceRoot":"","sources":["../../src/guardrails/evidence-system.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAW1C,SAAS,MAAM,CAAC,KAAa,EAAU;IACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAAA,CACxD;AAED,SAAS,SAAS,CAAC,IAAY,EAAQ;IACtC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;AAAA,CACD;AAED,+EAA+E;AAC/E,eAAe;AACf,+EAA+E;AAE/E,MAAM,OAAO,mBAAmB;IACvB,MAAM,CAAS;IACf,eAAe,GAAW,EAAE,CAAC;IAC7B,gBAAgB,GAAmB,EAAE,CAAC;IACtC,SAAS,GAAW,EAAE,CAAC;IACvB,OAAO,GAAoC,MAAM,CAAC;IAClD,SAAS,CAAS;IAClB,SAAS,CAAS;IAE1B,YAAY,MAAc,EAAE;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;IAAA,CACrB;IAED,QAAQ,CAAC,KAAa,EAAQ;QAC7B,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IAAA,CACZ;IAED,mBAAmB,CAAC,QAAoD,EAAQ;QAC/E,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC;YAC1B,GAAG,QAAQ;YACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,MAAM,EAAE,SAAS;SACjB,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IAAA,CACZ;IAED,YAAY,CAAC,IAAY,EAAQ;QAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IAAA,CACZ;IAED,UAAU,CAAC,OAAwC,EAAQ;QAC1D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IAAA,CACZ;IAED,oBAAoB,CAAC,KAAa,EAAE,MAAsB,EAAE,SAAkB,EAAQ;QACrF,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;QAClE,IAAI,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;YAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC;IAAA,CACZ;IAEO,KAAK,GAAS;QACrB,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAAA,CAC1C;IAED,KAAK,GAAiB;QACrB,OAAO;YACN,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,gBAAgB,EAAE,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC;YAC5C,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;SACzB,CAAC;IAAA,CACF;IAED,MAAM,CAAC,QAAQ,CAAC,IAAY,EAAgB;QAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAC;IAAA,CACxC;IAED,MAAM,CAAC,MAAM,CAAC,QAAsB,EAAU;QAC7C,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAAA,CACzC;CACD;AAED,+EAA+E;AAC/E,eAAe;AACf,+EAA+E;AAE/E,MAAM,OAAO,mBAAmB;IACvB,MAAM,CAAe;IACrB,OAAO,CAAS;IAExB,YAAY,MAAc,EAAE,UAAkB,EAAE;QAC/C,SAAS,CAAC,UAAU,CAAC,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG;YACb,MAAM;YACN,MAAM,EAAE,EAAE;YACV,UAAU;YACV,gBAAgB,EAAE,CAAC;SACnB,CAAC;QACF,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QACjB,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;QACb,CAAC;IAAA,CACD;IAED,MAAM,CAAC,KAA6D,EAAe;QAClF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjD,MAAM,SAAS,GAAgB;YAC9B,GAAG,KAAK;YACR,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE;YACnB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;SAC/B,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnC,OAAO,SAAS,CAAC;IAAA,CACjB;IAED,OAAO,GAAS;QACf,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC/D,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB;YAAE,OAAO;QAErD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;QACzF,MAAM,KAAK,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QACxE,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC;IAAA,CACxC;IAED,IAAI,GAAS;QACZ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;YAAE,OAAO;QAChD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,OAAO;aACpB,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;aACxC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgB,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,CAAC,CAAC;IAAA,CAChD;IAED,SAAS,GAA+B;QACvC,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAAA,CAC1B;IAED,SAAS,GAA2B;QACnC,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAAA,CAC1B;IAED,MAAM,CAAI,OAA8C,EAAO;QAC9D,MAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC9B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC;QACF,CAAC;QACD,OAAO,OAAO,CAAC;IAAA,CACf;IAED,YAAY,CAAC,IAAY,EAAQ;QAChC,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAAA,CAC1E;CACD;AAeD,MAAM,OAAO,YAAY;IAChB,OAAO,CAAsB;IAErC,YAAY,OAAO,GAAwB,EAAE,EAAE;QAC9C,IAAI,CAAC,OAAO,GAAG;YACd,gBAAgB,EAAE,CAAC;YACnB,WAAW,EAAE,IAAI;YACjB,0BAA0B,EAAE,IAAI;YAChC,GAAG,OAAO;SACV,CAAC;IAAA,CACF;IAED,KAAK,CAAC,QAAsB,EAAmB;QAC9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,CAAC;QAC3C,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;QACnE,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;QAE3F,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,kCAAkC;QAClC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,QAAQ,SAAS,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,gBAAgB,qCAAqC,CAAC,CAAC;QAC7G,CAAC;QAED,8BAA8B;QAC9B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,2BAA2B,CAAC,CAAC;QAC1D,CAAC;QAED,wBAAwB;QACxB,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACpE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;YAC1D,CAAC;QACF,CAAC;QAED,wCAAwC;QACxC,IAAI,IAAI,CAAC,OAAO,CAAC,0BAA0B,EAAE,CAAC;YAC7C,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,mBAAmB,IAAI,CAAC,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACvG,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9B,MAAM,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;YAClE,CAAC;QACF,CAAC;QAED,4BAA4B;QAC5B,IAAI,QAAQ,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,MAAuB,CAAC;QAC5B,IAAI,MAAc,CAAC;QACnB,IAAI,UAA8B,CAAC;QAEnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,GAAG,MAAM,CAAC;YAChB,MAAM,GAAG,yBAAyB,SAAS,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,4BAA4B,CAAC;QACnG,CAAC;aAAM,CAAC;YACP,uFAAuF;YACvF,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,OAAO,KAAK,MAAM,CAAC;YACxE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;YAC3E,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3B,UAAU,GAAG,8BAA8B,OAAO,CAAC,MAAM,oCAAoC,CAAC;QAC/F,CAAC;QAED,OAAO;YACN,MAAM,EAAE,eAAe;YACvB,MAAM;YACN,MAAM;YACN,UAAU;YACV,eAAe,EAAE,QAAQ;SACzB,CAAC;IAAA,CACF;CACD;AAED,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,MAAM,OAAO,mBAAmB;IACvB,KAAK,CAAiB;IAE9B,YAAY,KAAK,GAAmB,CAAC,IAAI,YAAY,EAAE,CAAC,EAAE;QACzD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAAA,CACnB;IAED,KAAK,CAAC,QAAsB,EAAmB;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAC9D,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC;QAEtE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO;gBACN,MAAM,EAAE,wBAAwB;gBAChC,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,cAAc,OAAO,CAAC,MAAM,aAAa,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBAC1F,UAAU,EAAE,+CAA+C;gBAC3D,eAAe,EAAE,QAAQ,CAAC,gBAAgB;aAC1C,CAAC;QACH,CAAC;QAED,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,OAAO;gBACN,MAAM,EAAE,wBAAwB;gBAChC,MAAM,EAAE,aAAa;gBACrB,MAAM,EAAE,qBAAqB,WAAW,CAAC,MAAM,mCAAmC;gBAClF,UAAU,EAAE,4DAA4D;gBACxE,eAAe,EAAE,QAAQ,CAAC,gBAAgB;aAC1C,CAAC;QACH,CAAC;QAED,OAAO;YACN,MAAM,EAAE,wBAAwB;YAChC,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,+EAA+E;YACvF,eAAe,EAAE,QAAQ,CAAC,gBAAgB;SAC1C,CAAC;IAAA,CACF;CACD;AAWD,MAAM,OAAO,gBAAgB;IACpB,OAAO,CAA0B;IAEzC,YAAY,OAAgC,EAAE;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAAA,CACvB;IAED,MAAM,CAAC,QAAsB,EAAE,SAA0B,EAAU;QAClE,MAAM,KAAK,GAAa;YACvB,qCAAmC,QAAQ,CAAC,MAAM,EAAE;YACpD,EAAE;YACF,cAAc;YACd,EAAE;YACF,mBAAmB;YACnB,mBAAmB;YACnB,aAAa,QAAQ,CAAC,eAAe,IAAI;YACzC,eAAe,QAAQ,CAAC,OAAO,IAAI;YACnC,kBAAkB,QAAQ,CAAC,SAAS,IAAI;YACxC,EAAE;YACF,cAAc;YACd,EAAE;YACF,yCAAyC;YACzC,0CAA0C;SAC1C,CAAC;QAEF,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,gBAAgB,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC,MAAM,MAAM,EAAE,CAAC,YAAY,IAAI,KAAG,MAAM,EAAE,CAAC,mBAAmB,IAAI,KAAG,IAAI,CAAC,CAAC;QAC7G,CAAC;QAED,KAAK,CAAC,IAAI,CACT,EAAE,EACF,gBAAgB,EAChB,EAAE,EACF,4BAA4B,EAC5B,4BAA4B,EAC5B,KAAK,SAAS,CAAC,MAAM,MAAM,SAAS,CAAC,MAAM,MAAM,SAAS,CAAC,MAAM,IAAI,EACrE,EAAE,CACF,CAAC;QAEF,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,mBAAmB,SAAS,CAAC,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAAA,CACxB;IAED,KAAK,CAAC,QAAsB,EAAE,SAA0B,EAAU;QACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,YAAY,CAAC,CAAC;QAC9E,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IAAA,CACZ;CACD","sourcesContent":["// allow: SIZE_OK - legacy evidence ledger; this change only keeps typed import/check compatibility.\nimport { createHash } from \"node:crypto\";\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type {\n\tEvidenceItem,\n\tEvidenceStatus,\n\tMergeGateResult,\n\tMergeGateStatus,\n\tReplayEvent,\n\tReplayLedger,\n\tTaskContract,\n} from \"../types/evidence.ts\";\n\nfunction sha256(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction ensureDir(path: string): void {\n\tconst dir = dirname(path);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n\n// ============================================================================\n// TaskContract\n// ============================================================================\n\nexport class TaskContractBuilder {\n\tprivate goalId: string;\n\tprivate completionClaim: string = \"\";\n\tprivate requiredEvidence: EvidenceItem[] = [];\n\tprivate finalRisk: string = \"\";\n\tprivate verdict: \"pass\" | \"fail\" | \"conditional\" = \"fail\";\n\tprivate createdAt: string;\n\tprivate updatedAt: string;\n\n\tconstructor(goalId: string) {\n\t\tthis.goalId = goalId;\n\t\tconst now = new Date().toISOString();\n\t\tthis.createdAt = now;\n\t\tthis.updatedAt = now;\n\t}\n\n\tsetClaim(claim: string): this {\n\t\tthis.completionClaim = claim;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\taddRequiredEvidence(evidence: Omit<EvidenceItem, \"timestamp\" | \"status\">): this {\n\t\tthis.requiredEvidence.push({\n\t\t\t...evidence,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tstatus: \"pending\",\n\t\t});\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tsetFinalRisk(risk: string): this {\n\t\tthis.finalRisk = risk;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tsetVerdict(verdict: \"pass\" | \"fail\" | \"conditional\"): this {\n\t\tthis.verdict = verdict;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tupdateEvidenceStatus(claim: string, status: EvidenceStatus, gapReason?: string): this {\n\t\tconst item = this.requiredEvidence.find((e) => e.claim === claim);\n\t\tif (item) {\n\t\t\titem.status = status;\n\t\t\titem.gapReason = gapReason;\n\t\t\titem.timestamp = new Date().toISOString();\n\t\t\tthis.touch();\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate touch(): void {\n\t\tthis.updatedAt = new Date().toISOString();\n\t}\n\n\tbuild(): TaskContract {\n\t\treturn {\n\t\t\tgoalId: this.goalId,\n\t\t\tcompletionClaim: this.completionClaim,\n\t\t\trequiredEvidence: [...this.requiredEvidence],\n\t\t\tfinalRisk: this.finalRisk,\n\t\t\tverdict: this.verdict,\n\t\t\tcreatedAt: this.createdAt,\n\t\t\tupdatedAt: this.updatedAt,\n\t\t};\n\t}\n\n\tstatic fromJSON(json: string): TaskContract {\n\t\treturn JSON.parse(json) as TaskContract;\n\t}\n\n\tstatic toJSON(contract: TaskContract): string {\n\t\treturn JSON.stringify(contract, null, 2);\n\t}\n}\n\n// ============================================================================\n// ReplayLedger\n// ============================================================================\n\nexport class ReplayLedgerManager {\n\tprivate ledger: ReplayLedger;\n\tprivate nextSeq: number;\n\n\tconstructor(goalId: string, ledgerPath: string) {\n\t\tensureDir(ledgerPath);\n\t\tthis.ledger = {\n\t\t\tgoalId,\n\t\t\tevents: [],\n\t\t\tledgerPath,\n\t\t\tlastPersistedSeq: 0,\n\t\t};\n\t\tthis.nextSeq = 1;\n\t\tif (existsSync(ledgerPath)) {\n\t\t\tthis.load();\n\t\t}\n\t}\n\n\tappend(event: Omit<ReplayEvent, \"seq\" | \"timestamp\" | \"payloadHash\">): ReplayEvent {\n\t\tconst payloadStr = JSON.stringify(event.payload);\n\t\tconst fullEvent: ReplayEvent = {\n\t\t\t...event,\n\t\t\tseq: this.nextSeq++,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpayloadHash: sha256(payloadStr),\n\t\t};\n\t\tthis.ledger.events.push(fullEvent);\n\t\treturn fullEvent;\n\t}\n\n\tpersist(): void {\n\t\tif (this.ledger.events.length === 0) return;\n\t\tconst last = this.ledger.events[this.ledger.events.length - 1];\n\t\tif (last.seq <= this.ledger.lastPersistedSeq) return;\n\n\t\tconst newEvents = this.ledger.events.filter((e) => e.seq > this.ledger.lastPersistedSeq);\n\t\tconst lines = `${newEvents.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n\t\tappendFileSync(this.ledger.ledgerPath, lines, \"utf-8\");\n\t\tthis.ledger.lastPersistedSeq = last.seq;\n\t}\n\n\tload(): void {\n\t\tif (!existsSync(this.ledger.ledgerPath)) return;\n\t\tconst content = readFileSync(this.ledger.ledgerPath, \"utf-8\");\n\t\tconst events = content\n\t\t\t.split(\"\\n\")\n\t\t\t.filter((line) => line.trim().length > 0)\n\t\t\t.map((line) => JSON.parse(line) as ReplayEvent);\n\t\tthis.ledger.events = events;\n\t\tthis.ledger.lastPersistedSeq = events.length > 0 ? events[events.length - 1].seq : 0;\n\t\tthis.nextSeq = this.ledger.lastPersistedSeq + 1;\n\t}\n\n\tgetEvents(): ReadonlyArray<ReplayEvent> {\n\t\treturn this.ledger.events;\n\t}\n\n\tgetLedger(): Readonly<ReplayLedger> {\n\t\treturn { ...this.ledger };\n\t}\n\n\treplay<T>(handler: (event: ReplayEvent) => T | undefined): T[] {\n\t\tconst results: T[] = [];\n\t\tfor (const event of this.ledger.events) {\n\t\t\tconst result = handler(event);\n\t\t\tif (result !== undefined) {\n\t\t\t\tresults.push(result);\n\t\t\t}\n\t\t}\n\t\treturn results;\n\t}\n\n\texportToFile(path: string): void {\n\t\tensureDir(path);\n\t\twriteFileSync(path, JSON.stringify(this.ledger.events, null, 2), \"utf-8\");\n\t}\n}\n\n// ============================================================================\n// EvidenceGate\n// ============================================================================\n\nexport interface EvidenceGateOptions {\n\t/** Minimum number of satisfied evidence items required. */\n\tminEvidenceCount?: number;\n\t/** Require at least one artifact with a SHA-256 hash. */\n\trequireHash?: boolean;\n\t/** Require at least one verification command. */\n\trequireVerificationCommand?: boolean;\n}\n\nexport class EvidenceGate {\n\tprivate options: EvidenceGateOptions;\n\n\tconstructor(options: EvidenceGateOptions = {}) {\n\t\tthis.options = {\n\t\t\tminEvidenceCount: 1,\n\t\t\trequireHash: true,\n\t\t\trequireVerificationCommand: true,\n\t\t\t...options,\n\t\t};\n\t}\n\n\tcheck(contract: TaskContract): MergeGateResult {\n\t\tconst evidence = contract.requiredEvidence;\n\t\tconst satisfied = evidence.filter((e) => e.status === \"satisfied\");\n\t\tconst failed = evidence.filter((e) => e.status === \"failed\");\n\t\tconst pending = evidence.filter((e) => e.status === \"pending\" || e.status === \"gathering\");\n\n\t\tconst checks: string[] = [];\n\n\t\t// Check 1: minimum evidence count\n\t\tif (satisfied.length < (this.options.minEvidenceCount ?? 1)) {\n\t\t\tchecks.push(`Only ${satisfied.length}/${this.options.minEvidenceCount} required evidence items satisfied.`);\n\t\t}\n\n\t\t// Check 2: no failed evidence\n\t\tif (failed.length > 0) {\n\t\t\tchecks.push(`${failed.length} evidence item(s) failed.`);\n\t\t}\n\n\t\t// Check 3: require hash\n\t\tif (this.options.requireHash) {\n\t\t\tconst hashed = satisfied.filter((e) => e.hash && e.hash.length > 0);\n\t\t\tif (hashed.length === 0) {\n\t\t\t\tchecks.push(\"No satisfied evidence has a SHA-256 hash.\");\n\t\t\t}\n\t\t}\n\n\t\t// Check 4: require verification command\n\t\tif (this.options.requireVerificationCommand) {\n\t\t\tconst withCommand = satisfied.filter((e) => e.verificationCommand && e.verificationCommand.length > 0);\n\t\t\tif (withCommand.length === 0) {\n\t\t\t\tchecks.push(\"No satisfied evidence has a verification command.\");\n\t\t\t}\n\t\t}\n\n\t\t// Check 5: contract verdict\n\t\tif (contract.verdict === \"fail\") {\n\t\t\tchecks.push(\"Task contract verdict is 'fail'.\");\n\t\t}\n\n\t\tlet status: MergeGateStatus;\n\t\tlet reason: string;\n\t\tlet suggestion: string | undefined;\n\n\t\tif (checks.length === 0) {\n\t\t\tstatus = \"open\";\n\t\t\treason = `Evidence gate passed: ${satisfied.length}/${evidence.length} evidence items satisfied.`;\n\t\t} else {\n\t\t\t// If there are pending items and no hard failures, it's conditional; otherwise blocked\n\t\t\tconst hasHardFailure = failed.length > 0 || contract.verdict === \"fail\";\n\t\t\tstatus = pending.length > 0 && !hasHardFailure ? \"conditional\" : \"blocked\";\n\t\t\treason = checks.join(\"; \");\n\t\t\tsuggestion = `Gather remaining evidence (${pending.length} pending) and re-run verification.`;\n\t\t}\n\n\t\treturn {\n\t\t\tgateId: \"evidence-gate\",\n\t\t\tstatus,\n\t\t\treason,\n\t\t\tsuggestion,\n\t\t\tevidenceChecked: evidence,\n\t\t};\n\t}\n}\n\n// ============================================================================\n// FailClosedMergeGate\n// ============================================================================\n\nexport class FailClosedMergeGate {\n\tprivate gates: EvidenceGate[];\n\n\tconstructor(gates: EvidenceGate[] = [new EvidenceGate()]) {\n\t\tthis.gates = gates;\n\t}\n\n\tcheck(contract: TaskContract): MergeGateResult {\n\t\tconst results = this.gates.map((gate) => gate.check(contract));\n\t\tconst blocked = results.filter((r) => r.status === \"blocked\");\n\t\tconst conditional = results.filter((r) => r.status === \"conditional\");\n\n\t\tif (blocked.length > 0) {\n\t\t\treturn {\n\t\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\treason: `Blocked by ${blocked.length} gate(s): ${blocked.map((b) => b.reason).join(\"; \")}`,\n\t\t\t\tsuggestion: \"Resolve all blocking conditions before merge.\",\n\t\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t\t};\n\t\t}\n\n\t\tif (conditional.length > 0) {\n\t\t\treturn {\n\t\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\t\tstatus: \"conditional\",\n\t\t\t\treason: `Conditional pass: ${conditional.length} gate(s) have pending conditions.`,\n\t\t\t\tsuggestion: \"Complete pending evidence or waive with explicit approval.\",\n\t\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\tstatus: \"open\",\n\t\t\treason: \"All merge gates passed. Evidence verified and contract verdict is acceptable.\",\n\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t};\n\t}\n}\n\n// ============================================================================\n// Verify Reporter v2\n// ============================================================================\n\nexport interface VerifyReporterV2Options {\n\toutputDir: string;\n\tgoalId: string;\n}\n\nexport class VerifyReporterV2 {\n\tprivate options: VerifyReporterV2Options;\n\n\tconstructor(options: VerifyReporterV2Options) {\n\t\tthis.options = options;\n\t}\n\n\trender(contract: TaskContract, mergeGate: MergeGateResult): string {\n\t\tconst lines: string[] = [\n\t\t\t`## OMK Verification Report v2 — ${contract.goalId}`,\n\t\t\t\"\",\n\t\t\t\"### Contract\",\n\t\t\t\"\",\n\t\t\t`| Field | Value |`,\n\t\t\t`|-------|-------|`,\n\t\t\t`| Claim | ${contract.completionClaim} |`,\n\t\t\t`| Verdict | ${contract.verdict} |`,\n\t\t\t`| Final Risk | ${contract.finalRisk} |`,\n\t\t\t\"\",\n\t\t\t\"### Evidence\",\n\t\t\t\"\",\n\t\t\t`| Claim | Status | Artifact | Command |`,\n\t\t\t`|-------|--------|----------|----------|`,\n\t\t];\n\n\t\tfor (const ev of contract.requiredEvidence) {\n\t\t\tlines.push(`| ${ev.claim} | ${ev.status} | ${ev.artifactPath ?? \"—\"} | ${ev.verificationCommand ?? \"—\"} |`);\n\t\t}\n\n\t\tlines.push(\n\t\t\t\"\",\n\t\t\t\"### Merge Gate\",\n\t\t\t\"\",\n\t\t\t`| Gate | Status | Reason |`,\n\t\t\t`|------|--------|--------|`,\n\t\t\t`| ${mergeGate.gateId} | ${mergeGate.status} | ${mergeGate.reason} |`,\n\t\t\t\"\",\n\t\t);\n\n\t\tif (mergeGate.suggestion) {\n\t\t\tlines.push(`**Suggestion:** ${mergeGate.suggestion}`, \"\");\n\t\t}\n\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\twrite(contract: TaskContract, mergeGate: MergeGateResult): string {\n\t\tconst markdown = this.render(contract, mergeGate);\n\t\tconst path = join(this.options.outputDir, `${this.options.goalId}.verify.md`);\n\t\tensureDir(path);\n\t\twriteFileSync(path, markdown, \"utf-8\");\n\t\treturn path;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"evidence-system.js","sourceRoot":"","sources":["../../src/guardrails/evidence-system.ts"],"names":[],"mappings":"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","sourcesContent":["// allow: SIZE_OK - evidence ledger guardrails: tamper-evident replay chain + fail-closed parsing.\nimport { createHash } from \"node:crypto\";\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport type {\n\tEvidenceItem,\n\tEvidenceStatus,\n\tMergeGateResult,\n\tMergeGateStatus,\n\tReplayEvent,\n\tReplayLedger,\n\tTaskContract,\n} from \"../types/evidence.ts\";\n\nfunction sha256(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction ensureDir(path: string): void {\n\tconst dir = dirname(path);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n\n// ============================================================================\n// TaskContract\n// ============================================================================\n\nexport class TaskContractBuilder {\n\tprivate goalId: string;\n\tprivate completionClaim: string = \"\";\n\tprivate requiredEvidence: EvidenceItem[] = [];\n\tprivate finalRisk: string = \"\";\n\tprivate verdict: \"pass\" | \"fail\" | \"conditional\" = \"fail\";\n\tprivate createdAt: string;\n\tprivate updatedAt: string;\n\n\tconstructor(goalId: string) {\n\t\tthis.goalId = goalId;\n\t\tconst now = new Date().toISOString();\n\t\tthis.createdAt = now;\n\t\tthis.updatedAt = now;\n\t}\n\n\tsetClaim(claim: string): this {\n\t\tthis.completionClaim = claim;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\taddRequiredEvidence(evidence: Omit<EvidenceItem, \"timestamp\" | \"status\">): this {\n\t\tthis.requiredEvidence.push({\n\t\t\t...evidence,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tstatus: \"pending\",\n\t\t});\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tsetFinalRisk(risk: string): this {\n\t\tthis.finalRisk = risk;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tsetVerdict(verdict: \"pass\" | \"fail\" | \"conditional\"): this {\n\t\tthis.verdict = verdict;\n\t\tthis.touch();\n\t\treturn this;\n\t}\n\n\tupdateEvidenceStatus(claim: string, status: EvidenceStatus, gapReason?: string): this {\n\t\tconst item = this.requiredEvidence.find((e) => e.claim === claim);\n\t\tif (item) {\n\t\t\titem.status = status;\n\t\t\titem.gapReason = gapReason;\n\t\t\titem.timestamp = new Date().toISOString();\n\t\t\tthis.touch();\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate touch(): void {\n\t\tthis.updatedAt = new Date().toISOString();\n\t}\n\n\tbuild(): TaskContract {\n\t\treturn structuredClone({\n\t\t\tgoalId: this.goalId,\n\t\t\tcompletionClaim: this.completionClaim,\n\t\t\trequiredEvidence: this.requiredEvidence,\n\t\t\tfinalRisk: this.finalRisk,\n\t\t\tverdict: this.verdict,\n\t\t\tcreatedAt: this.createdAt,\n\t\t\tupdatedAt: this.updatedAt,\n\t\t});\n\t}\n\n\t/** Parse and validate a serialized TaskContract. Fails closed on any shape violation. */\n\tstatic fromJSON(json: string): TaskContract {\n\t\tconst raw: unknown = JSON.parse(json);\n\t\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\t\tthrow new Error(\"TaskContract JSON must be an object\");\n\t\t}\n\t\tconst { goalId, completionClaim, finalRisk, verdict, createdAt, updatedAt, requiredEvidence } = raw as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>;\n\t\tif (\n\t\t\ttypeof goalId !== \"string\" ||\n\t\t\ttypeof completionClaim !== \"string\" ||\n\t\t\ttypeof finalRisk !== \"string\" ||\n\t\t\ttypeof createdAt !== \"string\" ||\n\t\t\ttypeof updatedAt !== \"string\"\n\t\t) {\n\t\t\tthrow new Error(\"TaskContract JSON has missing or mistyped string fields\");\n\t\t}\n\t\tif (verdict !== \"pass\" && verdict !== \"fail\" && verdict !== \"conditional\") {\n\t\t\tthrow new Error(`TaskContract verdict must be pass|fail|conditional, got ${String(verdict)}`);\n\t\t}\n\t\tif (!Array.isArray(requiredEvidence)) {\n\t\t\tthrow new Error(\"TaskContract requiredEvidence must be an array\");\n\t\t}\n\t\treturn {\n\t\t\tgoalId,\n\t\t\tcompletionClaim,\n\t\t\tfinalRisk,\n\t\t\tverdict,\n\t\t\tcreatedAt,\n\t\t\tupdatedAt,\n\t\t\trequiredEvidence: requiredEvidence.map((item, index) => parseEvidenceItem(item, index)),\n\t\t};\n\t}\n\n\tstatic toJSON(contract: TaskContract): string {\n\t\treturn JSON.stringify(contract, null, 2);\n\t}\n}\n\nconst EVIDENCE_STATUSES: ReadonlySet<string> = new Set([\"pending\", \"gathering\", \"satisfied\", \"failed\", \"waived\"]);\n\nfunction optionalStringField(value: unknown, field: string, index: number): string | undefined {\n\tif (value !== undefined && typeof value !== \"string\") {\n\t\tthrow new Error(`TaskContract evidence[${index}].${field} must be a string when present`);\n\t}\n\treturn value;\n}\n\nfunction parseEvidenceItem(raw: unknown, index: number): EvidenceItem {\n\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\tthrow new Error(`TaskContract evidence[${index}] must be an object`);\n\t}\n\tconst item = raw as Record<string, unknown>;\n\tconst { claim, category, timestamp, status } = item;\n\tif (typeof claim !== \"string\" || typeof category !== \"string\" || typeof timestamp !== \"string\") {\n\t\tthrow new Error(`TaskContract evidence[${index}] has missing or mistyped fields`);\n\t}\n\tif (typeof status !== \"string\" || !EVIDENCE_STATUSES.has(status)) {\n\t\tthrow new Error(`TaskContract evidence[${index}] has invalid status ${String(status)}`);\n\t}\n\treturn {\n\t\tclaim,\n\t\tcategory: category as EvidenceItem[\"category\"],\n\t\ttimestamp,\n\t\tstatus: status as EvidenceStatus,\n\t\tartifactPath: optionalStringField(item.artifactPath, \"artifactPath\", index),\n\t\tverificationCommand: optionalStringField(item.verificationCommand, \"verificationCommand\", index),\n\t\thash: optionalStringField(item.hash, \"hash\", index),\n\t\tgapReason: optionalStringField(item.gapReason, \"gapReason\", index),\n\t};\n}\n\n// ============================================================================\n// ReplayLedger\n// ============================================================================\n\n/** prevHash sentinel for the first event in a replay chain. */\nconst GENESIS_HASH = \"genesis\";\n\nfunction computeEventHash(event: Omit<ReplayEvent, \"eventHash\">): string {\n\treturn sha256(\n\t\tJSON.stringify([\n\t\t\tevent.seq,\n\t\t\tevent.type,\n\t\t\tevent.timestamp,\n\t\t\tevent.goalId,\n\t\t\tevent.laneId ?? null,\n\t\t\tevent.payloadHash,\n\t\t\tevent.prevHash,\n\t\t]),\n\t);\n}\n\nfunction parseReplayEventLine(line: string, lineNumber: number): ReplayEvent {\n\tlet raw: unknown;\n\ttry {\n\t\traw = JSON.parse(line);\n\t} catch {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: invalid JSON`);\n\t}\n\tif (raw === null || typeof raw !== \"object\" || Array.isArray(raw)) {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: event must be an object`);\n\t}\n\tconst event = raw as Record<string, unknown>;\n\tconst { seq, type, timestamp, goalId, laneId, payloadHash, prevHash, eventHash } = event;\n\tif (\n\t\ttypeof seq !== \"number\" ||\n\t\ttypeof type !== \"string\" ||\n\t\ttypeof timestamp !== \"string\" ||\n\t\ttypeof goalId !== \"string\" ||\n\t\ttypeof payloadHash !== \"string\" ||\n\t\ttypeof prevHash !== \"string\" ||\n\t\ttypeof eventHash !== \"string\" ||\n\t\t(laneId !== undefined && typeof laneId !== \"string\") ||\n\t\t!(\"payload\" in event)\n\t) {\n\t\tthrow new Error(`Replay ledger corrupted at line ${lineNumber}: event fields are missing or mistyped`);\n\t}\n\treturn {\n\t\tseq,\n\t\ttype: type as ReplayEvent[\"type\"],\n\t\ttimestamp,\n\t\tgoalId,\n\t\tlaneId,\n\t\tpayload: event.payload,\n\t\tpayloadHash,\n\t\tprevHash,\n\t\teventHash,\n\t};\n}\n\nexport class ReplayLedgerManager {\n\tprivate ledger: ReplayLedger;\n\tprivate nextSeq: number;\n\n\tconstructor(goalId: string, ledgerPath: string) {\n\t\tensureDir(ledgerPath);\n\t\tthis.ledger = {\n\t\t\tgoalId,\n\t\t\tevents: [],\n\t\t\tledgerPath,\n\t\t\tlastPersistedSeq: 0,\n\t\t};\n\t\tthis.nextSeq = 1;\n\t\tif (existsSync(ledgerPath)) {\n\t\t\tthis.load();\n\t\t}\n\t}\n\n\tappend(event: Omit<ReplayEvent, \"seq\" | \"timestamp\" | \"payloadHash\" | \"prevHash\" | \"eventHash\">): ReplayEvent {\n\t\tconst payloadStr = JSON.stringify(event.payload);\n\t\tconst previous = this.ledger.events[this.ledger.events.length - 1];\n\t\tconst chained: Omit<ReplayEvent, \"eventHash\"> = {\n\t\t\t...event,\n\t\t\tseq: this.nextSeq++,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tpayloadHash: sha256(payloadStr),\n\t\t\tprevHash: previous ? previous.eventHash : GENESIS_HASH,\n\t\t};\n\t\tconst fullEvent: ReplayEvent = { ...chained, eventHash: computeEventHash(chained) };\n\t\tthis.ledger.events.push(fullEvent);\n\t\treturn fullEvent;\n\t}\n\n\tpersist(): void {\n\t\tif (this.ledger.events.length === 0) return;\n\t\tconst last = this.ledger.events[this.ledger.events.length - 1];\n\t\tif (last.seq <= this.ledger.lastPersistedSeq) return;\n\n\t\tconst newEvents = this.ledger.events.filter((e) => e.seq > this.ledger.lastPersistedSeq);\n\t\tconst lines = `${newEvents.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n\t\tappendFileSync(this.ledger.ledgerPath, lines, \"utf-8\");\n\t\tthis.ledger.lastPersistedSeq = last.seq;\n\t}\n\n\t/**\n\t * Load and verify the persisted ledger. Fails closed: any schema violation,\n\t * sequence gap, payload-hash mismatch, or broken event-hash chain throws, so\n\t * a tampered ledger (edited, deleted, inserted, or reordered lines) is never\n\t * loaded silently.\n\t */\n\tload(): void {\n\t\tif (!existsSync(this.ledger.ledgerPath)) return;\n\t\tconst content = readFileSync(this.ledger.ledgerPath, \"utf-8\");\n\t\tconst lines = content.split(\"\\n\").filter((line) => line.trim().length > 0);\n\t\tconst events: ReplayEvent[] = [];\n\t\tlet prevHash = GENESIS_HASH;\n\t\tfor (let index = 0; index < lines.length; index++) {\n\t\t\tconst event = parseReplayEventLine(lines[index], index + 1);\n\t\t\tif (event.seq !== index + 1) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Replay ledger corrupted at line ${index + 1}: expected seq ${index + 1}, got ${event.seq}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (event.prevHash !== prevHash) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Replay ledger chain broken at line ${index + 1}: an event was inserted, deleted, or reordered`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (sha256(JSON.stringify(event.payload)) !== event.payloadHash) {\n\t\t\t\tthrow new Error(`Replay ledger payload tampered at line ${index + 1} (seq ${event.seq})`);\n\t\t\t}\n\t\t\tif (computeEventHash(event) !== event.eventHash) {\n\t\t\t\tthrow new Error(`Replay ledger event hash mismatch at line ${index + 1} (seq ${event.seq})`);\n\t\t\t}\n\t\t\tprevHash = event.eventHash;\n\t\t\tevents.push(event);\n\t\t}\n\t\tthis.ledger.events = events;\n\t\tthis.ledger.lastPersistedSeq = events.length > 0 ? events[events.length - 1].seq : 0;\n\t\tthis.nextSeq = this.ledger.lastPersistedSeq + 1;\n\t}\n\n\tgetEvents(): ReadonlyArray<ReplayEvent> {\n\t\treturn structuredClone(this.ledger.events);\n\t}\n\n\tgetLedger(): Readonly<ReplayLedger> {\n\t\treturn structuredClone(this.ledger);\n\t}\n\n\treplay<T>(handler: (event: ReplayEvent) => T | undefined): T[] {\n\t\tconst results: T[] = [];\n\t\tfor (const event of this.ledger.events) {\n\t\t\tconst result = handler(event);\n\t\t\tif (result !== undefined) {\n\t\t\t\tresults.push(result);\n\t\t\t}\n\t\t}\n\t\treturn results;\n\t}\n\n\texportToFile(path: string): void {\n\t\tensureDir(path);\n\t\twriteFileSync(path, JSON.stringify(this.ledger.events, null, 2), \"utf-8\");\n\t}\n}\n\n// ============================================================================\n// EvidenceGate\n// ============================================================================\n\nexport interface EvidenceGateOptions {\n\t/** Minimum number of satisfied evidence items required. */\n\tminEvidenceCount?: number;\n\t/** Require at least one artifact with a SHA-256 hash. */\n\trequireHash?: boolean;\n\t/** Require at least one verification command. */\n\trequireVerificationCommand?: boolean;\n}\n\nexport class EvidenceGate {\n\tprivate options: EvidenceGateOptions;\n\n\tconstructor(options: EvidenceGateOptions = {}) {\n\t\tthis.options = {\n\t\t\tminEvidenceCount: 1,\n\t\t\trequireHash: true,\n\t\t\trequireVerificationCommand: true,\n\t\t\t...options,\n\t\t};\n\t}\n\n\tcheck(contract: TaskContract): MergeGateResult {\n\t\tconst evidence = contract.requiredEvidence;\n\t\tconst satisfied = evidence.filter((e) => e.status === \"satisfied\");\n\t\tconst failed = evidence.filter((e) => e.status === \"failed\");\n\t\tconst pending = evidence.filter((e) => e.status === \"pending\" || e.status === \"gathering\");\n\n\t\tconst checks: string[] = [];\n\n\t\t// Check 1: minimum evidence count\n\t\tif (satisfied.length < (this.options.minEvidenceCount ?? 1)) {\n\t\t\tchecks.push(`Only ${satisfied.length}/${this.options.minEvidenceCount} required evidence items satisfied.`);\n\t\t}\n\n\t\t// Check 2: no failed evidence\n\t\tif (failed.length > 0) {\n\t\t\tchecks.push(`${failed.length} evidence item(s) failed.`);\n\t\t}\n\n\t\t// Check 3: require hash\n\t\tif (this.options.requireHash) {\n\t\t\tconst hashed = satisfied.filter((e) => e.hash && e.hash.length > 0);\n\t\t\tif (hashed.length === 0) {\n\t\t\t\tchecks.push(\"No satisfied evidence has a SHA-256 hash.\");\n\t\t\t}\n\t\t}\n\n\t\t// Check 4: require verification command\n\t\tif (this.options.requireVerificationCommand) {\n\t\t\tconst withCommand = satisfied.filter((e) => e.verificationCommand && e.verificationCommand.length > 0);\n\t\t\tif (withCommand.length === 0) {\n\t\t\t\tchecks.push(\"No satisfied evidence has a verification command.\");\n\t\t\t}\n\t\t}\n\n\t\t// Check 5: contract verdict\n\t\tif (contract.verdict === \"fail\") {\n\t\t\tchecks.push(\"Task contract verdict is 'fail'.\");\n\t\t}\n\n\t\tlet status: MergeGateStatus;\n\t\tlet reason: string;\n\t\tlet suggestion: string | undefined;\n\n\t\tif (checks.length === 0) {\n\t\t\tstatus = \"open\";\n\t\t\treason = `Evidence gate passed: ${satisfied.length}/${evidence.length} evidence items satisfied.`;\n\t\t} else {\n\t\t\t// If there are pending items and no hard failures, it's conditional; otherwise blocked\n\t\t\tconst hasHardFailure = failed.length > 0 || contract.verdict === \"fail\";\n\t\t\tstatus = pending.length > 0 && !hasHardFailure ? \"conditional\" : \"blocked\";\n\t\t\treason = checks.join(\"; \");\n\t\t\tsuggestion = `Gather remaining evidence (${pending.length} pending) and re-run verification.`;\n\t\t}\n\n\t\treturn {\n\t\t\tgateId: \"evidence-gate\",\n\t\t\tstatus,\n\t\t\treason,\n\t\t\tsuggestion,\n\t\t\tevidenceChecked: evidence,\n\t\t};\n\t}\n}\n\n// ============================================================================\n// FailClosedMergeGate\n// ============================================================================\n\nexport class FailClosedMergeGate {\n\tprivate gates: EvidenceGate[];\n\n\tconstructor(gates: EvidenceGate[] = [new EvidenceGate()]) {\n\t\tthis.gates = gates;\n\t}\n\n\tcheck(contract: TaskContract): MergeGateResult {\n\t\tconst results = this.gates.map((gate) => gate.check(contract));\n\t\tconst blocked = results.filter((r) => r.status === \"blocked\");\n\t\tconst conditional = results.filter((r) => r.status === \"conditional\");\n\n\t\tif (blocked.length > 0) {\n\t\t\treturn {\n\t\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\t\tstatus: \"blocked\",\n\t\t\t\treason: `Blocked by ${blocked.length} gate(s): ${blocked.map((b) => b.reason).join(\"; \")}`,\n\t\t\t\tsuggestion: \"Resolve all blocking conditions before merge.\",\n\t\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t\t};\n\t\t}\n\n\t\tif (conditional.length > 0) {\n\t\t\treturn {\n\t\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\t\tstatus: \"conditional\",\n\t\t\t\treason: `Conditional pass: ${conditional.length} gate(s) have pending conditions.`,\n\t\t\t\tsuggestion: \"Complete pending evidence or waive with explicit approval.\",\n\t\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tgateId: \"fail-closed-merge-gate\",\n\t\t\tstatus: \"open\",\n\t\t\treason: \"All merge gates passed. Evidence verified and contract verdict is acceptable.\",\n\t\t\tevidenceChecked: contract.requiredEvidence,\n\t\t};\n\t}\n}\n\n// ============================================================================\n// Verify Reporter v2\n// ============================================================================\n\n/** Escape pipes/newlines so untrusted claim or command text cannot restructure the report table. */\nfunction escapeTableCell(value: string): string {\n\treturn value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\\|/g, \"\\\\|\").replace(/\\r?\\n/g, \" \");\n}\n\nexport interface VerifyReporterV2Options {\n\toutputDir: string;\n\tgoalId: string;\n}\n\nexport class VerifyReporterV2 {\n\tprivate options: VerifyReporterV2Options;\n\n\tconstructor(options: VerifyReporterV2Options) {\n\t\tthis.options = options;\n\t}\n\n\trender(contract: TaskContract, mergeGate: MergeGateResult): string {\n\t\tconst lines: string[] = [\n\t\t\t`## OMK Verification Report v2 — ${contract.goalId}`,\n\t\t\t\"\",\n\t\t\t\"### Contract\",\n\t\t\t\"\",\n\t\t\t`| Field | Value |`,\n\t\t\t`|-------|-------|`,\n\t\t\t`| Claim | ${escapeTableCell(contract.completionClaim)} |`,\n\t\t\t`| Verdict | ${contract.verdict} |`,\n\t\t\t`| Final Risk | ${escapeTableCell(contract.finalRisk)} |`,\n\t\t\t\"\",\n\t\t\t\"### Evidence\",\n\t\t\t\"\",\n\t\t\t`| Claim | Status | Artifact | Command |`,\n\t\t\t`|-------|--------|----------|----------|`,\n\t\t];\n\n\t\tfor (const ev of contract.requiredEvidence) {\n\t\t\tlines.push(\n\t\t\t\t`| ${escapeTableCell(ev.claim)} | ${ev.status} | ${escapeTableCell(ev.artifactPath ?? \"—\")} | ${escapeTableCell(ev.verificationCommand ?? \"—\")} |`,\n\t\t\t);\n\t\t}\n\n\t\tlines.push(\n\t\t\t\"\",\n\t\t\t\"### Merge Gate\",\n\t\t\t\"\",\n\t\t\t`| Gate | Status | Reason |`,\n\t\t\t`|------|--------|--------|`,\n\t\t\t`| ${mergeGate.gateId} | ${mergeGate.status} | ${escapeTableCell(mergeGate.reason)} |`,\n\t\t\t\"\",\n\t\t);\n\n\t\tif (mergeGate.suggestion) {\n\t\t\tlines.push(`**Suggestion:** ${mergeGate.suggestion}`, \"\");\n\t\t}\n\n\t\treturn lines.join(\"\\n\");\n\t}\n\n\twrite(contract: TaskContract, mergeGate: MergeGateResult): string {\n\t\tconst markdown = this.render(contract, mergeGate);\n\t\tconst path = join(this.options.outputDir, `${this.options.goalId}.verify.md`);\n\t\tensureDir(path);\n\t\twriteFileSync(path, markdown, \"utf-8\");\n\t\treturn path;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-panel-layout.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAkB,KAAK,SAAS,EAA0B,MAAM,6BAA6B,CAAC;AAgBrG,eAAO,MAAM,uBAAuB,UAQnC,CAAC;AAEF,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,eAAO,MAAM,4BAA4B,QAAsC,CAAC;AAChF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAKzC,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,MAAM,CAAC;IAClC,oBAAoB,EAAE,MAAM,MAAM,CAAC;IACnC,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,UAAU,EAAE,MAAM,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,0BAA0B,CAAC;CAClD;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,sBAAsB,CAAC;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EAAE,EACtB,SAAS,SAAI,GACX,MAAM,EAAE,CAKV;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAGjG","sourcesContent":["import { truncateToWidth, visibleWidth } from \"omk-tui\";\nimport type { PiPackageIntakeSummary } from \"../../../core/pi-package-intake.ts\";\nimport { nextActiveTodo, type TodoState, summary as todoSummary } from \"../../../core/todo-state.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport {\n\tboxBottom,\n\tboxCenteredLine,\n\tboxTextLine,\n\tboxTop,\n\tcenterLine,\n\tcomposeColumns,\n\tdivider,\n\tfitLine,\n\tsidebarRule,\n\ttextLine,\n} from \"./control-panel-box.ts\";\nimport { SPARKLE_ROW_BOTTOM, SPARKLE_ROW_TOP, sparkleRow } from \"./control-panel-sparkles.ts\";\n\nexport const CONTROL_PANEL_ASCII_ART = [\n\t\" ███████ ██ ██ ██ ██\",\n\t\"██ ██ ███ ███ ██ ██ \",\n\t\"██ ██ ████ ████ ██ ██ \",\n\t\"██ ██ ██ ██ ██ ██ █████ \",\n\t\"██ ██ ██ ███ ██ ██ ██ \",\n\t\"██ ██ ██ █ ██ ██ ██ \",\n\t\" ███████ ██ ██ ██ ██\",\n];\nconst META_WIDTH = 31;\nexport const CONTROL_PANEL_OVERLAY_MIN_WIDTH = 112;\nexport const CONTROL_PANEL_DECK_MIN_WIDTH = CONTROL_PANEL_OVERLAY_MIN_WIDTH + 1;\nexport const CONTROL_PANEL_SIDEBAR_WIDTH = 38;\nexport const CONTROL_PANEL_GAP_WIDTH = 2;\nconst CONTEXT_METER_CELLS = 12;\nconst SPARKLINE_WIDTH = 16;\nconst SPARKLINE_GLYPHS = \"▁▂▃▄▅▆▇█\";\n\nexport interface ControlPanelContent {\n\tappName: string;\n\tversion: string;\n\tcompactInstructions: () => string;\n\texpandedInstructions: () => string;\n\tcompactOnboarding: () => string;\n\tonboarding: () => string;\n\tstatusSnapshot?: () => ControlPanelStatusSnapshot;\n}\n\nexport interface ControlPanelStatusSnapshot {\n\treadonly modelId?: string;\n\treadonly modelProvider?: string;\n\treadonly thinkingLevel?: string;\n\treadonly contextPercent?: number | null;\n\treadonly contextWindowTokens?: number;\n\treadonly contextTokens?: number | null;\n\treadonly headroomStatus?: string;\n\treadonly optimizerPolicy?: string;\n\treadonly skillCount?: number;\n\treadonly mcpCount?: number;\n\treadonly packageIntake?: PiPackageIntakeSummary;\n\treadonly cwdLabel?: string;\n\treadonly gitBranch?: string | null;\n\treadonly todoState?: TodoState;\n\treadonly runtimeState?: string;\n\treadonly routeState?: string;\n\treadonly evidenceState?: string;\n\treadonly controlState?: string;\n\treadonly dagOrchestrationState?: string;\n\treadonly ansiColorState?: string;\n\treadonly startupState?: string;\n\treadonly linkState?: string;\n\treadonly sidebarState?: string;\n}\n\nexport function renderControlPanelLayout(\n\tcontent: ControlPanelContent,\n\texpanded: boolean,\n\twidth: number,\n\tbannerFrame?: string[],\n\tsparkleMs = 0,\n): string[] {\n\tif (width <= 0) return [];\n\treturn expanded\n\t\t? renderExpanded(content, width, bannerFrame, sparkleMs)\n\t\t: renderCompact(content, width, bannerFrame, sparkleMs);\n}\n\nexport function renderControlPanelRightPane(content: ControlPanelContent, width: number): string[] {\n\tif (width <= 0) return [];\n\treturn sidebarPanel(content, width);\n}\n\nfunction renderCompact(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tif (width >= CONTROL_PANEL_DECK_MIN_WIDTH) {\n\t\tconst deck = renderDeck(content, width, bannerFrame, sparkleMs);\n\t\tif (deck.length > 0) return deck;\n\t}\n\treturn [\n\t\tdivider(width, \"OMK//CONTROL PANEL\", \"accent\"),\n\t\tstatusLine(content, width),\n\t\ttextLine(width, content.compactInstructions()),\n\t\ttextLine(width, content.compactOnboarding(), \"dim\"),\n\t];\n}\n\nfunction renderDeck(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tconst { leftWidth, sidebarWidth } = deckWidths(width);\n\tif (leftWidth < 72) return [];\n\tconst lines = composeColumns(\n\t\theroPanel(content, leftWidth, bannerFrame, sparkleMs),\n\t\tleftWidth,\n\t\tsidebarPanel(content, sidebarWidth),\n\t\tsidebarWidth,\n\t\tCONTROL_PANEL_GAP_WIDTH,\n\t\twidth,\n\t);\n\tlines.push(controlStripLine(content, width));\n\tlines.push(centerLine(width, content.compactOnboarding(), \"dim\"));\n\treturn lines;\n}\n\nfunction deckWidths(width: number): { leftWidth: number; sidebarWidth: number } {\n\tconst sidebarWidth = Math.min(CONTROL_PANEL_SIDEBAR_WIDTH, Math.max(34, Math.floor(width * 0.28)));\n\treturn { leftWidth: width - CONTROL_PANEL_GAP_WIDTH - sidebarWidth, sidebarWidth };\n}\n\nfunction heroPanel(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst innerWidth = Math.max(0, width - 4);\n\treturn [\n\t\tboxTop(width, `omk v${content.version} · OMK://CONTROL`),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK\"))),\n\t\tboxCenteredLine(width, theme.fg(\"muted\", \"route · verify · loop · control\")),\n\t\tboxTextLine(width, sparkleRow(innerWidth, SPARKLE_ROW_TOP, sparkleMs)),\n\t\t...(bannerFrame ?? CONTROL_PANEL_ASCII_ART).map((line, index) => {\n\t\t\tif (bannerFrame) return boxCenteredLine(width, line);\n\t\t\tconst color: ThemeColor = index < 2 ? \"mdCode\" : index < 4 ? \"accent\" : index < 6 ? \"success\" : \"warning\";\n\t\t\treturn boxCenteredLine(width, theme.fg(color, line));\n\t\t}),\n\t\tboxTextLine(width, sparkleRow(innerWidth, SPARKLE_ROW_BOTTOM, sparkleMs)),\n\t\tboxCenteredLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"warning\", \"●\")} ${theme.bold(\"OMK\")} / ${theme.fg(\"success\", heroModelLabel(snapshot))}`,\n\t\t),\n\t\tboxCenteredLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"mdCode\", \"◇\")} omk-control · ${theme.fg(\"accent\", \"route\")} · ${theme.fg(\"warning\", \"verify\")} · ${theme.fg(\"success\", \"loop\")} · ${theme.fg(\"mdCode\", \"control\")}`,\n\t\t),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction heroModelLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tif (!snapshot.modelId) return \"no-model\";\n\tconst think = snapshot.thinkingLevel;\n\treturn think && think !== \"off\" ? `${snapshot.modelId}:${think}` : snapshot.modelId;\n}\n\nfunction sidebarPanel(content: ControlPanelContent, width: number): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst headroomLabel = snapshot.headroomStatus ?? snapshot.optimizerPolicy ?? \"unknown\";\n\tconst mcpCount = snapshot.mcpCount;\n\tconst skillCount = snapshot.skillCount;\n\tconst packageIntakeLabel = packageIntakeStatusLabel(snapshot.packageIntake);\n\tconst contextMeter = contextMeterLabel(snapshot);\n\tconst activitySparkline = activitySparklineLabel(snapshot);\n\treturn [\n\t\tsidebarTabs(width),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK://CONTROL\"))),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"warning\", \"CYBERPUNK OPS CORE\"))),\n\t\tboxCenteredLine(width, `${theme.fg(\"mdCode\", \"MATRIX RAIN\")} // ${theme.fg(\"success\", \"NEON GRID ONLINE\")}`),\n\t\tboxCenteredLine(width, theme.fg(\"text\", \"NIGHT-CITY-MATRIX-V3\")),\n\t\tsidebarRule(width, \"STATUS\", \"accent\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"state:\")} ${coloredStatus(snapshot.runtimeState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${coloredStatus(snapshot.routeState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"evidence:\")} ${coloredStatus(snapshot.evidenceState ?? \"unknown\")}`),\n\t\tsidebarRule(width, \"TODO\", \"accent\"),\n\t\t...todoSidebarLines(snapshot.todoState, width),\n\t\tsidebarRule(width, \"SESSION\", \"accent\"),\n\t\tsemanticBoxTextLine(width, \"cwd\", snapshot.cwdLabel ?? \"?\", \"end\"),\n\t\tsemanticBoxTextLine(width, \"git\", snapshot.gitBranch ?? \"?\", \"start\"),\n\t\tsidebarRule(width, \"MODEL / CTX\", \"accent\"),\n\t\tsemanticBoxTextLine(width, \"model\", modelStatusLabel(snapshot), \"end\"),\n\t\tsemanticBoxTextLine(width, \"think\", snapshot.thinkingLevel ?? \"off\", \"start\"),\n\t\tboxTextLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"muted\", \"ctx:\")} ${theme.fg(statusColor(snapshot.contextPercent === undefined || snapshot.contextPercent === null ? \"unknown\" : \"ready\"), contextStatusLabel(snapshot))}`,\n\t\t),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"meter:\")} ${contextMeter}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"pulse:\")} ${activitySparkline}`),\n\t\tsidebarRule(width, \"RUNTIME / MCP / SKILLS\", \"accent\"),\n\t\tsemanticBoxTextLine(width, \"headroom\", headroomLabel, \"end\"),\n\t\tsemanticBoxTextLine(width, \"omk\", snapshot.dagOrchestrationState ?? \"unknown\", \"end\"),\n\t\tsemanticBoxTextLine(width, \"sidebar\", snapshot.sidebarState ?? \"unknown\", \"start\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"res:\")} MCP:${mcpCount ?? \"?\"} skills:${skillCount ?? \"?\"}`),\n\t\tsemanticBoxTextLine(width, \"pkg\", packageIntakeLabel, \"end\"),\n\t\tsidebarRule(width, \"CONTROL\", \"accent\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${coloredStatus(snapshot.routeState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"verify:\")} ${coloredStatus(snapshot.evidenceState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"control:\")} ${coloredStatus(snapshot.controlState ?? \"unknown\")}`),\n\t\tboxBottom(width, \"accent\"),\n\t];\n}\n\nfunction semanticBoxTextLine(\n\twidth: number,\n\tlabel: string,\n\tvalue: string,\n\tpreserve: \"start\" | \"middle\" | \"end\",\n): string {\n\tconst prefix = `${theme.fg(\"muted\", `${label}:`)} `;\n\tconst availableWidth = Math.max(0, width - 4 - visibleWidth(prefix));\n\treturn boxTextLine(width, `${prefix}${semanticTruncate(value, availableWidth, preserve)}`);\n}\n\nfunction semanticTruncate(value: string, maxWidth: number, preserve: \"start\" | \"middle\" | \"end\"): string {\n\tif (visibleWidth(value) <= maxWidth) return value;\n\tif (maxWidth <= 0) return \"\";\n\tif (preserve === \"start\") return truncateToWidth(value, maxWidth, \"…\");\n\tconst ellipsis = \"…\";\n\tconst targetWidth = maxWidth - visibleWidth(ellipsis);\n\tif (targetWidth <= 0) return truncateToWidth(ellipsis, maxWidth, \"\");\n\tif (preserve === \"middle\") {\n\t\tconst headWidth = Math.max(1, Math.floor(targetWidth / 2));\n\t\tconst tailWidth = Math.max(1, targetWidth - headWidth);\n\t\treturn `${takeStart(value, headWidth)}${ellipsis}${takeEnd(value, tailWidth)}`;\n\t}\n\treturn `${ellipsis}${takeEnd(value, targetWidth)}`;\n}\n\nfunction takeStart(value: string, maxWidth: number): string {\n\tlet prefix = \"\";\n\tfor (const char of Array.from(value)) {\n\t\tif (visibleWidth(prefix + char) > maxWidth) break;\n\t\tprefix += char;\n\t}\n\treturn prefix;\n}\n\nfunction takeEnd(value: string, maxWidth: number): string {\n\tlet suffix = \"\";\n\tfor (const char of Array.from(value).reverse()) {\n\t\tif (visibleWidth(char + suffix) > maxWidth) break;\n\t\tsuffix = char + suffix;\n\t}\n\treturn suffix;\n}\n\nfunction statusToken(label: string, value: string | undefined): string {\n\tconst state = value ?? \"unknown\";\n\treturn theme.fg(statusColor(state), `${label}:${state.toUpperCase()}`);\n}\n\nfunction coloredStatus(value: string): string {\n\treturn theme.fg(statusColor(value), value);\n}\n\nfunction statusColor(value: string): ThemeColor {\n\tconst normalized = value.toLowerCase();\n\tif ([\"ready\", \"on\", \"active\", \"available\", \"linked\", \"tracking\", \"pinned\"].includes(normalized)) return \"success\";\n\tif ([\"degraded\", \"limited\", \"blocked\", \"off\"].includes(normalized)) return \"warning\";\n\treturn \"muted\";\n}\n\nfunction todoSidebarLines(state: TodoState | undefined, width: number): string[] {\n\tif (!state || state.items.length === 0) {\n\t\treturn [\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"todo:\")} empty`),\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"next:\")} no active todos`),\n\t\t];\n\t}\n\tconst counts = todoSummary(state);\n\tconst next = nextActiveTodo(state);\n\treturn [\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"todo:\")} ${counts.done}/${counts.total} done`),\n\t\tsemanticBoxTextLine(width, \"next\", next?.label ?? \"complete\", \"middle\"),\n\t];\n}\n\nfunction packageIntakeStatusLabel(summary: PiPackageIntakeSummary | undefined): string {\n\tif (!summary) return \"ports:pending\";\n\tconst ready = summary.acceptedNative + summary.acceptedReference + summary.acceptedMeasurement;\n\tconst review = summary.deferred + summary.reject;\n\treturn `ports:${ready}/${summary.total} review:${review} block:${summary.hardForkBlocked}`;\n}\n\nfunction renderExpanded(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tif (width >= CONTROL_PANEL_DECK_MIN_WIDTH) {\n\t\tconst { leftWidth, sidebarWidth } = deckWidths(width);\n\t\tconst resourceLines = [\"\", ...content.onboarding().split(\"\\n\")];\n\t\tconst rightRail = blankSidebarRail(sidebarWidth, resourceLines.length);\n\t\tconst lines = [...renderDeck(content, width, bannerFrame, sparkleMs)];\n\t\tlines.push(...composeColumns(resourceLines, leftWidth, rightRail, sidebarWidth, CONTROL_PANEL_GAP_WIDTH, width));\n\t\treturn lines;\n\t}\n\n\tconst lines = [divider(width, \"OMK//CONTROL PANEL\", \"accent\"), statusLine(content, width)];\n\tif (width >= 32) lines.push(...brandLines(content, width, bannerFrame));\n\tlines.push(divider(width, \"SYSTEM MAP\", \"mdHeading\"));\n\tfor (const instruction of content.expandedInstructions().split(\"\\n\")) lines.push(textLine(width, instruction));\n\tlines.push(divider(width, \"STARTUP LINK\", \"success\"));\n\tfor (const onboardingLine of content.onboarding().split(\"\\n\")) lines.push(textLine(width, onboardingLine, \"dim\"));\n\tlines.push(divider(width, \"END\", \"borderMuted\"));\n\treturn lines;\n}\n\nfunction blankSidebarRail(width: number, lineCount: number): string[] {\n\treturn Array.from({ length: lineCount }, () => boxTextLine(width, \"\"));\n}\n\nfunction statusSnapshot(content: ControlPanelContent): ControlPanelStatusSnapshot {\n\treturn content.statusSnapshot?.() ?? {};\n}\n\nfunction modelStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tif (!snapshot.modelId) return \"no-model\";\n\treturn snapshot.modelProvider ? `${snapshot.modelProvider}/${snapshot.modelId}` : snapshot.modelId;\n}\n\nfunction contextStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst windowTokens = snapshot.contextWindowTokens ?? 0;\n\tconst windowLabel = windowTokens > 0 ? formatTokens(windowTokens) : \"?\";\n\treturn snapshot.contextPercent === null || snapshot.contextPercent === undefined\n\t\t? `?/${windowLabel}`\n\t\t: `${snapshot.contextPercent.toFixed(1)}%/${windowLabel}`;\n}\n\nfunction contextMeterLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst percent = normalizePercent(snapshot.contextPercent);\n\tif (percent === undefined) {\n\t\treturn `${theme.fg(\"borderMuted\", \"░\".repeat(CONTEXT_METER_CELLS))} ${theme.fg(\"muted\", \"??%\")}`;\n\t}\n\tconst filled = Math.max(0, Math.min(CONTEXT_METER_CELLS, Math.round((percent / 100) * CONTEXT_METER_CELLS)));\n\tconst color: ThemeColor = percent >= 85 ? \"warning\" : percent >= 65 ? \"mdCode\" : \"success\";\n\treturn `${theme.fg(color, \"█\".repeat(filled))}${theme.fg(\"borderMuted\", \"░\".repeat(CONTEXT_METER_CELLS - filled))} ${theme.fg(color, `${Math.round(percent)}%`)}`;\n}\n\nfunction activitySparklineLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst seed = hashSnapshot(snapshot);\n\tconst glyphs = Array.from(SPARKLINE_GLYPHS);\n\tconst parts: string[] = [];\n\tfor (let index = 0; index < SPARKLINE_WIDTH; index++) {\n\t\tconst glyph = glyphs[(seed + index * 5 + index * index) % glyphs.length] ?? \"▁\";\n\t\tconst color: ThemeColor = index % 5 === 0 ? \"accent\" : index % 3 === 0 ? \"warning\" : \"success\";\n\t\tparts.push(theme.fg(color, glyph));\n\t}\n\treturn parts.join(\"\");\n}\n\nfunction normalizePercent(value: number | null | undefined): number | undefined {\n\tif (value === null || value === undefined || !Number.isFinite(value)) return undefined;\n\tif (value < 0) return 0;\n\tif (value > 100) return 100;\n\treturn value;\n}\n\nfunction hashSnapshot(snapshot: ControlPanelStatusSnapshot): number {\n\tconst source = [\n\t\tsnapshot.modelProvider,\n\t\tsnapshot.modelId,\n\t\tsnapshot.contextPercent?.toFixed(1),\n\t\tsnapshot.contextTokens,\n\t\tsnapshot.contextWindowTokens,\n\t\tsnapshot.cwdLabel,\n\t\tsnapshot.gitBranch,\n\t].join(\"|\");\n\tlet hash = 2166136261;\n\tfor (let index = 0; index < source.length; index++) {\n\t\thash ^= source.charCodeAt(index);\n\t\thash = Math.imul(hash, 16777619);\n\t}\n\treturn hash >>> 0;\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nfunction sidebarTabs(width: number): string {\n\treturn boxTextLine(\n\t\twidth,\n\t\tfitLine(\n\t\t\t`${theme.bold(theme.fg(\"accent\", \"1:CONTROL\"))} ${theme.fg(\"muted\", \"2:HISTORY\")}`,\n\t\t\tMath.max(0, width - 4),\n\t\t),\n\t);\n}\n\nfunction controlStripLine(content: ControlPanelContent, width: number): string {\n\treturn centerLine(\n\t\twidth,\n\t\t`${theme.bold(theme.fg(\"accent\", \"OMK://CONTROL READ\"))} ${theme.fg(\"accent\", \"route/verify/loop/control\")} · ${content.compactInstructions()}`,\n\t);\n}\n\nfunction brandLines(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tconst art = bannerFrame ?? CONTROL_PANEL_ASCII_ART;\n\tif (bannerFrame) return art.map((line) => textLine(width, line));\n\tconst leftWidth = Math.max(...art.map((line) => visibleWidth(line)));\n\tconst minWideWidth = visibleWidth(\"| \") + leftWidth + visibleWidth(\" | \") + META_WIDTH;\n\tif (width < minWideWidth) return art.map((line) => textLine(width, theme.fg(\"accent\", line)));\n\tconst snapshot = statusSnapshot(content);\n\treturn art.map((line, index) =>\n\t\ttextLine(\n\t\t\twidth,\n\t\t\t`${fitLine(theme.fg(\"accent\", line), leftWidth)}${theme.fg(\"borderMuted\", \" | \")}${metadataLines(snapshot)[index] ?? \"\"}`,\n\t\t),\n\t);\n}\n\nfunction metadataLines(snapshot: ControlPanelStatusSnapshot): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\treturn [\n\t\t`${theme.fg(\"mdCode\", \"PANEL\")} ${coloredStatus(snapshot.runtimeState ?? \"unknown\")}`,\n\t\t`${theme.fg(\"mdCode\", \"THEME\")} ${theme.fg(\"accent\", themeName)}`,\n\t\t`${theme.fg(\"mdCode\", \"STARTUP\")} ${coloredStatus(snapshot.startupState ?? \"unknown\")}`,\n\t\t`${theme.fg(\"mdCode\", \"LINK\")} ${coloredStatus(snapshot.linkState ?? \"unknown\")}`,\n\t];\n}\n\nfunction statusLine(content: ControlPanelContent, width: number): string {\n\tconst snapshot = statusSnapshot(content);\n\tconst segments = [\n\t\ttheme.bold(theme.fg(\"accent\", `${content.appName.toUpperCase()} v${content.version}`)),\n\t\tstatusToken(\"CORE\", snapshot.runtimeState),\n\t\tstatusToken(\"ANSI\", snapshot.ansiColorState),\n\t\tstatusToken(\"STARTUP\", snapshot.startupState),\n\t\tstatusToken(\"LINK\", snapshot.linkState),\n\t\ttheme.fg(\"muted\", \"THEME:LIVE\"),\n\t];\n\treturn textLine(width, segments.join(theme.fg(\"borderMuted\", \" | \")));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"control-panel-layout.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAkB,KAAK,SAAS,EAA0B,MAAM,6BAA6B,CAAC;AAgBrG,eAAO,MAAM,uBAAuB,UAQnC,CAAC;AAEF,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,eAAO,MAAM,4BAA4B,QAAsC,CAAC;AAChF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAKzC,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,MAAM,CAAC;IAClC,oBAAoB,EAAE,MAAM,MAAM,CAAC;IACnC,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,UAAU,EAAE,MAAM,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,0BAA0B,CAAC;CAClD;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,sBAAsB,CAAC;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EAAE,EACtB,SAAS,SAAI,GACX,MAAM,EAAE,CAKV;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAGjG","sourcesContent":["import { truncateToWidth, visibleWidth } from \"omk-tui\";\nimport type { PiPackageIntakeSummary } from \"../../../core/pi-package-intake.ts\";\nimport { nextActiveTodo, type TodoState, summary as todoSummary } from \"../../../core/todo-state.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport {\n\tboxBottom,\n\tboxCenteredLine,\n\tboxTextLine,\n\tboxTop,\n\tcenterLine,\n\tcomposeColumns,\n\tdivider,\n\tfitLine,\n\tsidebarRule,\n\ttextLine,\n} from \"./control-panel-box.ts\";\nimport { SPARKLE_ROW_BOTTOM, SPARKLE_ROW_TOP, sparkleRow } from \"./control-panel-sparkles.ts\";\n\nexport const CONTROL_PANEL_ASCII_ART = [\n\t\" ███████ ██ ██ ██ ██\",\n\t\"██ ██ ███ ███ ██ ██ \",\n\t\"██ ██ ████ ████ ██ ██ \",\n\t\"██ ██ ██ ██ ██ ██ █████ \",\n\t\"██ ██ ██ ███ ██ ██ ██ \",\n\t\"██ ██ ██ █ ██ ██ ██ \",\n\t\" ███████ ██ ██ ██ ██\",\n];\nconst META_WIDTH = 31;\nexport const CONTROL_PANEL_OVERLAY_MIN_WIDTH = 112;\nexport const CONTROL_PANEL_DECK_MIN_WIDTH = CONTROL_PANEL_OVERLAY_MIN_WIDTH + 1;\nexport const CONTROL_PANEL_SIDEBAR_WIDTH = 38;\nexport const CONTROL_PANEL_GAP_WIDTH = 2;\nconst CONTEXT_METER_CELLS = 12;\nconst SPARKLINE_WIDTH = 16;\nconst SPARKLINE_GLYPHS = \"▁▂▃▄▅▆▇█\";\n\nexport interface ControlPanelContent {\n\tappName: string;\n\tversion: string;\n\tcompactInstructions: () => string;\n\texpandedInstructions: () => string;\n\tcompactOnboarding: () => string;\n\tonboarding: () => string;\n\tstatusSnapshot?: () => ControlPanelStatusSnapshot;\n}\n\nexport interface ControlPanelStatusSnapshot {\n\treadonly modelId?: string;\n\treadonly modelProvider?: string;\n\treadonly thinkingLevel?: string;\n\treadonly contextPercent?: number | null;\n\treadonly contextWindowTokens?: number;\n\treadonly contextTokens?: number | null;\n\treadonly headroomStatus?: string;\n\treadonly optimizerPolicy?: string;\n\treadonly skillCount?: number;\n\treadonly mcpCount?: number;\n\treadonly packageIntake?: PiPackageIntakeSummary;\n\treadonly cwdLabel?: string;\n\treadonly gitBranch?: string | null;\n\treadonly todoState?: TodoState;\n\treadonly runtimeState?: string;\n\treadonly routeState?: string;\n\treadonly evidenceState?: string;\n\treadonly controlState?: string;\n\treadonly dagOrchestrationState?: string;\n\treadonly ansiColorState?: string;\n\treadonly startupState?: string;\n\treadonly linkState?: string;\n\treadonly sidebarState?: string;\n}\n\nexport function renderControlPanelLayout(\n\tcontent: ControlPanelContent,\n\texpanded: boolean,\n\twidth: number,\n\tbannerFrame?: string[],\n\tsparkleMs = 0,\n): string[] {\n\tif (width <= 0) return [];\n\treturn expanded\n\t\t? renderExpanded(content, width, bannerFrame, sparkleMs)\n\t\t: renderCompact(content, width, bannerFrame, sparkleMs);\n}\n\nexport function renderControlPanelRightPane(content: ControlPanelContent, width: number): string[] {\n\tif (width <= 0) return [];\n\treturn sidebarPanel(content, width);\n}\n\nfunction renderCompact(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tif (width >= CONTROL_PANEL_DECK_MIN_WIDTH) {\n\t\tconst deck = renderDeck(content, width, bannerFrame, sparkleMs);\n\t\tif (deck.length > 0) return deck;\n\t}\n\treturn [\n\t\tdivider(width, \"OMK//CONTROL PANEL\", \"accent\"),\n\t\tstatusLine(content, width),\n\t\ttextLine(width, content.compactInstructions()),\n\t\ttextLine(width, content.compactOnboarding(), \"dim\"),\n\t];\n}\n\nfunction renderDeck(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tconst { leftWidth, sidebarWidth } = deckWidths(width);\n\tif (leftWidth < 72) return [];\n\tconst lines = composeColumns(\n\t\theroPanel(content, leftWidth, bannerFrame, sparkleMs),\n\t\tleftWidth,\n\t\tsidebarPanel(content, sidebarWidth),\n\t\tsidebarWidth,\n\t\tCONTROL_PANEL_GAP_WIDTH,\n\t\twidth,\n\t);\n\tlines.push(controlStripLine(content, width));\n\tlines.push(centerLine(width, content.compactOnboarding(), \"dim\"));\n\treturn lines;\n}\n\nfunction deckWidths(width: number): { leftWidth: number; sidebarWidth: number } {\n\tconst sidebarWidth = Math.min(CONTROL_PANEL_SIDEBAR_WIDTH, Math.max(34, Math.floor(width * 0.28)));\n\treturn { leftWidth: width - CONTROL_PANEL_GAP_WIDTH - sidebarWidth, sidebarWidth };\n}\n\nfunction heroPanel(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst innerWidth = Math.max(0, width - 4);\n\treturn [\n\t\tboxTop(width, `omk v${content.version} · OMK://CONTROL`),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK\"))),\n\t\tboxCenteredLine(width, theme.fg(\"muted\", \"route · verify · loop · control\")),\n\t\tboxTextLine(width, sparkleRow(innerWidth, SPARKLE_ROW_TOP, sparkleMs)),\n\t\t...(bannerFrame ?? CONTROL_PANEL_ASCII_ART).map((line, index) => {\n\t\t\tif (bannerFrame) return boxCenteredLine(width, line);\n\t\t\tconst color: ThemeColor = index < 2 ? \"mdCode\" : index < 4 ? \"accent\" : index < 6 ? \"success\" : \"warning\";\n\t\t\treturn boxCenteredLine(width, theme.fg(color, line));\n\t\t}),\n\t\tboxTextLine(width, sparkleRow(innerWidth, SPARKLE_ROW_BOTTOM, sparkleMs)),\n\t\tboxCenteredLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"warning\", \"●\")} ${theme.bold(\"OMK\")} / ${theme.fg(\"success\", heroModelLabel(snapshot))}`,\n\t\t),\n\t\tboxCenteredLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"mdCode\", \"◇\")} omk-control · ${theme.fg(\"accent\", \"route\")} · ${theme.fg(\"warning\", \"verify\")} · ${theme.fg(\"success\", \"loop\")} · ${theme.fg(\"mdCode\", \"control\")}`,\n\t\t),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction heroModelLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tif (!snapshot.modelId) return \"no-model\";\n\tconst think = snapshot.thinkingLevel;\n\treturn think && think !== \"off\" ? `${snapshot.modelId}:${think}` : snapshot.modelId;\n}\n\nfunction sidebarPanel(content: ControlPanelContent, width: number): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst headroomLabel = snapshot.headroomStatus ?? snapshot.optimizerPolicy ?? \"unknown\";\n\tconst mcpCount = snapshot.mcpCount;\n\tconst skillCount = snapshot.skillCount;\n\tconst packageIntakeLabel = packageIntakeStatusLabel(snapshot.packageIntake);\n\tconst contextMeter = contextMeterLabel(snapshot);\n\tconst activitySparkline = activitySparklineLabel(snapshot);\n\treturn [\n\t\tsidebarTabs(width),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK://CONTROL\"))),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"warning\", \"CYBERPUNK OPS CORE\"))),\n\t\tboxCenteredLine(width, `${theme.fg(\"mdCode\", \"MATRIX RAIN\")} // ${theme.fg(\"success\", \"NEON GRID ONLINE\")}`),\n\t\tboxCenteredLine(width, theme.fg(\"text\", \"NIGHT-CITY-MATRIX-V3\")),\n\t\tsidebarRule(width, \"STATUS\", \"accent\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"state:\")} ${coloredStatus(snapshot.runtimeState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${coloredStatus(snapshot.routeState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"evidence:\")} ${coloredStatus(snapshot.evidenceState ?? \"unknown\")}`),\n\t\tsidebarRule(width, \"TODO\", \"accent\"),\n\t\t...todoSidebarLines(snapshot.todoState, width),\n\t\tsidebarRule(width, \"SESSION\", \"accent\"),\n\t\tsemanticBoxTextLine(width, \"cwd\", snapshot.cwdLabel ?? \"?\", \"end\"),\n\t\tsemanticBoxTextLine(width, \"git\", snapshot.gitBranch ?? \"?\", \"start\"),\n\t\tsidebarRule(width, \"MODEL / CTX\", \"accent\"),\n\t\tsemanticBoxTextLine(width, \"model\", modelStatusLabel(snapshot), \"end\"),\n\t\tsemanticBoxTextLine(width, \"think\", snapshot.thinkingLevel ?? \"off\", \"start\"),\n\t\tboxTextLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"muted\", \"ctx:\")} ${theme.fg(statusColor(snapshot.contextPercent === undefined || snapshot.contextPercent === null ? \"unknown\" : \"ready\"), contextStatusLabel(snapshot))}`,\n\t\t),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"meter:\")} ${contextMeter}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"pulse:\")} ${activitySparkline}`),\n\t\tsidebarRule(width, \"RUNTIME / MCP / SKILLS\", \"accent\"),\n\t\tsemanticBoxTextLine(width, \"headroom\", headroomLabel, \"end\"),\n\t\tsemanticBoxTextLine(width, \"omk\", snapshot.dagOrchestrationState ?? \"unknown\", \"end\"),\n\t\tsemanticBoxTextLine(width, \"sidebar\", snapshot.sidebarState ?? \"unknown\", \"start\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"res:\")} MCP:${mcpCount ?? \"?\"} skills:${skillCount ?? \"?\"}`),\n\t\tsemanticBoxTextLine(width, \"pkg\", packageIntakeLabel, \"end\"),\n\t\tsidebarRule(width, \"CONTROL\", \"accent\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${coloredStatus(snapshot.routeState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"verify:\")} ${coloredStatus(snapshot.evidenceState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"control:\")} ${coloredStatus(snapshot.controlState ?? \"unknown\")}`),\n\t\tboxBottom(width, \"accent\"),\n\t];\n}\n\nfunction semanticBoxTextLine(\n\twidth: number,\n\tlabel: string,\n\tvalue: string,\n\tpreserve: \"start\" | \"middle\" | \"end\",\n): string {\n\tconst prefix = `${theme.fg(\"muted\", `${label}:`)} `;\n\tconst availableWidth = Math.max(0, width - 4 - visibleWidth(prefix));\n\treturn boxTextLine(width, `${prefix}${semanticTruncate(value, availableWidth, preserve)}`);\n}\n\nfunction semanticTruncate(value: string, maxWidth: number, preserve: \"start\" | \"middle\" | \"end\"): string {\n\tif (visibleWidth(value) <= maxWidth) return value;\n\tif (maxWidth <= 0) return \"\";\n\tif (preserve === \"start\") return truncateToWidth(value, maxWidth, \"…\");\n\tconst ellipsis = \"…\";\n\tconst targetWidth = maxWidth - visibleWidth(ellipsis);\n\tif (targetWidth <= 0) return truncateToWidth(ellipsis, maxWidth, \"\");\n\tif (preserve === \"middle\") {\n\t\tconst headWidth = Math.max(1, Math.floor(targetWidth / 2));\n\t\tconst tailWidth = Math.max(1, targetWidth - headWidth);\n\t\treturn `${takeStart(value, headWidth)}${ellipsis}${takeEnd(value, tailWidth)}`;\n\t}\n\treturn `${ellipsis}${takeEnd(value, targetWidth)}`;\n}\n\nfunction takeStart(value: string, maxWidth: number): string {\n\tlet prefix = \"\";\n\tfor (const char of Array.from(value)) {\n\t\tif (visibleWidth(prefix + char) > maxWidth) break;\n\t\tprefix += char;\n\t}\n\treturn prefix;\n}\n\nfunction takeEnd(value: string, maxWidth: number): string {\n\tlet suffix = \"\";\n\tfor (const char of Array.from(value).reverse()) {\n\t\tif (visibleWidth(char + suffix) > maxWidth) break;\n\t\tsuffix = char + suffix;\n\t}\n\treturn suffix;\n}\n\nfunction statusToken(label: string, value: string | undefined): string {\n\tconst state = value ?? \"unknown\";\n\treturn theme.fg(statusColor(state), `${label}:${state.toUpperCase()}`);\n}\n\nfunction coloredStatus(value: string): string {\n\treturn theme.fg(statusColor(value), value);\n}\n\nfunction statusColor(value: string): ThemeColor {\n\tconst normalized = value.toLowerCase();\n\tif ([\"ready\", \"on\", \"active\", \"available\", \"linked\", \"tracking\", \"pinned\"].includes(normalized)) return \"success\";\n\tif ([\"degraded\", \"limited\", \"blocked\", \"off\"].includes(normalized)) return \"warning\";\n\treturn \"muted\";\n}\n\nfunction todoSidebarLines(state: TodoState | undefined, width: number): string[] {\n\tif (!state || state.items.length === 0) {\n\t\treturn [\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"todo:\")} empty`),\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"next:\")} no active todos`),\n\t\t];\n\t}\n\tconst counts = todoSummary(state);\n\tconst next = nextActiveTodo(state);\n\treturn [\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"todo:\")} ${counts.done}/${counts.total} done`),\n\t\tsemanticBoxTextLine(width, \"next\", next?.label ?? \"complete\", \"middle\"),\n\t];\n}\n\nfunction packageIntakeStatusLabel(summary: PiPackageIntakeSummary | undefined): string {\n\tif (!summary) return \"ports:pending\";\n\tconst ready =\n\t\tsummary.acceptedNative + summary.acceptedReference + summary.acceptedMeasurement + summary.acceptedAdvisory;\n\tconst review = summary.deferred + summary.reject;\n\treturn `ports:${ready}/${summary.total} review:${review} block:${summary.hardForkBlocked}`;\n}\n\nfunction renderExpanded(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tif (width >= CONTROL_PANEL_DECK_MIN_WIDTH) {\n\t\tconst { leftWidth, sidebarWidth } = deckWidths(width);\n\t\tconst resourceLines = [\"\", ...content.onboarding().split(\"\\n\")];\n\t\tconst rightRail = blankSidebarRail(sidebarWidth, resourceLines.length);\n\t\tconst lines = [...renderDeck(content, width, bannerFrame, sparkleMs)];\n\t\tlines.push(...composeColumns(resourceLines, leftWidth, rightRail, sidebarWidth, CONTROL_PANEL_GAP_WIDTH, width));\n\t\treturn lines;\n\t}\n\n\tconst lines = [divider(width, \"OMK//CONTROL PANEL\", \"accent\"), statusLine(content, width)];\n\tif (width >= 32) lines.push(...brandLines(content, width, bannerFrame));\n\tlines.push(divider(width, \"SYSTEM MAP\", \"mdHeading\"));\n\tfor (const instruction of content.expandedInstructions().split(\"\\n\")) lines.push(textLine(width, instruction));\n\tlines.push(divider(width, \"STARTUP LINK\", \"success\"));\n\tfor (const onboardingLine of content.onboarding().split(\"\\n\")) lines.push(textLine(width, onboardingLine, \"dim\"));\n\tlines.push(divider(width, \"END\", \"borderMuted\"));\n\treturn lines;\n}\n\nfunction blankSidebarRail(width: number, lineCount: number): string[] {\n\treturn Array.from({ length: lineCount }, () => boxTextLine(width, \"\"));\n}\n\nfunction statusSnapshot(content: ControlPanelContent): ControlPanelStatusSnapshot {\n\treturn content.statusSnapshot?.() ?? {};\n}\n\nfunction modelStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tif (!snapshot.modelId) return \"no-model\";\n\treturn snapshot.modelProvider ? `${snapshot.modelProvider}/${snapshot.modelId}` : snapshot.modelId;\n}\n\nfunction contextStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst windowTokens = snapshot.contextWindowTokens ?? 0;\n\tconst windowLabel = windowTokens > 0 ? formatTokens(windowTokens) : \"?\";\n\treturn snapshot.contextPercent === null || snapshot.contextPercent === undefined\n\t\t? `?/${windowLabel}`\n\t\t: `${snapshot.contextPercent.toFixed(1)}%/${windowLabel}`;\n}\n\nfunction contextMeterLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst percent = normalizePercent(snapshot.contextPercent);\n\tif (percent === undefined) {\n\t\treturn `${theme.fg(\"borderMuted\", \"░\".repeat(CONTEXT_METER_CELLS))} ${theme.fg(\"muted\", \"??%\")}`;\n\t}\n\tconst filled = Math.max(0, Math.min(CONTEXT_METER_CELLS, Math.round((percent / 100) * CONTEXT_METER_CELLS)));\n\tconst color: ThemeColor = percent >= 85 ? \"warning\" : percent >= 65 ? \"mdCode\" : \"success\";\n\treturn `${theme.fg(color, \"█\".repeat(filled))}${theme.fg(\"borderMuted\", \"░\".repeat(CONTEXT_METER_CELLS - filled))} ${theme.fg(color, `${Math.round(percent)}%`)}`;\n}\n\nfunction activitySparklineLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst seed = hashSnapshot(snapshot);\n\tconst glyphs = Array.from(SPARKLINE_GLYPHS);\n\tconst parts: string[] = [];\n\tfor (let index = 0; index < SPARKLINE_WIDTH; index++) {\n\t\tconst glyph = glyphs[(seed + index * 5 + index * index) % glyphs.length] ?? \"▁\";\n\t\tconst color: ThemeColor = index % 5 === 0 ? \"accent\" : index % 3 === 0 ? \"warning\" : \"success\";\n\t\tparts.push(theme.fg(color, glyph));\n\t}\n\treturn parts.join(\"\");\n}\n\nfunction normalizePercent(value: number | null | undefined): number | undefined {\n\tif (value === null || value === undefined || !Number.isFinite(value)) return undefined;\n\tif (value < 0) return 0;\n\tif (value > 100) return 100;\n\treturn value;\n}\n\nfunction hashSnapshot(snapshot: ControlPanelStatusSnapshot): number {\n\tconst source = [\n\t\tsnapshot.modelProvider,\n\t\tsnapshot.modelId,\n\t\tsnapshot.contextPercent?.toFixed(1),\n\t\tsnapshot.contextTokens,\n\t\tsnapshot.contextWindowTokens,\n\t\tsnapshot.cwdLabel,\n\t\tsnapshot.gitBranch,\n\t].join(\"|\");\n\tlet hash = 2166136261;\n\tfor (let index = 0; index < source.length; index++) {\n\t\thash ^= source.charCodeAt(index);\n\t\thash = Math.imul(hash, 16777619);\n\t}\n\treturn hash >>> 0;\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nfunction sidebarTabs(width: number): string {\n\treturn boxTextLine(\n\t\twidth,\n\t\tfitLine(\n\t\t\t`${theme.bold(theme.fg(\"accent\", \"1:CONTROL\"))} ${theme.fg(\"muted\", \"2:HISTORY\")}`,\n\t\t\tMath.max(0, width - 4),\n\t\t),\n\t);\n}\n\nfunction controlStripLine(content: ControlPanelContent, width: number): string {\n\treturn centerLine(\n\t\twidth,\n\t\t`${theme.bold(theme.fg(\"accent\", \"OMK://CONTROL READ\"))} ${theme.fg(\"accent\", \"route/verify/loop/control\")} · ${content.compactInstructions()}`,\n\t);\n}\n\nfunction brandLines(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tconst art = bannerFrame ?? CONTROL_PANEL_ASCII_ART;\n\tif (bannerFrame) return art.map((line) => textLine(width, line));\n\tconst leftWidth = Math.max(...art.map((line) => visibleWidth(line)));\n\tconst minWideWidth = visibleWidth(\"| \") + leftWidth + visibleWidth(\" | \") + META_WIDTH;\n\tif (width < minWideWidth) return art.map((line) => textLine(width, theme.fg(\"accent\", line)));\n\tconst snapshot = statusSnapshot(content);\n\treturn art.map((line, index) =>\n\t\ttextLine(\n\t\t\twidth,\n\t\t\t`${fitLine(theme.fg(\"accent\", line), leftWidth)}${theme.fg(\"borderMuted\", \" | \")}${metadataLines(snapshot)[index] ?? \"\"}`,\n\t\t),\n\t);\n}\n\nfunction metadataLines(snapshot: ControlPanelStatusSnapshot): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\treturn [\n\t\t`${theme.fg(\"mdCode\", \"PANEL\")} ${coloredStatus(snapshot.runtimeState ?? \"unknown\")}`,\n\t\t`${theme.fg(\"mdCode\", \"THEME\")} ${theme.fg(\"accent\", themeName)}`,\n\t\t`${theme.fg(\"mdCode\", \"STARTUP\")} ${coloredStatus(snapshot.startupState ?? \"unknown\")}`,\n\t\t`${theme.fg(\"mdCode\", \"LINK\")} ${coloredStatus(snapshot.linkState ?? \"unknown\")}`,\n\t];\n}\n\nfunction statusLine(content: ControlPanelContent, width: number): string {\n\tconst snapshot = statusSnapshot(content);\n\tconst segments = [\n\t\ttheme.bold(theme.fg(\"accent\", `${content.appName.toUpperCase()} v${content.version}`)),\n\t\tstatusToken(\"CORE\", snapshot.runtimeState),\n\t\tstatusToken(\"ANSI\", snapshot.ansiColorState),\n\t\tstatusToken(\"STARTUP\", snapshot.startupState),\n\t\tstatusToken(\"LINK\", snapshot.linkState),\n\t\ttheme.fg(\"muted\", \"THEME:LIVE\"),\n\t];\n\treturn textLine(width, segments.join(theme.fg(\"borderMuted\", \" | \")));\n}\n"]}
|
|
@@ -199,7 +199,7 @@ function todoSidebarLines(state, width) {
|
|
|
199
199
|
function packageIntakeStatusLabel(summary) {
|
|
200
200
|
if (!summary)
|
|
201
201
|
return "ports:pending";
|
|
202
|
-
const ready = summary.acceptedNative + summary.acceptedReference + summary.acceptedMeasurement;
|
|
202
|
+
const ready = summary.acceptedNative + summary.acceptedReference + summary.acceptedMeasurement + summary.acceptedAdvisory;
|
|
203
203
|
const review = summary.deferred + summary.reject;
|
|
204
204
|
return `ports:${ready}/${summary.total} review:${review} block:${summary.hardForkBlocked}`;
|
|
205
205
|
}
|