infinicode 1.0.0 → 2.0.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.
Files changed (157) hide show
  1. package/.opencode/plugins/home-logo-animation.tsx +120 -0
  2. package/.opencode/plugins/routing-mode-display.tsx +135 -0
  3. package/.opencode/themes/infinibot-gold.json +222 -0
  4. package/.opencode/tui.json +8 -0
  5. package/README.md +527 -75
  6. package/dist/ascii-video-animation.d.ts +2 -0
  7. package/dist/ascii-video-animation.js +243 -0
  8. package/dist/cli.js +195 -50
  9. package/dist/commands/console.d.ts +6 -0
  10. package/dist/commands/console.js +111 -0
  11. package/dist/commands/kernel-setup.d.ts +3 -0
  12. package/dist/commands/kernel-setup.js +303 -0
  13. package/dist/commands/mcp.d.ts +12 -0
  14. package/dist/commands/mcp.js +96 -0
  15. package/dist/commands/mission.d.ts +16 -0
  16. package/dist/commands/mission.js +301 -0
  17. package/dist/commands/models.d.ts +3 -1
  18. package/dist/commands/models.js +111 -55
  19. package/dist/commands/providers.d.ts +6 -0
  20. package/dist/commands/providers.js +95 -0
  21. package/dist/commands/run.d.ts +2 -1
  22. package/dist/commands/run.js +349 -59
  23. package/dist/commands/serve.d.ts +18 -0
  24. package/dist/commands/serve.js +127 -0
  25. package/dist/commands/setup.d.ts +1 -1
  26. package/dist/commands/setup.js +77 -44
  27. package/dist/commands/status.d.ts +2 -1
  28. package/dist/commands/status.js +46 -30
  29. package/dist/commands/workers.d.ts +5 -0
  30. package/dist/commands/workers.js +103 -0
  31. package/dist/kernel/autonomy/goal-loop.d.ts +49 -0
  32. package/dist/kernel/autonomy/goal-loop.js +113 -0
  33. package/dist/kernel/autonomy/index.d.ts +8 -0
  34. package/dist/kernel/autonomy/index.js +7 -0
  35. package/dist/kernel/browser/browser-controller.d.ts +57 -0
  36. package/dist/kernel/browser/browser-controller.js +175 -0
  37. package/dist/kernel/checkpoint-engine.d.ts +17 -0
  38. package/dist/kernel/checkpoint-engine.js +128 -0
  39. package/dist/kernel/command-system.d.ts +37 -0
  40. package/dist/kernel/command-system.js +239 -0
  41. package/dist/kernel/config-schema.d.ts +53 -0
  42. package/dist/kernel/config-schema.js +36 -0
  43. package/dist/kernel/event-bus.d.ts +19 -0
  44. package/dist/kernel/event-bus.js +65 -0
  45. package/dist/kernel/federation/auto-update.d.ts +36 -0
  46. package/dist/kernel/federation/auto-update.js +57 -0
  47. package/dist/kernel/federation/compute-router.d.ts +27 -0
  48. package/dist/kernel/federation/compute-router.js +44 -0
  49. package/dist/kernel/federation/config-sync.d.ts +33 -0
  50. package/dist/kernel/federation/config-sync.js +37 -0
  51. package/dist/kernel/federation/discovery.d.ts +11 -0
  52. package/dist/kernel/federation/discovery.js +44 -0
  53. package/dist/kernel/federation/event-bridge.d.ts +30 -0
  54. package/dist/kernel/federation/event-bridge.js +61 -0
  55. package/dist/kernel/federation/federation-plugin.d.ts +24 -0
  56. package/dist/kernel/federation/federation-plugin.js +35 -0
  57. package/dist/kernel/federation/federation.d.ts +126 -0
  58. package/dist/kernel/federation/federation.js +335 -0
  59. package/dist/kernel/federation/index.d.ts +31 -0
  60. package/dist/kernel/federation/index.js +23 -0
  61. package/dist/kernel/federation/moltfed-adapter.d.ts +30 -0
  62. package/dist/kernel/federation/moltfed-adapter.js +52 -0
  63. package/dist/kernel/federation/moltfed-connector.d.ts +98 -0
  64. package/dist/kernel/federation/moltfed-connector.js +193 -0
  65. package/dist/kernel/federation/node-identity.d.ts +19 -0
  66. package/dist/kernel/federation/node-identity.js +86 -0
  67. package/dist/kernel/federation/peer-mesh.d.ts +46 -0
  68. package/dist/kernel/federation/peer-mesh.js +117 -0
  69. package/dist/kernel/federation/protocol.d.ts +20 -0
  70. package/dist/kernel/federation/protocol.js +61 -0
  71. package/dist/kernel/federation/role-context.d.ts +5 -0
  72. package/dist/kernel/federation/role-context.js +45 -0
  73. package/dist/kernel/federation/telemetry.d.ts +21 -0
  74. package/dist/kernel/federation/telemetry.js +138 -0
  75. package/dist/kernel/federation/transport-http.d.ts +44 -0
  76. package/dist/kernel/federation/transport-http.js +207 -0
  77. package/dist/kernel/federation/types.d.ts +212 -0
  78. package/dist/kernel/federation/types.js +3 -0
  79. package/dist/kernel/free-providers.d.ts +28 -0
  80. package/dist/kernel/free-providers.js +162 -0
  81. package/dist/kernel/frontend-scoring.d.ts +21 -0
  82. package/dist/kernel/frontend-scoring.js +125 -0
  83. package/dist/kernel/index.d.ts +63 -0
  84. package/dist/kernel/index.js +45 -0
  85. package/dist/kernel/kernel.d.ts +75 -0
  86. package/dist/kernel/kernel.js +223 -0
  87. package/dist/kernel/logger.d.ts +18 -0
  88. package/dist/kernel/logger.js +26 -0
  89. package/dist/kernel/mcp/index.d.ts +9 -0
  90. package/dist/kernel/mcp/index.js +8 -0
  91. package/dist/kernel/mcp/mcp-server.d.ts +27 -0
  92. package/dist/kernel/mcp/mcp-server.js +178 -0
  93. package/dist/kernel/mission-engine.d.ts +42 -0
  94. package/dist/kernel/mission-engine.js +160 -0
  95. package/dist/kernel/orchestrator.d.ts +51 -0
  96. package/dist/kernel/orchestrator.js +226 -0
  97. package/dist/kernel/plugin-manager.d.ts +28 -0
  98. package/dist/kernel/plugin-manager.js +76 -0
  99. package/dist/kernel/plugins/browser-plugin.d.ts +22 -0
  100. package/dist/kernel/plugins/browser-plugin.js +34 -0
  101. package/dist/kernel/plugins/dashboard-plugin.d.ts +17 -0
  102. package/dist/kernel/plugins/dashboard-plugin.js +72 -0
  103. package/dist/kernel/plugins/discord-plugin.d.ts +11 -0
  104. package/dist/kernel/plugins/discord-plugin.js +35 -0
  105. package/dist/kernel/plugins/metrics-plugin.d.ts +33 -0
  106. package/dist/kernel/plugins/metrics-plugin.js +86 -0
  107. package/dist/kernel/plugins/search-plugin.d.ts +17 -0
  108. package/dist/kernel/plugins/search-plugin.js +20 -0
  109. package/dist/kernel/plugins/slack-plugin.d.ts +11 -0
  110. package/dist/kernel/plugins/slack-plugin.js +35 -0
  111. package/dist/kernel/plugins/telegram-plugin.d.ts +20 -0
  112. package/dist/kernel/plugins/telegram-plugin.js +122 -0
  113. package/dist/kernel/policies.d.ts +33 -0
  114. package/dist/kernel/policies.js +105 -0
  115. package/dist/kernel/provider-discovery.d.ts +41 -0
  116. package/dist/kernel/provider-discovery.js +179 -0
  117. package/dist/kernel/provider-manager.d.ts +38 -0
  118. package/dist/kernel/provider-manager.js +206 -0
  119. package/dist/kernel/provider-url.d.ts +18 -0
  120. package/dist/kernel/provider-url.js +45 -0
  121. package/dist/kernel/providers/gemini-provider.d.ts +43 -0
  122. package/dist/kernel/providers/gemini-provider.js +203 -0
  123. package/dist/kernel/providers/ollama-provider.d.ts +44 -0
  124. package/dist/kernel/providers/ollama-provider.js +241 -0
  125. package/dist/kernel/providers/openai-compatible-provider.d.ts +43 -0
  126. package/dist/kernel/providers/openai-compatible-provider.js +233 -0
  127. package/dist/kernel/recovery-manager.d.ts +38 -0
  128. package/dist/kernel/recovery-manager.js +156 -0
  129. package/dist/kernel/router.d.ts +44 -0
  130. package/dist/kernel/router.js +222 -0
  131. package/dist/kernel/sample-missions.d.ts +11 -0
  132. package/dist/kernel/sample-missions.js +132 -0
  133. package/dist/kernel/scheduler.d.ts +30 -0
  134. package/dist/kernel/scheduler.js +147 -0
  135. package/dist/kernel/sdk/harness-sdk.d.ts +37 -0
  136. package/dist/kernel/sdk/harness-sdk.js +48 -0
  137. package/dist/kernel/sdk/plugin-sdk.d.ts +27 -0
  138. package/dist/kernel/sdk/plugin-sdk.js +40 -0
  139. package/dist/kernel/search/search-controller.d.ts +32 -0
  140. package/dist/kernel/search/search-controller.js +141 -0
  141. package/dist/kernel/setup.d.ts +13 -0
  142. package/dist/kernel/setup.js +59 -0
  143. package/dist/kernel/types.d.ts +479 -0
  144. package/dist/kernel/types.js +7 -0
  145. package/dist/kernel/verification-engine.d.ts +33 -0
  146. package/dist/kernel/verification-engine.js +287 -0
  147. package/dist/kernel/worker-runtime.d.ts +66 -0
  148. package/dist/kernel/worker-runtime.js +261 -0
  149. package/dist/kernel/workers/browser-worker.d.ts +6 -0
  150. package/dist/kernel/workers/browser-worker.js +92 -0
  151. package/dist/kernel/workers/builtin-workers.d.ts +20 -0
  152. package/dist/kernel/workers/builtin-workers.js +120 -0
  153. package/dist/kernel/workers/research-worker.d.ts +5 -0
  154. package/dist/kernel/workers/research-worker.js +90 -0
  155. package/dist/prompt-ascii-video-animation.d.ts +2 -0
  156. package/dist/prompt-ascii-video-animation.js +243 -0
  157. package/package.json +43 -9
