infinicode 1.0.0 → 2.1.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 (159) 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 +564 -72
  6. package/dist/ascii-video-animation.d.ts +2 -0
  7. package/dist/ascii-video-animation.js +243 -0
  8. package/dist/cli.js +199 -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 +14 -0
  14. package/dist/commands/mcp.js +100 -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 +20 -0
  24. package/dist/commands/serve.js +132 -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 +57 -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 +135 -0
  58. package/dist/kernel/federation/federation.js +378 -0
  59. package/dist/kernel/federation/index.d.ts +33 -0
  60. package/dist/kernel/federation/index.js +24 -0
  61. package/dist/kernel/federation/lan-discovery.d.ts +43 -0
  62. package/dist/kernel/federation/lan-discovery.js +135 -0
  63. package/dist/kernel/federation/moltfed-adapter.d.ts +30 -0
  64. package/dist/kernel/federation/moltfed-adapter.js +52 -0
  65. package/dist/kernel/federation/moltfed-connector.d.ts +98 -0
  66. package/dist/kernel/federation/moltfed-connector.js +193 -0
  67. package/dist/kernel/federation/node-identity.d.ts +19 -0
  68. package/dist/kernel/federation/node-identity.js +86 -0
  69. package/dist/kernel/federation/peer-mesh.d.ts +46 -0
  70. package/dist/kernel/federation/peer-mesh.js +117 -0
  71. package/dist/kernel/federation/protocol.d.ts +20 -0
  72. package/dist/kernel/federation/protocol.js +61 -0
  73. package/dist/kernel/federation/role-context.d.ts +5 -0
  74. package/dist/kernel/federation/role-context.js +45 -0
  75. package/dist/kernel/federation/telemetry.d.ts +21 -0
  76. package/dist/kernel/federation/telemetry.js +138 -0
  77. package/dist/kernel/federation/transport-http.d.ts +44 -0
  78. package/dist/kernel/federation/transport-http.js +207 -0
  79. package/dist/kernel/federation/types.d.ts +212 -0
  80. package/dist/kernel/federation/types.js +3 -0
  81. package/dist/kernel/free-providers.d.ts +28 -0
  82. package/dist/kernel/free-providers.js +162 -0
  83. package/dist/kernel/frontend-scoring.d.ts +21 -0
  84. package/dist/kernel/frontend-scoring.js +125 -0
  85. package/dist/kernel/index.d.ts +63 -0
  86. package/dist/kernel/index.js +45 -0
  87. package/dist/kernel/kernel.d.ts +75 -0
  88. package/dist/kernel/kernel.js +223 -0
  89. package/dist/kernel/logger.d.ts +18 -0
  90. package/dist/kernel/logger.js +26 -0
  91. package/dist/kernel/mcp/index.d.ts +9 -0
  92. package/dist/kernel/mcp/index.js +8 -0
  93. package/dist/kernel/mcp/mcp-server.d.ts +27 -0
  94. package/dist/kernel/mcp/mcp-server.js +178 -0
  95. package/dist/kernel/mission-engine.d.ts +42 -0
  96. package/dist/kernel/mission-engine.js +160 -0
  97. package/dist/kernel/orchestrator.d.ts +51 -0
  98. package/dist/kernel/orchestrator.js +226 -0
  99. package/dist/kernel/plugin-manager.d.ts +28 -0
  100. package/dist/kernel/plugin-manager.js +76 -0
  101. package/dist/kernel/plugins/browser-plugin.d.ts +22 -0
  102. package/dist/kernel/plugins/browser-plugin.js +34 -0
  103. package/dist/kernel/plugins/dashboard-plugin.d.ts +17 -0
  104. package/dist/kernel/plugins/dashboard-plugin.js +72 -0
  105. package/dist/kernel/plugins/discord-plugin.d.ts +11 -0
  106. package/dist/kernel/plugins/discord-plugin.js +35 -0
  107. package/dist/kernel/plugins/metrics-plugin.d.ts +33 -0
  108. package/dist/kernel/plugins/metrics-plugin.js +86 -0
  109. package/dist/kernel/plugins/search-plugin.d.ts +17 -0
  110. package/dist/kernel/plugins/search-plugin.js +20 -0
  111. package/dist/kernel/plugins/slack-plugin.d.ts +11 -0
  112. package/dist/kernel/plugins/slack-plugin.js +35 -0
  113. package/dist/kernel/plugins/telegram-plugin.d.ts +20 -0
  114. package/dist/kernel/plugins/telegram-plugin.js +122 -0
  115. package/dist/kernel/policies.d.ts +33 -0
  116. package/dist/kernel/policies.js +105 -0
  117. package/dist/kernel/provider-discovery.d.ts +41 -0
  118. package/dist/kernel/provider-discovery.js +179 -0
  119. package/dist/kernel/provider-manager.d.ts +38 -0
  120. package/dist/kernel/provider-manager.js +206 -0
  121. package/dist/kernel/provider-url.d.ts +18 -0
  122. package/dist/kernel/provider-url.js +45 -0
  123. package/dist/kernel/providers/gemini-provider.d.ts +43 -0
  124. package/dist/kernel/providers/gemini-provider.js +203 -0
  125. package/dist/kernel/providers/ollama-provider.d.ts +44 -0
  126. package/dist/kernel/providers/ollama-provider.js +241 -0
  127. package/dist/kernel/providers/openai-compatible-provider.d.ts +43 -0
  128. package/dist/kernel/providers/openai-compatible-provider.js +233 -0
  129. package/dist/kernel/recovery-manager.d.ts +38 -0
  130. package/dist/kernel/recovery-manager.js +156 -0
  131. package/dist/kernel/router.d.ts +44 -0
  132. package/dist/kernel/router.js +222 -0
  133. package/dist/kernel/sample-missions.d.ts +11 -0
  134. package/dist/kernel/sample-missions.js +132 -0
  135. package/dist/kernel/scheduler.d.ts +30 -0
  136. package/dist/kernel/scheduler.js +147 -0
  137. package/dist/kernel/sdk/harness-sdk.d.ts +37 -0
  138. package/dist/kernel/sdk/harness-sdk.js +48 -0
  139. package/dist/kernel/sdk/plugin-sdk.d.ts +27 -0
  140. package/dist/kernel/sdk/plugin-sdk.js +40 -0
  141. package/dist/kernel/search/search-controller.d.ts +32 -0
  142. package/dist/kernel/search/search-controller.js +141 -0
  143. package/dist/kernel/setup.d.ts +13 -0
  144. package/dist/kernel/setup.js +59 -0
  145. package/dist/kernel/types.d.ts +479 -0
  146. package/dist/kernel/types.js +7 -0
  147. package/dist/kernel/verification-engine.d.ts +33 -0
  148. package/dist/kernel/verification-engine.js +287 -0
  149. package/dist/kernel/worker-runtime.d.ts +66 -0
  150. package/dist/kernel/worker-runtime.js +261 -0
  151. package/dist/kernel/workers/browser-worker.d.ts +6 -0
  152. package/dist/kernel/workers/browser-worker.js +92 -0
  153. package/dist/kernel/workers/builtin-workers.d.ts +20 -0
  154. package/dist/kernel/workers/builtin-workers.js +120 -0
  155. package/dist/kernel/workers/research-worker.d.ts +5 -0
  156. package/dist/kernel/workers/research-worker.js +90 -0
  157. package/dist/prompt-ascii-video-animation.d.ts +2 -0
  158. package/dist/prompt-ascii-video-animation.js +243 -0
  159. package/package.json +43 -9
