@jellyos/agent 0.1.3 → 0.1.5

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 (90) hide show
  1. package/README.md +9 -9
  2. package/README.npm.md +212 -0
  3. package/bin/jellyos-mcp +26 -0
  4. package/dist/api/ExtensionAPI.d.ts +6 -0
  5. package/dist/api/Registry.js +3 -1
  6. package/dist/cli.js +117 -42
  7. package/dist/index.d.ts +24 -1
  8. package/dist/index.js +19 -2
  9. package/dist/mcp/entry.d.ts +2 -0
  10. package/dist/mcp/entry.js +71 -0
  11. package/dist/mcp/server.d.ts +31 -0
  12. package/dist/mcp/server.js +128 -0
  13. package/dist/models/CostTracker.d.ts +66 -0
  14. package/dist/models/CostTracker.js +148 -0
  15. package/dist/models/ModelRegistry.d.ts +157 -0
  16. package/dist/models/ModelRegistry.js +496 -0
  17. package/dist/models/index.d.ts +5 -0
  18. package/dist/models/index.js +3 -0
  19. package/dist/runner/AgentRunner.d.ts +23 -2
  20. package/dist/runner/AgentRunner.js +264 -24
  21. package/dist/runner/ModelClient.d.ts +26 -6
  22. package/dist/runner/ModelClient.js +147 -28
  23. package/dist/runner/SwarmRouter.d.ts +10 -7
  24. package/dist/runner/SwarmRouter.js +85 -28
  25. package/dist/runner/ToolDispatcher.d.ts +10 -0
  26. package/dist/runner/ToolDispatcher.js +106 -2
  27. package/dist/scheduler/AgentScheduler.d.ts +118 -0
  28. package/dist/scheduler/AgentScheduler.js +253 -0
  29. package/dist/session/ContextStore.d.ts +96 -0
  30. package/dist/session/ContextStore.js +207 -0
  31. package/dist/session/GoalManager.d.ts +101 -0
  32. package/dist/session/GoalManager.js +167 -0
  33. package/dist/session/MemoryStore.d.ts +48 -0
  34. package/dist/session/MemoryStore.js +166 -0
  35. package/dist/session/SessionManager.d.ts +45 -4
  36. package/dist/session/SessionManager.js +151 -8
  37. package/dist/telemetry/Tracer.d.ts +48 -0
  38. package/dist/telemetry/Tracer.js +102 -0
  39. package/dist/tests/ContextStore.test.d.ts +2 -0
  40. package/dist/tests/ContextStore.test.js +74 -0
  41. package/dist/tests/ModelRegistry.test.d.ts +2 -0
  42. package/dist/tests/ModelRegistry.test.js +69 -0
  43. package/dist/tests/SessionManager.test.d.ts +2 -0
  44. package/dist/tests/SessionManager.test.js +108 -0
  45. package/dist/tests/TechnicalAnalysis.test.d.ts +2 -0
  46. package/dist/tests/TechnicalAnalysis.test.js +109 -0
  47. package/dist/tools/MarketSentiment.d.ts +166 -0
  48. package/dist/tools/MarketSentiment.js +209 -0
  49. package/dist/tools/NewsSentiment.d.ts +67 -0
  50. package/dist/tools/NewsSentiment.js +226 -0
  51. package/dist/tools/PriceFeed.d.ts +105 -0
  52. package/dist/tools/PriceFeed.js +282 -0
  53. package/dist/tools/TechnicalAnalysis.d.ts +110 -0
  54. package/dist/tools/TechnicalAnalysis.js +357 -0
  55. package/dist/tools/index.d.ts +7 -0
  56. package/dist/tools/index.js +4 -0
  57. package/dist/tui/App.d.ts +7 -5
  58. package/dist/tui/App.js +350 -65
  59. package/dist/tui/REPL.d.ts +2 -1
  60. package/dist/tui/REPL.js +11 -6
  61. package/dist/tui/StatusBar.js +1 -1
  62. package/package.json +9 -4
  63. package/dist/api/ExtensionAPI.d.ts.map +0 -1
  64. package/dist/api/ExtensionAPI.js.map +0 -1
  65. package/dist/api/Registry.d.ts.map +0 -1
  66. package/dist/api/Registry.js.map +0 -1
  67. package/dist/cli.d.ts.map +0 -1
  68. package/dist/cli.js.map +0 -1
  69. package/dist/index.d.ts.map +0 -1
  70. package/dist/index.js.map +0 -1
  71. package/dist/loader.d.ts.map +0 -1
  72. package/dist/loader.js.map +0 -1
  73. package/dist/runner/AgentRunner.d.ts.map +0 -1
  74. package/dist/runner/AgentRunner.js.map +0 -1
  75. package/dist/runner/ModelClient.d.ts.map +0 -1
  76. package/dist/runner/ModelClient.js.map +0 -1
  77. package/dist/runner/SwarmRouter.d.ts.map +0 -1
  78. package/dist/runner/SwarmRouter.js.map +0 -1
  79. package/dist/runner/ToolDispatcher.d.ts.map +0 -1
  80. package/dist/runner/ToolDispatcher.js.map +0 -1
  81. package/dist/session/SessionManager.d.ts.map +0 -1
  82. package/dist/session/SessionManager.js.map +0 -1
  83. package/dist/tui/App.d.ts.map +0 -1
  84. package/dist/tui/App.js.map +0 -1
  85. package/dist/tui/REPL.d.ts.map +0 -1
  86. package/dist/tui/REPL.js.map +0 -1
  87. package/dist/tui/StatusBar.d.ts.map +0 -1
  88. package/dist/tui/StatusBar.js.map +0 -1
  89. package/dist/tui/theme.d.ts.map +0 -1
  90. package/dist/tui/theme.js.map +0 -1