@@ -0,0 +1,160 @@
1
+ /**
2
+ * OpenKernel — Mission Engine
3
+ *
4
+ * Responsible for lifecycle, checkpointing, pause, resume, completion.
5
+ * State machine: NEW → PLANNING → READY → RUNNING → VERIFYING → WAITING → FAILED → COMPLETED
6
+ */
7
+ import { nanoid } from 'nanoid';
8
+ export class MissionEngine {
9
+ deps;
10
+ constructor(deps) {
11
+ this.deps = deps;
12
+ }
13
+ create(input) {
14
+ const id = nanoid(12);
15
+ const policyRef = this.deps.policies.resolveRef(input.policy);
16
+ const workspace = input.workspace ?? { root: process.cwd() };
17
+ const mission = {
18
+ id,
19
+ name: input.name ?? `mission-${id.slice(0, 6)}`,
20
+ description: input.description,
21
+ goal: input.goal,
22
+ status: 'NEW',
23
+ objectives: [],
24
+ tasks: [],
25
+ policy: policyRef,
26
+ workspace,
27
+ checkpoints: [],
28
+ createdAt: Date.now(),
29
+ metadata: input.metadata,
30
+ };
31
+ this.deps.logger.info(`Mission created: ${id} — ${mission.name}`);
32
+ return mission;
33
+ }
34
+ plan(mission, input) {
35
+ this.transition(mission, 'PLANNING');
36
+ const { objectives, tasks } = this.deps.scheduler.planMission(mission.id, input);
37
+ mission.objectives = objectives;
38
+ mission.tasks = tasks;
39
+ this.transition(mission, 'READY');
40
+ this.deps.logger.info(`Mission planned: ${mission.id} — ${tasks.length} task(s)`);
41
+ return mission;
42
+ }
43
+ start(mission) {
44
+ if (mission.status === 'COMPLETED' || mission.status === 'FAILED' || mission.status === 'CANCELLED') {
45
+ throw new Error(`Mission ${mission.id} is ${mission.status}; cannot start`);
46
+ }
47
+ this.transition(mission, 'RUNNING');
48
+ mission.startedAt = mission.startedAt ?? Date.now();
49
+ this.emit(mission, 'MISSION_STARTED', { missionId: mission.id, name: mission.name });
50
+ return mission;
51
+ }
52
+ pause(mission) {
53
+ if (mission.status !== 'RUNNING')
54
+ return mission;
55
+ this.transition(mission, 'PAUSED');
56
+ void this.deps.checkpoints.save(mission, [], 'pause');
57
+ this.emit(mission, 'MISSION_PAUSED', { missionId: mission.id });
58
+ return mission;
59
+ }
60
+ resume(mission) {
61
+ if (mission.status !== 'PAUSED' && mission.status !== 'WAITING')
62
+ return mission;
63
+ const latest = this.deps.checkpoints.latest(mission.id);
64
+ if (latest) {
65
+ // restore mutable state
66
+ mission.objectives = latest.mission.objectives;
67
+ mission.tasks = latest.mission.tasks;
68
+ }
69
+ this.transition(mission, 'RUNNING');
70
+ this.emit(mission, 'MISSION_RESUMED', { missionId: mission.id });
71
+ return mission;
72
+ }
73
+ cancel(mission) {
74
+ this.transition(mission, 'CANCELLED');
75
+ this.emit(mission, 'MISSION_CANCELLED', { missionId: mission.id });
76
+ return mission;
77
+ }
78
+ complete(mission) {
79
+ this.transition(mission, 'VERIFYING');
80
+ this.deps.scheduler.reconcileObjectives(mission);
81
+ if (this.deps.scheduler.allObjectivesComplete(mission)) {
82
+ this.transition(mission, 'COMPLETED');
83
+ mission.completedAt = Date.now();
84
+ this.emit(mission, 'MISSION_COMPLETED', { missionId: mission.id });
85
+ }
86
+ else {
87
+ this.transition(mission, 'RUNNING');
88
+ }
89
+ return mission;
90
+ }
91
+ /**
92
+ * Mission-level verification during the VERIFYING phase.
93
+ * Runs the Verification Engine pipeline for every completed task that
94
+ * has output, then completes the mission only if all checks pass (or
95
+ * verification is not strict). Returns the aggregated result.
96
+ */
97
+ async verify(mission) {
98
+ if (!this.deps.verification) {
99
+ return { passed: true, summary: 'no verification engine' };
100
+ }
101
+ this.transition(mission, 'VERIFYING');
102
+ const policy = mission.policy.policy;
103
+ let allPassed = true;
104
+ const summaries = [];
105
+ for (const task of mission.tasks) {
106
+ if (task.status !== 'COMPLETED' || !task.output)
107
+ continue;
108
+ const result = await this.deps.verification.verify(mission, task, policy);
109
+ if (!result.passed) {
110
+ allPassed = false;
111
+ summaries.push(`${task.id}: ${result.summary}`);
112
+ }
113
+ }
114
+ if (allPassed) {
115
+ this.transition(mission, 'COMPLETED');
116
+ mission.completedAt = Date.now();
117
+ this.emit(mission, 'MISSION_COMPLETED', { missionId: mission.id });
118
+ }
119
+ else {
120
+ this.transition(mission, 'FAILED');
121
+ mission.error = `Verification failed: ${summaries.join('; ')}`;
122
+ this.emit(mission, 'MISSION_FAILED', { missionId: mission.id, error: mission.error });
123
+ }
124
+ return { passed: allPassed, summary: allPassed ? 'all verifications passed' : summaries.join('; ') };
125
+ }
126
+ fail(mission, error) {
127
+ mission.error = error;
128
+ this.transition(mission, 'FAILED');
129
+ this.emit(mission, 'MISSION_FAILED', { missionId: mission.id, error });
130
+ return mission;
131
+ }
132
+ /**
133
+ * Resync mission state from a checkpoint (crash recovery).
134
+ */
135
+ restoreFromCheckpoint(mission) {
136
+ const latest = this.deps.checkpoints.latest(mission.id);
137
+ if (!latest)
138
+ return mission;
139
+ mission.status = latest.mission.status === 'RUNNING' ? 'WAITING' : latest.mission.status;
140
+ mission.objectives = latest.mission.objectives;
141
+ mission.tasks = latest.mission.tasks;
142
+ mission.checkpoints = latest.mission.checkpoints;
143
+ this.deps.logger.info(`Mission ${mission.id} restored from checkpoint ${latest.id}`);
144
+ return mission;
145
+ }
146
+ transition(mission, next) {
147
+ const prev = mission.status;
148
+ mission.status = next;
149
+ this.deps.logger.info(`Mission ${mission.id}: ${prev} → ${next}`);
150
+ }
151
+ emit(mission, type, data) {
152
+ const event = {
153
+ type,
154
+ timestamp: Date.now(),
155
+ missionId: mission.id,
156
+ data,
157
+ };
158
+ this.deps.eventBus.emit(event);
159
+ }
160
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * OpenKernel — Native Orchestrator
3
+ *
4
+ * Always running. Coordinates Mission Engine, Scheduler, Worker Runtime,
5
+ * Provider Manager, Router, Verification Engine, Recovery Manager,
6
+ * Checkpoints. Never executes tasks directly.
7
+ */
8
+ import type { Logger, Mission, MissionInput } from './types.js';
9
+ import type { EventBus } from './event-bus.js';
10
+ import type { MissionEngine } from './mission-engine.js';
11
+ import type { Scheduler } from './scheduler.js';
12
+ import type { WorkerRuntime } from './worker-runtime.js';
13
+ import type { CheckpointEngine } from './checkpoint-engine.js';
14
+ import type { ProviderManager } from './provider-manager.js';
15
+ import type { CapabilityRouter } from './router.js';
16
+ import type { VerificationEngine } from './verification-engine.js';
17
+ import type { RecoveryManager } from './recovery-manager.js';
18
+ export interface OrchestratorDeps {
19
+ eventBus: EventBus;
20
+ missionEngine: MissionEngine;
21
+ scheduler: Scheduler;
22
+ workerRuntime: WorkerRuntime;
23
+ checkpoints: CheckpointEngine;
24
+ providerManager: ProviderManager;
25
+ router: CapabilityRouter;
26
+ verification: VerificationEngine;
27
+ recovery: RecoveryManager;
28
+ logger: Logger;
29
+ }
30
+ export declare class Orchestrator {
31
+ private deps;
32
+ private pausedMissions;
33
+ private cancelledMissions;
34
+ private workersByMission;
35
+ /** Per-task forced provider/model overrides set by the recovery manager. */
36
+ private taskOverrides;
37
+ /** Per-task forced worker type override set by the recovery manager. */
38
+ private taskWorkerOverrides;
39
+ constructor(deps: OrchestratorDeps);
40
+ run(mission: Mission, input: MissionInput): Promise<Mission>;
41
+ private runTasks;
42
+ private executeTask;
43
+ private handleRecovery;
44
+ private skipBlockedTasks;
45
+ private trackWorker;
46
+ private collectWorkerStates;
47
+ pauseMission(missionId: string): void;
48
+ resumeMission(missionId: string): void;
49
+ cancelMission(missionId: string): void;
50
+ private sleep;
51
+ }
@@ -0,0 +1,226 @@
1
+ export class Orchestrator {
2
+ deps;
3
+ pausedMissions = new Set();
4
+ cancelledMissions = new Set();
5
+ workersByMission = new Map();
6
+ /** Per-task forced provider/model overrides set by the recovery manager. */
7
+ taskOverrides = new Map();
8
+ /** Per-task forced worker type override set by the recovery manager. */
9
+ taskWorkerOverrides = new Map();
10
+ constructor(deps) {
11
+ this.deps = deps;
12
+ }
13
+ async run(mission, input) {
14
+ // 1. Plan if not yet planned
15
+ if (mission.tasks.length === 0) {
16
+ this.deps.missionEngine.plan(mission, input);
17
+ }
18
+ // 2. Start
19
+ this.deps.missionEngine.start(mission);
20
+ // 3. Execute tasks
21
+ const parallelism = mission.policy.policy.execution?.parallelism ?? 4;
22
+ await this.runTasks(mission, parallelism);
23
+ // 4. Verify the mission as a whole (objective-level + LLM judge)
24
+ if (this.deps.scheduler.isMissionComplete(mission)) {
25
+ const vresult = await this.deps.missionEngine.verify(mission);
26
+ if (!vresult.passed && !mission.policy.policy.verification?.strict) {
27
+ // non-strict: fall back to simple completion if verification engine returned failure
28
+ this.deps.missionEngine.complete(mission);
29
+ }
30
+ }
31
+ else if (this.deps.scheduler.hasFailed(mission)) {
32
+ this.deps.missionEngine.fail(mission, 'One or more tasks failed beyond retry limit');
33
+ }
34
+ // 5. Final checkpoint
35
+ await this.deps.checkpoints.save(mission, this.collectWorkerStates(mission.id), 'task');
36
+ return mission;
37
+ }
38
+ async runTasks(mission, parallelism) {
39
+ while (mission.status === 'RUNNING') {
40
+ if (this.cancelledMissions.has(mission.id))
41
+ return;
42
+ // handle pause
43
+ while (this.pausedMissions.has(mission.id) && mission.status === 'RUNNING') {
44
+ await this.sleep(200);
45
+ if (this.cancelledMissions.has(mission.id))
46
+ return;
47
+ }
48
+ const ready = this.deps.scheduler.nextReadyTasks(mission, parallelism);
49
+ if (ready.length === 0) {
50
+ // nothing ready — check if done
51
+ if (this.deps.scheduler.isMissionComplete(mission))
52
+ return;
53
+ if (this.deps.scheduler.hasFailed(mission))
54
+ return;
55
+ // stuck waiting on dependencies that failed → mark blocked tasks skipped
56
+ this.skipBlockedTasks(mission);
57
+ if (this.deps.scheduler.isMissionComplete(mission))
58
+ return;
59
+ // truly nothing more to do
60
+ return;
61
+ }
62
+ // execute ready tasks in parallel
63
+ await Promise.all(ready.map(task => this.executeTask(mission, task)));
64
+ // checkpoint after each wave if policy says so
65
+ if (mission.policy.policy.checkpoint?.everyTask) {
66
+ await this.deps.checkpoints.save(mission, this.collectWorkerStates(mission.id), 'task');
67
+ }
68
+ }
69
+ }
70
+ async executeTask(mission, task) {
71
+ const overrideType = this.taskWorkerOverrides.get(task.id);
72
+ let workerType = overrideType ?? this.deps.workerRuntime.matchWorkerType(task.capabilities);
73
+ if (!workerType) {
74
+ task.status = 'FAILED';
75
+ task.error = `No worker registered for capabilities: ${task.capabilities.join(', ')}`;
76
+ this.deps.logger.error(`Task ${task.id}: ${task.error}`);
77
+ return;
78
+ }
79
+ // Apply recovery-mandated provider/model override if any.
80
+ const providerOverride = this.taskOverrides.get(task.id);
81
+ const instance = await this.deps.workerRuntime.spawn(workerType, mission.id, task.id, task.capabilities);
82
+ this.trackWorker(mission.id, instance);
83
+ try {
84
+ task.attempts += 1;
85
+ if (providerOverride) {
86
+ instance.providerId = providerOverride.providerId;
87
+ instance.modelId = providerOverride.modelId;
88
+ }
89
+ await this.deps.workerRuntime.execute(instance, task, providerOverride ?? undefined);
90
+ // Per-task verification via the Verification Engine pipeline.
91
+ const vresult = await this.deps.verification.verify(mission, task, mission.policy.policy);
92
+ if (!vresult.passed) {
93
+ // Treat as a verification-failure recovery case.
94
+ await this.handleRecovery(mission, task, `verification failed: ${vresult.summary}`, instance);
95
+ }
96
+ }
97
+ catch (err) {
98
+ const message = err instanceof Error ? err.message : String(err);
99
+ await this.handleRecovery(mission, task, message, instance);
100
+ }
101
+ finally {
102
+ await this.deps.workerRuntime.destroy(instance);
103
+ }
104
+ }
105
+ async handleRecovery(mission, task, message, instance) {
106
+ this.deps.logger.warn(`Task ${task.id} attempt ${task.attempts} failed: ${message}`);
107
+ const decision = await this.deps.recovery.decide({
108
+ mission,
109
+ task,
110
+ error: message,
111
+ attempts: task.attempts,
112
+ policy: mission.policy.policy,
113
+ currentProviderId: instance.providerId,
114
+ currentModelId: instance.modelId,
115
+ });
116
+ const maxAttempts = mission.policy.policy.retry?.maxAttempts ?? 3;
117
+ switch (decision.action) {
118
+ case 'retry': {
119
+ task.status = 'PENDING';
120
+ task.error = message;
121
+ const backoff = mission.policy.policy.retry?.backoffMs ?? 1000;
122
+ await this.sleep(backoff);
123
+ break;
124
+ }
125
+ case 'rotate-provider': {
126
+ if (decision.rotateToProviderId && decision.rotateToModelId) {
127
+ this.taskOverrides.set(task.id, {
128
+ providerId: decision.rotateToProviderId,
129
+ modelId: decision.rotateToModelId,
130
+ });
131
+ this.deps.eventBus.emit({
132
+ type: 'PROVIDER_CHANGED',
133
+ timestamp: Date.now(),
134
+ missionId: mission.id,
135
+ taskId: task.id,
136
+ data: {
137
+ from: { providerId: instance.providerId, modelId: instance.modelId },
138
+ to: { providerId: decision.rotateToProviderId, modelId: decision.rotateToModelId },
139
+ reason: decision.reason,
140
+ },
141
+ });
142
+ }
143
+ task.status = 'PENDING';
144
+ task.error = message;
145
+ break;
146
+ }
147
+ case 'spawn-different-worker': {
148
+ if (decision.newWorkerType) {
149
+ this.taskWorkerOverrides.set(task.id, decision.newWorkerType);
150
+ this.deps.logger.info(`Task ${task.id}: switching worker to ${decision.newWorkerType}`);
151
+ }
152
+ task.status = 'PENDING';
153
+ task.error = message;
154
+ break;
155
+ }
156
+ case 'replan':
157
+ case 'checkpoint': {
158
+ await this.deps.checkpoints.save(mission, this.collectWorkerStates(mission.id), 'crash');
159
+ task.status = 'PENDING';
160
+ task.error = message;
161
+ break;
162
+ }
163
+ case 'continue': {
164
+ task.status = 'PENDING';
165
+ task.error = message;
166
+ break;
167
+ }
168
+ case 'abort':
169
+ default: {
170
+ task.status = 'FAILED';
171
+ task.error = message;
172
+ this.deps.eventBus.emit({
173
+ type: 'TASK_FAILED',
174
+ timestamp: Date.now(),
175
+ missionId: mission.id,
176
+ taskId: task.id,
177
+ workerId: instance.id,
178
+ data: { error: message, attempts: task.attempts, maxAttempts },
179
+ });
180
+ break;
181
+ }
182
+ }
183
+ }
184
+ skipBlockedTasks(mission) {
185
+ const failed = new Set(mission.tasks.filter(t => t.status === 'FAILED' && t.attempts >= t.maxAttempts).map(t => t.id));
186
+ for (const task of mission.tasks) {
187
+ if (task.status !== 'PENDING' && task.status !== 'QUEUED')
188
+ continue;
189
+ if (task.dependencies.some(d => failed.has(d))) {
190
+ task.status = 'SKIPPED';
191
+ task.error = 'Skipped: dependency failed';
192
+ }
193
+ }
194
+ }
195
+ trackWorker(missionId, instance) {
196
+ let arr = this.workersByMission.get(missionId);
197
+ if (!arr) {
198
+ arr = [];
199
+ this.workersByMission.set(missionId, arr);
200
+ }
201
+ arr.push(instance);
202
+ }
203
+ collectWorkerStates(missionId) {
204
+ return (this.workersByMission.get(missionId) ?? []).map(w => ({
205
+ workerId: w.id,
206
+ status: w.status,
207
+ taskId: w.taskId,
208
+ providerId: w.providerId,
209
+ modelId: w.modelId,
210
+ partialOutput: w.result?.content,
211
+ }));
212
+ }
213
+ pauseMission(missionId) {
214
+ this.pausedMissions.add(missionId);
215
+ }
216
+ resumeMission(missionId) {
217
+ this.pausedMissions.delete(missionId);
218
+ }
219
+ cancelMission(missionId) {
220
+ this.cancelledMissions.add(missionId);
221
+ this.pausedMissions.delete(missionId);
222
+ }
223
+ sleep(ms) {
224
+ return new Promise(r => setTimeout(r, ms));
225
+ }
226
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * OpenKernel — Plugin Manager
3
+ *
4
+ * Core stays tiny. Everything else is a plugin: Telegram, Discord, Slack,
5
+ * GitHub, Browser, Search, Vision, Email, Database, Storage, Monitoring.
6
+ */
7
+ import type { EventBus } from './event-bus.js';
8
+ import type { Logger, Plugin, PluginCommand } from './types.js';
9
+ export interface PluginContext {
10
+ eventBus: EventBus;
11
+ logger: Logger;
12
+ commands: PluginManager;
13
+ }
14
+ export declare class PluginManager {
15
+ private eventBus;
16
+ private logger;
17
+ private plugins;
18
+ private commands;
19
+ private unsubscribers;
20
+ constructor(eventBus: EventBus, logger: Logger);
21
+ register(plugin: Plugin): void;
22
+ registerWithContext(plugin: Plugin, kernel: unknown): Promise<void>;
23
+ unregister(name: string): void;
24
+ listPlugins(): Plugin[];
25
+ registerCommand(cmd: PluginCommand): void;
26
+ listCommands(): PluginCommand[];
27
+ destroy(): void;
28
+ }
@@ -0,0 +1,76 @@
1
+ export class PluginManager {
2
+ eventBus;
3
+ logger;
4
+ plugins = new Map();
5
+ commands = [];
6
+ unsubscribers = [];
7
+ constructor(eventBus, logger) {
8
+ this.eventBus = eventBus;
9
+ this.logger = logger;
10
+ }
11
+ register(plugin) {
12
+ if (this.plugins.has(plugin.manifest.name)) {
13
+ this.logger.warn(`Plugin already registered: ${plugin.manifest.name}`);
14
+ return;
15
+ }
16
+ this.plugins.set(plugin.manifest.name, plugin);
17
+ if (plugin.manifest.commands) {
18
+ for (const cmd of plugin.manifest.commands) {
19
+ this.registerCommand(cmd);
20
+ }
21
+ }
22
+ if (plugin.manifest.subscriptions && plugin.onEvent) {
23
+ const unsub = this.eventBus.on(plugin.manifest.subscriptions, plugin.onEvent.bind(plugin));
24
+ this.unsubscribers.push(unsub);
25
+ }
26
+ this.logger.info(`Plugin registered: ${plugin.manifest.name} v${plugin.manifest.version}`);
27
+ }
28
+ async registerWithContext(plugin, kernel) {
29
+ this.register(plugin);
30
+ if (plugin.onRegister) {
31
+ await plugin.onRegister(kernel);
32
+ }
33
+ }
34
+ unregister(name) {
35
+ const plugin = this.plugins.get(name);
36
+ if (!plugin)
37
+ return;
38
+ if (plugin.onDestroy) {
39
+ try {
40
+ plugin.onDestroy();
41
+ }
42
+ catch (err) {
43
+ this.logger.warn(`Plugin ${name} destroy error: ${err}`);
44
+ }
45
+ }
46
+ this.plugins.delete(name);
47
+ this.commands = this.commands.filter(c => c.name !== `${name}.${c.name}`);
48
+ this.logger.info(`Plugin unregistered: ${name}`);
49
+ }
50
+ listPlugins() {
51
+ return [...this.plugins.values()];
52
+ }
53
+ registerCommand(cmd) {
54
+ this.commands.push(cmd);
55
+ }
56
+ listCommands() {
57
+ return [...this.commands];
58
+ }
59
+ destroy() {
60
+ for (const unsub of this.unsubscribers)
61
+ unsub();
62
+ this.unsubscribers = [];
63
+ for (const plugin of this.plugins.values()) {
64
+ if (plugin.onDestroy) {
65
+ try {
66
+ plugin.onDestroy();
67
+ }
68
+ catch (err) {
69
+ this.logger.warn(`Plugin destroy error: ${err}`);
70
+ }
71
+ }
72
+ }
73
+ this.plugins.clear();
74
+ this.commands = [];
75
+ }
76
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * OpenKernel — Browser Plugin
3
+ *
4
+ * Playwright-backed browser automation. Provides a shared BrowserController
5
+ * that the browser worker handler uses. Emits NOTIFICATION events on
6
+ * significant browser actions (configurable).
7
+ *
8
+ * If Playwright is not installed, the controller degrades to fetch-based
9
+ * read-only mode automatically.
10
+ */
11
+ import type { Plugin } from '../types.js';
12
+ import { BrowserController } from '../browser/browser-controller.js';
13
+ import type { BrowserControllerOptions } from '../browser/browser-controller.js';
14
+ export interface BrowserPluginOptions extends BrowserControllerOptions {
15
+ /** Emit a NOTIFICATION event on these action types. */
16
+ notifyOn?: string[];
17
+ /** Keep the browser open between actions (default: true). */
18
+ persistent?: boolean;
19
+ }
20
+ export declare function createBrowserPlugin(options?: BrowserPluginOptions): Plugin;
21
+ export { BrowserController };
22
+ export type { BrowserControllerOptions, BrowserAction, BrowserActionResult } from '../browser/browser-controller.js';
@@ -0,0 +1,34 @@
1
+ import { BrowserController } from '../browser/browser-controller.js';
2
+ import { setBrowserController } from '../workers/browser-worker.js';
3
+ export function createBrowserPlugin(options = {}) {
4
+ const controller = new BrowserController(options);
5
+ let initialized = false;
6
+ const notifyOn = new Set(options.notifyOn ?? ['navigate', 'screenshot']);
7
+ const persistent = options.persistent ?? true;
8
+ async function ensureInit() {
9
+ if (!initialized) {
10
+ await controller.init();
11
+ initialized = true;
12
+ }
13
+ return controller;
14
+ }
15
+ return {
16
+ manifest: {
17
+ name: 'browser',
18
+ version: '0.2.0',
19
+ description: 'Playwright browser automation (Phase 4)',
20
+ subscriptions: [],
21
+ },
22
+ onRegister() {
23
+ void ensureInit().then(c => setBrowserController(c));
24
+ },
25
+ onEvent() {
26
+ // stateless; the worker handler drives the controller directly
27
+ },
28
+ async onDestroy() {
29
+ if (initialized && !persistent)
30
+ await controller.close();
31
+ },
32
+ };
33
+ }
34
+ export { BrowserController };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * OpenKernel — Dashboard Plugin
3
+ *
4
+ * Serves an HTTP endpoint with live mission/provider/worker status.
5
+ * GET / → HTML dashboard
6
+ * GET /status → JSON status snapshot
7
+ * GET /missions → JSON mission list
8
+ * GET /events → JSON recent event history
9
+ *
10
+ * Read-only. No auth. Intended for local dev / ops visibility.
11
+ */
12
+ import type { Plugin } from '../types.js';
13
+ export interface DashboardPluginOptions {
14
+ port?: number;
15
+ host?: string;
16
+ }
17
+ export declare function createDashboardPlugin(options?: DashboardPluginOptions): Plugin;