infinicode 2.3.1 → 2.3.3

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.
Files changed (39) hide show
  1. package/dist/cli.js +2 -1
  2. package/dist/commands/mcp.js +2 -0
  3. package/dist/commands/serve.d.ts +1 -0
  4. package/dist/commands/serve.js +36 -1
  5. package/dist/kernel/capability-map.d.ts +18 -0
  6. package/dist/kernel/capability-map.js +84 -0
  7. package/dist/kernel/checkpoint-engine.js +9 -0
  8. package/dist/kernel/command-system.d.ts +3 -0
  9. package/dist/kernel/command-system.js +315 -10
  10. package/dist/kernel/config-schema.d.ts +11 -0
  11. package/dist/kernel/config-schema.js +1 -0
  12. package/dist/kernel/federation/event-bridge.js +3 -1
  13. package/dist/kernel/federation/federation.d.ts +10 -0
  14. package/dist/kernel/federation/federation.js +28 -9
  15. package/dist/kernel/free-providers.d.ts +23 -0
  16. package/dist/kernel/free-providers.js +73 -1
  17. package/dist/kernel/index.d.ts +4 -0
  18. package/dist/kernel/index.js +3 -0
  19. package/dist/kernel/kernel.d.ts +12 -0
  20. package/dist/kernel/kernel.js +29 -2
  21. package/dist/kernel/mission-engine.js +14 -0
  22. package/dist/kernel/orchestrator.d.ts +3 -0
  23. package/dist/kernel/orchestrator.js +74 -2
  24. package/dist/kernel/planner.d.ts +42 -0
  25. package/dist/kernel/planner.js +174 -0
  26. package/dist/kernel/recovery-manager.d.ts +9 -0
  27. package/dist/kernel/recovery-manager.js +64 -3
  28. package/dist/kernel/router.d.ts +12 -1
  29. package/dist/kernel/router.js +36 -1
  30. package/dist/kernel/scheduler.d.ts +17 -0
  31. package/dist/kernel/scheduler.js +97 -1
  32. package/dist/kernel/setup.d.ts +9 -0
  33. package/dist/kernel/setup.js +38 -0
  34. package/dist/kernel/types.d.ts +65 -2
  35. package/dist/kernel/verification-engine.js +83 -27
  36. package/dist/kernel/verification-exec.d.ts +14 -0
  37. package/dist/kernel/verification-exec.js +65 -0
  38. package/dist/kernel/worker-runtime.js +3 -2
  39. package/package.json +7 -7
@@ -19,6 +19,15 @@ export interface Objective {
19
19
  status: TaskStatus;
20
20
  createdAt: number;
21
21
  completedAt?: number;
22
+ /** Human phase label for large projects (e.g. "architecture", "frontend"). */
23
+ phase?: string;
24
+ /**
25
+ * Minimum model benchmark (0–100) this phase should route to. Lets the
26
+ * orchestrator assign a more capable model to demanding phases (architecture,
27
+ * verification) and a cheaper one to light phases (docs) — instead of one
28
+ * model for the whole mission. Flows into each task's routing floor.
29
+ */
30
+ minBenchmark?: number;
22
31
  }
