@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.
- package/README.md +44 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +175 -0
- package/dist/lib/aar-middleware.d.ts +6 -0
- package/dist/lib/aar-middleware.js +70 -0
- package/dist/lib/aar-types.d.ts +34 -0
- package/dist/lib/aar-types.js +4 -0
- package/dist/lib/acp-engine.d.ts +68 -0
- package/dist/lib/acp-engine.js +123 -0
- package/dist/lib/acp.d.ts +61 -0
- package/dist/lib/acp.js +425 -0
- package/dist/lib/agent-registry.d.ts +50 -0
- package/dist/lib/agent-registry.js +137 -0
- package/dist/lib/anthropic-direct.d.ts +27 -0
- package/dist/lib/anthropic-direct.js +109 -0
- package/dist/lib/asmr-extractor.d.ts +40 -0
- package/dist/lib/asmr-extractor.js +151 -0
- package/dist/lib/asmr-retrieval.d.ts +76 -0
- package/dist/lib/asmr-retrieval.js +311 -0
- package/dist/lib/asmr.d.ts +8 -0
- package/dist/lib/asmr.js +24 -0
- package/dist/lib/brainx-client.d.ts +72 -0
- package/dist/lib/brainx-client.js +200 -0
- package/dist/lib/brainx-embed.d.ts +14 -0
- package/dist/lib/brainx-embed.js +139 -0
- package/dist/lib/brainx-swarm-bridge.d.ts +93 -0
- package/dist/lib/brainx-swarm-bridge.js +242 -0
- package/dist/lib/byterover-client.d.ts +19 -0
- package/dist/lib/byterover-client.js +59 -0
- package/dist/lib/ceo-wallet.d.ts +37 -0
- package/dist/lib/ceo-wallet.js +176 -0
- package/dist/lib/chomsky-gate.d.ts +24 -0
- package/dist/lib/chomsky-gate.js +178 -0
- package/dist/lib/chomsky-runner.d.ts +29 -0
- package/dist/lib/chomsky-runner.js +157 -0
- package/dist/lib/citizen-score-contract.d.ts +72 -0
- package/dist/lib/citizen-score-contract.js +16 -0
- package/dist/lib/citizen-score-registry.d.ts +25 -0
- package/dist/lib/citizen-score-registry.js +65 -0
- package/dist/lib/citizenship.d.ts +103 -0
- package/dist/lib/citizenship.js +272 -0
- package/dist/lib/clawrouter-client.d.ts +37 -0
- package/dist/lib/clawrouter-client.js +148 -0
- package/dist/lib/code-asset-crystalliser.d.ts +41 -0
- package/dist/lib/code-asset-crystalliser.js +181 -0
- package/dist/lib/code-failure-logger.d.ts +27 -0
- package/dist/lib/code-failure-logger.js +42 -0
- package/dist/lib/cto-approval-gate.d.ts +45 -0
- package/dist/lib/cto-approval-gate.js +478 -0
- package/dist/lib/cto-gate-types.d.ts +28 -0
- package/dist/lib/cto-gate-types.js +8 -0
- package/dist/lib/decomposer-feedback.d.ts +54 -0
- package/dist/lib/decomposer-feedback.js +115 -0
- package/dist/lib/emotional-safety-gate.d.ts +48 -0
- package/dist/lib/emotional-safety-gate.js +97 -0
- package/dist/lib/engine-paths.d.ts +13 -0
- package/dist/lib/engine-paths.js +32 -0
- package/dist/lib/event-bus-listener.d.ts +8 -0
- package/dist/lib/event-bus-listener.js +144 -0
- package/dist/lib/event-bus-publisher.d.ts +25 -0
- package/dist/lib/event-bus-publisher.js +188 -0
- package/dist/lib/event-bus-types.d.ts +73 -0
- package/dist/lib/event-bus-types.js +23 -0
- package/dist/lib/failure-library.d.ts +178 -0
- package/dist/lib/failure-library.js +349 -0
- package/dist/lib/ksl/error-log.d.ts +28 -0
- package/dist/lib/ksl/error-log.js +43 -0
- package/dist/lib/ksl/index.d.ts +9 -0
- package/dist/lib/ksl/index.js +25 -0
- package/dist/lib/ksl/orchestrator-tap.d.ts +16 -0
- package/dist/lib/ksl/orchestrator-tap.js +85 -0
- package/dist/lib/ksl/record-writer.d.ts +46 -0
- package/dist/lib/ksl/record-writer.js +45 -0
- package/dist/lib/llm-cost-table.d.ts +36 -0
- package/dist/lib/llm-cost-table.js +90 -0
- package/dist/lib/local-model-router.d.ts +27 -0
- package/dist/lib/local-model-router.js +61 -0
- package/dist/lib/mc-client.d.ts +51 -0
- package/dist/lib/mc-client.js +249 -0
- package/dist/lib/model-router-contract.d.ts +91 -0
- package/dist/lib/model-router-contract.js +19 -0
- package/dist/lib/model-router-registry.d.ts +24 -0
- package/dist/lib/model-router-registry.js +52 -0
- package/dist/lib/model-router.d.ts +20 -0
- package/dist/lib/model-router.js +79 -0
- package/dist/lib/monotask-state-machine.d.ts +19 -0
- package/dist/lib/monotask-state-machine.js +131 -0
- package/dist/lib/neutral-prompt-checker.d.ts +22 -0
- package/dist/lib/neutral-prompt-checker.js +130 -0
- package/dist/lib/notion-direct.d.ts +92 -0
- package/dist/lib/notion-direct.js +381 -0
- package/dist/lib/ollama-client.d.ts +37 -0
- package/dist/lib/ollama-client.js +158 -0
- package/dist/lib/omel/credential-vault.d.ts +57 -0
- package/dist/lib/omel/credential-vault.js +324 -0
- package/dist/lib/omel/human-brake.d.ts +32 -0
- package/dist/lib/omel/human-brake.js +289 -0
- package/dist/lib/omel/index.d.ts +10 -0
- package/dist/lib/omel/index.js +26 -0
- package/dist/lib/omel/phantom-workspace.d.ts +31 -0
- package/dist/lib/omel/phantom-workspace.js +256 -0
- package/dist/lib/omel/wipe-witness.d.ts +75 -0
- package/dist/lib/omel/wipe-witness.js +398 -0
- package/dist/lib/orchestrate-engine.d.ts +25 -0
- package/dist/lib/orchestrate-engine.js +4436 -0
- package/dist/lib/perm-judge.d.ts +46 -0
- package/dist/lib/perm-judge.js +173 -0
- package/dist/lib/plumber/conformance.d.ts +54 -0
- package/dist/lib/plumber/conformance.js +121 -0
- package/dist/lib/plumber/index.d.ts +9 -0
- package/dist/lib/plumber/index.js +25 -0
- package/dist/lib/plumber/observer.d.ts +52 -0
- package/dist/lib/plumber/observer.js +180 -0
- package/dist/lib/plumber/types.d.ts +78 -0
- package/dist/lib/plumber/types.js +29 -0
- package/dist/lib/research-impl-gate.d.ts +16 -0
- package/dist/lib/research-impl-gate.js +105 -0
- package/dist/lib/sherlock-memory.d.ts +29 -0
- package/dist/lib/sherlock-memory.js +105 -0
- package/dist/lib/skill-crystalliser.d.ts +44 -0
- package/dist/lib/skill-crystalliser.js +60 -0
- package/dist/lib/sprint-runner-engine.d.ts +27 -0
- package/dist/lib/sprint-runner-engine.js +1042 -0
- package/dist/lib/sprint-state.d.ts +71 -0
- package/dist/lib/sprint-state.js +202 -0
- package/dist/lib/stuck-handler.d.ts +17 -0
- package/dist/lib/stuck-handler.js +249 -0
- package/dist/lib/task-contract-checker.d.ts +17 -0
- package/dist/lib/task-contract-checker.js +29 -0
- package/dist/lib/task-router/index.d.ts +17 -0
- package/dist/lib/task-router/index.js +52 -0
- package/dist/lib/task-router/router/generate-execution-id.d.ts +10 -0
- package/dist/lib/task-router/router/generate-execution-id.js +24 -0
- package/dist/lib/task-router/router/resolve-route.d.ts +2 -0
- package/dist/lib/task-router/router/resolve-route.js +49 -0
- package/dist/lib/task-router/types.d.ts +79 -0
- package/dist/lib/task-router/types.js +39 -0
- package/dist/lib/token-budget-validator.d.ts +44 -0
- package/dist/lib/token-budget-validator.js +84 -0
- package/dist/lib/trust-score-updater.d.ts +30 -0
- package/dist/lib/trust-score-updater.js +107 -0
- package/dist/lib/wallet-state.d.ts +26 -0
- package/dist/lib/wallet-state.js +85 -0
- 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;
|