@vincemakes/kiso-subagent-ext 0.39.2 → 0.40.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/dist/kiso-subagent.mjs +45 -13
- package/package.json +2 -2
package/dist/kiso-subagent.mjs
CHANGED
|
@@ -98,7 +98,10 @@ export default async function createSubagentExtension() {
|
|
|
98
98
|
execute: async (input, ctx) => {
|
|
99
99
|
const tasks = ((input ?? {}).tasks ?? []).slice(0, 8);
|
|
100
100
|
if (tasks.length === 0) return { content: "delegate: no tasks", isError: true, errorKind: "precondition" };
|
|
101
|
-
|
|
101
|
+
// 0.40.0: the parent's own folder (one per project), handed over
|
|
102
|
+
// by the CLI; a pinned KISO_SESSIONS_DIR, or the legacy folder,
|
|
103
|
+
// when no CLI said (a direct load)
|
|
104
|
+
const sessionsDir = delegationConfig().sessionsDir ?? (process.env.KISO_SESSIONS_DIR || join(process.env.KISO_HOME ?? join(homedir(), ".kiso"), "sessions"));
|
|
102
105
|
// P3: the loop now threads the session id through
|
|
103
106
|
// ToolContext.sessionId — the discovery heuristic below is
|
|
104
107
|
// kept ONLY as a fallback for direct tool use / tests.
|
|
@@ -149,14 +152,10 @@ export default async function createSubagentExtension() {
|
|
|
149
152
|
// Partial success is not overall failure — only ALL failed
|
|
150
153
|
// makes the whole result an error.
|
|
151
154
|
// W12: the blob opens with a machine-readable summary line —
|
|
152
|
-
// the ONE-LINE shape the TUI's settled row renders
|
|
153
|
-
// tool calls · R roles · F failed · /last for the report).
|
|
155
|
+
// the ONE-LINE shape the TUI's settled row renders verbatim.
|
|
154
156
|
// The per-section text below is unchanged — the model's
|
|
155
157
|
// view is preserved, the summary is additive.
|
|
156
|
-
const
|
|
157
|
-
const roles = new Set(tasks.map((t) => t.role)).size;
|
|
158
|
-
const failed = sections.filter((s) => s.failed).length;
|
|
159
|
-
const summary = `summary: ${toolCalls} tool calls · ${roles} role${roles === 1 ? "" : "s"} · ${failed} failed`;
|
|
158
|
+
const summary = delegateSummary(sections, new Set(tasks.map((t) => t.role)).size);
|
|
160
159
|
return { content: `${summary}\n${sections.map((s) => s.text).join("\n")}`, isError: sections.every((s) => s.failed) };
|
|
161
160
|
},
|
|
162
161
|
},
|
|
@@ -253,7 +252,7 @@ async function runChild({ childId, role, task, scope, acceptance, model, after,
|
|
|
253
252
|
// the fixed UNRESOLVED instruction the result parser reads back.
|
|
254
253
|
const taskPath = join(manifestDir ?? policyDir, `${childId}.task`);
|
|
255
254
|
writeFileSync(taskPath, `${task}\n\n${UNRESOLVED_INSTRUCTION}\n`, "utf8");
|
|
256
|
-
const { code, stdout, killed } = await runProcess(childId, bin, childCwd, policyDir, taskPath, timeout, signal, model);
|
|
255
|
+
const { code, stdout, killed } = await runProcess(childId, bin, childCwd, policyDir, taskPath, timeout, signal, model, sessionsDir);
|
|
257
256
|
const extraction = await extractChildResult(sessionsDir, childId, `exit ${code}\n${stdout}`);
|
|
258
257
|
const status = killed === "timeout" ? "timeout" : killed === "abort" ? "killed" : code !== 0 && extraction.outcome === "missing" ? "spawn-failed" : extraction.outcome;
|
|
259
258
|
let failed = code !== 0 || killed !== null || extraction.failed;
|
|
@@ -347,13 +346,43 @@ async function runChild({ childId, role, task, scope, acceptance, model, after,
|
|
|
347
346
|
text += `\n FAILED: collecting the worktree's changes: ${collection.reason}${collection.partialPath !== undefined ? ` (partial patch at ${collection.partialPath})` : ""}\n worktree kept at: ${worktree}`;
|
|
348
347
|
}
|
|
349
348
|
}
|
|
350
|
-
return { failed, text, toolCalls: extraction.toolCalls };
|
|
349
|
+
return { failed, ...(failed ? { failKind: failKindOf(status, verification) } : {}), text, toolCalls: extraction.toolCalls };
|
|
351
350
|
} finally {
|
|
352
351
|
rmSync(policyDir, { recursive: true, force: true });
|
|
353
352
|
if (worktree !== null && ownsWorktree && !keepWorktree) removeWorktree(parentCwd, worktree);
|
|
354
353
|
}
|
|
355
354
|
}
|
|
356
355
|
|
|
356
|
+
/** 0.40.0 — why a child failed, in the three words a person acts on: its
|
|
357
|
+
* clock ran out (`timeout`: raise it, or split the task), the parent's
|
|
358
|
+
* acceptance check RAN and failed (`acceptance`: the work is wrong), or
|
|
359
|
+
* anything else (`error`: a crash, an abort, a missing or ambiguous
|
|
360
|
+
* result, a spawn or collection failure — read the report). */
|
|
361
|
+
export function failKindOf(status, verification) {
|
|
362
|
+
if (status === "timeout") return "timeout";
|
|
363
|
+
if (verification !== null && verification !== undefined && verification.passed === false) return "acceptance";
|
|
364
|
+
return "error";
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const FAIL_KIND_ORDER = ["timeout", "acceptance", "error"];
|
|
368
|
+
|
|
369
|
+
/** 0.40.0 — the settled row's marker: how many tasks ran, and — when any
|
|
370
|
+
* failed — how many of each kind, in a fixed order with zero counts
|
|
371
|
+
* omitted. A failed section without a kind counts as an error: a failure
|
|
372
|
+
* is never silently left out of the count. */
|
|
373
|
+
export function delegateSummary(sections, roles) {
|
|
374
|
+
const n = sections.length;
|
|
375
|
+
const toolCalls = sections.reduce((sum, s) => sum + (s.toolCalls ?? 0), 0);
|
|
376
|
+
const failedSections = sections.filter((s) => s.failed);
|
|
377
|
+
const counts = new Map(FAIL_KIND_ORDER.map((k) => [k, 0]));
|
|
378
|
+
for (const s of failedSections) {
|
|
379
|
+
const k = FAIL_KIND_ORDER.includes(s.failKind) ? s.failKind : "error";
|
|
380
|
+
counts.set(k, counts.get(k) + 1);
|
|
381
|
+
}
|
|
382
|
+
const kinds = FAIL_KIND_ORDER.filter((k) => counts.get(k) > 0).map((k) => `${counts.get(k)} ${k}`);
|
|
383
|
+
return `summary: ${n} task${n === 1 ? "" : "s"} · ${toolCalls} tool calls · ${roles} role${roles === 1 ? "" : "s"} · ${failedSections.length} failed${kinds.length > 0 ? ` (${kinds.join(", ")})` : ""}`;
|
|
384
|
+
}
|
|
385
|
+
|
|
357
386
|
/** DT-1a: the fixed trailer every task file ends with — the parser reads the section back. */
|
|
358
387
|
export const UNRESOLVED_INSTRUCTION = 'When you finish, end your reply with a section titled UNRESOLVED listing what you could not do or verify, one item per line starting with "- ", or the single word none.';
|
|
359
388
|
|
|
@@ -363,9 +392,9 @@ function delegationConfig() {
|
|
|
363
392
|
try {
|
|
364
393
|
const raw = process.env.KISO_DELEGATION_CONFIG_JSON;
|
|
365
394
|
const parsed = raw === undefined ? {} : JSON.parse(raw);
|
|
366
|
-
return { checks: parsed.checks ?? {}, profiles: parsed.profiles ?? [] };
|
|
395
|
+
return { checks: parsed.checks ?? {}, profiles: parsed.profiles ?? [], sessionsDir: typeof parsed.sessionsDir === "string" && parsed.sessionsDir !== "" ? parsed.sessionsDir : undefined };
|
|
367
396
|
} catch {
|
|
368
|
-
return { checks: {}, profiles: [] };
|
|
397
|
+
return { checks: {}, profiles: [], sessionsDir: undefined };
|
|
369
398
|
}
|
|
370
399
|
}
|
|
371
400
|
|
|
@@ -590,7 +619,7 @@ export function childArgs(bin, childId, taskPath, model) {
|
|
|
590
619
|
return [bin, ...(model !== undefined ? ["--model", model] : []), "chat", childId, "--task-file", taskPath];
|
|
591
620
|
}
|
|
592
621
|
|
|
593
|
-
function runProcess(childId, bin, cwd, policyDir, taskPath, timeout, signal, model) {
|
|
622
|
+
function runProcess(childId, bin, cwd, policyDir, taskPath, timeout, signal, model, sessionsDir) {
|
|
594
623
|
const depth = Number.parseInt(process.env.KISO_SUBAGENT_DEPTH ?? "0", 10) || 0;
|
|
595
624
|
const child = spawn(process.execPath, childArgs(bin, childId, taskPath, model), {
|
|
596
625
|
cwd,
|
|
@@ -598,6 +627,9 @@ function runProcess(childId, bin, cwd, policyDir, taskPath, timeout, signal, mod
|
|
|
598
627
|
...process.env,
|
|
599
628
|
KISO_SUBAGENT_DEPTH: String(depth + 1),
|
|
600
629
|
KISO_EXTENSIONS_DIR: policyDir,
|
|
630
|
+
// 0.40.0: the child writes its log beside its parent's — the
|
|
631
|
+
// parent reads it back from there — whatever cwd it runs in
|
|
632
|
+
KISO_SESSIONS_DIR: sessionsDir,
|
|
601
633
|
// Modes: a headless child has no human — the mode tiers'
|
|
602
634
|
// ask would stall it. Bypass is the neutral tier here; the
|
|
603
635
|
// role policy dir (allow/deny only — a child must never
|
|
@@ -848,7 +880,7 @@ function removeWorktree(parentCwd, worktree) {
|
|
|
848
880
|
}
|
|
849
881
|
|
|
850
882
|
function failSection(childId, role, task, reason) {
|
|
851
|
-
return { failed: true, text: `[subagent] ${role}: ${task}\n FAILED: ${reason}`, toolCalls: 0 };
|
|
883
|
+
return { failed: true, failKind: "error", text: `[subagent] ${role}: ${task}\n FAILED: ${reason}`, toolCalls: 0 };
|
|
852
884
|
}
|
|
853
885
|
|
|
854
886
|
const msg = (err) => (err instanceof Error ? err.message : String(err));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-subagent-ext",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "kiso official subagent extension \u2014 child kiso processes with role policies, kernel untouched",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"test": "vitest run"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@vincemakes/kiso-core": "0.
|
|
26
|
+
"@vincemakes/kiso-core": "0.40.0",
|
|
27
27
|
"@types/node": "^26.1.2",
|
|
28
28
|
"typescript": "^5.7.2",
|
|
29
29
|
"vitest": "^3.0.0"
|