@kal-elsam/kairo-runtime 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/package.json +1 -1
- package/scripts/cockpit-smoke.mjs +1 -1
- package/src/cli.js +59 -0
- package/src/global/ink/cockpit-controller.js +10 -0
- package/src/global/ink/cockpit-focus.js +18 -3
- package/src/global/ink/cockpit-models.js +8 -1
- package/src/global/ink/cockpit-reviews.js +62 -0
- package/src/global/ink/cockpit-runs.js +11 -2
- package/src/global/ink/cockpit-views.js +19 -2
- package/src/global/ink/orchestrator-app.js +23 -3
- package/src/global/ink/orchestrator-state.js +2 -0
- package/src/global/ink/use-orchestrator-data.js +30 -0
- package/src/global/paths.js +1 -0
- package/src/global/runtime/execution-adapters/codex.js +2 -1
- package/src/global/runtime/execution-adapters/create-execution-adapter.js +1 -0
- package/src/global/runtime/execution-adapters/index.js +1 -0
- package/src/global/runtime/execution-adapters/pi.js +2 -1
- package/src/global/runtime/review/index.js +37 -0
- package/src/global/runtime/review/review-cli.js +150 -0
- package/src/global/runtime/review/review-codex.js +132 -0
- package/src/global/runtime/review/review-exec.js +136 -0
- package/src/global/runtime/review/review-fs.js +52 -0
- package/src/global/runtime/review/review-git.js +212 -0
- package/src/global/runtime/review/review-patch.js +122 -0
- package/src/global/runtime/review/review-pi.js +168 -0
- package/src/global/runtime/review/review-receipts.js +128 -0
- package/src/global/runtime/review/review-runner.js +119 -0
- package/src/global/runtime/review/review-types.js +108 -0
- package/src/global/runtime/review/review-validate.js +280 -0
- package/src/global/runtime/write-atomic-json.js +24 -20
package/README.md
CHANGED
|
@@ -301,6 +301,30 @@ Launches `pi --mode json --no-session` (optional `--model`). `read-only` maps to
|
|
|
301
301
|
to `--approve`). Custom `PI_CODING_AGENT_DIR` blocks config writes in 0.6.0 but does
|
|
302
302
|
not block runtime. Kairo does not install Pi or assert subscription/entitlement.
|
|
303
303
|
|
|
304
|
+
### Bounded review (Codex / Pi)
|
|
305
|
+
|
|
306
|
+
Read-only native review against a Git snapshot. Never mutates the repo, never
|
|
307
|
+
auto-fixes, and never persists prompts, diffs, or transcripts. Receipts land under
|
|
308
|
+
`~/.harness/reviews/<reviewId>/receipt.json` (secret-free). Only Codex and Pi are
|
|
309
|
+
`reviewCompatible` in 0.7.0; Cursor / Claude / OpenCode report `false` until audited.
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
kairo review --agent codex
|
|
313
|
+
kairo review --agent pi --base main
|
|
314
|
+
kairo review --agent codex --commit <sha>
|
|
315
|
+
kairo review --agent codex --fail-on high --json
|
|
316
|
+
kairo reviews list [--limit N] [--json]
|
|
317
|
+
kairo reviews show <reviewId> [--json]
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`--agent` is always required. Scope is working-tree by default; use `--base` or
|
|
321
|
+
`--commit` (mutually exclusive). Private paths need `--include-private` plus TTY
|
|
322
|
+
confirm or `--yes`/`--confirm`. Exit codes: `0` ok, `1` severity threshold,
|
|
323
|
+
`2` operational/stale/invalid/cancel.
|
|
324
|
+
|
|
325
|
+
Cockpit: **Runs → Reviews** lists receipts and opens a read-only detail view
|
|
326
|
+
(no launch from Cockpit in v1).
|
|
327
|
+
|
|
304
328
|
Primary governance flow:
|
|
305
329
|
|
|
306
330
|
```txt
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kal-elsam/kairo-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Kairo Runtime — local agent operating system for Codex, Cursor, Claude, Pi, Engram, and Graphify.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/Kal-elSam/harness#readme",
|
|
@@ -26,7 +26,7 @@ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
26
26
|
const require = createRequire(import.meta.url);
|
|
27
27
|
const pkg = require(join(root, "package.json"));
|
|
28
28
|
|
|
29
|
-
assert.equal(pkg.version, "0.
|
|
29
|
+
assert.equal(pkg.version, "0.7.0");
|
|
30
30
|
assert.ok(pkg.dependencies["ansi-escapes"]);
|
|
31
31
|
|
|
32
32
|
assert.equal(resolveLayoutMode({ columns: 120, rows: 40 }), LAYOUT_MODES.WIDE);
|
package/src/cli.js
CHANGED
|
@@ -38,6 +38,7 @@ import { runWorkspaceDetect, runWorkspaceDoctor, runWorkspaceInit, runWorkspaceU
|
|
|
38
38
|
import { runOrchestratorDiagnostics, runOrchestratorShell } from "./global/orchestrator.js";
|
|
39
39
|
import { runIntelligenceCli } from "./global/intelligence-cli.js";
|
|
40
40
|
import { runGlobalRun, runGlobalRuns } from "./global/runtime/run-cli.js";
|
|
41
|
+
import { runGlobalReview, runGlobalReviews } from "./global/runtime/review/review-cli.js";
|
|
41
42
|
import {
|
|
42
43
|
LEGACY_PACKAGE_NAME,
|
|
43
44
|
PACKAGE_NAME,
|
|
@@ -111,6 +112,12 @@ export async function runCli(argv) {
|
|
|
111
112
|
case "runs":
|
|
112
113
|
await runGlobalRuns(optionsWithPolicy, packageManifest);
|
|
113
114
|
return;
|
|
115
|
+
case "review":
|
|
116
|
+
await runGlobalReview(optionsWithPolicy, packageManifest);
|
|
117
|
+
return;
|
|
118
|
+
case "reviews":
|
|
119
|
+
await runGlobalReviews(optionsWithPolicy, packageManifest);
|
|
120
|
+
return;
|
|
114
121
|
case "intelligence":
|
|
115
122
|
await runIntelligenceCli(optionsWithPolicy, packageManifest);
|
|
116
123
|
return;
|
|
@@ -371,6 +378,11 @@ export function parseArgs(argv) {
|
|
|
371
378
|
intelligencePaths: [],
|
|
372
379
|
runsAction: null,
|
|
373
380
|
runId: null,
|
|
381
|
+
reviewId: null,
|
|
382
|
+
reviewsAction: null,
|
|
383
|
+
base: null,
|
|
384
|
+
commit: null,
|
|
385
|
+
failOn: null,
|
|
374
386
|
agent: null,
|
|
375
387
|
task: null,
|
|
376
388
|
model: null,
|
|
@@ -401,6 +413,10 @@ export function parseArgs(argv) {
|
|
|
401
413
|
parseRunsAction(args, options);
|
|
402
414
|
}
|
|
403
415
|
|
|
416
|
+
if (command === "reviews") {
|
|
417
|
+
parseReviewsAction(args, options);
|
|
418
|
+
}
|
|
419
|
+
|
|
404
420
|
if (command === "intelligence") {
|
|
405
421
|
parseIntelligenceAction(args, options);
|
|
406
422
|
}
|
|
@@ -495,6 +511,15 @@ export function parseArgs(argv) {
|
|
|
495
511
|
else if (arg.startsWith("--timeout=")) options.timeoutMs = parsePositiveInt(arg.slice("--timeout=".length), "timeout") * 1000;
|
|
496
512
|
else if (arg === "--include-private") options.includePrivate = true;
|
|
497
513
|
else if (arg === "--cloud-consent") options.cloudConsent = true;
|
|
514
|
+
else if (arg === "--base") options.base = requireFlagValue("--base", args[++index]);
|
|
515
|
+
else if (arg.startsWith("--base=")) options.base = requireFlagValue("--base", arg.slice("--base=".length));
|
|
516
|
+
else if (arg === "--commit") options.commit = requireFlagValue("--commit", args[++index]);
|
|
517
|
+
else if (arg.startsWith("--commit=")) {
|
|
518
|
+
options.commit = requireFlagValue("--commit", arg.slice("--commit=".length));
|
|
519
|
+
} else if (arg === "--fail-on") options.failOn = requireFlagValue("--fail-on", args[++index]);
|
|
520
|
+
else if (arg.startsWith("--fail-on=")) {
|
|
521
|
+
options.failOn = requireFlagValue("--fail-on", arg.slice("--fail-on=".length));
|
|
522
|
+
}
|
|
498
523
|
else if (arg === "--help" || arg === "-h") options.help = true;
|
|
499
524
|
else if (arg === "--version" || arg === "-v") options.version = true;
|
|
500
525
|
else throw new Error(`Unknown option "${arg}".`);
|
|
@@ -624,6 +649,26 @@ function parseRunsAction(args, options) {
|
|
|
624
649
|
}
|
|
625
650
|
}
|
|
626
651
|
|
|
652
|
+
function parseReviewsAction(args, options) {
|
|
653
|
+
const action = args[0];
|
|
654
|
+
if (!action || action.startsWith("-")) {
|
|
655
|
+
options.reviewsAction = "list";
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (!new Set(["list", "show"]).has(action)) {
|
|
659
|
+
throw new Error(`Unknown reviews action "${action}". Use list or show.`);
|
|
660
|
+
}
|
|
661
|
+
args.shift();
|
|
662
|
+
options.reviewsAction = action;
|
|
663
|
+
if (action === "show") {
|
|
664
|
+
const reviewId = args[0];
|
|
665
|
+
if (!reviewId || reviewId.startsWith("-")) {
|
|
666
|
+
throw new Error(`Missing review id. Use: ${formatCliCommand("reviews show <reviewId>")}`);
|
|
667
|
+
}
|
|
668
|
+
options.reviewId = args.shift();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
627
672
|
function parseIntelligenceAction(args, options) {
|
|
628
673
|
const action = args[0];
|
|
629
674
|
if (!action || action.startsWith("-")) {
|
|
@@ -657,6 +702,13 @@ function parsePositiveInt(value, label) {
|
|
|
657
702
|
return parsed;
|
|
658
703
|
}
|
|
659
704
|
|
|
705
|
+
function requireFlagValue(flag, value) {
|
|
706
|
+
if (value == null || value === "" || String(value).startsWith("-")) {
|
|
707
|
+
throw new Error(`Missing value for ${flag}.`);
|
|
708
|
+
}
|
|
709
|
+
return value;
|
|
710
|
+
}
|
|
711
|
+
|
|
660
712
|
function parseScope(value) {
|
|
661
713
|
if (!SCOPES.has(value)) {
|
|
662
714
|
throw new Error(`Invalid scope "${value}". Use agent-global or workspace.`);
|
|
@@ -673,6 +725,8 @@ function normalizeCommand(command) {
|
|
|
673
725
|
if (command === "orchestrator") return "orchestrator";
|
|
674
726
|
if (command === "run") return "run";
|
|
675
727
|
if (command === "runs") return "runs";
|
|
728
|
+
if (command === "review") return "review";
|
|
729
|
+
if (command === "reviews") return "reviews";
|
|
676
730
|
if (command === "intelligence" || command === "intel") return "intelligence";
|
|
677
731
|
if (command === "setup") return "setup";
|
|
678
732
|
if (command === "status") return "status";
|
|
@@ -747,6 +801,9 @@ Usage:
|
|
|
747
801
|
${cli} runs list [--json] [--limit <n>] [--active-only]
|
|
748
802
|
${cli} runs show <runId> [--json] [--limit <n>] [--follow]
|
|
749
803
|
${cli} runs stop <runId> [--json]
|
|
804
|
+
${cli} review --agent codex|pi [--base <ref>|--commit <sha>] [--model <name>] [--include-private] [--yes|--confirm] [--fail-on high|medium|low] [--json]
|
|
805
|
+
${cli} reviews list [--limit <n>] [--json]
|
|
806
|
+
${cli} reviews show <reviewId> [--json]
|
|
750
807
|
${cli} orchestrator [--json] Read-only agent capability diagnostics
|
|
751
808
|
${cli} intelligence [status|models|context|route|ask] [--json]
|
|
752
809
|
${cli} intelligence models --backend opencode-go|opencode-zen|opencode
|
|
@@ -795,6 +852,8 @@ Commands:
|
|
|
795
852
|
Tab switches region only when content is interactive (runs/launch).
|
|
796
853
|
run Launch a managed agent run with local audit trail.
|
|
797
854
|
runs List, inspect, or cancel agent runs under ~/.harness/runs/.
|
|
855
|
+
review Bounded read-only review via Codex or Pi; receipts under ~/.harness/reviews/.
|
|
856
|
+
reviews List or show prior review receipts (secret-free).
|
|
798
857
|
orchestrator Read-only capability registry diagnostics (--json supported).
|
|
799
858
|
intelligence Harness Engineering layer: backends, context packs, routing, budgets.
|
|
800
859
|
Local-first (Ollama). Cloud only with --cloud-consent.
|
|
@@ -144,6 +144,16 @@ export function reduceCockpitUi(state, action) {
|
|
|
144
144
|
returnView: null
|
|
145
145
|
};
|
|
146
146
|
}
|
|
147
|
+
if (state.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL) {
|
|
148
|
+
const back = ORCHESTRATOR_VIEWS.REVIEWS;
|
|
149
|
+
return {
|
|
150
|
+
...state,
|
|
151
|
+
view: back,
|
|
152
|
+
listIndex: 0,
|
|
153
|
+
region: defaultRegionForView(back, state.layoutMode),
|
|
154
|
+
returnView: null
|
|
155
|
+
};
|
|
156
|
+
}
|
|
147
157
|
if (isRunsChildView(state.view)) {
|
|
148
158
|
return goRunsHub(state);
|
|
149
159
|
}
|
|
@@ -18,6 +18,8 @@ const CONTENT_INTERACTIVE_VIEWS = new Set([
|
|
|
18
18
|
ORCHESTRATOR_VIEWS.ACTIVE_RUNS,
|
|
19
19
|
ORCHESTRATOR_VIEWS.RECENT_RUNS,
|
|
20
20
|
ORCHESTRATOR_VIEWS.RUN_DETAIL,
|
|
21
|
+
ORCHESTRATOR_VIEWS.REVIEWS,
|
|
22
|
+
ORCHESTRATOR_VIEWS.REVIEW_DETAIL,
|
|
21
23
|
ORCHESTRATOR_VIEWS.LAUNCH,
|
|
22
24
|
ORCHESTRATOR_VIEWS.ACTIVITY
|
|
23
25
|
]);
|
|
@@ -41,7 +43,11 @@ export function defaultRegionForView(view, layoutMode = LAYOUT_MODES.COMPACT) {
|
|
|
41
43
|
|
|
42
44
|
export function interactiveRegionsFor(state) {
|
|
43
45
|
const regions = regionsForLayout(state.layoutMode);
|
|
44
|
-
if (
|
|
46
|
+
if (
|
|
47
|
+
!isContentInteractiveView(state.view)
|
|
48
|
+
|| state.view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
49
|
+
|| state.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL
|
|
50
|
+
) {
|
|
45
51
|
return regions.filter((region) => region === COCKPIT_REGIONS.NAV);
|
|
46
52
|
}
|
|
47
53
|
return regions.filter(
|
|
@@ -51,7 +57,12 @@ export function interactiveRegionsFor(state) {
|
|
|
51
57
|
|
|
52
58
|
export function canTabBetweenRegions(state) {
|
|
53
59
|
if (!isContentInteractiveView(state.view)) return false;
|
|
54
|
-
if (
|
|
60
|
+
if (
|
|
61
|
+
state.view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
62
|
+
|| state.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL
|
|
63
|
+
) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
55
66
|
return interactiveRegionsFor(state).length >= 2;
|
|
56
67
|
}
|
|
57
68
|
|
|
@@ -70,7 +81,11 @@ export function routeCockpitKey(state, keyAction) {
|
|
|
70
81
|
return { type: "arrow", direction: keyAction.direction };
|
|
71
82
|
}
|
|
72
83
|
if (state.region === COCKPIT_REGIONS.CONTENT && isContentInteractiveView(state.view)) {
|
|
73
|
-
if (
|
|
84
|
+
if (
|
|
85
|
+
state.view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
86
|
+
|| state.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL
|
|
87
|
+
|| state.view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
88
|
+
) {
|
|
74
89
|
return null;
|
|
75
90
|
}
|
|
76
91
|
return {
|
|
@@ -250,6 +250,11 @@ export function buildFooterModel({
|
|
|
250
250
|
return { text: parts.join(` ${glyphs.bullet} `) };
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
if (view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL) {
|
|
254
|
+
parts.push("Esc Back");
|
|
255
|
+
return { text: parts.join(` ${glyphs.bullet} `) };
|
|
256
|
+
}
|
|
257
|
+
|
|
253
258
|
if (view === ORCHESTRATOR_VIEWS.CHANGES) {
|
|
254
259
|
return { text: buildChangesFooterParts(changesPhase).join(` ${glyphs.bullet} `) };
|
|
255
260
|
}
|
|
@@ -263,6 +268,7 @@ export function buildFooterModel({
|
|
|
263
268
|
const showTab = view === ORCHESTRATOR_VIEWS.RUNS
|
|
264
269
|
|| view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
265
270
|
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
271
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEWS
|
|
266
272
|
|| view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
267
273
|
|| view === ORCHESTRATOR_VIEWS.ACTIVITY;
|
|
268
274
|
if (showTab) {
|
|
@@ -280,7 +286,8 @@ export function buildFooterModel({
|
|
|
280
286
|
|| region === COCKPIT_REGIONS.NAV
|
|
281
287
|
|| view === ORCHESTRATOR_VIEWS.RUNS
|
|
282
288
|
|| view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
283
|
-
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
289
|
+
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
290
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEWS) {
|
|
284
291
|
parts.push("Enter Open");
|
|
285
292
|
}
|
|
286
293
|
if (view !== ORCHESTRATOR_VIEWS.LAUNCH) {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Read-only Cockpit helpers for review receipts under ~/.harness/reviews/. */
|
|
2
|
+
|
|
3
|
+
export function countFindingsBySeverity(findings = []) {
|
|
4
|
+
const counts = { high: 0, medium: 0, low: 0 };
|
|
5
|
+
for (const finding of findings) {
|
|
6
|
+
const severity = String(finding?.severity ?? "").toLowerCase();
|
|
7
|
+
if (counts[severity] != null) counts[severity] += 1;
|
|
8
|
+
}
|
|
9
|
+
return counts;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function selectReviewFromList(receipts = [], listIndex = 0) {
|
|
13
|
+
if (!Array.isArray(receipts) || receipts.length === 0) return null;
|
|
14
|
+
const index = Math.min(Math.max(0, listIndex), receipts.length - 1);
|
|
15
|
+
return receipts[index] ?? null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function formatReviewListLines(receipts = []) {
|
|
19
|
+
if (!Array.isArray(receipts) || receipts.length === 0) {
|
|
20
|
+
return ["No review receipts yet. Run kairo review --agent codex|pi."];
|
|
21
|
+
}
|
|
22
|
+
return receipts.map((receipt) => {
|
|
23
|
+
const counts = countFindingsBySeverity(receipt.findings);
|
|
24
|
+
const findingTotal = (receipt.findings ?? []).length;
|
|
25
|
+
const created = String(receipt.createdAt ?? "").slice(0, 19).replace("T", " ");
|
|
26
|
+
return `${receipt.reviewId} ${String(receipt.state).padEnd(10)} ${String(receipt.agentId).padEnd(6)} ${findingTotal}f (h${counts.high}/m${counts.medium}/l${counts.low}) ${created}`;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function formatReviewDetailLines(receipt) {
|
|
31
|
+
if (!receipt) return ["Review receipt not found."];
|
|
32
|
+
const counts = countFindingsBySeverity(receipt.findings);
|
|
33
|
+
const snapshot = receipt.snapshot ?? {};
|
|
34
|
+
const totals = snapshot.totals ?? {};
|
|
35
|
+
const lines = [
|
|
36
|
+
`Id: ${receipt.reviewId}`,
|
|
37
|
+
`Agent: ${receipt.agentId}${receipt.model ? ` · model ${receipt.model}` : ""}`,
|
|
38
|
+
`State: ${receipt.state}`,
|
|
39
|
+
`Created: ${receipt.createdAt ?? "n/a"}`,
|
|
40
|
+
`Findings: ${(receipt.findings ?? []).length} (high ${counts.high}, medium ${counts.medium}, low ${counts.low})`,
|
|
41
|
+
`Snapshot: ${snapshot.mode ?? "n/a"} · files ${totals.fileCount ?? 0} · fingerprint ${String(snapshot.fingerprint ?? "").slice(0, 12) || "n/a"}`
|
|
42
|
+
];
|
|
43
|
+
if ((receipt.warnings ?? []).length > 0) {
|
|
44
|
+
lines.push(`Warnings: ${receipt.warnings.length}`);
|
|
45
|
+
}
|
|
46
|
+
const findings = receipt.findings ?? [];
|
|
47
|
+
if (findings.length === 0) {
|
|
48
|
+
lines.push("", "No findings recorded.");
|
|
49
|
+
return lines;
|
|
50
|
+
}
|
|
51
|
+
lines.push("", "Findings (read-only):");
|
|
52
|
+
for (const finding of findings.slice(0, 20)) {
|
|
53
|
+
const loc = finding.line != null ? `:${finding.line}` : "";
|
|
54
|
+
lines.push(
|
|
55
|
+
` [${String(finding.severity).toUpperCase()}] ${finding.path ?? "?"}${loc} — ${finding.title ?? "untitled"}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (findings.length > 20) {
|
|
59
|
+
lines.push(` … ${findings.length - 20} more (use kairo reviews show ${receipt.reviewId})`);
|
|
60
|
+
}
|
|
61
|
+
return lines;
|
|
62
|
+
}
|
|
@@ -13,6 +13,12 @@ export const RUNS_HUB_ITEMS = [
|
|
|
13
13
|
view: ORCHESTRATOR_VIEWS.RECENT_RUNS,
|
|
14
14
|
description: "Review completed and failed run outcomes."
|
|
15
15
|
},
|
|
16
|
+
{
|
|
17
|
+
id: "reviews",
|
|
18
|
+
label: "Reviews",
|
|
19
|
+
view: ORCHESTRATOR_VIEWS.REVIEWS,
|
|
20
|
+
description: "Browse secret-free bounded-review receipts (read-only)."
|
|
21
|
+
},
|
|
16
22
|
{
|
|
17
23
|
id: "launch",
|
|
18
24
|
label: "New run",
|
|
@@ -27,13 +33,16 @@ export function isRunsBranchView(view) {
|
|
|
27
33
|
|| view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
28
34
|
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
29
35
|
|| view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
30
|
-
|| view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
36
|
+
|| view === ORCHESTRATOR_VIEWS.RUN_DETAIL
|
|
37
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEWS
|
|
38
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL;
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
export function isRunsChildView(view) {
|
|
34
42
|
return view === ORCHESTRATOR_VIEWS.ACTIVE_RUNS
|
|
35
43
|
|| view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
36
|
-
|| view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
44
|
+
|| view === ORCHESTRATOR_VIEWS.LAUNCH
|
|
45
|
+
|| view === ORCHESTRATOR_VIEWS.REVIEWS;
|
|
37
46
|
}
|
|
38
47
|
|
|
39
48
|
export function resolveRunsHubItem(listIndex = 0, items = RUNS_HUB_ITEMS) {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { windowLinesForLayout } from "./cockpit-models.js";
|
|
15
15
|
import { LAYOUT_MODES } from "./layout.js";
|
|
16
16
|
import { formatRunsHubLines, RUNS_HUB_ITEMS } from "./cockpit-runs.js";
|
|
17
|
+
import { formatReviewDetailLines, formatReviewListLines } from "./cockpit-reviews.js";
|
|
17
18
|
import { formatChangesActionLines } from "./cockpit-changes.js";
|
|
18
19
|
import { formatRecoveryLines } from "./cockpit-recovery.js";
|
|
19
20
|
|
|
@@ -74,6 +75,8 @@ export function renderCockpitView({
|
|
|
74
75
|
layoutMode = LAYOUT_MODES.COMPACT,
|
|
75
76
|
selectedRun,
|
|
76
77
|
selectedEvents,
|
|
78
|
+
reviews = [],
|
|
79
|
+
selectedReview = null,
|
|
77
80
|
changesAction = null,
|
|
78
81
|
recoveryAction = null,
|
|
79
82
|
colorEnabled = true
|
|
@@ -112,7 +115,7 @@ export function renderCockpitView({
|
|
|
112
115
|
formatRunsHubLines(RUNS_HUB_ITEMS),
|
|
113
116
|
listIndex,
|
|
114
117
|
colorEnabled,
|
|
115
|
-
"Choose Active runs, History, or New run."
|
|
118
|
+
"Choose Active runs, History, Reviews, or New run."
|
|
116
119
|
);
|
|
117
120
|
case ORCHESTRATOR_VIEWS.ACTIVE_RUNS:
|
|
118
121
|
return listBlock(
|
|
@@ -136,6 +139,14 @@ export function renderCockpitView({
|
|
|
136
139
|
colorEnabled,
|
|
137
140
|
"Open Runs after governance is healthy."
|
|
138
141
|
);
|
|
142
|
+
case ORCHESTRATOR_VIEWS.REVIEWS:
|
|
143
|
+
return listBlock(
|
|
144
|
+
"Reviews",
|
|
145
|
+
formatReviewListLines(reviews),
|
|
146
|
+
listIndex,
|
|
147
|
+
colorEnabled,
|
|
148
|
+
"Receipts are read-only. Launch reviews via kairo review --agent codex|pi."
|
|
149
|
+
);
|
|
139
150
|
case ORCHESTRATOR_VIEWS.LAUNCH:
|
|
140
151
|
if (launchableAgents.length === 0) {
|
|
141
152
|
return React.createElement(CockpitEmptyState, {
|
|
@@ -165,6 +176,12 @@ export function renderCockpitView({
|
|
|
165
176
|
formatRunDetailLines(selectedRun, selectedEvents)
|
|
166
177
|
.map((line) => React.createElement(Text, { key: line }, line))
|
|
167
178
|
);
|
|
179
|
+
case ORCHESTRATOR_VIEWS.REVIEW_DETAIL:
|
|
180
|
+
return React.createElement(Box, { flexDirection: "column" },
|
|
181
|
+
React.createElement(Text, { bold: true }, "Review detail"),
|
|
182
|
+
formatReviewDetailLines(selectedReview)
|
|
183
|
+
.map((line) => React.createElement(Text, { key: line }, line))
|
|
184
|
+
);
|
|
168
185
|
case ORCHESTRATOR_VIEWS.DIAGNOSTICS:
|
|
169
186
|
return governanceList(
|
|
170
187
|
"System health",
|
|
@@ -178,7 +195,7 @@ export function renderCockpitView({
|
|
|
178
195
|
React.createElement(Text, null, "Kairo keeps IDEs and agents aligned with project architecture and workflows."),
|
|
179
196
|
React.createElement(Text, null, "Primary flow: scan → findings → preview → confirm → apply → re-scan."),
|
|
180
197
|
React.createElement(Text, null, "↑↓ navigate · Enter open · Esc back/exit · R refresh/retry · ? help"),
|
|
181
|
-
React.createElement(Text, null, "Runs are secondary after setup and repairs.")
|
|
198
|
+
React.createElement(Text, null, "Runs are secondary after setup and repairs. Reviews are read-only receipts.")
|
|
182
199
|
);
|
|
183
200
|
default: {
|
|
184
201
|
const _exhaustive = view;
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import { buildControlCenterModel } from "./cockpit-control-center.js";
|
|
26
26
|
import { resolveEnterNavIntent } from "./cockpit-enter.js";
|
|
27
27
|
import { resolveRunsHubItem, RUNS_HUB_ITEMS } from "./cockpit-runs.js";
|
|
28
|
+
import { selectReviewFromList } from "./cockpit-reviews.js";
|
|
28
29
|
import { resolveProjectReadiness } from "../dashboard-guidance.js";
|
|
29
30
|
import { CONTROL_PLANE_HEALTH } from "../control-plane-snapshot.js";
|
|
30
31
|
import { CockpitShell } from "./cockpit/primitives.js";
|
|
@@ -142,6 +143,7 @@ export function OrchestratorApp({
|
|
|
142
143
|
return;
|
|
143
144
|
}
|
|
144
145
|
data.setSelectedRun(null);
|
|
146
|
+
data.setSelectedReview(null);
|
|
145
147
|
data.resetLaunchWizard();
|
|
146
148
|
dispatch({ type: "escape" });
|
|
147
149
|
return;
|
|
@@ -158,9 +160,11 @@ export function OrchestratorApp({
|
|
|
158
160
|
? (data.dashboard?.activeRuns ?? []).length
|
|
159
161
|
: ui.view === ORCHESTRATOR_VIEWS.RECENT_RUNS
|
|
160
162
|
? (data.dashboard?.recentRuns ?? []).length
|
|
161
|
-
: ui.view === ORCHESTRATOR_VIEWS.
|
|
162
|
-
?
|
|
163
|
-
:
|
|
163
|
+
: ui.view === ORCHESTRATOR_VIEWS.REVIEWS
|
|
164
|
+
? (data.reviews ?? []).length
|
|
165
|
+
: ui.view === ORCHESTRATOR_VIEWS.ACTIVITY
|
|
166
|
+
? listRecoverySnapshots(data.snapshot).length
|
|
167
|
+
: 0;
|
|
164
168
|
|
|
165
169
|
let routed = null;
|
|
166
170
|
if (key.tab) {
|
|
@@ -229,6 +233,9 @@ export function OrchestratorApp({
|
|
|
229
233
|
if (hubItem.action === "launch") {
|
|
230
234
|
data.resetLaunchWizard();
|
|
231
235
|
}
|
|
236
|
+
if (hubItem.view === ORCHESTRATOR_VIEWS.REVIEWS) {
|
|
237
|
+
data.loadReviews().catch(() => {});
|
|
238
|
+
}
|
|
232
239
|
dispatch({
|
|
233
240
|
type: "set-view",
|
|
234
241
|
view: hubItem.view,
|
|
@@ -247,6 +254,13 @@ export function OrchestratorApp({
|
|
|
247
254
|
return;
|
|
248
255
|
}
|
|
249
256
|
|
|
257
|
+
if (ui.region === COCKPIT_REGIONS.CONTENT
|
|
258
|
+
&& ui.view === ORCHESTRATOR_VIEWS.REVIEWS
|
|
259
|
+
&& key.return) {
|
|
260
|
+
data.openReviewDetail(selectReviewFromList(data.reviews ?? [], ui.listIndex), dispatch);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
250
264
|
if (ui.view === ORCHESTRATOR_VIEWS.RUN_DETAIL) {
|
|
251
265
|
if (inputKey.toLowerCase() === "c" && isRunCancellable(data.selectedRun)) {
|
|
252
266
|
data.handleCancelRun();
|
|
@@ -304,6 +318,10 @@ export function OrchestratorApp({
|
|
|
304
318
|
}
|
|
305
319
|
|
|
306
320
|
if (inputKey.toLowerCase() === "r" && ui.view !== ORCHESTRATOR_VIEWS.LAUNCH) {
|
|
321
|
+
if (ui.view === ORCHESTRATOR_VIEWS.REVIEWS || ui.view === ORCHESTRATOR_VIEWS.REVIEW_DETAIL) {
|
|
322
|
+
data.loadReviews().catch(() => {});
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
307
325
|
data.reload().catch(() => {});
|
|
308
326
|
}
|
|
309
327
|
});
|
|
@@ -406,6 +424,8 @@ export function OrchestratorApp({
|
|
|
406
424
|
launchableAgents: data.launchableAgents,
|
|
407
425
|
selectedRun: data.selectedRun,
|
|
408
426
|
selectedEvents: data.selectedEvents,
|
|
427
|
+
reviews: data.reviews,
|
|
428
|
+
selectedReview: data.selectedReview,
|
|
409
429
|
changesAction: data.changesAction,
|
|
410
430
|
recoveryAction: data.recoveryAction,
|
|
411
431
|
controlCenter,
|
|
@@ -22,6 +22,8 @@ export const ORCHESTRATOR_VIEWS = {
|
|
|
22
22
|
ACTIVE_RUNS: "active-runs",
|
|
23
23
|
RECENT_RUNS: "recent-runs",
|
|
24
24
|
RUN_DETAIL: "run-detail",
|
|
25
|
+
REVIEWS: "reviews",
|
|
26
|
+
REVIEW_DETAIL: "review-detail",
|
|
25
27
|
PROVIDERS: "providers",
|
|
26
28
|
LAUNCH: "launch",
|
|
27
29
|
DIAGNOSTICS: "diagnostics",
|
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
listRecoverySnapshots,
|
|
34
34
|
reduceRecoveryAction
|
|
35
35
|
} from "./cockpit-recovery.js";
|
|
36
|
+
import { listReviewReceipts } from "../runtime/review/review-receipts.js";
|
|
37
|
+
import { assertReceiptSecretFree } from "../runtime/review/review-validate.js";
|
|
36
38
|
|
|
37
39
|
export function useOrchestratorData({
|
|
38
40
|
homeDir,
|
|
@@ -50,6 +52,8 @@ export function useOrchestratorData({
|
|
|
50
52
|
const [snapshot, setSnapshot] = useState(null);
|
|
51
53
|
const [selectedRun, setSelectedRun] = useState(null);
|
|
52
54
|
const [selectedEvents, setSelectedEvents] = useState([]);
|
|
55
|
+
const [reviews, setReviews] = useState([]);
|
|
56
|
+
const [selectedReview, setSelectedReview] = useState(null);
|
|
53
57
|
const [statusMessage, setStatusMessage] = useState(null);
|
|
54
58
|
const [launchAgentIndex, setLaunchAgentIndex] = useState(0);
|
|
55
59
|
const [launchStep, setLaunchStep] = useState(LAUNCH_WIZARD_STEPS.AGENT);
|
|
@@ -124,6 +128,27 @@ export function useOrchestratorData({
|
|
|
124
128
|
});
|
|
125
129
|
};
|
|
126
130
|
|
|
131
|
+
const loadReviews = async () => {
|
|
132
|
+
const receipts = (await listReviewReceipts({ homeDir, limit: 50 }))
|
|
133
|
+
.map((receipt) => assertReceiptSecretFree(receipt));
|
|
134
|
+
setReviews(receipts);
|
|
135
|
+
if (selectedReview) {
|
|
136
|
+
const refreshed = receipts.find((entry) => entry.reviewId === selectedReview.reviewId) ?? null;
|
|
137
|
+
setSelectedReview(refreshed);
|
|
138
|
+
}
|
|
139
|
+
return receipts;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const openReviewDetail = (receipt, dispatch) => {
|
|
143
|
+
if (!receipt) return;
|
|
144
|
+
setSelectedReview(assertReceiptSecretFree(receipt));
|
|
145
|
+
dispatch({
|
|
146
|
+
type: "set-view",
|
|
147
|
+
view: ORCHESTRATOR_VIEWS.REVIEW_DETAIL,
|
|
148
|
+
returnView: ORCHESTRATOR_VIEWS.REVIEWS
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
127
152
|
const handleLaunch = async (draft, profile, dispatch) => {
|
|
128
153
|
if (!draft.agentId || !draft.task.trim()) {
|
|
129
154
|
setError("Agent and task are required.");
|
|
@@ -353,6 +378,9 @@ export function useOrchestratorData({
|
|
|
353
378
|
selectedRun,
|
|
354
379
|
setSelectedRun,
|
|
355
380
|
selectedEvents,
|
|
381
|
+
reviews,
|
|
382
|
+
selectedReview,
|
|
383
|
+
setSelectedReview,
|
|
356
384
|
statusMessage,
|
|
357
385
|
launchAgentIndex,
|
|
358
386
|
setLaunchAgentIndex,
|
|
@@ -369,6 +397,8 @@ export function useOrchestratorData({
|
|
|
369
397
|
reload,
|
|
370
398
|
resetLaunchWizard,
|
|
371
399
|
openRunDetail,
|
|
400
|
+
loadReviews,
|
|
401
|
+
openReviewDetail,
|
|
372
402
|
handleLaunch,
|
|
373
403
|
handleCancelRun,
|
|
374
404
|
previewChanges,
|
package/src/global/paths.js
CHANGED
|
@@ -18,6 +18,7 @@ export function harnessHomePaths(homeDir) {
|
|
|
18
18
|
profilePath: join(root, "profile.json"),
|
|
19
19
|
historyPath: join(root, "history.jsonl"),
|
|
20
20
|
runsDir: join(root, "runs"),
|
|
21
|
+
reviewsDir: join(root, "reviews"),
|
|
21
22
|
coreDir: join(root, "core"),
|
|
22
23
|
backupsDir: join(root, "backups")
|
|
23
24
|
};
|
|
@@ -26,6 +26,7 @@ export function inspectExecutionAdapters(context = {}) {
|
|
|
26
26
|
label: adapter.label,
|
|
27
27
|
executable: adapter.executable,
|
|
28
28
|
capabilities: adapter.capabilities,
|
|
29
|
+
reviewCompatible: Boolean(adapter.capabilities?.reviewCompatible),
|
|
29
30
|
...adapter.availability(context)
|
|
30
31
|
}));
|
|
31
32
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export {
|
|
2
|
+
REVIEW_SCOPE_MODES, REVIEW_SEVERITIES, REVIEW_STATES, REVIEW_EXIT_CODES, REVIEW_AGENTS,
|
|
3
|
+
REVIEW_LIMITS, REVIEW_SNAPSHOT_ERROR_CODES, ReviewSnapshotError, resolveReviewScopeMode,
|
|
4
|
+
createFindingId, canonicalFingerprint, isReviewPrivatePath, isBinaryContent,
|
|
5
|
+
assertReviewPathSafe, assertWithinReviewLimits, requirePrivateConsent
|
|
6
|
+
} from "./review-types.js";
|
|
7
|
+
export {
|
|
8
|
+
resolveReviewSnapshot, fingerprintReviewSnapshot, detectReviewSnapshotDrift,
|
|
9
|
+
readReviewRegularFile
|
|
10
|
+
} from "./review-git.js";
|
|
11
|
+
export {
|
|
12
|
+
REVIEW_PATCH_ERROR_CODES, filterDiffToAdmittedPaths, buildScopedReviewPatch
|
|
13
|
+
} from "./review-patch.js";
|
|
14
|
+
export {
|
|
15
|
+
REVIEW_VALIDATION_ERROR_CODES, ReviewValidationError,
|
|
16
|
+
validateReviewOutput, assertReceiptSecretFree
|
|
17
|
+
} from "./review-validate.js";
|
|
18
|
+
export {
|
|
19
|
+
assertSafeReviewId, createReviewId, reviewPaths,
|
|
20
|
+
buildReviewReceipt, saveReviewReceipt, loadReviewReceipt, listReviewReceipts
|
|
21
|
+
} from "./review-receipts.js";
|
|
22
|
+
export {
|
|
23
|
+
REVIEW_EXEC_ERROR_CODES, REVIEW_EXEC_LIMITS, ReviewExecError,
|
|
24
|
+
runBoundedProcess, assertBoundedProcessOk
|
|
25
|
+
} from "./review-exec.js";
|
|
26
|
+
export {
|
|
27
|
+
REVIEW_CODEX_ERROR_CODES, buildCodexReviewArgs, buildCodexCliEnv,
|
|
28
|
+
buildCodexReviewPrompt, parseCodexReviewJsonl, runCodexReview
|
|
29
|
+
} from "./review-codex.js";
|
|
30
|
+
export {
|
|
31
|
+
REVIEW_PI_ERROR_CODES, buildPiReviewArgs, buildPiCliEnv,
|
|
32
|
+
buildPiReviewPrompt, buildPiReviewStdin, parsePiReviewJsonl, runPiReview
|
|
33
|
+
} from "./review-pi.js";
|
|
34
|
+
export {
|
|
35
|
+
REVIEW_RUNNER_ERROR_CODES, ReviewRunnerError,
|
|
36
|
+
resolveReviewAgent, resolveReviewExitCode, runReview
|
|
37
|
+
} from "./review-runner.js";
|