@kal-elsam/kairo-runtime 0.7.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 +12 -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 +10 -1
- package/src/global/adapters/pi.js +1 -1
- package/src/global/runtime/execution-adapters/pi.js +12 -2
- 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/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
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import {
|
|
5
|
+
RUN_STRATEGIES,
|
|
6
|
+
normalizeRunStrategy,
|
|
7
|
+
createOrchLineage,
|
|
8
|
+
resolveKairoMinionExtensionPath
|
|
9
|
+
} from "./orchestration/index.js";
|
|
10
|
+
|
|
11
|
+
export { RUN_STRATEGIES, normalizeRunStrategy };
|
|
12
|
+
|
|
13
|
+
const ORCH_MODULE_PATH = join(dirname(fileURLToPath(import.meta.url)), "orchestration", "index.js");
|
|
14
|
+
|
|
15
|
+
export const ORCH_RUNTIME_ENV = Object.freeze({
|
|
16
|
+
HOME: "KAIRO_ORCH_HOME",
|
|
17
|
+
ROOT_RUN_ID: "KAIRO_ORCH_ROOT_RUN_ID",
|
|
18
|
+
ROOT_TASK_ID: "KAIRO_ORCH_ROOT_TASK_ID",
|
|
19
|
+
CLI_VERSION: "KAIRO_ORCH_CLI_VERSION",
|
|
20
|
+
MODULE: "KAIRO_ORCH_MODULE"
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/** Reject orchestrated for non-Pi before any run I/O. */
|
|
24
|
+
export function assertOrchestratedAgent(agentId, strategy) {
|
|
25
|
+
const normalized = normalizeRunStrategy(strategy);
|
|
26
|
+
if (normalized === RUN_STRATEGIES.ORCHESTRATED && agentId !== "pi") {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Strategy "orchestrated" requires agent "pi" (got "${agentId}").`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Fail closed when managed extension is missing or not a regular file. */
|
|
35
|
+
export async function assertManagedMinionExtension(homeDir) {
|
|
36
|
+
const extensionPath = resolveKairoMinionExtensionPath(homeDir);
|
|
37
|
+
let info;
|
|
38
|
+
try {
|
|
39
|
+
info = await stat(extensionPath);
|
|
40
|
+
} catch {
|
|
41
|
+
throw new Error(`Managed Kairo minion extension missing: ${extensionPath}`);
|
|
42
|
+
}
|
|
43
|
+
if (!info.isFile()) {
|
|
44
|
+
throw new Error(`Managed Kairo minion extension is not a regular file: ${extensionPath}`);
|
|
45
|
+
}
|
|
46
|
+
return extensionPath;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createRootRunLineage(runId) {
|
|
50
|
+
return createOrchLineage({ rootRunId: runId, parentRunId: null, depth: 0 });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Supervisor derives extension path from homeDir only — never from CLI/handoff. */
|
|
54
|
+
export function resolveOrchestratedExtensionPath(homeDir, strategy) {
|
|
55
|
+
if (normalizeRunStrategy(strategy) !== RUN_STRATEGIES.ORCHESTRATED) return null;
|
|
56
|
+
return resolveKairoMinionExtensionPath(homeDir);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function buildOrchestratedRuntimeEnv({
|
|
60
|
+
homeDir, rootRunId, rootTaskId, cliVersion = null,
|
|
61
|
+
strategy = RUN_STRATEGIES.DIRECT, baseEnv = process.env
|
|
62
|
+
} = {}) {
|
|
63
|
+
const env = { ...baseEnv };
|
|
64
|
+
if (normalizeRunStrategy(strategy) !== RUN_STRATEGIES.ORCHESTRATED) return env;
|
|
65
|
+
env[ORCH_RUNTIME_ENV.HOME] = homeDir;
|
|
66
|
+
env[ORCH_RUNTIME_ENV.ROOT_RUN_ID] = rootRunId;
|
|
67
|
+
env[ORCH_RUNTIME_ENV.ROOT_TASK_ID] = rootTaskId;
|
|
68
|
+
env[ORCH_RUNTIME_ENV.CLI_VERSION] = cliVersion == null ? "" : String(cliVersion);
|
|
69
|
+
env[ORCH_RUNTIME_ENV.MODULE] = ORCH_MODULE_PATH;
|
|
70
|
+
return env;
|
|
71
|
+
}
|
|
@@ -18,6 +18,12 @@ import { consumeRunHandoff } from "./run-handoff.js";
|
|
|
18
18
|
import { isRunCancelRequested } from "./run-cancel-signal.js";
|
|
19
19
|
import { readSupervisorLock, touchSupervisorLock, writeSupervisorLock } from "./run-supervisor-lock.js";
|
|
20
20
|
import { shouldPersistTranscript } from "./run-redact.js";
|
|
21
|
+
import {
|
|
22
|
+
buildOrchestratedRuntimeEnv,
|
|
23
|
+
normalizeRunStrategy,
|
|
24
|
+
resolveOrchestratedExtensionPath
|
|
25
|
+
} from "./run-strategy.js";
|
|
26
|
+
import { finalizeOrchState, RUN_STRATEGIES } from "./orchestration/index.js";
|
|
21
27
|
|
|
22
28
|
async function shouldPreserveCancelledState(homeDir, runId) {
|
|
23
29
|
const fresh = await readRunState(homeDir, runId);
|
|
@@ -66,12 +72,16 @@ export async function supervisePreparedRun({
|
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
const captureTranscript = handoff.captureTranscript === true;
|
|
75
|
+
const strategy = normalizeRunStrategy(handoff.strategy ?? metadata.strategy ?? "direct");
|
|
76
|
+
const extensionPath = resolveOrchestratedExtensionPath(homeDir, strategy);
|
|
69
77
|
const launch = adapter.buildLaunch({
|
|
70
78
|
task: handoff.task,
|
|
71
79
|
cwd: handoff.cwd,
|
|
72
80
|
model: handoff.model,
|
|
73
81
|
permissions: handoff.permissions ?? [],
|
|
74
|
-
profile: handoff.profile ?? null
|
|
82
|
+
profile: handoff.profile ?? null,
|
|
83
|
+
strategy,
|
|
84
|
+
extensionPath
|
|
75
85
|
});
|
|
76
86
|
|
|
77
87
|
metadata = {
|
|
@@ -90,7 +100,14 @@ export async function supervisePreparedRun({
|
|
|
90
100
|
|
|
91
101
|
const child = spawnImpl(launch.command, launch.args, {
|
|
92
102
|
cwd: launch.cwd,
|
|
93
|
-
env:
|
|
103
|
+
env: buildOrchestratedRuntimeEnv({
|
|
104
|
+
homeDir,
|
|
105
|
+
rootRunId: runId,
|
|
106
|
+
rootTaskId: metadata.lineage?.taskId,
|
|
107
|
+
cliVersion: metadata.cliVersion,
|
|
108
|
+
strategy,
|
|
109
|
+
baseEnv: launch.env ?? process.env
|
|
110
|
+
}),
|
|
94
111
|
stdio: ["ignore", "pipe", "pipe"]
|
|
95
112
|
});
|
|
96
113
|
activeProcesses?.set(runId, child);
|
|
@@ -244,6 +261,9 @@ export async function supervisePreparedRun({
|
|
|
244
261
|
type: failed ? "run.failed" : "run.completed",
|
|
245
262
|
data: { exitCode }
|
|
246
263
|
}), { captureTranscript: shouldPersistTranscript(captureTranscript) });
|
|
264
|
+
if (!failed && strategy === RUN_STRATEGIES.ORCHESTRATED) {
|
|
265
|
+
await finalizeOrchState(runId, { homeDir, recovered: false });
|
|
266
|
+
}
|
|
247
267
|
resolve(metadata);
|
|
248
268
|
});
|
|
249
269
|
} catch (error) {
|
|
@@ -83,7 +83,9 @@ export function createRunMetadata({
|
|
|
83
83
|
permissions = [],
|
|
84
84
|
captureTranscript = false,
|
|
85
85
|
cliVersion,
|
|
86
|
-
profileSources = null
|
|
86
|
+
profileSources = null,
|
|
87
|
+
strategy = "direct",
|
|
88
|
+
lineage = null
|
|
87
89
|
}) {
|
|
88
90
|
const { taskDigest, taskLength } = createTaskFingerprint(task);
|
|
89
91
|
const now = new Date().toISOString();
|
|
@@ -100,6 +102,8 @@ export function createRunMetadata({
|
|
|
100
102
|
captureTranscript,
|
|
101
103
|
cliVersion,
|
|
102
104
|
profileSources,
|
|
105
|
+
strategy,
|
|
106
|
+
lineage,
|
|
103
107
|
state: RUN_STATES.PENDING,
|
|
104
108
|
pid: null,
|
|
105
109
|
supervisorPid: null,
|