@@ -0,0 +1,31 @@
1
+ /**
2
+ * MCPServer — Model Context Protocol server over stdio. (#28)
3
+ *
4
+ * Exposes all JellyOS registered tools as MCP tools so they can be used
5
+ * by Claude Desktop, Cursor, Continue, and any MCP-compatible client.
6
+ *
7
+ * Protocol: JSON-RPC 2.0 over stdin/stdout (MCP stdio transport).
8
+ *
9
+ * Usage:
10
+ * jellyos-mcp # exposes built-in tools
11
+ * jellyos-mcp --extension ./my.ts # includes extension tools
12
+ *
13
+ * Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
14
+ * {
15
+ * "mcpServers": {
16
+ * "jellyos": {
17
+ * "command": "jellyos-mcp",
18
+ * "env": { "OPENROUTER_API_KEY": "sk-or-..." }
19
+ * }
20
+ * }
21
+ * }
22
+ */
23
+ import type { Registry } from "../api/Registry.js";
24
+ export declare class MCPServer {
25
+ private registry;
26
+ constructor(registry: Registry);
27
+ run(): Promise<void>;
28
+ private respond;
29
+ private handle;
30
+ }
31
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1,128 @@
1
+ /**
2
+ * MCPServer — Model Context Protocol server over stdio. (#28)
3
+ *
4
+ * Exposes all JellyOS registered tools as MCP tools so they can be used
5
+ * by Claude Desktop, Cursor, Continue, and any MCP-compatible client.
6
+ *
7
+ * Protocol: JSON-RPC 2.0 over stdin/stdout (MCP stdio transport).
8
+ *
9
+ * Usage:
10
+ * jellyos-mcp # exposes built-in tools
11
+ * jellyos-mcp --extension ./my.ts # includes extension tools
12
+ *
13
+ * Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
14
+ * {
15
+ * "mcpServers": {
16
+ * "jellyos": {
17
+ * "command": "jellyos-mcp",
18
+ * "env": { "OPENROUTER_API_KEY": "sk-or-..." }
19
+ * }
20
+ * }
21
+ * }
22
+ */
23
+ import { createInterface } from "node:readline";
24
+ export class MCPServer {
25
+ registry;
26
+ constructor(registry) {
27
+ this.registry = registry;
28
+ }
29
+ async run() {
30
+ const rl = createInterface({ input: process.stdin, terminal: false });
31
+ // MCP uses newline-delimited JSON-RPC 2.0
32
+ rl.on("line", async (line) => {
33
+ const trimmed = line.trim();
34
+ if (!trimmed)
35
+ return;
36
+ let req;
37
+ try {
38
+ req = JSON.parse(trimmed);
39
+ }
40
+ catch {
41
+ this.respond({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } });
42
+ return;
43
+ }
44
+ const response = await this.handle(req);
45
+ this.respond(response);
46
+ });
47
+ rl.on("close", () => process.exit(0));
48
+ // MCP servers send an initialization notification on stderr
49
+ process.stderr.write("[JellyOS MCP] Server ready\n");
50
+ }
51
+ respond(res) {
52
+ process.stdout.write(JSON.stringify(res) + "\n");
53
+ }
54
+ async handle(req) {
55
+ try {
56
+ switch (req.method) {
57
+ case "initialize":
58
+ return {
59
+ jsonrpc: "2.0", id: req.id,
60
+ result: {
61
+ protocolVersion: "2024-11-05",
62
+ capabilities: { tools: {} },
63
+ serverInfo: { name: "jellyos", version: "0.1.5" },
64
+ },
65
+ };
66
+ case "notifications/initialized":
67
+ // No response needed for notifications
68
+ return { jsonrpc: "2.0", id: req.id, result: null };
69
+ case "tools/list":
70
+ return {
71
+ jsonrpc: "2.0", id: req.id,
72
+ result: {
73
+ tools: this.registry.listTools().map(t => ({
74
+ name: t.name,
75
+ description: t.description,
76
+ inputSchema: {
77
+ ...t.parameters,
78
+ type: "object", // MCP requires explicit type
79
+ },
80
+ })),
81
+ },
82
+ };
83
+ case "tools/call": {
84
+ const { name, arguments: args } = (req.params ?? {});
85
+ if (!name) {
86
+ return { jsonrpc: "2.0", id: req.id, error: { code: -32602, message: "Missing tool name" } };
87
+ }
88
+ const tool = this.registry.getTool(name);
89
+ if (!tool) {
90
+ return { jsonrpc: "2.0", id: req.id, error: { code: -32601, message: `Tool not found: ${name}` } };
91
+ }
92
+ try {
93
+ const result = await tool.execute("mcp", (args ?? {}));
94
+ return {
95
+ jsonrpc: "2.0", id: req.id,
96
+ result: {
97
+ content: result.content.map(c => ({ type: "text", text: c.text })),
98
+ isError: false,
99
+ },
100
+ };
101
+ }
102
+ catch (e) {
103
+ const msg = e instanceof Error ? e.message : String(e);
104
+ return {
105
+ jsonrpc: "2.0", id: req.id,
106
+ result: {
107
+ content: [{ type: "text", text: `Tool error: ${msg}` }],
108
+ isError: true,
109
+ },
110
+ };
111
+ }
112
+ }
113
+ case "ping":
114
+ return { jsonrpc: "2.0", id: req.id, result: {} };
115
+ default:
116
+ return {
117
+ jsonrpc: "2.0", id: req.id,
118
+ error: { code: -32601, message: `Method not found: ${req.method}` },
119
+ };
120
+ }
121
+ }
122
+ catch (e) {
123
+ const msg = e instanceof Error ? e.message : String(e);
124
+ return { jsonrpc: "2.0", id: req.id, error: { code: -32603, message: `Internal error: ${msg}` } };
125
+ }
126
+ }
127
+ }
128
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1,66 @@
1
+ /**
2
+ * CostTracker — tracks per-session and lifetime token usage and cost.
3
+ *
4
+ * Uses OpenRouter response headers (x-credit-used) when available,
5
+ * otherwise estimates from the model registry's pricing data.
6
+ * Emits budget warnings and provides cost reports via tool/command.
7
+ */
8
+ import type { ModelRegistry } from "./ModelRegistry.js";
9
+ export interface UsageEntry {
10
+ model: string;
11
+ timestamp: number;
12
+ promptTokens: number;
13
+ completionTokens: number;
14
+ /** Estimated cost in nano-dollars (billionths of a dollar) */
15
+ estimatedCost: number;
16
+ /** Duration in ms */
17
+ duration: number;
18
+ }
19
+ export interface SessionUsage {
20
+ promptTokens: number;
21
+ completionTokens: number;
22
+ estimatedCost: number;
23
+ calls: number;
24
+ errors: number;
25
+ }
26
+ export interface LifetimeUsage {
27
+ promptTokens: number;
28
+ completionTokens: number;
29
+ estimatedCost: number;
30
+ calls: number;
31
+ modelsUsed: Set<string>;
32
+ }
33
+ export declare class CostTracker {
34
+ private modelReg;
35
+ private session;
36
+ private lifetime;
37
+ private budgetSession;
38
+ private budgetLifetime;
39
+ private budgetWarned;
40
+ constructor(modelReg: ModelRegistry);
41
+ record(modelId: string, promptTokens: number, completionTokens: number, duration: number): void;
42
+ recordError(): void;
43
+ private maybeWarnBudget;
44
+ getBudgetStatus(): {
45
+ session: SessionUsage;
46
+ lifetime: LifetimeUsage;
47
+ warnings: string[];
48
+ };
49
+ /** True if the next call would exceed the session budget. */
50
+ wouldExceed(estimatedCallCost: number): boolean;
51
+ private loadLifetime;
52
+ saveLifetime(): void;
53
+ private formatNano;
54
+ statusLine(): string;
55
+ report(): string;
56
+ readonly costReportParams: import("@sinclair/typebox").TObject<{}>;
57
+ costReportTool(): Promise<{
58
+ content: Array<{
59
+ type: "text";
60
+ text: string;
61
+ }>;
62
+ details: Record<string, unknown>;
63
+ }>;
64
+ resetSession(): void;
65
+ }
66
+ //# sourceMappingURL=CostTracker.d.ts.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * CostTracker — tracks per-session and lifetime token usage and cost.
3
+ *
4
+ * Uses OpenRouter response headers (x-credit-used) when available,
5
+ * otherwise estimates from the model registry's pricing data.
6
+ * Emits budget warnings and provides cost reports via tool/command.
7
+ */
8
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { homedir } from "node:os";
11
+ import { Type } from "@sinclair/typebox";
12
+ const JELLY_HOME = process.env.JELLYOS_HOME ?? join(homedir(), ".jelly");
13
+ const COST_FILE = join(JELLY_HOME, "usage.json");
14
+ // ── CostTracker class ─────────────────────────────────────────────────────────
15
+ const DEFAULT_BUDGET_PER_SESSION = 1_000_000_000_000; // $1.00 in nano-dollars
16
+ const DEFAULT_BUDGET_LIFETIME = 25_000_000_000_000; // $25.00
17
+ export class CostTracker {
18
+ modelReg;
19
+ session = { promptTokens: 0, completionTokens: 0, estimatedCost: 0, calls: 0, errors: 0 };
20
+ lifetime = { promptTokens: 0, completionTokens: 0, estimatedCost: 0, calls: 0, modelsUsed: new Set() };
21
+ budgetSession;
22
+ budgetLifetime;
23
+ budgetWarned = false;
24
+ constructor(modelReg) {
25
+ this.modelReg = modelReg;
26
+ this.budgetSession = parseInt(process.env.JELLY_BUDGET_SESSION ?? String(DEFAULT_BUDGET_PER_SESSION));
27
+ this.budgetLifetime = parseInt(process.env.JELLY_BUDGET_LIFETIME ?? String(DEFAULT_BUDGET_LIFETIME));
28
+ this.loadLifetime();
29
+ }
30
+ // ── Recording ─────────────────────────────────────────────────────────────
31
+ record(modelId, promptTokens, completionTokens, duration) {
32
+ const tm = this.modelReg["modelMap"]?.get(modelId);
33
+ const costPer1K = tm?.costPer1K ?? 50_000; // fallback: 50 nano-dollars/1K (=$0.00005/1K)
34
+ const estimatedCost = Math.round(costPer1K * (promptTokens + completionTokens) / 1000);
35
+ const entry = { model: modelId, timestamp: Date.now(), promptTokens, completionTokens, estimatedCost, duration };
36
+ // Session
37
+ this.session.promptTokens += promptTokens;
38
+ this.session.completionTokens += completionTokens;
39
+ this.session.estimatedCost += estimatedCost;
40
+ this.session.calls++;
41
+ // Lifetime
42
+ this.lifetime.promptTokens += promptTokens;
43
+ this.lifetime.completionTokens += completionTokens;
44
+ this.lifetime.estimatedCost += estimatedCost;
45
+ this.lifetime.calls++;
46
+ this.lifetime.modelsUsed.add(modelId);
47
+ this.maybeWarnBudget();
48
+ }
49
+ recordError() {
50
+ this.session.errors++;
51
+ this.lifetime.calls++;
52
+ }
53
+ // ── Budget ────────────────────────────────────────────────────────────────
54
+ maybeWarnBudget() {
55
+ if (this.session.estimatedCost > this.budgetSession * 0.8 && !this.budgetWarned) {
56
+ this.budgetWarned = true;
57
+ // Budget warning is surfaced via getBudgetStatus() — callers can check
58
+ }
59
+ }
60
+ getBudgetStatus() {
61
+ const warnings = [];
62
+ const pctSession = this.session.estimatedCost / this.budgetSession * 100;
63
+ const pctLifetime = this.lifetime.estimatedCost / this.budgetLifetime * 100;
64
+ if (pctSession >= 90)
65
+ warnings.push(`⚠ Session budget at ${pctSession.toFixed(0)}% — near limit`);
66
+ else if (pctSession >= 75)
67
+ warnings.push(`⚡ Session budget at ${pctSession.toFixed(0)}%`);
68
+ if (pctLifetime >= 90)
69
+ warnings.push(`⚠ Lifetime budget at ${pctLifetime.toFixed(0)}% — near limit`);
70
+ else if (pctLifetime >= 75)
71
+ warnings.push(`⚡ Lifetime budget at ${pctLifetime.toFixed(0)}%`);
72
+ return { session: { ...this.session }, lifetime: { ...this.lifetime, modelsUsed: new Set(this.lifetime.modelsUsed) }, warnings };
73
+ }
74
+ /** True if the next call would exceed the session budget. */
75
+ wouldExceed(estimatedCallCost) {
76
+ return this.session.estimatedCost + estimatedCallCost > this.budgetSession;
77
+ }
78
+ // ── Persistence ────────────────────────────────────────────────────────────
79
+ loadLifetime() {
80
+ try {
81
+ if (!existsSync(COST_FILE))
82
+ return;
83
+ const raw = JSON.parse(readFileSync(COST_FILE, "utf-8"));
84
+ this.lifetime.promptTokens = raw.promptTokens ?? 0;
85
+ this.lifetime.completionTokens = raw.completionTokens ?? 0;
86
+ this.lifetime.estimatedCost = raw.estimatedCost ?? 0;
87
+ this.lifetime.calls = raw.calls ?? 0;
88
+ this.lifetime.modelsUsed = new Set(raw.modelsUsed ?? []);
89
+ }
90
+ catch { /* best effort */ }
91
+ }
92
+ saveLifetime() {
93
+ try {
94
+ mkdirSync(JELLY_HOME, { recursive: true });
95
+ writeFileSync(COST_FILE, JSON.stringify({
96
+ promptTokens: this.lifetime.promptTokens,
97
+ completionTokens: this.lifetime.completionTokens,
98
+ estimatedCost: this.lifetime.estimatedCost,
99
+ calls: this.lifetime.calls,
100
+ modelsUsed: [...this.lifetime.modelsUsed],
101
+ }));
102
+ }
103
+ catch { /* best effort */ }
104
+ }
105
+ // ── Formatting ─────────────────────────────────────────────────────────────
106
+ formatNano(cost) {
107
+ return `$${(cost / 1_000_000_000).toFixed(4)}`;
108
+ }
109
+ statusLine() {
110
+ return `${this.formatNano(this.session.estimatedCost)} · ${this.session.calls}calls`;
111
+ }
112
+ report() {
113
+ const s = this.session;
114
+ return [
115
+ `Session: ${s.calls} calls · ${s.errors} errors · ${this.formatNano(s.estimatedCost)}`,
116
+ ` Prompt: ${s.promptTokens.toLocaleString()} tokens`,
117
+ ` Completion: ${s.completionTokens.toLocaleString()} tokens`,
118
+ `Lifetime: ${this.lifetime.calls} calls · ${this.formatNano(this.lifetime.estimatedCost)}`,
119
+ ` Models used: ${this.lifetime.modelsUsed.size}`,
120
+ ].join("\n");
121
+ }
122
+ // ── Tool: cost_report ──────────────────────────────────────────────────────
123
+ costReportParams = Type.Object({});
124
+ async costReportTool() {
125
+ const status = this.getBudgetStatus();
126
+ let text = this.report();
127
+ if (status.warnings.length > 0) {
128
+ text += "\n\n" + status.warnings.join("\n");
129
+ }
130
+ return {
131
+ content: [{ type: "text", text }],
132
+ details: {
133
+ session_cost_nano: status.session.estimatedCost,
134
+ lifetime_cost_nano: status.lifetime.estimatedCost,
135
+ session_calls: status.session.calls,
136
+ lifetime_calls: status.lifetime.calls,
137
+ models_used: status.lifetime.modelsUsed.size,
138
+ warnings: status.warnings,
139
+ },
140
+ };
141
+ }
142
+ // ── Reset ──────────────────────────────────────────────────────────────────
143
+ resetSession() {
144
+ this.session = { promptTokens: 0, completionTokens: 0, estimatedCost: 0, calls: 0, errors: 0 };
145
+ this.budgetWarned = false;
146
+ }
147
+ }
148
+ //# sourceMappingURL=CostTracker.js.map
@@ -0,0 +1,157 @@
1
+ /**
2
+ * ModelRegistry — dynamic model discovery, tiering, and routing.
3
+ *
4
+ * Queries OpenRouter's /models endpoint at startup to build a local registry
5
+ * of all available models. Classifies them into tiers (orchestrator / analyst /
6
+ * worker / free), tracks performance, handles deprecation, and exposes
7
+ * model selection as both a tool and a REPL command.
8
+ *
9
+ * Also supports direct provider routing (Anthropic/OpenAI/Google keys) for
10
+ * models that can be reached cheaper outside OpenRouter.
11
+ */
12
+ import type { ModelConfig } from "../runner/ModelClient.js";
13
+ import { type Static } from "@sinclair/typebox";
14
+ export interface OpenRouterModel {
15
+ id: string;
16
+ name: string;
17
+ created: number;
18
+ description: string;
19
+ context_length: number;
20
+ architecture: {
21
+ modality: string;
22
+ tokenizer: string;
23
+ instruct_type: string | null;
24
+ };
25
+ pricing: {
26
+ prompt: string;
27
+ completion: string;
28
+ image?: string;
29
+ request?: string;
30
+ input_cache_read?: string;
31
+ input_cache_write?: string;
32
+ };
33
+ top_provider: {
34
+ context_length: number;
35
+ max_completion_tokens: number | null;
36
+ is_moderated: boolean;
37
+ };
38
+ per_request_limits?: Record<string, string>;
39
+ }
40
+ export type ModelTier = "orchestrator" | "analyst" | "worker" | "free";
41
+ export interface TieredModel {
42
+ tier: ModelTier;
43
+ model: OpenRouterModel;
44
+ /** Nano-dollars per 1K prompt tokens (for cost comparison) */
45
+ costPer1K: number;
46
+ /** Whether this model is currently available (not deprecated / rate-limited) */
47
+ available: boolean;
48
+ /** Consecutive failures — if >= 3, mark unavailable temporarily */
49
+ failures: number;
50
+ /** Timestamp of last failure (ms) */
51
+ lastFailure: number;
52
+ /** Average stream latency in ms */
53
+ avgLatency: number;
54
+ /** Number of times used */
55
+ uses: number;
56
+ }
57
+ export interface TieredPool {
58
+ orchestrator: TieredModel[];
59
+ analyst: TieredModel[];
60
+ worker: TieredModel[];
61
+ free: TieredModel[];
62
+ }
63
+ export declare function classifyModel(model: OpenRouterModel): ModelTier;
64
+ export declare class ModelRegistry {
65
+ private allModels;
66
+ private tieredPool;
67
+ private modelMap;
68
+ private loaded;
69
+ private lastFetch;
70
+ /** Query OpenRouter /models and build the tiered pool. Called once at startup. */
71
+ initialise(apiKey?: string): Promise<void>;
72
+ /** Rebuild tier classifications (call after cache load or fetch). */
73
+ private rebuildTiers;
74
+ private saveCache;
75
+ private loadFromCache;
76
+ /** True if cache is stale and we should re-fetch on next opportunity. */
77
+ get cacheStale(): boolean;
78
+ /**
79
+ * Pick the best available model for a given tier.
80
+ * Favours lower cost among available models, excluding those with >= 3
81
+ * consecutive failures (which get a 5-minute cooldown).
82
+ */
83
+ pick(tier: ModelTier): OpenRouterModel | null;
84
+ /**
85
+ * Per-model and per-tier temperature profiles.
86
+ * Reasoning/thinking models REQUIRE temperature=1.0 (API enforces).
87
+ * Code/structured tasks want low temp; creative analysis wants higher.
88
+ */
89
+ private getTemperature;
90
+ /**
91
+ * Per-tier max token budgets.
92
+ * Orchestrators get generous budgets; free workers get minimal.
93
+ */
94
+ private getTokenBudget;
95
+ /**
96
+ * Build a full ModelConfig chain from the tiered pool.
97
+ * Uses user-configured models from env first, then fills with tiered picks.
98
+ */
99
+ buildModelChain(userModels: string[]): ModelConfig[];
100
+ /** Build a single ModelConfig, preferring direct provider when possible. */
101
+ buildConfig(modelId: string, maxTokens: number, temperature: number, tier?: ModelTier): ModelConfig | null;
102
+ recordFailure(modelId: string): void;
103
+ recordSuccess(modelId: string, latencyMs: number): void;
104
+ /** Mark a model as permanently deprecated (404, model removed). */
105
+ markDeprecated(modelId: string): void;
106
+ getTier(modelId: string): ModelTier;
107
+ get modelCount(): number;
108
+ /** Search / filter models by keyword (case-insensitive). */
109
+ search(query: string, maxResults?: number): TieredModel[];
110
+ /** Get all models for a tier. */
111
+ getPool(tier: ModelTier): TieredModel[];
112
+ /** Human-readable summary of the current pool. */
113
+ summary(): string;
114
+ /**
115
+ * Find the cheapest model meeting minimum requirements.
116
+ * @param tier - Required tier (or "any")
117
+ * @param minContext - Minimum context length
118
+ * @param maxCost - Maximum cost in nano-dollars per 1K prompt
119
+ */
120
+ findCheapest(tier: ModelTier | "any", minContext?: number, maxCost?: number): TieredModel | null;
121
+ readonly listModelsParams: import("@sinclair/typebox").TObject<{
122
+ query: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
123
+ tier: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
124
+ limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
125
+ available_only: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
126
+ }>;
127
+ listModelsTool(_id: string, params: Static<typeof this.listModelsParams>): Promise<{
128
+ content: Array<{
129
+ type: "text";
130
+ text: string;
131
+ }>;
132
+ details: Record<string, unknown>;
133
+ }>;
134
+ readonly pickModelParams: import("@sinclair/typebox").TObject<{
135
+ tier: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
136
+ min_context: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
137
+ max_cost: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
138
+ }>;
139
+ pickModelTool(_id: string, params: Static<typeof this.pickModelParams>): Promise<{
140
+ content: Array<{
141
+ type: "text";
142
+ text: string;
143
+ }>;
144
+ details: Record<string, unknown>;
145
+ }>;
146
+ readonly summaryParams: import("@sinclair/typebox").TObject<{}>;
147
+ summaryTool(): Promise<{
148
+ content: Array<{
149
+ type: "text";
150
+ text: string;
151
+ }>;
152
+ details: Record<string, unknown>;
153
+ }>;
154
+ }
155
+ /** Singleton — initialised once at startup, used everywhere */
156
+ export declare const modelRegistry: ModelRegistry;
157
+ //# sourceMappingURL=ModelRegistry.d.ts.map