@kognai/orchestrator-core 0.2.3 → 0.2.5

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.
@@ -886,15 +886,52 @@ async function runSprintCycle(opts) {
886
886
  // overlapping orphans can't accumulate even if cron ever gets re-armed
887
887
  const orchestratorTimeoutMs = PER_RUN_HARD_TIMEOUT_MIN * 60 * 1000;
888
888
  log(`Spawning orchestrator with ${PER_RUN_HARD_TIMEOUT_MIN}-min hard timeout`);
889
- const result = (0, child_process_1.spawnSync)('npx', ['ts-node', orchestratorPath, activePath], {
890
- stdio: 'inherit',
891
- cwd: ROOT,
892
- env: { ...process.env },
893
- timeout: orchestratorTimeoutMs,
894
- killSignal: 'SIGKILL', // SIGTERM ignored when blocked on subprocess I/O
889
+ // TICKET-347: spawnSync's `timeout` only SIGKILLs the DIRECT child (`npx`).
890
+ // Its `ts-node` child + the orchestrator grandchildren (and everything THEY
891
+ // spawn) get orphaned and keep running — reparented to init. A live incident
892
+ // (2026-06-13) had a "timed-out" run keep executing for ~2h after the runner
893
+ // declared it failed, burning ~1.3M tokens + attempting wallet settlements.
894
+ // Fix: spawn DETACHED so the child leads its own process group, then SIGKILL
895
+ // the WHOLE group (negative pid) on timeout — taking the entire subtree down.
896
+ const result = await new Promise((resolveRun) => {
897
+ const child = (0, child_process_1.spawn)('npx', ['ts-node', orchestratorPath, activePath], {
898
+ stdio: 'inherit',
899
+ cwd: ROOT,
900
+ env: { ...process.env },
901
+ detached: true, // new process group (setsid) → killable as a unit
902
+ });
903
+ let timedOut = false;
904
+ let settled = false;
905
+ const finish = (status) => {
906
+ if (settled)
907
+ return;
908
+ settled = true;
909
+ clearTimeout(timer);
910
+ resolveRun({ status, timedOut });
911
+ };
912
+ const timer = setTimeout(() => {
913
+ timedOut = true;
914
+ // Negative pid = signal the entire process group (the detached subtree).
915
+ try {
916
+ if (child.pid)
917
+ process.kill(-child.pid, 'SIGKILL');
918
+ }
919
+ catch { /* group already gone */ }
920
+ // Belt-and-braces: also target the direct child in case the group call missed.
921
+ try {
922
+ child.kill('SIGKILL');
923
+ }
924
+ catch { /* already dead */ }
925
+ }, orchestratorTimeoutMs);
926
+ child.on('error', (err) => { log(`Orchestrator spawn error: ${err.message}`); finish(1); });
927
+ child.on('exit', (code) => finish(code));
895
928
  });
896
929
  const elapsed = Math.round((Date.now() - start) / 60000);
897
- const status = result.status === 0 ? '✅ Completed' : `❌ Failed (exit ${result.status})`;
930
+ const status = result.status === 0
931
+ ? '✅ Completed'
932
+ : result.timedOut
933
+ ? `⏱️ Killed — ${PER_RUN_HARD_TIMEOUT_MIN}-min hard timeout (process group SIGKILLed)`
934
+ : `❌ Failed (exit ${result.status})`;
898
935
  log(`Orchestrator finished: ${status} (${elapsed} min)`);
899
936
  // Founder directive 2026-05-26: on non-zero exit, write an incident record
900
937
  // and emit an event for CTO + CEO to investigate autonomously. The swarm
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kognai/orchestrator-core",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Kognai sovereign orchestrator — core engine (template-agnostic). Shared by all products (Kognai/coding, Voxight/market-intel, Invoica/fin-compliance); each supplies only its template. Replaces per-repo forks of orchestrate-agents-v2 / sprint-runner / lib.",
5
5
  "license": "MIT",
6
6
  "author": "SkinGem",
@@ -1,80 +0,0 @@
1
- /**
2
- * Defines the task kind for decomposition gate scoring.
3
- * @category DecompositionGate
4
- */
5
- export type TaskKind = 'MODIFY' | 'CREATE';
6
- /**
7
- * Represents an edit point in the task context.
8
- * @property kind - The type of edit (e.g., 'import', 'function_body')
9
- * @property location - The file path or symbol where the edit occurs
10
- * @property description - Optional description of the edit
11
- * @category DecompositionGate
12
- */
13
- export interface EditPoint {
14
- kind: 'import' | 'guard' | 'function_body' | 'export';
15
- location: string;
16
- description?: string;
17
- }
18
- /**
19
- * Complexity score for a task before decomposition.
20
- * @property editPointCount - Total number of edit points
21
- * @property editPoints - Array of edit points
22
- * @property estimatedLines - Estimated number of lines to modify
23
- * @property taskKind - The kind of task (MODIFY or CREATE)
24
- * @category DecompositionGate
25
- */
26
- export interface ComplexityScore {
27
- editPointCount: number;
28
- editPoints: EditPoint[];
29
- estimatedLines: number;
30
- taskKind: TaskKind;
31
- }
32
- /**
33
- * Decision outcome from the decomposition gate.
34
- * @property accepted - Whether the task is accepted for decomposition
35
- * @property reason - Reason for acceptance or rejection
36
- * @property score - Complexity score of the task
37
- * @property redecomposeFeedback - Optional feedback for redecomposing the task (if rejected)
38
- * @category DecompositionGate
39
- */
40
- export interface GateDecision {
41
- accepted: boolean;
42
- reason: string;
43
- score: ComplexityScore;
44
- redecomposeFeedback?: string;
45
- }
46
- /**
47
- * Mode of the decomposition gate.
48
- * @property shadow - Logs only (no hard rejection)
49
- * @property enforce - Hard rejects tasks that exceed complexity limits
50
- * @category DecompositionGate
51
- */
52
- export type GateMode = 'shadow' | 'enforce';
53
- /**
54
- * Configuration for the decomposition gate.
55
- * @property mode - Operation mode (shadow or enforce)
56
- * @property maxEditPointsModify - Maximum edit points for modify tasks (default: 1)
57
- * @property maxLinesCreate - Maximum lines for create tasks (default: 200)
58
- * @category DecompositionGate
59
- */
60
- export interface GateConfig {
61
- mode: GateMode;
62
- maxEditPointsModify: number;
63
- maxLinesCreate: number;
64
- }
65
- /**
66
- * Input for the decomposition gate scoring.
67
- * @property id - Unique identifier for the task
68
- * @property taskKind - The kind of task (MODIFY or CREATE)
69
- * @property description - Description of the task
70
- * @property targetFiles - List of target files for the task
71
- * @property estimatedLines - Estimated number of lines (optional)
72
- * @category DecompositionGate
73
- */
74
- export interface TicketInput {
75
- id: string;
76
- taskKind: TaskKind;
77
- description: string;
78
- targetFiles: string[];
79
- estimatedLines?: number;
80
- }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
File without changes
@@ -1,3 +0,0 @@
1
- "use strict";
2
- // ERROR: Generation failed - ClawRouter timeout (480s)
3
- // Task: cto_20260605_004_3-A