@@ -0,0 +1,72 @@
1
+ import { createServer } from 'node:http';
2
+ export function createDashboardPlugin(options = {}) {
3
+ let kernelRef;
4
+ let server;
5
+ const port = options.port ?? 7331;
6
+ const host = options.host ?? '127.0.0.1';
7
+ function json(res, code, body) {
8
+ const payload = JSON.stringify(body, null, 2);
9
+ res.writeHead(code, { 'Content-Type': 'application/json' });
10
+ res.end(payload);
11
+ }
12
+ function handle(req, res) {
13
+ const url = req.url ?? '/';
14
+ if (url === '/status') {
15
+ json(res, 200, {
16
+ ok: true,
17
+ missions: kernelRef?.listMissions().length ?? 0,
18
+ plugins: kernelRef?.listPlugins().map(p => ({ name: p.manifest.name, version: p.manifest.version })) ?? [],
19
+ uptime: process.uptime(),
20
+ });
21
+ return;
22
+ }
23
+ if (url === '/missions') {
24
+ json(res, 200, kernelRef?.listMissions().map(m => ({
25
+ id: m.id, name: m.name, status: m.status, goal: m.goal,
26
+ tasks: m.tasks.length, completed: m.tasks.filter(t => t.status === 'COMPLETED').length,
27
+ })) ?? []);
28
+ return;
29
+ }
30
+ if (url === '/events') {
31
+ json(res, 200, kernelRef?.getEventHistory().slice(-50) ?? []);
32
+ return;
33
+ }
34
+ // HTML dashboard
35
+ const missions = kernelRef?.listMissions() ?? [];
36
+ const html = `<!doctype html><html><head><meta charset="utf-8"><title>OpenKernel Dashboard</title>
37
+ <style>body{font:14px/1.5 system-ui;margin:2rem}table{border-collapse:collapse}td,th{border:1px solid #ccc;padding:4px 8px}</style></head>
38
+ <body><h1>OpenKernel Dashboard</h1>
39
+ <h2>Missions (${missions.length})</h2>
40
+ <table><tr><th>ID</th><th>Name</th><th>Status</th><th>Goal</th><th>Tasks</th></tr>
41
+ ${missions.map(m => `<tr><td>${m.id}</td><td>${m.name}</td><td>${m.status}</td><td>${escapeHtml(m.goal)}</td><td>${m.tasks.filter(t => t.status === 'COMPLETED').length}/${m.tasks.length}</td></tr>`).join('')}
42
+ </table>
43
+ <p><a href="/status">/status</a> · <a href="/missions">/missions</a> · <a href="/events">/events</a></p>
44
+ </body></html>`;
45
+ res.writeHead(200, { 'Content-Type': 'text/html' });
46
+ res.end(html);
47
+ }
48
+ return {
49
+ manifest: {
50
+ name: 'dashboard',
51
+ version: '0.1.0',
52
+ description: 'HTTP status dashboard',
53
+ subscriptions: [],
54
+ },
55
+ onRegister(kernel) {
56
+ kernelRef = kernel;
57
+ server = createServer(handle);
58
+ server.listen(port, host, () => {
59
+ // best-effort log
60
+ });
61
+ },
62
+ onDestroy() {
63
+ server?.close();
64
+ },
65
+ onEvent(_event) {
66
+ // could push to SSE stream in future; for now stateless
67
+ },
68
+ };
69
+ }
70
+ function escapeHtml(s) {
71
+ return s.replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
72
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OpenKernel — Discord Plugin (Stub)
3
+ *
4
+ * Notifications via Discord webhook. Requires DISCORD_WEBHOOK_URL.
5
+ */
6
+ import type { KernelEventType, Plugin } from '../types.js';
7
+ export interface DiscordPluginOptions {
8
+ webhookUrl?: string;
9
+ notifyOn?: KernelEventType[];
10
+ }
11
+ export declare function createDiscordPlugin(options: DiscordPluginOptions): Plugin;
@@ -0,0 +1,35 @@
1
+ export function createDiscordPlugin(options) {
2
+ const notifyOn = new Set(options.notifyOn ?? [
3
+ 'MISSION_STARTED',
4
+ 'MISSION_COMPLETED',
5
+ 'MISSION_FAILED',
6
+ ]);
7
+ async function send(content) {
8
+ if (!options.webhookUrl)
9
+ return;
10
+ try {
11
+ await fetch(options.webhookUrl, {
12
+ method: 'POST',
13
+ headers: { 'Content-Type': 'application/json' },
14
+ body: JSON.stringify({ content }),
15
+ signal: AbortSignal.timeout(5000),
16
+ });
17
+ }
18
+ catch {
19
+ // best-effort
20
+ }
21
+ }
22
+ return {
23
+ manifest: {
24
+ name: 'discord',
25
+ version: '0.1.0',
26
+ description: 'Discord webhook notifications',
27
+ subscriptions: [...notifyOn],
28
+ },
29
+ onEvent(event) {
30
+ if (!notifyOn.has(event.type))
31
+ return;
32
+ void send(`**${event.type}** — mission \`${event.missionId ?? '-'}\``);
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * OpenKernel — Metrics Plugin
3
+ *
4
+ * Subscribes to all events and maintains runtime counters:
5
+ * missions started/completed/failed, tasks started/completed/failed,
6
+ * workers spawned/exited, provider changes, recoveries, checkpoints,
7
+ * total tokens in/out, total duration.
8
+ *
9
+ * Exposes a snapshot() the Dashboard plugin or external monitors can read.
10
+ */
11
+ import type { Plugin } from '../types.js';
12
+ export interface MetricsSnapshot {
13
+ missionsStarted: number;
14
+ missionsCompleted: number;
15
+ missionsFailed: number;
16
+ tasksStarted: number;
17
+ tasksCompleted: number;
18
+ tasksFailed: number;
19
+ workersSpawned: number;
20
+ workersExited: number;
21
+ providerChanged: number;
22
+ recoveries: number;
23
+ checkpoints: number;
24
+ verificationsPassed: number;
25
+ verificationsFailed: number;
26
+ totalTokensIn: number;
27
+ totalTokensOut: number;
28
+ totalDurationMs: number;
29
+ lastEventAt?: number;
30
+ }
31
+ export declare function createMetricsPlugin(): Plugin & {
32
+ snapshot(): MetricsSnapshot;
33
+ };
@@ -0,0 +1,86 @@
1
+ export function createMetricsPlugin() {
2
+ const m = {
3
+ missionsStarted: 0,
4
+ missionsCompleted: 0,
5
+ missionsFailed: 0,
6
+ tasksStarted: 0,
7
+ tasksCompleted: 0,
8
+ tasksFailed: 0,
9
+ workersSpawned: 0,
10
+ workersExited: 0,
11
+ providerChanged: 0,
12
+ recoveries: 0,
13
+ checkpoints: 0,
14
+ verificationsPassed: 0,
15
+ verificationsFailed: 0,
16
+ totalTokensIn: 0,
17
+ totalTokensOut: 0,
18
+ totalDurationMs: 0,
19
+ };
20
+ const all = [
21
+ 'MISSION_STARTED', 'MISSION_COMPLETED', 'MISSION_FAILED',
22
+ 'TASK_STARTED', 'TASK_COMPLETED', 'TASK_FAILED',
23
+ 'WORKER_SPAWNED', 'WORKER_EXITED',
24
+ 'PROVIDER_CHANGED', 'RECOVERY', 'CHECKPOINT',
25
+ 'VERIFY_SUCCESS', 'VERIFY_FAILED',
26
+ ];
27
+ function onEvent(event) {
28
+ m.lastEventAt = event.timestamp;
29
+ switch (event.type) {
30
+ case 'MISSION_STARTED':
31
+ m.missionsStarted++;
32
+ break;
33
+ case 'MISSION_COMPLETED':
34
+ m.missionsCompleted++;
35
+ break;
36
+ case 'MISSION_FAILED':
37
+ m.missionsFailed++;
38
+ break;
39
+ case 'TASK_STARTED':
40
+ m.tasksStarted++;
41
+ break;
42
+ case 'TASK_COMPLETED': {
43
+ m.tasksCompleted++;
44
+ const d = event.data;
45
+ m.totalTokensIn += d?.output?.tokensIn ?? 0;
46
+ m.totalTokensOut += d?.output?.tokensOut ?? 0;
47
+ m.totalDurationMs += d?.output?.durationMs ?? 0;
48
+ break;
49
+ }
50
+ case 'TASK_FAILED':
51
+ m.tasksFailed++;
52
+ break;
53
+ case 'WORKER_SPAWNED':
54
+ m.workersSpawned++;
55
+ break;
56
+ case 'WORKER_EXITED':
57
+ m.workersExited++;
58
+ break;
59
+ case 'PROVIDER_CHANGED':
60
+ m.providerChanged++;
61
+ break;
62
+ case 'RECOVERY':
63
+ m.recoveries++;
64
+ break;
65
+ case 'CHECKPOINT':
66
+ m.checkpoints++;
67
+ break;
68
+ case 'VERIFY_SUCCESS':
69
+ m.verificationsPassed++;
70
+ break;
71
+ case 'VERIFY_FAILED':
72
+ m.verificationsFailed++;
73
+ break;
74
+ }
75
+ }
76
+ return {
77
+ manifest: {
78
+ name: 'metrics',
79
+ version: '0.1.0',
80
+ description: 'Runtime metrics counters',
81
+ subscriptions: all,
82
+ },
83
+ onEvent,
84
+ snapshot() { return { ...m }; },
85
+ };
86
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * OpenKernel — Search Plugin
3
+ *
4
+ * Default stack: SearXNG → Crawler (Crawl4AI-style fetch) → Markdown → LLM.
5
+ * Provides a shared SearchController that the research worker handler uses.
6
+ *
7
+ * If no SearXNG URL is configured, the controller falls back to a
8
+ * DuckDuckGo HTML scrape so the research worker remains functional.
9
+ */
10
+ import type { Plugin } from '../types.js';
11
+ import { SearchController } from '../search/search-controller.js';
12
+ import type { SearchOptions } from '../search/search-controller.js';
13
+ export interface SearchPluginOptions extends SearchOptions {
14
+ }
15
+ export declare function createSearchPlugin(options?: SearchPluginOptions): Plugin;
16
+ export { SearchController };
17
+ export type { SearchOptions, SearchResult } from '../search/search-controller.js';
@@ -0,0 +1,20 @@
1
+ import { SearchController } from '../search/search-controller.js';
2
+ import { setSearchController } from '../workers/research-worker.js';
3
+ export function createSearchPlugin(options = {}) {
4
+ const controller = new SearchController(options);
5
+ return {
6
+ manifest: {
7
+ name: 'search',
8
+ version: '0.2.0',
9
+ description: 'SearXNG + crawler search stack (Phase 4)',
10
+ subscriptions: [],
11
+ },
12
+ onRegister() {
13
+ setSearchController(controller);
14
+ },
15
+ onEvent() {
16
+ // stateless; the research worker drives the controller directly
17
+ },
18
+ };
19
+ }
20
+ export { SearchController };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OpenKernel — Slack Plugin (Stub)
3
+ *
4
+ * Notifications via Slack webhook. Requires SLACK_WEBHOOK_URL.
5
+ */
6
+ import type { KernelEventType, Plugin } from '../types.js';
7
+ export interface SlackPluginOptions {
8
+ webhookUrl?: string;
9
+ notifyOn?: KernelEventType[];
10
+ }
11
+ export declare function createSlackPlugin(options: SlackPluginOptions): Plugin;
@@ -0,0 +1,35 @@
1
+ export function createSlackPlugin(options) {
2
+ const notifyOn = new Set(options.notifyOn ?? [
3
+ 'MISSION_STARTED',
4
+ 'MISSION_COMPLETED',
5
+ 'MISSION_FAILED',
6
+ ]);
7
+ async function send(text) {
8
+ if (!options.webhookUrl)
9
+ return;
10
+ try {
11
+ await fetch(options.webhookUrl, {
12
+ method: 'POST',
13
+ headers: { 'Content-Type': 'application/json' },
14
+ body: JSON.stringify({ text }),
15
+ signal: AbortSignal.timeout(5000),
16
+ });
17
+ }
18
+ catch {
19
+ // best-effort
20
+ }
21
+ }
22
+ return {
23
+ manifest: {
24
+ name: 'slack',
25
+ version: '0.1.0',
26
+ description: 'Slack webhook notifications',
27
+ subscriptions: [...notifyOn],
28
+ },
29
+ onEvent(event) {
30
+ if (!notifyOn.has(event.type))
31
+ return;
32
+ void send(`*${event.type}* — mission \`${event.missionId ?? '-'}\``);
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * OpenKernel — Telegram Plugin
3
+ *
4
+ * Notifications (outbound) + slash commands (inbound polling).
5
+ * Commands: /status /workers /logs /pause /resume /retry /models
6
+ * /providers /checkpoints /goal
7
+ *
8
+ * Real bot wiring requires TELEGRAM_BOT_TOKEN + chatId.
9
+ * Phase 4 will flesh out natural chat with the orchestrator.
10
+ */
11
+ import type { KernelEventType, Plugin } from '../types.js';
12
+ export interface TelegramPluginOptions {
13
+ token?: string;
14
+ chatId?: string;
15
+ /** When true, polls Telegram getUpdates and dispatches slash commands. */
16
+ pollCommands?: boolean;
17
+ pollIntervalMs?: number;
18
+ notifyOn?: KernelEventType[];
19
+ }
20
+ export declare function createTelegramPlugin(options: TelegramPluginOptions): Plugin;
@@ -0,0 +1,122 @@
1
+ export function createTelegramPlugin(options) {
2
+ const notifyOn = new Set(options.notifyOn ?? [
3
+ 'MISSION_STARTED',
4
+ 'MISSION_COMPLETED',
5
+ 'MISSION_FAILED',
6
+ 'MISSION_PAUSED',
7
+ 'MISSION_RESUMED',
8
+ 'PROVIDER_CHANGED',
9
+ 'NOTIFICATION',
10
+ 'RECOVERY',
11
+ ]);
12
+ let kernelRef;
13
+ let pollTimer;
14
+ let offset = 0;
15
+ async function send(message) {
16
+ if (!options.token || !options.chatId)
17
+ return;
18
+ try {
19
+ await fetch(`https://api.telegram.org/bot${options.token}/sendMessage`, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({ chat_id: options.chatId, text: message, parse_mode: 'Markdown' }),
23
+ signal: AbortSignal.timeout(5000),
24
+ });
25
+ }
26
+ catch {
27
+ // best-effort
28
+ }
29
+ }
30
+ async function pollOnce() {
31
+ if (!options.token)
32
+ return;
33
+ try {
34
+ const res = await fetch(`https://api.telegram.org/bot${options.token}/getUpdates?offset=${offset}&timeout=0`, { signal: AbortSignal.timeout(5000) });
35
+ const json = (await res.json());
36
+ if (!json.ok || !json.result)
37
+ return;
38
+ for (const upd of json.result) {
39
+ offset = upd.update_id + 1;
40
+ const text = upd.message?.text?.trim();
41
+ const chatId = upd.message?.chat?.id;
42
+ if (!text || !chatId)
43
+ continue;
44
+ if (options.chatId && String(chatId) !== String(options.chatId))
45
+ continue;
46
+ await dispatchCommand(text);
47
+ }
48
+ }
49
+ catch {
50
+ // best-effort
51
+ }
52
+ }
53
+ async function dispatchCommand(text) {
54
+ if (!text.startsWith('/'))
55
+ return;
56
+ const k = kernelRef;
57
+ if (!k) {
58
+ await send('kernel not registered');
59
+ return;
60
+ }
61
+ // Delegate to the kernel's canonical command registry — the same surface
62
+ // the CLI console uses — so behavior is defined once for every channel.
63
+ const result = await k.command(text);
64
+ await send(result.text || (result.ok ? 'ok' : 'error'));
65
+ }
66
+ return {
67
+ manifest: {
68
+ name: 'telegram',
69
+ version: '0.2.0',
70
+ description: 'Telegram notifications + slash commands',
71
+ subscriptions: [...notifyOn],
72
+ commands: [
73
+ {
74
+ name: 'tg',
75
+ description: 'Send a Telegram notification manually',
76
+ handler: async (args) => {
77
+ await send(args.join(' ') || 'test from openkernel');
78
+ console.log('sent');
79
+ },
80
+ },
81
+ ],
82
+ },
83
+ onRegister(kernel) {
84
+ kernelRef = kernel;
85
+ if (options.pollCommands && options.token) {
86
+ const interval = options.pollIntervalMs ?? 2000;
87
+ pollTimer = setInterval(() => { void pollOnce(); }, interval);
88
+ }
89
+ },
90
+ onEvent(event) {
91
+ if (!notifyOn.has(event.type))
92
+ return;
93
+ const summary = formatEvent(event);
94
+ void send(summary);
95
+ },
96
+ onDestroy() {
97
+ if (pollTimer)
98
+ clearInterval(pollTimer);
99
+ },
100
+ };
101
+ }
102
+ function formatEvent(event) {
103
+ if (event.type === 'NOTIFICATION') {
104
+ const data = event.data;
105
+ return `*NOTIFICATION* ${data?.message ?? ''}`;
106
+ }
107
+ const parts = [`*${event.type}*`];
108
+ if (event.missionId)
109
+ parts.push(`mission: \`${event.missionId}\``);
110
+ if (event.taskId)
111
+ parts.push(`task: \`${event.taskId}\``);
112
+ if (event.data && typeof event.data === 'object') {
113
+ const d = event.data;
114
+ if ('error' in d)
115
+ parts.push(`error: ${String(d.error)}`);
116
+ if ('decision' in d && d.decision && typeof d.decision === 'object') {
117
+ const dec = d.decision;
118
+ parts.push(`recovery: ${dec.action ?? ''} — ${dec.reason ?? ''}`);
119
+ }
120
+ }
121
+ return parts.join(' ');
122
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * OpenKernel — Policy Engine
3
+ *
4
+ * Built-in policies: free-first, fastest, highest-quality, local-first,
5
+ * privacy-first, balanced, offline.
6
+ */
7
+ import type { Policy, PolicyName, PolicyRef } from './types.js';
8
+ export declare const PolicyPresets: {
9
+ freeFirst(): Policy;
10
+ fastest(): Policy;
11
+ highestQuality(): Policy;
12
+ localFirst(): Policy;
13
+ privacyFirst(): Policy;
14
+ balanced(): Policy;
15
+ offline(): Policy;
16
+ };
17
+ export declare const POLICY_FACTORIES: {
18
+ freeFirst(): Policy;
19
+ fastest(): Policy;
20
+ highestQuality(): Policy;
21
+ localFirst(): Policy;
22
+ privacyFirst(): Policy;
23
+ balanced(): Policy;
24
+ offline(): Policy;
25
+ };
26
+ export declare class PolicyEngine {
27
+ private policies;
28
+ constructor();
29
+ register(policy: Policy): void;
30
+ get(name: PolicyName): Policy;
31
+ resolveRef(name?: PolicyName): PolicyRef;
32
+ list(): Policy[];
33
+ }
@@ -0,0 +1,105 @@
1
+ export const PolicyPresets = {
2
+ freeFirst() {
3
+ return {
4
+ name: 'free-first',
5
+ execution: { parallelism: 2 },
6
+ routing: { mode: 'free-first' },
7
+ verification: { strict: false },
8
+ checkpoint: { everyTask: true },
9
+ retry: { maxAttempts: 3, backoffMs: 1000 },
10
+ notifications: { channels: [] },
11
+ };
12
+ },
13
+ fastest() {
14
+ return {
15
+ name: 'fastest',
16
+ execution: { parallelism: 4 },
17
+ routing: { mode: 'fastest' },
18
+ verification: { strict: false },
19
+ checkpoint: { everyTask: false },
20
+ retry: { maxAttempts: 2, backoffMs: 500 },
21
+ notifications: { channels: [] },
22
+ };
23
+ },
24
+ highestQuality() {
25
+ return {
26
+ name: 'highest-quality',
27
+ execution: { parallelism: 2, timeout: 300_000 },
28
+ routing: { mode: 'quality' },
29
+ verification: { strict: true },
30
+ checkpoint: { everyTask: true },
31
+ retry: { maxAttempts: 5, backoffMs: 2000 },
32
+ notifications: { channels: [] },
33
+ };
34
+ },
35
+ localFirst() {
36
+ return {
37
+ name: 'local-first',
38
+ execution: { parallelism: 2 },
39
+ routing: { mode: 'local', preferredProviders: ['ollama'] },
40
+ verification: { strict: false },
41
+ checkpoint: { everyTask: true },
42
+ retry: { maxAttempts: 3, backoffMs: 1000 },
43
+ notifications: { channels: [] },
44
+ };
45
+ },
46
+ privacyFirst() {
47
+ return {
48
+ name: 'privacy-first',
49
+ execution: { parallelism: 2 },
50
+ routing: { mode: 'privacy', preferredProviders: ['ollama'], blockedProviders: [] },
51
+ verification: { strict: true },
52
+ checkpoint: { everyTask: true },
53
+ retry: { maxAttempts: 3, backoffMs: 1000 },
54
+ notifications: { channels: [] },
55
+ };
56
+ },
57
+ balanced() {
58
+ return {
59
+ name: 'balanced',
60
+ execution: { parallelism: 4 },
61
+ routing: { mode: 'balanced' },
62
+ verification: { strict: false },
63
+ checkpoint: { everyTask: true },
64
+ retry: { maxAttempts: 3, backoffMs: 1500 },
65
+ notifications: { channels: [] },
66
+ };
67
+ },
68
+ offline() {
69
+ return {
70
+ name: 'offline',
71
+ execution: { parallelism: 2 },
72
+ routing: { mode: 'offline', preferredProviders: ['ollama'] },
73
+ verification: { strict: false },
74
+ checkpoint: { everyTask: true },
75
+ retry: { maxAttempts: 3, backoffMs: 1000 },
76
+ notifications: { channels: [] },
77
+ };
78
+ },
79
+ };
80
+ export const POLICY_FACTORIES = PolicyPresets;
81
+ export class PolicyEngine {
82
+ policies = new Map();
83
+ constructor() {
84
+ this.register(PolicyPresets.freeFirst());
85
+ this.register(PolicyPresets.fastest());
86
+ this.register(PolicyPresets.highestQuality());
87
+ this.register(PolicyPresets.localFirst());
88
+ this.register(PolicyPresets.privacyFirst());
89
+ this.register(PolicyPresets.balanced());
90
+ this.register(PolicyPresets.offline());
91
+ }
92
+ register(policy) {
93
+ this.policies.set(policy.name, policy);
94
+ }
95
+ get(name) {
96
+ return this.policies.get(name) ?? PolicyPresets.balanced();
97
+ }
98
+ resolveRef(name) {
99
+ const policy = this.get(name ?? 'balanced');
100
+ return { name: policy.name, policy };
101
+ }
102
+ list() {
103
+ return [...this.policies.values()];
104
+ }
105
+ }
@@ -0,0 +1,41 @@
1
+ import type { ProviderModel, WorkerType } from '../kernel/types.js';
2
+ import type { CloudProviderConfig, WorkerModelPref } from './config-schema.js';
3
+ import { type FreeProviderPreset } from './free-providers.js';
4
+ export interface DiscoveredModel {
5
+ providerId: string;
6
+ model: ProviderModel;
7
+ }
8
+ export interface ProviderTestResult {
9
+ providerId: string;
10
+ name: string;
11
+ ok: boolean;
12
+ error?: string;
13
+ modelCount: number;
14
+ latencyMs?: number;
15
+ }
16
+ export declare function testOllamaProvider(baseURL: string, fallbackURL?: string, timeoutMs?: number): Promise<ProviderTestResult>;
17
+ export declare function testCloudProvider(cfg: CloudProviderConfig, timeoutMs?: number): Promise<ProviderTestResult>;
18
+ export declare function testProviderInteractive(label: string, fn: () => Promise<ProviderTestResult>): Promise<ProviderTestResult>;
19
+ /**
20
+ * Discover all available models across all configured providers (Ollama + cloud).
21
+ * Returns a flat list tagged with providerId.
22
+ */
23
+ export declare function discoverAllModels(opts: {
24
+ masterUrl: string;
25
+ tailscaleMasterUrl?: string;
26
+ cloudProviders: CloudProviderConfig[];
27
+ }): Promise<DiscoveredModel[]>;
28
+ /**
29
+ * Build a friendly display label for a discovered model.
30
+ */
31
+ export declare function modelLabel(dm: DiscoveredModel): string;
32
+ /**
33
+ * Suggest a recommended model for a worker type from the discovered pool.
34
+ * Falls back to the provider preset's recommended list if discovery found nothing.
35
+ */
36
+ export declare function suggestModelForWorker(workerType: WorkerType, discovered: DiscoveredModel[]): DiscoveredModel | undefined;
37
+ /**
38
+ * Build a complete workerModels map by suggesting a model for each builtin worker type.
39
+ */
40
+ export declare function suggestAllWorkerModels(discovered: DiscoveredModel[]): Partial<Record<WorkerType, WorkerModelPref>>;
41
+ export type { FreeProviderPreset };