@kognai/orchestrator-core 0.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 (144) hide show
  1. package/README.md +44 -0
  2. package/dist/index.d.ts +63 -0
  3. package/dist/index.js +175 -0
  4. package/dist/lib/aar-middleware.d.ts +6 -0
  5. package/dist/lib/aar-middleware.js +70 -0
  6. package/dist/lib/aar-types.d.ts +34 -0
  7. package/dist/lib/aar-types.js +4 -0
  8. package/dist/lib/acp-engine.d.ts +68 -0
  9. package/dist/lib/acp-engine.js +123 -0
  10. package/dist/lib/acp.d.ts +61 -0
  11. package/dist/lib/acp.js +425 -0
  12. package/dist/lib/agent-registry.d.ts +50 -0
  13. package/dist/lib/agent-registry.js +137 -0
  14. package/dist/lib/anthropic-direct.d.ts +27 -0
  15. package/dist/lib/anthropic-direct.js +109 -0
  16. package/dist/lib/asmr-extractor.d.ts +40 -0
  17. package/dist/lib/asmr-extractor.js +151 -0
  18. package/dist/lib/asmr-retrieval.d.ts +76 -0
  19. package/dist/lib/asmr-retrieval.js +311 -0
  20. package/dist/lib/asmr.d.ts +8 -0
  21. package/dist/lib/asmr.js +24 -0
  22. package/dist/lib/brainx-client.d.ts +72 -0
  23. package/dist/lib/brainx-client.js +200 -0
  24. package/dist/lib/brainx-embed.d.ts +14 -0
  25. package/dist/lib/brainx-embed.js +139 -0
  26. package/dist/lib/brainx-swarm-bridge.d.ts +93 -0
  27. package/dist/lib/brainx-swarm-bridge.js +242 -0
  28. package/dist/lib/byterover-client.d.ts +19 -0
  29. package/dist/lib/byterover-client.js +59 -0
  30. package/dist/lib/ceo-wallet.d.ts +37 -0
  31. package/dist/lib/ceo-wallet.js +176 -0
  32. package/dist/lib/chomsky-gate.d.ts +24 -0
  33. package/dist/lib/chomsky-gate.js +178 -0
  34. package/dist/lib/chomsky-runner.d.ts +29 -0
  35. package/dist/lib/chomsky-runner.js +157 -0
  36. package/dist/lib/citizen-score-contract.d.ts +72 -0
  37. package/dist/lib/citizen-score-contract.js +16 -0
  38. package/dist/lib/citizen-score-registry.d.ts +25 -0
  39. package/dist/lib/citizen-score-registry.js +65 -0
  40. package/dist/lib/citizenship.d.ts +103 -0
  41. package/dist/lib/citizenship.js +272 -0
  42. package/dist/lib/clawrouter-client.d.ts +37 -0
  43. package/dist/lib/clawrouter-client.js +148 -0
  44. package/dist/lib/code-asset-crystalliser.d.ts +41 -0
  45. package/dist/lib/code-asset-crystalliser.js +181 -0
  46. package/dist/lib/code-failure-logger.d.ts +27 -0
  47. package/dist/lib/code-failure-logger.js +42 -0
  48. package/dist/lib/cto-approval-gate.d.ts +45 -0
  49. package/dist/lib/cto-approval-gate.js +478 -0
  50. package/dist/lib/cto-gate-types.d.ts +28 -0
  51. package/dist/lib/cto-gate-types.js +8 -0
  52. package/dist/lib/decomposer-feedback.d.ts +54 -0
  53. package/dist/lib/decomposer-feedback.js +115 -0
  54. package/dist/lib/emotional-safety-gate.d.ts +48 -0
  55. package/dist/lib/emotional-safety-gate.js +97 -0
  56. package/dist/lib/engine-paths.d.ts +13 -0
  57. package/dist/lib/engine-paths.js +32 -0
  58. package/dist/lib/event-bus-listener.d.ts +8 -0
  59. package/dist/lib/event-bus-listener.js +144 -0
  60. package/dist/lib/event-bus-publisher.d.ts +25 -0
  61. package/dist/lib/event-bus-publisher.js +188 -0
  62. package/dist/lib/event-bus-types.d.ts +73 -0
  63. package/dist/lib/event-bus-types.js +23 -0
  64. package/dist/lib/failure-library.d.ts +178 -0
  65. package/dist/lib/failure-library.js +349 -0
  66. package/dist/lib/ksl/error-log.d.ts +28 -0
  67. package/dist/lib/ksl/error-log.js +43 -0
  68. package/dist/lib/ksl/index.d.ts +9 -0
  69. package/dist/lib/ksl/index.js +25 -0
  70. package/dist/lib/ksl/orchestrator-tap.d.ts +16 -0
  71. package/dist/lib/ksl/orchestrator-tap.js +85 -0
  72. package/dist/lib/ksl/record-writer.d.ts +46 -0
  73. package/dist/lib/ksl/record-writer.js +45 -0
  74. package/dist/lib/llm-cost-table.d.ts +36 -0
  75. package/dist/lib/llm-cost-table.js +90 -0
  76. package/dist/lib/local-model-router.d.ts +27 -0
  77. package/dist/lib/local-model-router.js +61 -0
  78. package/dist/lib/mc-client.d.ts +51 -0
  79. package/dist/lib/mc-client.js +249 -0
  80. package/dist/lib/model-router-contract.d.ts +91 -0
  81. package/dist/lib/model-router-contract.js +19 -0
  82. package/dist/lib/model-router-registry.d.ts +24 -0
  83. package/dist/lib/model-router-registry.js +52 -0
  84. package/dist/lib/model-router.d.ts +20 -0
  85. package/dist/lib/model-router.js +79 -0
  86. package/dist/lib/monotask-state-machine.d.ts +19 -0
  87. package/dist/lib/monotask-state-machine.js +131 -0
  88. package/dist/lib/neutral-prompt-checker.d.ts +22 -0
  89. package/dist/lib/neutral-prompt-checker.js +130 -0
  90. package/dist/lib/notion-direct.d.ts +92 -0
  91. package/dist/lib/notion-direct.js +381 -0
  92. package/dist/lib/ollama-client.d.ts +37 -0
  93. package/dist/lib/ollama-client.js +158 -0
  94. package/dist/lib/omel/credential-vault.d.ts +57 -0
  95. package/dist/lib/omel/credential-vault.js +324 -0
  96. package/dist/lib/omel/human-brake.d.ts +32 -0
  97. package/dist/lib/omel/human-brake.js +289 -0
  98. package/dist/lib/omel/index.d.ts +10 -0
  99. package/dist/lib/omel/index.js +26 -0
  100. package/dist/lib/omel/phantom-workspace.d.ts +31 -0
  101. package/dist/lib/omel/phantom-workspace.js +256 -0
  102. package/dist/lib/omel/wipe-witness.d.ts +75 -0
  103. package/dist/lib/omel/wipe-witness.js +398 -0
  104. package/dist/lib/orchestrate-engine.d.ts +25 -0
  105. package/dist/lib/orchestrate-engine.js +4436 -0
  106. package/dist/lib/perm-judge.d.ts +46 -0
  107. package/dist/lib/perm-judge.js +173 -0
  108. package/dist/lib/plumber/conformance.d.ts +54 -0
  109. package/dist/lib/plumber/conformance.js +121 -0
  110. package/dist/lib/plumber/index.d.ts +9 -0
  111. package/dist/lib/plumber/index.js +25 -0
  112. package/dist/lib/plumber/observer.d.ts +52 -0
  113. package/dist/lib/plumber/observer.js +180 -0
  114. package/dist/lib/plumber/types.d.ts +78 -0
  115. package/dist/lib/plumber/types.js +29 -0
  116. package/dist/lib/research-impl-gate.d.ts +16 -0
  117. package/dist/lib/research-impl-gate.js +105 -0
  118. package/dist/lib/sherlock-memory.d.ts +29 -0
  119. package/dist/lib/sherlock-memory.js +105 -0
  120. package/dist/lib/skill-crystalliser.d.ts +44 -0
  121. package/dist/lib/skill-crystalliser.js +60 -0
  122. package/dist/lib/sprint-runner-engine.d.ts +27 -0
  123. package/dist/lib/sprint-runner-engine.js +1042 -0
  124. package/dist/lib/sprint-state.d.ts +71 -0
  125. package/dist/lib/sprint-state.js +202 -0
  126. package/dist/lib/stuck-handler.d.ts +17 -0
  127. package/dist/lib/stuck-handler.js +249 -0
  128. package/dist/lib/task-contract-checker.d.ts +17 -0
  129. package/dist/lib/task-contract-checker.js +29 -0
  130. package/dist/lib/task-router/index.d.ts +17 -0
  131. package/dist/lib/task-router/index.js +52 -0
  132. package/dist/lib/task-router/router/generate-execution-id.d.ts +10 -0
  133. package/dist/lib/task-router/router/generate-execution-id.js +24 -0
  134. package/dist/lib/task-router/router/resolve-route.d.ts +2 -0
  135. package/dist/lib/task-router/router/resolve-route.js +49 -0
  136. package/dist/lib/task-router/types.d.ts +79 -0
  137. package/dist/lib/task-router/types.js +39 -0
  138. package/dist/lib/token-budget-validator.d.ts +44 -0
  139. package/dist/lib/token-budget-validator.js +84 -0
  140. package/dist/lib/trust-score-updater.d.ts +30 -0
  141. package/dist/lib/trust-score-updater.js +107 -0
  142. package/dist/lib/wallet-state.d.ts +26 -0
  143. package/dist/lib/wallet-state.js +85 -0
  144. package/package.json +27 -0
