@kal-elsam/kairo-runtime 0.6.0 → 0.8.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 +36 -0
- package/global-template/components/catalog.json +4 -1
- package/global-template/components/orchestrator/extensions/pi/kairo-minion.js +604 -0
- package/package.json +1 -1
- package/scripts/cockpit-smoke.mjs +1 -1
- package/src/cli.js +69 -1
- package/src/global/adapters/pi.js +1 -1
- 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 +14 -3
- package/src/global/runtime/orchestration/index.js +23 -0
- package/src/global/runtime/orchestration/orch-receipts.js +234 -0
- package/src/global/runtime/orchestration/orch-types.js +173 -0
- package/src/global/runtime/orchestration/orch-validate.js +63 -0
- 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/run-cli.js +1 -0
- package/src/global/runtime/run-manager.js +59 -4
- package/src/global/runtime/run-strategy.js +71 -0
- package/src/global/runtime/run-supervisor.js +22 -2
- package/src/global/runtime/run-types.js +5 -1
- package/src/global/runtime/write-atomic-json.js +24 -20
package/src/cli.js
CHANGED
|
@@ -38,6 +38,8 @@ 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";
|
|
42
|
+
import { normalizeRunStrategy } from "./global/runtime/run-strategy.js";
|
|
41
43
|
import {
|
|
42
44
|
LEGACY_PACKAGE_NAME,
|
|
43
45
|
PACKAGE_NAME,
|
|
@@ -111,6 +113,12 @@ export async function runCli(argv) {
|
|
|
111
113
|
case "runs":
|
|
112
114
|
await runGlobalRuns(optionsWithPolicy, packageManifest);
|
|
113
115
|
return;
|
|
116
|
+
case "review":
|
|
117
|
+
await runGlobalReview(optionsWithPolicy, packageManifest);
|
|
118
|
+
return;
|
|
119
|
+
case "reviews":
|
|
120
|
+
await runGlobalReviews(optionsWithPolicy, packageManifest);
|
|
121
|
+
return;
|
|
114
122
|
case "intelligence":
|
|
115
123
|
await runIntelligenceCli(optionsWithPolicy, packageManifest);
|
|
116
124
|
return;
|
|
@@ -371,9 +379,15 @@ export function parseArgs(argv) {
|
|
|
371
379
|
intelligencePaths: [],
|
|
372
380
|
runsAction: null,
|
|
373
381
|
runId: null,
|
|
382
|
+
reviewId: null,
|
|
383
|
+
reviewsAction: null,
|
|
384
|
+
base: null,
|
|
385
|
+
commit: null,
|
|
386
|
+
failOn: null,
|
|
374
387
|
agent: null,
|
|
375
388
|
task: null,
|
|
376
389
|
model: null,
|
|
390
|
+
strategy: "direct",
|
|
377
391
|
intelligenceBackend: null,
|
|
378
392
|
permissions: null,
|
|
379
393
|
captureTranscript: false,
|
|
@@ -401,6 +415,10 @@ export function parseArgs(argv) {
|
|
|
401
415
|
parseRunsAction(args, options);
|
|
402
416
|
}
|
|
403
417
|
|
|
418
|
+
if (command === "reviews") {
|
|
419
|
+
parseReviewsAction(args, options);
|
|
420
|
+
}
|
|
421
|
+
|
|
404
422
|
if (command === "intelligence") {
|
|
405
423
|
parseIntelligenceAction(args, options);
|
|
406
424
|
}
|
|
@@ -483,6 +501,13 @@ export function parseArgs(argv) {
|
|
|
483
501
|
else if (arg.startsWith("--agent=")) options.agent = arg.slice("--agent=".length);
|
|
484
502
|
else if (arg === "--model") options.model = args[++index];
|
|
485
503
|
else if (arg.startsWith("--model=")) options.model = arg.slice("--model=".length);
|
|
504
|
+
else if (arg === "--strategy") {
|
|
505
|
+
options.strategy = normalizeRunStrategy(requireFlagValue("--strategy", args[++index]));
|
|
506
|
+
} else if (arg.startsWith("--strategy=")) {
|
|
507
|
+
options.strategy = normalizeRunStrategy(
|
|
508
|
+
requireFlagValue("--strategy", arg.slice("--strategy=".length))
|
|
509
|
+
);
|
|
510
|
+
}
|
|
486
511
|
else if (arg === "--backend") options.intelligenceBackend = args[++index] ?? "";
|
|
487
512
|
else if (arg.startsWith("--backend=")) options.intelligenceBackend = arg.slice("--backend=".length);
|
|
488
513
|
else if (arg === "--permissions") options.permissions = parsePathList(args[++index]);
|
|
@@ -495,6 +520,15 @@ export function parseArgs(argv) {
|
|
|
495
520
|
else if (arg.startsWith("--timeout=")) options.timeoutMs = parsePositiveInt(arg.slice("--timeout=".length), "timeout") * 1000;
|
|
496
521
|
else if (arg === "--include-private") options.includePrivate = true;
|
|
497
522
|
else if (arg === "--cloud-consent") options.cloudConsent = true;
|
|
523
|
+
else if (arg === "--base") options.base = requireFlagValue("--base", args[++index]);
|
|
524
|
+
else if (arg.startsWith("--base=")) options.base = requireFlagValue("--base", arg.slice("--base=".length));
|
|
525
|
+
else if (arg === "--commit") options.commit = requireFlagValue("--commit", args[++index]);
|
|
526
|
+
else if (arg.startsWith("--commit=")) {
|
|
527
|
+
options.commit = requireFlagValue("--commit", arg.slice("--commit=".length));
|
|
528
|
+
} else if (arg === "--fail-on") options.failOn = requireFlagValue("--fail-on", args[++index]);
|
|
529
|
+
else if (arg.startsWith("--fail-on=")) {
|
|
530
|
+
options.failOn = requireFlagValue("--fail-on", arg.slice("--fail-on=".length));
|
|
531
|
+
}
|
|
498
532
|
else if (arg === "--help" || arg === "-h") options.help = true;
|
|
499
533
|
else if (arg === "--version" || arg === "-v") options.version = true;
|
|
500
534
|
else throw new Error(`Unknown option "${arg}".`);
|
|
@@ -624,6 +658,26 @@ function parseRunsAction(args, options) {
|
|
|
624
658
|
}
|
|
625
659
|
}
|
|
626
660
|
|
|
661
|
+
function parseReviewsAction(args, options) {
|
|
662
|
+
const action = args[0];
|
|
663
|
+
if (!action || action.startsWith("-")) {
|
|
664
|
+
options.reviewsAction = "list";
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (!new Set(["list", "show"]).has(action)) {
|
|
668
|
+
throw new Error(`Unknown reviews action "${action}". Use list or show.`);
|
|
669
|
+
}
|
|
670
|
+
args.shift();
|
|
671
|
+
options.reviewsAction = action;
|
|
672
|
+
if (action === "show") {
|
|
673
|
+
const reviewId = args[0];
|
|
674
|
+
if (!reviewId || reviewId.startsWith("-")) {
|
|
675
|
+
throw new Error(`Missing review id. Use: ${formatCliCommand("reviews show <reviewId>")}`);
|
|
676
|
+
}
|
|
677
|
+
options.reviewId = args.shift();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
627
681
|
function parseIntelligenceAction(args, options) {
|
|
628
682
|
const action = args[0];
|
|
629
683
|
if (!action || action.startsWith("-")) {
|
|
@@ -657,6 +711,13 @@ function parsePositiveInt(value, label) {
|
|
|
657
711
|
return parsed;
|
|
658
712
|
}
|
|
659
713
|
|
|
714
|
+
function requireFlagValue(flag, value) {
|
|
715
|
+
if (value == null || value === "" || String(value).startsWith("-")) {
|
|
716
|
+
throw new Error(`Missing value for ${flag}.`);
|
|
717
|
+
}
|
|
718
|
+
return value;
|
|
719
|
+
}
|
|
720
|
+
|
|
660
721
|
function parseScope(value) {
|
|
661
722
|
if (!SCOPES.has(value)) {
|
|
662
723
|
throw new Error(`Invalid scope "${value}". Use agent-global or workspace.`);
|
|
@@ -673,6 +734,8 @@ function normalizeCommand(command) {
|
|
|
673
734
|
if (command === "orchestrator") return "orchestrator";
|
|
674
735
|
if (command === "run") return "run";
|
|
675
736
|
if (command === "runs") return "runs";
|
|
737
|
+
if (command === "review") return "review";
|
|
738
|
+
if (command === "reviews") return "reviews";
|
|
676
739
|
if (command === "intelligence" || command === "intel") return "intelligence";
|
|
677
740
|
if (command === "setup") return "setup";
|
|
678
741
|
if (command === "status") return "status";
|
|
@@ -743,10 +806,13 @@ Usage:
|
|
|
743
806
|
${cli} --dry-run Setup dry-run (scriptable)
|
|
744
807
|
${cli} --version
|
|
745
808
|
${cli} shell Operations cockpit (TTY)
|
|
746
|
-
${cli} run --agent <id> --task "..." [--model <name>] [--cwd <dir>] [--permissions force] [--capture-transcript] [--follow] [--no-wait] [--json]
|
|
809
|
+
${cli} run --agent <id> --task "..." [--strategy direct|orchestrated] [--model <name>] [--cwd <dir>] [--permissions force] [--capture-transcript] [--follow] [--no-wait] [--json]
|
|
747
810
|
${cli} runs list [--json] [--limit <n>] [--active-only]
|
|
748
811
|
${cli} runs show <runId> [--json] [--limit <n>] [--follow]
|
|
749
812
|
${cli} runs stop <runId> [--json]
|
|
813
|
+
${cli} review --agent codex|pi [--base <ref>|--commit <sha>] [--model <name>] [--include-private] [--yes|--confirm] [--fail-on high|medium|low] [--json]
|
|
814
|
+
${cli} reviews list [--limit <n>] [--json]
|
|
815
|
+
${cli} reviews show <reviewId> [--json]
|
|
750
816
|
${cli} orchestrator [--json] Read-only agent capability diagnostics
|
|
751
817
|
${cli} intelligence [status|models|context|route|ask] [--json]
|
|
752
818
|
${cli} intelligence models --backend opencode-go|opencode-zen|opencode
|
|
@@ -795,6 +861,8 @@ Commands:
|
|
|
795
861
|
Tab switches region only when content is interactive (runs/launch).
|
|
796
862
|
run Launch a managed agent run with local audit trail.
|
|
797
863
|
runs List, inspect, or cancel agent runs under ~/.harness/runs/.
|
|
864
|
+
review Bounded read-only review via Codex or Pi; receipts under ~/.harness/reviews/.
|
|
865
|
+
reviews List or show prior review receipts (secret-free).
|
|
798
866
|
orchestrator Read-only capability registry diagnostics (--json supported).
|
|
799
867
|
intelligence Harness Engineering layer: backends, context packs, routing, budgets.
|
|
800
868
|
Local-first (Ollama). Cloud only with --cloud-consent.
|
|
@@ -7,7 +7,7 @@ export const PI_CODING_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
|
|
|
7
7
|
|
|
8
8
|
const CUSTOM_DIR_MESSAGE =
|
|
9
9
|
`Pi config writes are unsupported when ${PI_CODING_AGENT_DIR_ENV} points away from `
|
|
10
|
-
+ `~/${PI_DEFAULT_ROOT_DIR} (out of scope for Kairo
|
|
10
|
+
+ `~/${PI_DEFAULT_ROOT_DIR} (out of scope for Kairo managed config). `
|
|
11
11
|
+ `Unset ${PI_CODING_AGENT_DIR_ENV} to manage the default directory; runtime launches remain available.`;
|
|
12
12
|
|
|
13
13
|
/** True when PI_CODING_AGENT_DIR is set to a non-default absolute/relative path. */
|
|
@@ -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
|
}
|
|
@@ -30,12 +30,15 @@ export function buildPiPermissionsArgs(permissions = []) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
throw new Error(
|
|
33
|
-
`Pi permissions only support "read-only"
|
|
33
|
+
`Pi permissions only support "read-only" (got: ${permissions.join(", ")}). `
|
|
34
34
|
+ "Other aliases are rejected and are never translated to --approve."
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function buildPiLaunch({
|
|
38
|
+
export function buildPiLaunch({
|
|
39
|
+
task, cwd, model, permissions = [], env = process.env,
|
|
40
|
+
strategy = "direct", extensionPath = null
|
|
41
|
+
} = {}) {
|
|
39
42
|
const args = [
|
|
40
43
|
"--mode",
|
|
41
44
|
"json",
|
|
@@ -47,6 +50,13 @@ export function buildPiLaunch({ task, cwd, model, permissions = [], env = proces
|
|
|
47
50
|
args.push("--model", model);
|
|
48
51
|
}
|
|
49
52
|
|
|
53
|
+
if (strategy === "orchestrated") {
|
|
54
|
+
if (typeof extensionPath !== "string" || !extensionPath) {
|
|
55
|
+
throw new Error("Orchestrated Pi requires a managed extension path.");
|
|
56
|
+
}
|
|
57
|
+
args.push("--no-extensions", "--extension", extensionPath);
|
|
58
|
+
}
|
|
59
|
+
|
|
50
60
|
args.push(task);
|
|
51
61
|
|
|
52
62
|
return {
|
|
@@ -175,7 +185,8 @@ export default createExecutionAdapter({
|
|
|
175
185
|
tokens: true,
|
|
176
186
|
diff: false,
|
|
177
187
|
cancel: true,
|
|
178
|
-
transcript: true
|
|
188
|
+
transcript: true,
|
|
189
|
+
reviewCompatible: true
|
|
179
190
|
},
|
|
180
191
|
checkAvailability: checkPiAvailability,
|
|
181
192
|
buildLaunch: buildPiLaunch,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { harnessHomePaths } from "../../paths.js";
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
RUN_STRATEGIES, DAG_NODE_STATES, DAG_TERMINAL_STATES, ORCH_LIMITS, ORCH_ERROR_CODES,
|
|
6
|
+
OrchContractError, createTaskId, isTerminalDagState, normalizeRunStrategy,
|
|
7
|
+
createOrchLineage, createBudgetUsage, createDagNode, createMinionBrief,
|
|
8
|
+
createMinionResult, digestAllowlisted
|
|
9
|
+
} from "./orch-types.js";
|
|
10
|
+
export { assertOrchReceiptSecretFree, FORBIDDEN_KEYS, walkForbiddenKeys } from "./orch-validate.js";
|
|
11
|
+
export {
|
|
12
|
+
orchPaths, buildOrchReceipt, saveOrchReceipt, loadOrchReceipt,
|
|
13
|
+
createOrchState, saveOrchState, loadOrchState, terminalizeOrchNodes,
|
|
14
|
+
updateOrchState, applyMinionDagUpdate, finalizeOrchState, reconcileOrchState
|
|
15
|
+
} from "./orch-receipts.js";
|
|
16
|
+
|
|
17
|
+
export const KAIRO_MINION_RELATIVE_ASSET =
|
|
18
|
+
"components/orchestrator/extensions/pi/kairo-minion.js";
|
|
19
|
+
|
|
20
|
+
/** Materialized extension path under ~/.harness (never Pi global auto-discover). */
|
|
21
|
+
export function resolveKairoMinionExtensionPath(homeDir) {
|
|
22
|
+
return join(harnessHomePaths(homeDir).root, KAIRO_MINION_RELATIVE_ASSET);
|
|
23
|
+
}
|