23
32
  export interface Task {
24
33
  id: string;
@@ -39,6 +48,11 @@ export interface Task {
39
48
  startedAt?: number;
40
49
  completedAt?: number;
41
50
  error?: string;
51
+ /** Routing floor for this task/phase (0–100). Escalates upward on repeated
52
+ * verification failure so the task climbs to a more capable model. */
53
+ minBenchmark?: number;
54
+ /** How many times this task's output failed verification — drives model escalation. */
55
+ verificationFailures?: number;
42
56
  }
43
57
  export interface TaskInput {
44
58
  prompt: string;
@@ -258,6 +272,12 @@ export interface Checkpoint {
258
272
  workerStates: WorkerInstanceState[];
259
273
  timestamp: number;
260
274
  reason: 'task' | 'interval' | 'pause' | 'crash' | 'manual';
275
+ /** Recent event log for this mission — restored so long-running/crash recovery
276
+ * keeps its activity trail. */
277
+ logs?: KernelEvent[];
278
+ /** Free-form memory bag persisted with the checkpoint (mission.metadata plus
279
+ * any harness-supplied working memory). */
280
+ memory?: Record<string, unknown>;
261
281
  }
262
282
  export interface WorkerInstanceState {
263
283
  workerId: string;
@@ -267,7 +287,7 @@ export interface WorkerInstanceState {
267
287
  modelId?: string;
268
288
  partialOutput?: string;
269
289
  }
270
- export type KernelEventType = 'MISSION_STARTED' | 'MISSION_COMPLETED' | 'MISSION_FAILED' | 'MISSION_PAUSED' | 'MISSION_RESUMED' | 'MISSION_CANCELLED' | 'OBJECTIVE_STARTED' | 'OBJECTIVE_COMPLETED' | 'TASK_QUEUED' | 'TASK_STARTED' | 'TASK_COMPLETED' | 'TASK_FAILED' | 'WORKER_SPAWNED' | 'WORKER_EXITED' | 'ROUTING_DECISION' | 'MODEL_CHANGED' | 'PROVIDER_CHANGED' | 'CHECKPOINT' | 'VERIFY_SUCCESS' | 'VERIFY_FAILED' | 'RECOVERY' | 'PROVIDER_HEALTH' | 'NOTIFICATION' | 'LOG';
290
+ export type KernelEventType = 'MISSION_STARTED' | 'MISSION_COMPLETED' | 'MISSION_FAILED' | 'MISSION_PAUSED' | 'MISSION_RESUMED' | 'MISSION_CANCELLED' | 'PLAN_CREATED' | 'OBJECTIVE_STARTED' | 'OBJECTIVE_COMPLETED' | 'TASK_QUEUED' | 'TASK_STARTED' | 'TASK_COMPLETED' | 'TASK_FAILED' | 'WORKER_SPAWNED' | 'WORKER_EXITED' | 'ROUTING_DECISION' | 'MODEL_CHANGED' | 'PROVIDER_CHANGED' | 'CHECKPOINT' | 'VERIFY_SUCCESS' | 'VERIFY_FAILED' | 'RECOVERY' | 'PROVIDER_HEALTH' | 'NOTIFICATION' | 'LOG';
271
291
  export interface KernelEvent<T = unknown> {
272
292
  type: KernelEventType;
273
293
  timestamp: number;
@@ -290,6 +310,12 @@ export interface RoutingRequest {
290
310
  * preventing "request too large" failures on rate-limited tiers.
291
311
  */
292
312
  estimatedRequestTokens?: number;
313
+ /**
314
+ * Minimum acceptable model benchmark (0–100). Models below the floor are
315
+ * heavily penalized (not hard-excluded), so demanding phases route to capable
316
+ * models and escalation can climb the tier ladder.
317
+ */
318
+ minBenchmark?: number;
293
319
  }
294
320
  export interface RoutingDecision {
295
321
  providerId: string;
@@ -382,13 +408,16 @@ export interface LlmJudgeOptions {
382
408
  modelId?: string;
383
409
  }
384
410
  export type FailureType = '429' | 'timeout' | 'provider-down' | 'hallucination' | 'tool-failure' | 'verification-failure' | 'planning-failure' | 'browser-failure' | 'unknown';
385
- export type RecoveryAction = 'retry' | 'rotate-provider' | 'spawn-different-worker' | 'replan' | 'checkpoint' | 'continue' | 'abort';
411
+ export type RecoveryAction = 'retry' | 'rotate-provider' | 'escalate-model' | 'spawn-different-worker' | 'replan' | 'checkpoint' | 'continue' | 'abort';
386
412
  export interface RecoveryDecision {
387
413
  action: RecoveryAction;
388
414
  reason: string;
389
415
  rotateToProviderId?: string;
390
416
  rotateToModelId?: string;
391
417
  newWorkerType?: WorkerType;
418
+ /** For 'escalate-model': the benchmark of the more-capable target, so the
419
+ * orchestrator can raise the task's routing floor and keep climbing. */
420
+ escalateToBenchmark?: number;
392
421
  }
393
422
  export interface PluginManifest {
394
423
  name: string;
@@ -442,6 +471,21 @@ export interface MissionInput {
442
471
  policy?: PolicyName;
443
472
  workspace?: WorkspaceRef;
444
473
  tasks?: MissionTaskInput[];
474
+ /**
475
+ * Explicit phase breakdown (each phase → one objective). When present it wins
476
+ * over `tasks`. Harnesses (e.g. InfiniBot) can pass phases directly with
477
+ * capabilities written naturally ("backend", "ui", "tests") — they're
478
+ * normalized to canonical capabilities and each phase routes to its own model
479
+ * tier via its benchmark floor.
480
+ */
481
+ phases?: MissionPhaseInput[];
482
+ /**
483
+ * When true and neither `tasks` nor `phases` is given, the MissionPlanner
484
+ * decomposes `goal` into phases (LLM-driven, heuristic fallback) before
485
+ * execution — turning a one-line goal into architecture → implementation →
486
+ * verification phases, each with an appropriate model.
487
+ */
488
+ autoPlan?: boolean;
445
489
  metadata?: Record<string, unknown>;
446
490
  }
447
491
  export interface MissionTaskInput {
@@ -450,6 +494,25 @@ export interface MissionTaskInput {
450
494
  dependencies?: string[];
451
495
  input: TaskInput;
452
496
  }
497
+ /**
498
+ * A phase of a mission — becomes one Objective plus its tasks. Capabilities may
499
+ * be written naturally (natural language / synonyms) and are normalized; the
500
+ * phase's model tier is derived from them unless `minBenchmark` is set.
501
+ */
502
+ export interface MissionPhaseInput {
503
+ /** Short phase label, e.g. "architecture", "backend", "frontend", "tests". */
504
+ name: string;
505
+ description: string;
506
+ /** Needed capabilities — natural names allowed ("backend", "ui", "testing"). */
507
+ capabilities?: string[];
508
+ acceptanceCriteria?: string[];
509
+ /** Names of phases that must finish before this one starts. */
510
+ dependsOn?: string[];
511
+ /** Explicit tasks; if omitted the phase runs as a single task from its description. */
512
+ tasks?: MissionTaskInput[];
513
+ /** Explicit model benchmark floor (0–100); else derived from capabilities. */
514
+ minBenchmark?: number;
515
+ }
453
516
  export interface ProviderVerifyResult {
454
517
  ok: boolean;
455
518
  latencyMs: number;
@@ -1,4 +1,7 @@
1
1
  import { scoreFrontend } from './frontend-scoring.js';
2
+ import { runCommand, tail } from './verification-exec.js';
3
+ import { readFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
2
5
  const PIPELINE_ORDER = [
3
6
  'objective',
4
7
  'compile',
@@ -169,38 +172,91 @@ function objectiveVerifier() {
169
172
  };
170
173
  }
171
174
  function compileVerifier() {
172
- return {
173
- stage: 'compile',
174
- enabled: ctx => hasArtifact(ctx.output, 'code') || hasArtifact(ctx.output, 'file'),
175
- async run(ctx) {
176
- const codeArtifacts = ctx.output.artifacts?.filter(a => a.type === 'code' || a.type === 'file') ?? [];
177
- if (codeArtifacts.length === 0) {
178
- return { name: 'compile', passed: true, message: 'no code artifacts' };
179
- }
180
- const hasSyntax = codeArtifacts.every(a => (a.content ?? '').trim().length > 0);
181
- return {
182
- name: 'compile',
183
- passed: hasSyntax,
184
- message: hasSyntax ? `${codeArtifacts.length} artifact(s) non-empty` : 'empty code artifact',
185
- };
186
- },
187
- };
175
+ return commandVerifier('compile', 'compile');
188
176
  }
189
177
  function testsVerifier() {
190
- return {
191
- stage: 'tests',
192
- enabled: () => false,
193
- async run() {
194
- return { name: 'tests', passed: true, message: 'test runner not configured' };
195
- },
196
- };
178
+ return commandVerifier('tests', 'test');
197
179
  }
198
180
  function lintVerifier() {
181
+ return commandVerifier('lint', 'lint');
182
+ }
183
+ /** package.json script names to auto-detect per stage, in priority order. */
184
+ const SCRIPT_CANDIDATES = {
185
+ compile: ['build', 'compile', 'typecheck'],
186
+ test: ['test'],
187
+ lint: ['lint'],
188
+ };
189
+ /** True when the task actually produced code/file artifacts to verify. */
190
+ function producedCode(output) {
191
+ return hasArtifact(output, 'code') || hasArtifact(output, 'file');
192
+ }
193
+ /** Read `<root>/package.json` scripts, or undefined if missing/invalid. */
194
+ function readScripts(root) {
195
+ try {
196
+ const raw = readFileSync(join(root, 'package.json'), 'utf8');
197
+ const pkg = JSON.parse(raw);
198
+ return pkg.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : undefined;
199
+ }
200
+ catch {
201
+ return undefined;
202
+ }
203
+ }
204
+ /** Pull an explicit command for `kind` from task context, then mission metadata. */
205
+ function explicitCommand(ctx, kind) {
206
+ const sources = [ctx.task.input.context?.verify, ctx.mission.metadata?.verify];
207
+ for (const src of sources) {
208
+ if (src && typeof src === 'object') {
209
+ const cmd = src[kind];
210
+ if (typeof cmd === 'string' && cmd.trim().length > 0)
211
+ return cmd.trim();
212
+ }
213
+ }
214
+ return undefined;
215
+ }
216
+ /**
217
+ * Resolve the command for a stage: explicit context/metadata command first,
218
+ * else an auto-detected `npm run <script>` from the workspace package.json.
219
+ * Returns undefined when nothing can be resolved (stage stays disabled).
220
+ */
221
+ function resolveCommand(ctx, kind) {
222
+ const explicit = explicitCommand(ctx, kind);
223
+ if (explicit)
224
+ return explicit;
225
+ const scripts = readScripts(ctx.workspace.root);
226
+ if (!scripts)
227
+ return undefined;
228
+ for (const name of SCRIPT_CANDIDATES[kind]) {
229
+ if (typeof scripts[name] === 'string' && scripts[name].trim().length > 0) {
230
+ return `npm run ${name}`;
231
+ }
232
+ }
233
+ return undefined;
234
+ }
235
+ /**
236
+ * A verifier that runs a real project command (compile/tests/lint) in the
237
+ * mission workspace root and passes/fails on the process exit code. Enabled
238
+ * only when a command resolves AND the task produced code/file artifacts.
239
+ */
240
+ function commandVerifier(stage, kind) {
199
241
  return {
200
- stage: 'lint',
201
- enabled: () => false,
202
- async run() {
203
- return { name: 'lint', passed: true, message: 'linter not configured' };
242
+ stage,
243
+ enabled(ctx) {
244
+ return producedCode(ctx.output) && resolveCommand(ctx, kind) !== undefined;
245
+ },
246
+ async run(ctx) {
247
+ const command = resolveCommand(ctx, kind);
248
+ if (!command) {
249
+ // Should not happen (gated by enabled), but stay safe & non-blocking.
250
+ return { name: stage, passed: true, message: `no ${kind} command resolved` };
251
+ }
252
+ const result = await runCommand(command, ctx.workspace.root);
253
+ const passed = result.code === 0 && !result.timedOut;
254
+ const combined = tail(`${result.stdout}\n${result.stderr}`.trim());
255
+ const status = result.timedOut ? 'timed out' : `exit ${result.code}`;
256
+ const message = passed
257
+ ? `\`${command}\` passed (${status})`
258
+ : `\`${command}\` failed (${status})${combined ? `\n${combined}` : ''}`;
259
+ return { name: stage, passed, message };
204
260
  },
205
261
  };
206
262
  }
@@ -0,0 +1,14 @@
1
+ export interface ExecResult {
2
+ code: number | null;
3
+ stdout: string;
4
+ stderr: string;
5
+ timedOut: boolean;
6
+ }
7
+ /** Keep only the last `max` characters of a string. */
8
+ export declare function tail(text: string, max?: number): string;
9
+ /**
10
+ * Run `command` in `cwd` via a shell, capturing stdout/stderr. On Windows we
11
+ * use `shell: true` so npm scripts / .cmd shims resolve. Resolves (never
12
+ * rejects) with the exit code and captured output; kills the process on timeout.
13
+ */
14
+ export declare function runCommand(command: string, cwd: string, timeoutMs?: number): Promise<ExecResult>;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * OpenKernel — Verification Exec Helper
3
+ *
4
+ * Runs a real project command (compile/tests/lint) in a workspace directory
5
+ * and reports pass/fail on the process exit code. Effect-free, no new deps —
6
+ * just node:child_process.
7
+ */
8
+ import { spawn } from 'node:child_process';
9
+ const DEFAULT_TIMEOUT_MS = 180_000;
10
+ const MAX_TAIL_CHARS = 4000;
11
+ /** Keep only the last `max` characters of a string. */
12
+ export function tail(text, max = MAX_TAIL_CHARS) {
13
+ if (text.length <= max)
14
+ return text;
15
+ return `…${text.slice(text.length - max)}`;
16
+ }
17
+ /**
18
+ * Run `command` in `cwd` via a shell, capturing stdout/stderr. On Windows we
19
+ * use `shell: true` so npm scripts / .cmd shims resolve. Resolves (never
20
+ * rejects) with the exit code and captured output; kills the process on timeout.
21
+ */
22
+ export function runCommand(command, cwd, timeoutMs = DEFAULT_TIMEOUT_MS) {
23
+ return new Promise(resolve => {
24
+ let stdout = '';
25
+ let stderr = '';
26
+ let timedOut = false;
27
+ let settled = false;
28
+ const child = spawn(command, {
29
+ cwd,
30
+ shell: true,
31
+ windowsHide: true,
32
+ });
33
+ const timer = setTimeout(() => {
34
+ timedOut = true;
35
+ try {
36
+ child.kill('SIGKILL');
37
+ }
38
+ catch {
39
+ /* already gone */
40
+ }
41
+ }, timeoutMs);
42
+ child.stdout?.on('data', (chunk) => {
43
+ stdout += chunk.toString();
44
+ if (stdout.length > MAX_TAIL_CHARS * 2)
45
+ stdout = tail(stdout, MAX_TAIL_CHARS * 2);
46
+ });
47
+ child.stderr?.on('data', (chunk) => {
48
+ stderr += chunk.toString();
49
+ if (stderr.length > MAX_TAIL_CHARS * 2)
50
+ stderr = tail(stderr, MAX_TAIL_CHARS * 2);
51
+ });
52
+ const finish = (code) => {
53
+ if (settled)
54
+ return;
55
+ settled = true;
56
+ clearTimeout(timer);
57
+ resolve({ code, stdout, stderr, timedOut });
58
+ };
59
+ child.on('error', err => {
60
+ stderr += `\n${err instanceof Error ? err.message : String(err)}`;
61
+ finish(1);
62
+ });
63
+ child.on('close', code => finish(code));
64
+ });
65
+ }
@@ -109,7 +109,7 @@ export class WorkerRuntime {
109
109
  instance.status = 'INITIALIZED';
110
110
  const plan = override
111
111
  ? this.lockedPlan(override, 'recovery override')
112
- : await this.planRoute(task.capabilities, handler.preferences, instance.type, estimateRequestTokens(task));
112
+ : await this.planRoute(task.capabilities, handler.preferences, instance.type, estimateRequestTokens(task), task.minBenchmark);
113
113
  // Surface the orchestrator's choice (ranked candidates + winner) so the
114
114
  // CLI / TUI can animate and highlight it.
115
115
  this.emitRouting(instance, task, plan);
@@ -189,7 +189,7 @@ export class WorkerRuntime {
189
189
  * router considered. Honors worker pins (a hard lock), otherwise defers to
190
190
  * the policy-driven capability router.
191
191
  */
192
- async planRoute(capabilities, preferences, workerType, estimatedRequestTokens) {
192
+ async planRoute(capabilities, preferences, workerType, estimatedRequestTokens, minBenchmark) {
193
193
  const providers = (await this.ctx.providerManager.getHealthyProviders()).map(p => p.info);
194
194
  const models = await this.ctx.providerManager.getAvailableModels();
195
195
  const policy = this.ctx.policies.get(this.defaultPolicyName);
@@ -214,6 +214,7 @@ export class WorkerRuntime {
214
214
  availableProviders: providers,
215
215
  availableModels: models,
216
216
  estimatedRequestTokens,
217
+ minBenchmark,
217
218
  });
218
219
  if (ranked.length === 0) {
219
220
  return { chosen: null, candidates: [], locked: false, mode };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infinicode",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "OpenKernel — provider-agnostic AI execution kernel. Native coding agent + mission-driven execution runtime.",
5
5
  "type": "module",
6
6
  "main": "./dist/kernel/index.js",
@@ -85,11 +85,11 @@
85
85
  },
86
86
  "optionalDependencies": {
87
87
  "playwright": "^1.47.0",
88
- "infinicode-tui-win32-x64": "2.3.1",
89
- "infinicode-tui-win32-arm64": "2.3.1",
90
- "infinicode-tui-linux-x64": "2.3.1",
91
- "infinicode-tui-linux-arm64": "2.3.1",
92
- "infinicode-tui-darwin-x64": "2.3.1",
93
- "infinicode-tui-darwin-arm64": "2.3.1"
88
+ "infinicode-tui-win32-x64": "^2.3.0",
89
+ "infinicode-tui-win32-arm64": "^2.3.0",
90
+ "infinicode-tui-linux-x64": "^2.3.0",
91
+ "infinicode-tui-linux-arm64": "^2.3.0",
92
+ "infinicode-tui-darwin-x64": "^2.3.0",
93
+ "infinicode-tui-darwin-arm64": "^2.3.0"
94
94
  }
95
95
  }