@@ -0,0 +1,36 @@
1
+ /**
2
+ * llm-cost-table.ts — Per-model USD-per-million-tokens pricing + computeCost.
3
+ *
4
+ * Used by the orchestrator (after every callLLM) to compute the dollar cost
5
+ * of each LLM call from real input/output token counts, then write that to
6
+ * the wallet ledger so the anti-burn gates (sprint-runner's isOverDailyBudget
7
+ * + ceo-wallet's deductCost) actually have data to act on.
8
+ *
9
+ * Pricing as of 2026-05-20. Update when vendors change rates. Sources:
10
+ * - Anthropic public pricing page
11
+ * - MiniMax public pricing (M2.5)
12
+ * - Google Gemini 2.5 Flash via OpenClaw gateway (with 5% router fee)
13
+ * - Ollama local models = $0
14
+ *
15
+ * Stdlib only. NO LLM calls.
16
+ *
17
+ * Created: sprint-1566 task `wallet_ledger_anthropic_calls` (F3) +
18
+ * `fix_cost_model_recorder` (F0e).
19
+ */
20
+ export interface ModelPricing {
21
+ input_per_million: number;
22
+ output_per_million: number;
23
+ }
24
+ /** Pricing table. Keys are canonical model identifiers used by the orchestrator
25
+ * and ClawRouter. Aliases (legacy names, short names) map to the canonical
26
+ * via the resolveModel helper below. */
27
+ export declare const LLM_COSTS: Record<string, ModelPricing>;
28
+ /** Normalize a model name to the table key. Strips common prefixes (provider/)
29
+ * if not in the table, lowercases for case-insensitive lookup of legacy names. */
30
+ export declare function resolveModel(model: string): {
31
+ canonical: string;
32
+ pricing: ModelPricing;
33
+ known: boolean;
34
+ };
35
+ /** Compute USD cost for one LLM call. Returns 0 for unknown tokens (defensive). */
36
+ export declare function computeCost(model: string, input_tokens: number, output_tokens: number): number;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ /**
3
+ * llm-cost-table.ts — Per-model USD-per-million-tokens pricing + computeCost.
4
+ *
5
+ * Used by the orchestrator (after every callLLM) to compute the dollar cost
6
+ * of each LLM call from real input/output token counts, then write that to
7
+ * the wallet ledger so the anti-burn gates (sprint-runner's isOverDailyBudget
8
+ * + ceo-wallet's deductCost) actually have data to act on.
9
+ *
10
+ * Pricing as of 2026-05-20. Update when vendors change rates. Sources:
11
+ * - Anthropic public pricing page
12
+ * - MiniMax public pricing (M2.5)
13
+ * - Google Gemini 2.5 Flash via OpenClaw gateway (with 5% router fee)
14
+ * - Ollama local models = $0
15
+ *
16
+ * Stdlib only. NO LLM calls.
17
+ *
18
+ * Created: sprint-1566 task `wallet_ledger_anthropic_calls` (F3) +
19
+ * `fix_cost_model_recorder` (F0e).
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.LLM_COSTS = void 0;
23
+ exports.resolveModel = resolveModel;
24
+ exports.computeCost = computeCost;
25
+ /** Pricing table. Keys are canonical model identifiers used by the orchestrator
26
+ * and ClawRouter. Aliases (legacy names, short names) map to the canonical
27
+ * via the resolveModel helper below. */
28
+ exports.LLM_COSTS = {
29
+ // Anthropic
30
+ 'claude-sonnet-4-20250514': { input_per_million: 3.00, output_per_million: 15.00 },
31
+ 'claude-haiku-4-5-20251001': { input_per_million: 0.80, output_per_million: 4.00 },
32
+ 'claude-opus-4': { input_per_million: 15.00, output_per_million: 75.00 },
33
+ // MiniMax
34
+ 'MiniMax-M2.5': { input_per_million: 0.50, output_per_million: 2.00 },
35
+ 'minimax-m2.5': { input_per_million: 0.50, output_per_million: 2.00 },
36
+ // OpenAI (Codex review path)
37
+ 'gpt-5-codex': { input_per_million: 5.00, output_per_million: 20.00 },
38
+ 'codex': { input_per_million: 5.00, output_per_million: 20.00 },
39
+ // Google via OpenClaw gateway (+5% router fee already in the published rate)
40
+ 'google/gemini-2.5-flash': { input_per_million: 0.35, output_per_million: 1.40 },
41
+ 'gemini-2.5-flash': { input_per_million: 0.35, output_per_million: 1.40 },
42
+ // xAI Grok (X Premium subscription model — flat-rate, but per-token rate
43
+ // here approximates the marginal cost beyond subscription. Adjust if we
44
+ // ever pay metered.)
45
+ 'grok-4': { input_per_million: 3.00, output_per_million: 15.00 },
46
+ // Ollama / local models — free
47
+ 'qwen3:0.6b': { input_per_million: 0, output_per_million: 0 },
48
+ 'qwen3:4b': { input_per_million: 0, output_per_million: 0 },
49
+ 'qwen3:14b': { input_per_million: 0, output_per_million: 0 },
50
+ 'qwen3:32b': { input_per_million: 0, output_per_million: 0 },
51
+ 'deepseek-r1:14b': { input_per_million: 0, output_per_million: 0 },
52
+ 'gemma4:26b': { input_per_million: 0, output_per_million: 0 },
53
+ // OpenClaw gateway pseudo-models (routed downstream — billed there too;
54
+ // here we mark them 0 so we don't double-count when the real model also
55
+ // gets recorded via the routeCall result.model field)
56
+ 'openclaw/main': { input_per_million: 0, output_per_million: 0 },
57
+ 'openclaw': { input_per_million: 0, output_per_million: 0 },
58
+ 'openclaw/default': { input_per_million: 0, output_per_million: 0 },
59
+ };
60
+ /** Fallback used when an unknown model name shows up. Conservative — assumes
61
+ * Sonnet-grade cost so we over-count rather than under-count. Logs a warning
62
+ * via the recorder so we know to add the model to the table. */
63
+ const UNKNOWN_FALLBACK = { input_per_million: 3.00, output_per_million: 15.00 };
64
+ /** Normalize a model name to the table key. Strips common prefixes (provider/)
65
+ * if not in the table, lowercases for case-insensitive lookup of legacy names. */
66
+ function resolveModel(model) {
67
+ if (!model || model === 'unknown') {
68
+ return { canonical: 'unknown', pricing: UNKNOWN_FALLBACK, known: false };
69
+ }
70
+ if (exports.LLM_COSTS[model])
71
+ return { canonical: model, pricing: exports.LLM_COSTS[model], known: true };
72
+ // Try lowercase
73
+ const lower = model.toLowerCase();
74
+ if (exports.LLM_COSTS[lower])
75
+ return { canonical: lower, pricing: exports.LLM_COSTS[lower], known: true };
76
+ // Try without provider/ prefix
77
+ const noPrefix = model.includes('/') ? model.split('/').pop() : model;
78
+ if (exports.LLM_COSTS[noPrefix])
79
+ return { canonical: noPrefix, pricing: exports.LLM_COSTS[noPrefix], known: true };
80
+ return { canonical: model, pricing: UNKNOWN_FALLBACK, known: false };
81
+ }
82
+ /** Compute USD cost for one LLM call. Returns 0 for unknown tokens (defensive). */
83
+ function computeCost(model, input_tokens, output_tokens) {
84
+ if (!input_tokens && !output_tokens)
85
+ return 0;
86
+ const { pricing } = resolveModel(model);
87
+ const inCost = (input_tokens || 0) * pricing.input_per_million / 1_000_000;
88
+ const outCost = (output_tokens || 0) * pricing.output_per_million / 1_000_000;
89
+ return inCost + outCost;
90
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * local-model-router.ts — Decides whether a task runs locally (Ollama) or
3
+ * goes to the cloud (ClawRouter), and which local model to use.
4
+ */
5
+ import { WalletState } from './wallet-state';
6
+ export type LocalTier = 'nano' | 'local' | 'power' | 'heavy';
7
+ export interface LocalRoute {
8
+ model: string;
9
+ tier: LocalTier;
10
+ }
11
+ interface Task {
12
+ task_target?: string;
13
+ task_type?: string;
14
+ title?: string;
15
+ priority?: string;
16
+ }
17
+ /**
18
+ * Decide whether this task should run locally.
19
+ * Priority: explicit task_target > wallet state > task complexity heuristic
20
+ */
21
+ export declare function shouldRunLocally(task: Task, wallet: WalletState, sovereign?: boolean): boolean;
22
+ /**
23
+ * Select the local Ollama model for a given task type.
24
+ * Falls back to qwen3:14b (the always-loaded workhorse) if unknown type.
25
+ */
26
+ export declare function selectLocalModel(taskType: string): LocalRoute;
27
+ export {};
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /**
3
+ * local-model-router.ts — Decides whether a task runs locally (Ollama) or
4
+ * goes to the cloud (ClawRouter), and which local model to use.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.shouldRunLocally = shouldRunLocally;
8
+ exports.selectLocalModel = selectLocalModel;
9
+ const LOCAL_MODEL_MAP = {
10
+ // Nano tier — sub-second, ~120 tok/s
11
+ classify: { model: 'qwen3:0.6b', tier: 'nano' },
12
+ format: { model: 'qwen3:0.6b', tier: 'nano' },
13
+ // Local tier — fast, ~60 tok/s
14
+ summarize: { model: 'qwen3:4b', tier: 'local' },
15
+ draft: { model: 'qwen3:4b', tier: 'local' },
16
+ 'codebase-scan': { model: 'qwen3:4b', tier: 'local' },
17
+ // Power tier — workhorse, ~50 tok/s
18
+ code: { model: 'qwen3:14b', tier: 'power' },
19
+ content: { model: 'qwen3:14b', tier: 'power' },
20
+ data: { model: 'qwen3:14b', tier: 'power' },
21
+ lang: { model: 'qwen3:14b', tier: 'power' },
22
+ audit: { model: 'qwen3:14b', tier: 'power' },
23
+ util: { model: 'qwen3:14b', tier: 'power' },
24
+ 'agent-framework': { model: 'qwen3:14b', tier: 'power' },
25
+ // Reasoning — deepseek-r1, ~40 tok/s
26
+ reason: { model: 'deepseek-r1:14b', tier: 'power' },
27
+ // Heavy tier — qwen3:32b, 20GB RAM, single-task only
28
+ 'refactor-complex': { model: 'qwen3:32b', tier: 'heavy' },
29
+ };
30
+ /**
31
+ * Decide whether this task should run locally.
32
+ * Priority: explicit task_target > wallet state > task complexity heuristic
33
+ */
34
+ function shouldRunLocally(task, wallet, sovereign = false) {
35
+ // Explicit override: sovereign mode or wallet frozen → everything local
36
+ if (sovereign || wallet.isFrozen)
37
+ return true;
38
+ // Explicit task_target: local → always local
39
+ if (task.task_target === 'local')
40
+ return true;
41
+ // Explicit cloud targets → never local (unless sovereign)
42
+ if (task.task_target === 'cloud-exec' || task.task_target === 'cloud-post')
43
+ return false;
44
+ // Wallet degraded (≥80%) → push non-critical tasks local
45
+ if (wallet.isDegraded) {
46
+ const taskType = (task.task_type || '').toLowerCase();
47
+ const nonCritical = ['util', 'content', 'data', 'summarize', 'draft', 'codebase-scan'];
48
+ if (nonCritical.includes(taskType))
49
+ return true;
50
+ if (task.priority !== 'critical' && task.priority !== 'high')
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ /**
56
+ * Select the local Ollama model for a given task type.
57
+ * Falls back to qwen3:14b (the always-loaded workhorse) if unknown type.
58
+ */
59
+ function selectLocalModel(taskType) {
60
+ return LOCAL_MODEL_MAP[taskType] || LOCAL_MODEL_MAP['code'];
61
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * mc-client.ts — Mission Control API client for Invoica agents
3
+ *
4
+ * Provides lightweight connect/heartbeat/token-reporting for any Invoica agent
5
+ * to register itself with the Mission Control dashboard.
6
+ *
7
+ * API (from builderz-labs/mission-control):
8
+ * POST /api/connect → register, get connection_id + agent_id
9
+ * POST /api/agents/:id/heartbeat → keep-alive (every 30s for long-running agents)
10
+ * POST /api/tokens → report token usage for cost tracking
11
+ * DELETE /api/connect → deregister on shutdown
12
+ *
13
+ * Auth: Bearer token = MC_API_KEY env var
14
+ * Base URL: MC_BASE_URL env var (default http://localhost:3010)
15
+ *
16
+ * Fails silently if Mission Control is not running or MC_API_KEY is not set.
17
+ *
18
+ * Usage (long-running agent):
19
+ * const mc = new MCClient('ceo-ai-bot', 'executive');
20
+ * await mc.connect();
21
+ * mc.startHeartbeatLoop(30_000);
22
+ * process.on('SIGTERM', async () => { mc.stopHeartbeatLoop(); await mc.disconnect(); });
23
+ *
24
+ * Usage (cron agent):
25
+ * const mc = new MCClient('memory-agent', 'observer');
26
+ * await mc.connect();
27
+ * // ... do work ...
28
+ * await mc.reportTokens('claude-haiku-4-5', 800, 400);
29
+ * await mc.disconnect();
30
+ */
31
+ export declare class MCClient {
32
+ private readonly agentName;
33
+ private readonly agentRole;
34
+ private connectionId;
35
+ private agentId;
36
+ private heartbeatTimer;
37
+ private connected;
38
+ constructor(agentName: string, agentRole?: string);
39
+ connect(): Promise<boolean>;
40
+ heartbeat(tokenUsage?: {
41
+ model: string;
42
+ inputTokens: number;
43
+ outputTokens: number;
44
+ }): Promise<void>;
45
+ reportTokens(model: string, inputTokens: number, outputTokens: number, operation?: string): Promise<void>;
46
+ disconnect(): Promise<void>;
47
+ startHeartbeatLoop(intervalMs?: number): void;
48
+ stopHeartbeatLoop(): void;
49
+ runSession<T>(fn: (mc: MCClient) => Promise<T>): Promise<T>;
50
+ }
51
+ export declare function createMCClient(agentName: string, agentRole?: string): MCClient;
@@ -0,0 +1,249 @@
1
+ "use strict";
2
+ /**
3
+ * mc-client.ts — Mission Control API client for Invoica agents
4
+ *
5
+ * Provides lightweight connect/heartbeat/token-reporting for any Invoica agent
6
+ * to register itself with the Mission Control dashboard.
7
+ *
8
+ * API (from builderz-labs/mission-control):
9
+ * POST /api/connect → register, get connection_id + agent_id
10
+ * POST /api/agents/:id/heartbeat → keep-alive (every 30s for long-running agents)
11
+ * POST /api/tokens → report token usage for cost tracking
12
+ * DELETE /api/connect → deregister on shutdown
13
+ *
14
+ * Auth: Bearer token = MC_API_KEY env var
15
+ * Base URL: MC_BASE_URL env var (default http://localhost:3010)
16
+ *
17
+ * Fails silently if Mission Control is not running or MC_API_KEY is not set.
18
+ *
19
+ * Usage (long-running agent):
20
+ * const mc = new MCClient('ceo-ai-bot', 'executive');
21
+ * await mc.connect();
22
+ * mc.startHeartbeatLoop(30_000);
23
+ * process.on('SIGTERM', async () => { mc.stopHeartbeatLoop(); await mc.disconnect(); });
24
+ *
25
+ * Usage (cron agent):
26
+ * const mc = new MCClient('memory-agent', 'observer');
27
+ * await mc.connect();
28
+ * // ... do work ...
29
+ * await mc.reportTokens('claude-haiku-4-5', 800, 400);
30
+ * await mc.disconnect();
31
+ */
32
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
33
+ if (k2 === undefined) k2 = k;
34
+ var desc = Object.getOwnPropertyDescriptor(m, k);
35
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
36
+ desc = { enumerable: true, get: function() { return m[k]; } };
37
+ }
38
+ Object.defineProperty(o, k2, desc);
39
+ }) : (function(o, m, k, k2) {
40
+ if (k2 === undefined) k2 = k;
41
+ o[k2] = m[k];
42
+ }));
43
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
44
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
45
+ }) : function(o, v) {
46
+ o["default"] = v;
47
+ });
48
+ var __importStar = (this && this.__importStar) || (function () {
49
+ var ownKeys = function(o) {
50
+ ownKeys = Object.getOwnPropertyNames || function (o) {
51
+ var ar = [];
52
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
53
+ return ar;
54
+ };
55
+ return ownKeys(o);
56
+ };
57
+ return function (mod) {
58
+ if (mod && mod.__esModule) return mod;
59
+ var result = {};
60
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
61
+ __setModuleDefault(result, mod);
62
+ return result;
63
+ };
64
+ })();
65
+ Object.defineProperty(exports, "__esModule", { value: true });
66
+ exports.MCClient = void 0;
67
+ exports.createMCClient = createMCClient;
68
+ const http = __importStar(require("http"));
69
+ const https = __importStar(require("https"));
70
+ // ── Config ──────────────────────────────────────────────────────────────────
71
+ const MC_BASE_URL = process.env.MC_BASE_URL || 'http://localhost:3010';
72
+ const MC_API_KEY = process.env.MC_API_KEY || '';
73
+ const TOOL_NAME = 'invoica-agent';
74
+ const TOOL_VERSION = '1.0.0';
75
+ // Mission Control model pricing keys for common Invoica models
76
+ // (maps our internal names to MC's pricing table names)
77
+ const MODEL_MAP = {
78
+ 'claude-haiku-4-5': 'claude-haiku-4-5',
79
+ 'claude-3-5-haiku-latest': 'anthropic/claude-3-5-haiku-latest',
80
+ 'claude-sonnet-4-5': 'claude-sonnet-4',
81
+ 'claude-3-5-sonnet-20241022': 'claude-sonnet-4',
82
+ 'MiniMax-Text-01': 'minimax/minimax-m2.1',
83
+ };
84
+ // ── HTTP helper ──────────────────────────────────────────────────────────────
85
+ function mcRequest(method, path, body, timeoutMs = 8_000) {
86
+ return new Promise((resolve) => {
87
+ if (!MC_API_KEY) {
88
+ resolve(null);
89
+ return;
90
+ }
91
+ const bodyStr = body ? JSON.stringify(body) : '';
92
+ let url;
93
+ try {
94
+ url = new URL(path, MC_BASE_URL);
95
+ }
96
+ catch {
97
+ resolve(null);
98
+ return;
99
+ }
100
+ const isHttps = url.protocol === 'https:';
101
+ const lib = isHttps ? https : http;
102
+ const defaultPort = isHttps ? 443 : 3010;
103
+ const options = {
104
+ hostname: url.hostname,
105
+ port: url.port ? parseInt(url.port, 10) : defaultPort,
106
+ path: url.pathname + url.search,
107
+ method,
108
+ headers: {
109
+ 'Content-Type': 'application/json',
110
+ 'Authorization': `Bearer ${MC_API_KEY}`,
111
+ ...(bodyStr ? { 'Content-Length': String(Buffer.byteLength(bodyStr)) } : {}),
112
+ },
113
+ };
114
+ const req = lib.request(options, (res) => {
115
+ let data = '';
116
+ res.on('data', (c) => (data += c));
117
+ res.on('end', () => {
118
+ try {
119
+ resolve(JSON.parse(data));
120
+ }
121
+ catch {
122
+ resolve({});
123
+ }
124
+ });
125
+ });
126
+ req.setTimeout(timeoutMs, () => { req.destroy(); resolve(null); });
127
+ req.on('error', () => resolve(null));
128
+ if (bodyStr)
129
+ req.write(bodyStr);
130
+ req.end();
131
+ });
132
+ }
133
+ // ── MCClient class ───────────────────────────────────────────────────────────
134
+ class MCClient {
135
+ agentName;
136
+ agentRole;
137
+ connectionId = null;
138
+ agentId = null;
139
+ heartbeatTimer = null;
140
+ connected = false;
141
+ constructor(agentName, agentRole = 'agent') {
142
+ this.agentName = agentName;
143
+ this.agentRole = agentRole;
144
+ }
145
+ // ── Connect ───────────────────────────────────────────────────────────────
146
+ async connect() {
147
+ if (!MC_API_KEY)
148
+ return false;
149
+ try {
150
+ const res = await mcRequest('POST', '/api/connect', {
151
+ tool_name: TOOL_NAME,
152
+ tool_version: TOOL_VERSION,
153
+ agent_name: this.agentName,
154
+ agent_role: this.agentRole,
155
+ });
156
+ if (!res?.connection_id)
157
+ return false;
158
+ this.connectionId = res.connection_id;
159
+ this.agentId = Number(res.agent_id);
160
+ this.connected = true;
161
+ console.log(`[MCClient] ${this.agentName} connected to Mission Control (agent_id=${this.agentId})`);
162
+ return true;
163
+ }
164
+ catch {
165
+ return false;
166
+ }
167
+ }
168
+ // ── Heartbeat ─────────────────────────────────────────────────────────────
169
+ async heartbeat(tokenUsage) {
170
+ if (!this.agentId || !this.connected)
171
+ return;
172
+ try {
173
+ const body = { connection_id: this.connectionId };
174
+ if (tokenUsage) {
175
+ body.token_usage = {
176
+ model: MODEL_MAP[tokenUsage.model] || tokenUsage.model,
177
+ inputTokens: tokenUsage.inputTokens,
178
+ outputTokens: tokenUsage.outputTokens,
179
+ };
180
+ }
181
+ await mcRequest('POST', `/api/agents/${this.agentId}/heartbeat`, body);
182
+ }
183
+ catch { /* silent */ }
184
+ }
185
+ // ── Report token usage (standalone, without heartbeat) ────────────────────
186
+ async reportTokens(model, inputTokens, outputTokens, operation = 'agent_run') {
187
+ if (!MC_API_KEY)
188
+ return;
189
+ try {
190
+ await mcRequest('POST', '/api/tokens', {
191
+ model: MODEL_MAP[model] || model,
192
+ sessionId: `${this.agentName}:cli`,
193
+ inputTokens,
194
+ outputTokens,
195
+ operation,
196
+ });
197
+ }
198
+ catch { /* silent */ }
199
+ }
200
+ // ── Disconnect ────────────────────────────────────────────────────────────
201
+ async disconnect() {
202
+ if (!this.connectionId || !this.connected)
203
+ return;
204
+ try {
205
+ await mcRequest('DELETE', '/api/connect', { connection_id: this.connectionId });
206
+ console.log(`[MCClient] ${this.agentName} disconnected from Mission Control`);
207
+ }
208
+ catch { /* silent */ }
209
+ finally {
210
+ this.connected = false;
211
+ this.connectionId = null;
212
+ this.agentId = null;
213
+ }
214
+ }
215
+ // ── Heartbeat loop (for long-running agents) ──────────────────────────────
216
+ startHeartbeatLoop(intervalMs = 30_000) {
217
+ if (this.heartbeatTimer)
218
+ clearInterval(this.heartbeatTimer);
219
+ this.heartbeatTimer = setInterval(() => {
220
+ this.heartbeat().catch(() => { });
221
+ }, intervalMs);
222
+ // Don't block process exit
223
+ if (typeof this.heartbeatTimer === 'object' && 'unref' in this.heartbeatTimer) {
224
+ this.heartbeatTimer.unref();
225
+ }
226
+ }
227
+ stopHeartbeatLoop() {
228
+ if (this.heartbeatTimer) {
229
+ clearInterval(this.heartbeatTimer);
230
+ this.heartbeatTimer = null;
231
+ }
232
+ }
233
+ // ── Convenience: full lifecycle for cron agents ───────────────────────────
234
+ // Connects, runs the provided function, then disconnects.
235
+ async runSession(fn) {
236
+ await this.connect();
237
+ try {
238
+ return await fn(this);
239
+ }
240
+ finally {
241
+ await this.disconnect();
242
+ }
243
+ }
244
+ }
245
+ exports.MCClient = MCClient;
246
+ // ── Convenience singleton factory ─────────────────────────────────────────────
247
+ function createMCClient(agentName, agentRole) {
248
+ return new MCClient(agentName, agentRole);
249
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * model-router-contract.ts — TICKET-215 Wave C: the engine's model-routing SEAM.
3
+ *
4
+ * This is the zero-dep interface that decouples the orchestrator from any
5
+ * concrete router. The orchestrator depends ONLY on this contract; the actual
6
+ * implementation is injected at startup (see model-router-registry.ts).
7
+ *
8
+ * WHY: ClawRouter v2 (the production router) imports `viem` for on-chain x402
9
+ * payments. We do NOT want `viem` in @kognai/orchestrator-core — that would make
10
+ * every adopter (e.g. Invoica, which never routes on-chain) carry web3. So the
11
+ * viem-backed router lives OUTSIDE core (Kognai injects it); products that don't
12
+ * route on-chain inject their own / use a local default. viem stays absent from
13
+ * core's dependency tree entirely.
14
+ *
15
+ * These request/response types are the canonical source of truth — clawrouter-v2
16
+ * imports them from here and re-exports for back-compat.
17
+ */
18
+ export type TierClass = 'text' | 'creative';
19
+ export type TextComplexity = 'nano' | 'local' | 'power' | 'exec' | 'apex';
20
+ export type CreativeModality = 'image' | 'video' | 'speech' | 'music' | 'transcription' | 'visual_understanding';
21
+ export type CreativeQuality = 'fast' | 'high' | 'emotional';
22
+ export interface ClawRouterV2Request {
23
+ task_type: string;
24
+ tier_class: TierClass;
25
+ complexity?: TextComplexity;
26
+ modality?: CreativeModality;
27
+ quality?: CreativeQuality;
28
+ context_tokens: number;
29
+ constitutional_flag: boolean;
30
+ agent_id: string;
31
+ payload: {
32
+ system?: string;
33
+ prompt?: string;
34
+ messages?: Array<{
35
+ role: string;
36
+ content: string;
37
+ }>;
38
+ [key: string]: any;
39
+ };
40
+ }
41
+ export interface ClawRouterV2Response {
42
+ content: string;
43
+ model: string;
44
+ tier: string;
45
+ local: boolean;
46
+ cost_usd: number;
47
+ input_tokens: number;
48
+ output_tokens: number;
49
+ qcg_compressed: boolean;
50
+ tokens_saved_by_qcg: number;
51
+ agent_id: string;
52
+ task_type: string;
53
+ timestamp: string;
54
+ }
55
+ export interface CallLLMOptions {
56
+ systemPrompt?: string;
57
+ complexity?: TextComplexity;
58
+ constitutional?: boolean;
59
+ agentId?: string;
60
+ taskType?: string;
61
+ maxTokens?: number;
62
+ }
63
+ export interface CallLLMResult {
64
+ content: string;
65
+ model: string;
66
+ tier: string;
67
+ cost_usd: number;
68
+ }
69
+ export interface HealthCheckResult {
70
+ ollama: boolean;
71
+ gateway: boolean;
72
+ models: string[];
73
+ }
74
+ export interface DailyCostDigest {
75
+ total_usd: number;
76
+ by_tier: Record<string, number>;
77
+ by_agent: Record<string, number>;
78
+ tokens_saved_by_qcg: number;
79
+ call_count: number;
80
+ }
81
+ /**
82
+ * The single door every LLM call passes through (Execution Protocol §17).
83
+ * Implementations: Kognai's viem-backed ClawRouter v2 (injected), a future
84
+ * local-only default, or any product-specific router.
85
+ */
86
+ export interface ModelRouter {
87
+ routeCall(req: ClawRouterV2Request): Promise<ClawRouterV2Response>;
88
+ callLLM(prompt: string, opts?: CallLLMOptions): Promise<CallLLMResult>;
89
+ healthCheck(): Promise<HealthCheckResult>;
90
+ getDailyCostDigest(): DailyCostDigest;
91
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ /**
3
+ * model-router-contract.ts — TICKET-215 Wave C: the engine's model-routing SEAM.
4
+ *
5
+ * This is the zero-dep interface that decouples the orchestrator from any
6
+ * concrete router. The orchestrator depends ONLY on this contract; the actual
7
+ * implementation is injected at startup (see model-router-registry.ts).
8
+ *
9
+ * WHY: ClawRouter v2 (the production router) imports `viem` for on-chain x402
10
+ * payments. We do NOT want `viem` in @kognai/orchestrator-core — that would make
11
+ * every adopter (e.g. Invoica, which never routes on-chain) carry web3. So the
12
+ * viem-backed router lives OUTSIDE core (Kognai injects it); products that don't
13
+ * route on-chain inject their own / use a local default. viem stays absent from
14
+ * core's dependency tree entirely.
15
+ *
16
+ * These request/response types are the canonical source of truth — clawrouter-v2
17
+ * imports them from here and re-exports for back-compat.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * model-router-registry.ts — TICKET-215 Wave C: the injection point for the
3
+ * model-routing seam (see model-router-contract.ts).
4
+ *
5
+ * Products wire their router at startup:
6
+ * - Kognai: setModelRouter(<viem-backed ClawRouter v2 adapter>)
7
+ * - others: inject a local/product-specific ModelRouter, or leave the
8
+ * fail-loud default in place until one is provided.
9
+ *
10
+ * The default is intentionally NOT a silent no-op: a routeCall with no router
11
+ * configured throws a clear, actionable error rather than returning empty
12
+ * completions. Replace with a real local-only default when one exists.
13
+ *
14
+ * Per-agent gating note: an injected router MAY consult the ACP capability
15
+ * registry (see ./acp) before performing a paid/on-chain call, so that only
16
+ * capability-bearing agents can spend — everyone else falls back to local.
17
+ */
18
+ import type { ModelRouter } from './model-router-contract';
19
+ /** Inject the concrete router. Call once at process startup, before routing. */
20
+ export declare function setModelRouter(router: ModelRouter): void;
21
+ /** Resolve the active router. The orchestrator routes every LLM call through this. */
22
+ export declare function getModelRouter(): ModelRouter;
23
+ /** True once a real router has been injected (i.e. not the fail-loud default). */
24
+ export declare function isModelRouterConfigured(): boolean;