@kal-elsam/kairo-runtime 0.20.0 → 0.21.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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ Historical entries below may reference the legacy `@kal-elsam/harness` package n
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 0.21.0 — 2026-09-18 (Kairo Runtime)
|
|
9
|
+
|
|
10
|
+
Patch-level polish. When a role resolves to a manual-only provider
|
|
11
|
+
(OpenCode Go), the `planExecution` preview now carries the exact
|
|
12
|
+
real task text an automatic run would use, and the cockpit pushes it
|
|
13
|
+
into the transcript ready to paste into that provider's own chat —
|
|
14
|
+
instead of only naming the assigned model.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `buildExecutionTaskPrompt` (service.js) is the one real formula for a
|
|
19
|
+
task's launch text, shared by `executePlan` (real automatic runs)
|
|
20
|
+
and `planExecution`'s `MANUAL_HANDOFF` preview (`taskPrompt`).
|
|
21
|
+
|
|
8
22
|
## 0.20.0 — 2026-09-18 (Kairo Runtime)
|
|
9
23
|
|
|
10
24
|
Minor release. Promotes Cursor to a real automatic execution provider.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kal-elsam/kairo-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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",
|
|
@@ -190,6 +190,16 @@ export async function runCockpitApp({
|
|
|
190
190
|
return runAction("Asking PROJECT TEAM who should execute this", async () => {
|
|
191
191
|
const decision = await service.planExecution({ cwd, taskId, role });
|
|
192
192
|
view.showExecuteConfirm(taskId, decision);
|
|
193
|
+
// MANUAL_HANDOFF: Kairo can't launch this itself, so the real
|
|
194
|
+
// task text (the exact same one an automatic run would get —
|
|
195
|
+
// see service.js's buildExecutionTaskPrompt) goes into the
|
|
196
|
+
// transcript, ready to paste into the assigned provider's own
|
|
197
|
+
// chat — never just naming the model and leaving the human to
|
|
198
|
+
// reconstruct the prompt themselves.
|
|
199
|
+
if (decision.decision === "MANUAL_HANDOFF" && decision.taskPrompt) {
|
|
200
|
+
const modelLabel = decision.modelRef?.displayName ?? decision.model ?? "the assigned model";
|
|
201
|
+
pushTranscript("kairo", `${decision.provider} · ${modelLabel} can't be launched automatically — paste this into its chat:\n\n${decision.taskPrompt}`);
|
|
202
|
+
}
|
|
193
203
|
});
|
|
194
204
|
},
|
|
195
205
|
onExecute: (taskId, decision) => {
|
|
@@ -367,6 +367,24 @@ export function createConversationService(deps = {}) {
|
|
|
367
367
|
|
|
368
368
|
async function root(cwd) { return resolveRoot(cwd); }
|
|
369
369
|
|
|
370
|
+
/**
|
|
371
|
+
* The exact real task text an automatic run gets launched with (see
|
|
372
|
+
* executePlan) — the ONE real formula, never a second one invented for
|
|
373
|
+
* display purposes. Reused by toExecutionPreview so a MANUAL_HANDOFF
|
|
374
|
+
* preview can hand the human this same text, ready to paste into the
|
|
375
|
+
* manual-only provider's own chat, instead of just naming the model.
|
|
376
|
+
* @param {string} planMarkdown
|
|
377
|
+
*/
|
|
378
|
+
function buildExecutionTaskPrompt(planMarkdown) {
|
|
379
|
+
return [
|
|
380
|
+
"Implement the explicitly approved architecture plan below.",
|
|
381
|
+
"Follow repository AGENTS.md and Gentle governance. Do not treat plan approval as any additional governance receipt.",
|
|
382
|
+
"Use safe, non-bypassed permissions for this session.",
|
|
383
|
+
"",
|
|
384
|
+
planMarkdown
|
|
385
|
+
].join("\n");
|
|
386
|
+
}
|
|
387
|
+
|
|
370
388
|
/**
|
|
371
389
|
* Projects a real project-router decision into the public
|
|
372
390
|
* ProjectExecutionPreview shape — never recalculates anything the
|
|
@@ -380,20 +398,28 @@ export function createConversationService(deps = {}) {
|
|
|
380
398
|
* - Everything else (MANUAL_HANDOFF, or WAIT_FOR_PROJECT_TEAM with no
|
|
381
399
|
* real alternative): null — nothing to confirm into an automatic run.
|
|
382
400
|
* @param {ReturnType<typeof resolveProjectRoute>} route
|
|
401
|
+
* @param {{planMarkdown: string}|null} [record] - present only from
|
|
402
|
+
* planExecution (which already read the real plan record); used to
|
|
403
|
+
* attach `taskPrompt` for a MANUAL_HANDOFF decision only — a ROUTED/
|
|
404
|
+
* WAIT_FOR_PROJECT_TEAM preview never needs it, since executePlan
|
|
405
|
+
* builds the real task text itself from the SAME buildExecutionTaskPrompt.
|
|
383
406
|
*/
|
|
384
|
-
function toExecutionPreview(route) {
|
|
407
|
+
function toExecutionPreview(route, record = null) {
|
|
385
408
|
let confirmationTarget = null;
|
|
386
409
|
if (route.decision === "ROUTED" && route.model) {
|
|
387
410
|
confirmationTarget = { role: route.role, selection: "assigned", strategyFingerprint: route.strategyFingerprint, candidateKey: route.model.candidateKey ?? null };
|
|
388
411
|
} else if (route.decision === "WAIT_FOR_PROJECT_TEAM" && route.suggestedAlternative?.model) {
|
|
389
412
|
confirmationTarget = { role: route.role, selection: "suggested-alternative", strategyFingerprint: route.strategyFingerprint, candidateKey: route.suggestedAlternative.model.candidateKey ?? null };
|
|
390
413
|
}
|
|
414
|
+
const taskPrompt = route.decision === "MANUAL_HANDOFF" && record?.planMarkdown
|
|
415
|
+
? buildExecutionTaskPrompt(record.planMarkdown)
|
|
416
|
+
: null;
|
|
391
417
|
return {
|
|
392
418
|
decision: route.decision, role: route.role,
|
|
393
419
|
provider: route.provider, model: route.model?.modelId ?? null, modelRef: route.model,
|
|
394
420
|
assignmentSource: route.assignmentSource, strategyFingerprint: route.strategyFingerprint, why: route.why,
|
|
395
421
|
blockedAssignment: route.blockedAssignment, suggestedAlternative: route.suggestedAlternative,
|
|
396
|
-
confirmationTarget
|
|
422
|
+
confirmationTarget, taskPrompt
|
|
397
423
|
};
|
|
398
424
|
}
|
|
399
425
|
|
|
@@ -961,7 +987,7 @@ export function createConversationService(deps = {}) {
|
|
|
961
987
|
const record = await readPlan(projectRoot, taskId);
|
|
962
988
|
if (!record) throw new Error(`Plan "${taskId}" not found.`);
|
|
963
989
|
const route = await this.routeProjectExecution(role, projectRoot);
|
|
964
|
-
return { ...toExecutionPreview(route), projectRoot, taskId };
|
|
990
|
+
return { ...toExecutionPreview(route, record), projectRoot, taskId };
|
|
965
991
|
},
|
|
966
992
|
/**
|
|
967
993
|
* @param {object} args
|
|
@@ -1010,13 +1036,7 @@ export function createConversationService(deps = {}) {
|
|
|
1010
1036
|
if (!raced) throw error;
|
|
1011
1037
|
return { ...publicPlan(record, raced), projectRoot, reused: true };
|
|
1012
1038
|
}
|
|
1013
|
-
const task =
|
|
1014
|
-
"Implement the explicitly approved architecture plan below.",
|
|
1015
|
-
"Follow repository AGENTS.md and Gentle governance. Do not treat plan approval as any additional governance receipt.",
|
|
1016
|
-
"Use safe, non-bypassed permissions for this session.",
|
|
1017
|
-
"",
|
|
1018
|
-
record.planMarkdown
|
|
1019
|
-
].join("\n");
|
|
1039
|
+
const task = buildExecutionTaskPrompt(record.planMarkdown);
|
|
1020
1040
|
try {
|
|
1021
1041
|
const started = await launchRun({
|
|
1022
1042
|
homeDir, runId, agentId: resolvedAgentId, task, cwd: projectRoot, model: resolvedModel,
|
|
@@ -89,7 +89,7 @@ export async function readCodexModels({
|
|
|
89
89
|
child.once?.("close", () => { if (!finished) finish(unknown("codex app-server closed before model list")); });
|
|
90
90
|
|
|
91
91
|
writeRequest(child, 1, "initialize", {
|
|
92
|
-
clientInfo: { name: "kairo", title: "Kairo", version: "0.
|
|
92
|
+
clientInfo: { name: "kairo", title: "Kairo", version: "0.21.0" },
|
|
93
93
|
capabilities: {}
|
|
94
94
|
});
|
|
95
95
|
});
|
|
@@ -151,7 +151,7 @@ export async function readCodexUsage({
|
|
|
151
151
|
child.once?.("close", () => { if (!finished) finish(unknown("codex app-server closed before rate limits")); });
|
|
152
152
|
|
|
153
153
|
writeRequest(child, 1, "initialize", {
|
|
154
|
-
clientInfo: { name: "kairo", title: "Kairo", version: "0.
|
|
154
|
+
clientInfo: { name: "kairo", title: "Kairo", version: "0.21.0" },
|
|
155
155
|
capabilities: {}
|
|
156
156
|
});
|
|
157
157
|
});
|