@vextlabs/theron-agent-sdk 0.3.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 (70) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/LICENSE +21 -0
  3. package/README.md +270 -0
  4. package/dist/adapters/theron.cjs +92 -0
  5. package/dist/adapters/theron.d.cts +42 -0
  6. package/dist/adapters/theron.d.ts +42 -0
  7. package/dist/adapters/theron.js +89 -0
  8. package/dist/agent/index.cjs +33 -0
  9. package/dist/agent/index.d.cts +84 -0
  10. package/dist/agent/index.d.ts +84 -0
  11. package/dist/agent/index.js +31 -0
  12. package/dist/council/index.cjs +68 -0
  13. package/dist/council/index.d.cts +96 -0
  14. package/dist/council/index.d.ts +96 -0
  15. package/dist/council/index.js +66 -0
  16. package/dist/index.cjs +1288 -0
  17. package/dist/index.d.cts +60 -0
  18. package/dist/index.d.ts +60 -0
  19. package/dist/index.js +1244 -0
  20. package/dist/loop/index.cjs +106 -0
  21. package/dist/loop/index.d.cts +285 -0
  22. package/dist/loop/index.d.ts +285 -0
  23. package/dist/loop/index.js +95 -0
  24. package/dist/mcp/index.cjs +153 -0
  25. package/dist/mcp/index.d.cts +69 -0
  26. package/dist/mcp/index.d.ts +69 -0
  27. package/dist/mcp/index.js +150 -0
  28. package/dist/memory/index.cjs +53 -0
  29. package/dist/memory/index.d.cts +73 -0
  30. package/dist/memory/index.d.ts +73 -0
  31. package/dist/memory/index.js +50 -0
  32. package/dist/patterns/index.cjs +159 -0
  33. package/dist/patterns/index.d.cts +200 -0
  34. package/dist/patterns/index.d.ts +200 -0
  35. package/dist/patterns/index.js +150 -0
  36. package/dist/receipts/index.cjs +151 -0
  37. package/dist/receipts/index.d.cts +132 -0
  38. package/dist/receipts/index.d.ts +132 -0
  39. package/dist/receipts/index.js +146 -0
  40. package/dist/runtime/index.cjs +205 -0
  41. package/dist/runtime/index.d.cts +148 -0
  42. package/dist/runtime/index.d.ts +148 -0
  43. package/dist/runtime/index.js +203 -0
  44. package/dist/session/index.cjs +49 -0
  45. package/dist/session/index.d.cts +79 -0
  46. package/dist/session/index.d.ts +79 -0
  47. package/dist/session/index.js +47 -0
  48. package/dist/tools/index.cjs +51 -0
  49. package/dist/tools/index.d.cts +52 -0
  50. package/dist/tools/index.d.ts +52 -0
  51. package/dist/tools/index.js +46 -0
  52. package/dist/verifiers/index.cjs +96 -0
  53. package/dist/verifiers/index.d.cts +63 -0
  54. package/dist/verifiers/index.d.ts +63 -0
  55. package/dist/verifiers/index.js +93 -0
  56. package/examples/01_code_reviewer.ts +90 -0
  57. package/examples/02_research_assistant.ts +85 -0
  58. package/examples/03_council_of_three.ts +91 -0
  59. package/examples/_adapters/openrouter.ts +90 -0
  60. package/examples/adapters/openrouter.ts +144 -0
  61. package/examples/adapters/theron.ts +105 -0
  62. package/examples/basic-agent.ts +56 -0
  63. package/examples/council-deliberation.ts +90 -0
  64. package/examples/cyber-recon-bot.ts +163 -0
  65. package/examples/loop-primitives.ts +50 -0
  66. package/examples/meeting-prep-bot.ts +172 -0
  67. package/examples/reasoning-patterns.ts +125 -0
  68. package/examples/support-triage-bot.ts +181 -0
  69. package/examples/verifier-kernel.ts +108 -0
  70. package/package.json +154 -0
@@ -0,0 +1,146 @@
1
+ // src/receipts/index.ts
2
+ var ReceiptEmitter = class {
3
+ sinks;
4
+ signer;
5
+ issuer;
6
+ actor;
7
+ tenant_id;
8
+ constructor(config) {
9
+ if (!config.sinks || config.sinks.length === 0) {
10
+ throw new Error("ReceiptEmitter requires at least one sink.");
11
+ }
12
+ this.sinks = config.sinks;
13
+ this.signer = config.signer;
14
+ this.issuer = config.signer?.issuer ?? config.issuer ?? "did:web:local";
15
+ this.actor = config.actor;
16
+ this.tenant_id = config.tenant_id;
17
+ }
18
+ async emit(input) {
19
+ const ts = Date.now();
20
+ const payload = {
21
+ input: input.input,
22
+ output: input.output,
23
+ ...input.metadata !== void 0 ? { metadata: input.metadata } : {}
24
+ };
25
+ const content_hash = await sha256Hex(canonicalize(payload));
26
+ let receipt = {
27
+ v: "stoa.receipt.v1",
28
+ id: ulid(),
29
+ cap: input.cap,
30
+ issuer: this.issuer,
31
+ actor: input.actor ?? this.actor,
32
+ ts,
33
+ session_id: input.session_id,
34
+ tenant_id: input.tenant_id ?? this.tenant_id,
35
+ content_hash,
36
+ payload
37
+ };
38
+ if (this.signer) {
39
+ const signature = await this.signer.sign(receipt);
40
+ receipt = { ...receipt, signature };
41
+ }
42
+ await Promise.all(
43
+ this.sinks.map(async (sink) => {
44
+ try {
45
+ await sink.emit(receipt);
46
+ } catch (err) {
47
+ console.warn(`[receipts] sink "${sink.name}" failed:`, err);
48
+ }
49
+ })
50
+ );
51
+ return receipt;
52
+ }
53
+ };
54
+ var InMemoryReceiptSink = class {
55
+ name = "in-memory";
56
+ records = [];
57
+ async emit(receipt) {
58
+ this.records.push(receipt);
59
+ }
60
+ list() {
61
+ return this.records;
62
+ }
63
+ clear() {
64
+ this.records.length = 0;
65
+ }
66
+ };
67
+ function fileReceiptSink(path) {
68
+ return {
69
+ name: `file:${path}`,
70
+ async emit(receipt) {
71
+ const fs = await import('fs/promises');
72
+ await fs.appendFile(path, JSON.stringify(receipt) + "\n", "utf8");
73
+ }
74
+ };
75
+ }
76
+ function httpReceiptSink(opts) {
77
+ const timeout_ms = opts.timeout_ms ?? 5e3;
78
+ return {
79
+ name: `http:${new URL(opts.url).host}`,
80
+ async emit(receipt) {
81
+ const ac = new AbortController();
82
+ const timer = setTimeout(() => ac.abort(), timeout_ms);
83
+ try {
84
+ const res = await fetch(opts.url, {
85
+ method: "POST",
86
+ signal: ac.signal,
87
+ headers: {
88
+ "Content-Type": "application/json",
89
+ ...opts.token ? { Authorization: `Bearer ${opts.token}` } : {}
90
+ },
91
+ body: JSON.stringify(receipt)
92
+ });
93
+ if (!res.ok) {
94
+ const text = await res.text().catch(() => "");
95
+ throw new Error(
96
+ `http sink ${opts.url} returned ${res.status}: ${text.slice(0, 200)}`
97
+ );
98
+ }
99
+ } finally {
100
+ clearTimeout(timer);
101
+ }
102
+ }
103
+ };
104
+ }
105
+ function canonicalize(value) {
106
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
107
+ if (Array.isArray(value)) {
108
+ return "[" + value.map((v) => canonicalize(v)).join(",") + "]";
109
+ }
110
+ const keys = Object.keys(value).sort();
111
+ return "{" + keys.map(
112
+ (k) => JSON.stringify(k) + ":" + canonicalize(value[k])
113
+ ).join(",") + "}";
114
+ }
115
+ async function sha256Hex(input) {
116
+ const data = new TextEncoder().encode(input);
117
+ const buf = await globalThis.crypto.subtle.digest("SHA-256", data);
118
+ const bytes = new Uint8Array(buf);
119
+ let hex = "";
120
+ for (let i = 0; i < bytes.length; i++) {
121
+ hex += bytes[i].toString(16).padStart(2, "0");
122
+ }
123
+ return hex;
124
+ }
125
+ function ulid() {
126
+ const ts = Date.now();
127
+ const ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
128
+ let tsPart = "";
129
+ let t = ts;
130
+ for (let i = 9; i >= 0; i--) {
131
+ tsPart = ALPHABET[t % 32] + tsPart;
132
+ t = Math.floor(t / 32);
133
+ }
134
+ let randPart = "";
135
+ if (globalThis.crypto?.getRandomValues) {
136
+ const bytes = new Uint8Array(16);
137
+ globalThis.crypto.getRandomValues(bytes);
138
+ for (let i = 0; i < 16; i++) randPart += ALPHABET[bytes[i] % 32];
139
+ } else {
140
+ for (let i = 0; i < 16; i++)
141
+ randPart += ALPHABET[Math.floor(Math.random() * 32)];
142
+ }
143
+ return tsPart + randPart;
144
+ }
145
+
146
+ export { InMemoryReceiptSink, ReceiptEmitter, fileReceiptSink, httpReceiptSink };
@@ -0,0 +1,205 @@
1
+ 'use strict';
2
+
3
+ // src/runtime/index.ts
4
+ var Runner = class {
5
+ model;
6
+ default_model;
7
+ memory;
8
+ session;
9
+ tool_context;
10
+ listeners = [];
11
+ constructor(config) {
12
+ if (!config.model) throw new Error("Runner requires a `model` adapter.");
13
+ if (!config.default_model) throw new Error("Runner requires a `default_model` identifier.");
14
+ this.model = config.model;
15
+ this.default_model = config.default_model;
16
+ this.memory = config.memory;
17
+ this.session = config.session;
18
+ this.tool_context = config.tool_context ?? { cwd: process.cwd(), yolo: false };
19
+ }
20
+ /** Subscribe to runner events for streaming UIs / observability. */
21
+ on(handler) {
22
+ this.listeners.push(handler);
23
+ return () => {
24
+ this.listeners = this.listeners.filter((h) => h !== handler);
25
+ };
26
+ }
27
+ emit(event) {
28
+ for (const h of this.listeners) h(event);
29
+ }
30
+ /**
31
+ * Run a single agent on a query.
32
+ *
33
+ * Loop:
34
+ * 1. Send messages + tool schemas to the model
35
+ * 2. If model returns a tool call → execute the tool → append result to messages → repeat
36
+ * 3. If model returns content + end_turn → finalize
37
+ * 4. Run any registered verifier kernels on the final output
38
+ * 5. Return the AgentResult
39
+ */
40
+ async run(agent, query) {
41
+ const startedAt = Date.now();
42
+ this.emit({ type: "agent_start", agent: agent.name, query });
43
+ const messages = [
44
+ { role: "system", content: agent.instruction.system }
45
+ ];
46
+ for (const ex of agent.instruction.examples ?? []) {
47
+ messages.push({ role: "user", content: ex.user });
48
+ messages.push({ role: "assistant", content: ex.assistant });
49
+ }
50
+ messages.push({ role: "user", content: query });
51
+ const toolCalls = [];
52
+ let tokensIn = 0;
53
+ let tokensOut = 0;
54
+ let finalOutput = "";
55
+ for (let turn = 0; turn < agent.max_turns; turn++) {
56
+ const response = await this.model.chat({
57
+ model: agent.model ?? this.default_model,
58
+ messages,
59
+ tools: agent.toolSchemas(),
60
+ onDelta: (delta) => this.emit({ type: "agent_thinking", agent: agent.name, delta })
61
+ });
62
+ tokensIn += response.tokens.input;
63
+ tokensOut += response.tokens.output;
64
+ if (response.tool_calls && response.tool_calls.length > 0) {
65
+ messages.push({ role: "assistant", content: response.content });
66
+ for (const call of response.tool_calls) {
67
+ const tool = agent.tools.find((t) => t.schema.name === call.name);
68
+ if (!tool) {
69
+ this.emit({
70
+ type: "error",
71
+ agent: agent.name,
72
+ message: `Model called unknown tool: ${call.name}`
73
+ });
74
+ messages.push({ role: "tool", content: `error: unknown tool ${call.name}` });
75
+ continue;
76
+ }
77
+ this.emit({ type: "tool_call_start", agent: agent.name, tool: call.name, input: call.input });
78
+ const t0 = Date.now();
79
+ try {
80
+ const output = await tool.execute(call.input, this.tool_context);
81
+ const ms = Date.now() - t0;
82
+ this.emit({ type: "tool_call_done", agent: agent.name, tool: call.name, output, ms });
83
+ toolCalls.push({ name: call.name, input: call.input, output });
84
+ messages.push({ role: "tool", content: JSON.stringify(output) });
85
+ } catch (err) {
86
+ const msg = err instanceof Error ? err.message : String(err);
87
+ this.emit({ type: "error", agent: agent.name, message: `Tool ${call.name} threw: ${msg}` });
88
+ messages.push({ role: "tool", content: `error: ${msg}` });
89
+ }
90
+ }
91
+ continue;
92
+ }
93
+ finalOutput = response.content;
94
+ messages.push({ role: "assistant", content: finalOutput });
95
+ break;
96
+ }
97
+ this.emit({ type: "agent_output", agent: agent.name, output: finalOutput });
98
+ const verifier_results = [];
99
+ for (const v of agent.verifiers) {
100
+ try {
101
+ const result = await v.check(finalOutput);
102
+ verifier_results.push(result);
103
+ this.emit({ type: "verifier_run", agent: agent.name, kernel: v.name, result });
104
+ } catch (err) {
105
+ this.emit({
106
+ type: "error",
107
+ agent: agent.name,
108
+ message: `Verifier ${v.name} threw: ${err instanceof Error ? err.message : String(err)}`
109
+ });
110
+ }
111
+ }
112
+ const latency_ms = Date.now() - startedAt;
113
+ return {
114
+ agent: agent.name,
115
+ output: finalOutput,
116
+ tool_calls: toolCalls,
117
+ verifier_results,
118
+ tokens_used: { input: tokensIn, output: tokensOut },
119
+ cost_usd: 0,
120
+ // adapter-specific; populated by adapter
121
+ latency_ms
122
+ };
123
+ }
124
+ /**
125
+ * Run a Council on a query.
126
+ *
127
+ * Fan out to all specialists in parallel (with timeout), gather outputs,
128
+ * run council-level verifier kernels on each, and reconcile.
129
+ */
130
+ async runCouncil(council, query) {
131
+ const startedAt = Date.now();
132
+ this.emit({ type: "council_start", council: council.name, query });
133
+ const withTimeout = (p, ms) => Promise.race([
134
+ p,
135
+ new Promise((resolve) => setTimeout(() => resolve(null), ms))
136
+ ]);
137
+ const specialistResults = await Promise.all(
138
+ council.specialists.map(async (spec) => {
139
+ try {
140
+ const result = await withTimeout(this.run(spec, query), council.specialist_timeout_ms);
141
+ if (result === null) {
142
+ this.emit({
143
+ type: "error",
144
+ agent: spec.name,
145
+ message: `Specialist timed out after ${council.specialist_timeout_ms}ms`
146
+ });
147
+ return null;
148
+ }
149
+ const councilVerifierResults = [];
150
+ for (const v of council.verifiers) {
151
+ try {
152
+ const r = await v.check(result.output);
153
+ councilVerifierResults.push(r);
154
+ this.emit({ type: "verifier_run", agent: spec.name, kernel: v.name, result: r });
155
+ } catch (err) {
156
+ this.emit({
157
+ type: "error",
158
+ agent: spec.name,
159
+ message: `Council verifier ${v.name} threw: ${err instanceof Error ? err.message : String(err)}`
160
+ });
161
+ }
162
+ }
163
+ const out = {
164
+ specialist: spec.name,
165
+ output: result.output,
166
+ claims: [],
167
+ // claim extraction is the reconciler's job
168
+ // AgentResult.verifier_results widens issues to unknown[]; at the
169
+ // runtime layer we know every entry came from a Verifier.check()
170
+ // call (which produces VerifierIssue[]), so the cast is sound.
171
+ verifier_results: [
172
+ ...result.verifier_results,
173
+ ...councilVerifierResults
174
+ ],
175
+ cost_usd: result.cost_usd,
176
+ latency_ms: result.latency_ms
177
+ };
178
+ this.emit({ type: "specialist_done", specialist: spec.name, output: out });
179
+ return out;
180
+ } catch (err) {
181
+ this.emit({
182
+ type: "error",
183
+ agent: spec.name,
184
+ message: err instanceof Error ? err.message : String(err)
185
+ });
186
+ return null;
187
+ }
188
+ })
189
+ );
190
+ const survivors = specialistResults.filter((s) => s !== null);
191
+ const reconciled = await council.reconciler(survivors);
192
+ const output = {
193
+ answer: reconciled.answer,
194
+ specialists: survivors,
195
+ consensus: reconciled.consensus,
196
+ disagreements: reconciled.disagreements,
197
+ total_cost_usd: survivors.reduce((s, x) => s + x.cost_usd, 0),
198
+ total_latency_ms: Date.now() - startedAt
199
+ };
200
+ this.emit({ type: "council_done", council: council.name, output });
201
+ return output;
202
+ }
203
+ };
204
+
205
+ exports.Runner = Runner;
@@ -0,0 +1,148 @@
1
+ import { Agent, AgentResult } from '../agent/index.cjs';
2
+ import { CouncilSpecialistOutput, CouncilOutput, Council } from '../council/index.cjs';
3
+ import { Session } from '../session/index.cjs';
4
+ import { Memory } from '../memory/index.cjs';
5
+ import { ToolContext } from '../tools/index.cjs';
6
+ import { VerifierResult } from '../verifiers/index.cjs';
7
+ import 'zod';
8
+
9
+ /** Events the Runner emits as it executes. Subscribe via runner.on(). */
10
+ type RunnerEvent = {
11
+ type: "agent_start";
12
+ agent: string;
13
+ query: string;
14
+ } | {
15
+ type: "agent_thinking";
16
+ agent: string;
17
+ delta: string;
18
+ } | {
19
+ type: "tool_call_start";
20
+ agent: string;
21
+ tool: string;
22
+ input: unknown;
23
+ } | {
24
+ type: "tool_call_done";
25
+ agent: string;
26
+ tool: string;
27
+ output: unknown;
28
+ ms: number;
29
+ } | {
30
+ type: "verifier_run";
31
+ agent: string;
32
+ kernel: string;
33
+ result: VerifierResult;
34
+ } | {
35
+ type: "agent_output";
36
+ agent: string;
37
+ output: string;
38
+ } | {
39
+ type: "council_start";
40
+ council: string;
41
+ query: string;
42
+ } | {
43
+ type: "specialist_done";
44
+ specialist: string;
45
+ output: CouncilSpecialistOutput;
46
+ } | {
47
+ type: "council_done";
48
+ council: string;
49
+ output: CouncilOutput;
50
+ } | {
51
+ type: "error";
52
+ agent: string;
53
+ message: string;
54
+ };
55
+ /**
56
+ * Model adapter — the function the Runner calls to talk to an LLM.
57
+ *
58
+ * Implement once per provider. Three reference adapters ship in
59
+ * `examples/adapters/`: OpenRouter, OpenAI, Anthropic. Production users write
60
+ * their own adapter for their preferred endpoint (Vext-hosted Theron, AWS
61
+ * Bedrock, Cerebras, Groq, your own OSS endpoint).
62
+ */
63
+ interface ModelAdapter {
64
+ name: string;
65
+ /** Chat completion. Streams deltas via the optional `onDelta` callback. */
66
+ chat(opts: {
67
+ model: string;
68
+ messages: Array<{
69
+ role: "system" | "user" | "assistant" | "tool";
70
+ content: string;
71
+ }>;
72
+ tools?: Array<{
73
+ name: string;
74
+ description: string;
75
+ input_schema: Record<string, unknown>;
76
+ }>;
77
+ max_tokens?: number;
78
+ temperature?: number;
79
+ onDelta?: (delta: string) => void;
80
+ }): Promise<{
81
+ content: string;
82
+ tool_calls?: Array<{
83
+ name: string;
84
+ input: unknown;
85
+ }>;
86
+ tokens: {
87
+ input: number;
88
+ output: number;
89
+ };
90
+ }>;
91
+ }
92
+ interface RunnerConfig {
93
+ /** The model adapter to use. */
94
+ model: ModelAdapter;
95
+ /** Default model identifier (e.g., "gpt-4o", "claude-3-5-sonnet"). */
96
+ default_model: string;
97
+ /** Optional Memory implementation. */
98
+ memory?: Memory;
99
+ /** Optional Session for event logging. */
100
+ session?: Session;
101
+ /** Optional tool execution context (cwd, yolo, tenant). */
102
+ tool_context?: ToolContext;
103
+ }
104
+ /**
105
+ * Runner — executes Agents and Councils.
106
+ *
107
+ * Minimal usage:
108
+ * const runner = new Runner({
109
+ * model: openrouterAdapter({ apiKey: process.env.OPENROUTER_API_KEY }),
110
+ * default_model: "openai/gpt-4o-mini",
111
+ * });
112
+ * const result = await runner.run(myAgent, "What's 2+2?");
113
+ *
114
+ * With a council:
115
+ * const result = await runner.runCouncil(myCouncil, "Review this PR");
116
+ */
117
+ declare class Runner {
118
+ readonly model: ModelAdapter;
119
+ readonly default_model: string;
120
+ readonly memory: Memory | undefined;
121
+ readonly session: Session | undefined;
122
+ readonly tool_context: ToolContext;
123
+ private listeners;
124
+ constructor(config: RunnerConfig);
125
+ /** Subscribe to runner events for streaming UIs / observability. */
126
+ on(handler: (event: RunnerEvent) => void): () => void;
127
+ private emit;
128
+ /**
129
+ * Run a single agent on a query.
130
+ *
131
+ * Loop:
132
+ * 1. Send messages + tool schemas to the model
133
+ * 2. If model returns a tool call → execute the tool → append result to messages → repeat
134
+ * 3. If model returns content + end_turn → finalize
135
+ * 4. Run any registered verifier kernels on the final output
136
+ * 5. Return the AgentResult
137
+ */
138
+ run(agent: Agent, query: string): Promise<AgentResult>;
139
+ /**
140
+ * Run a Council on a query.
141
+ *
142
+ * Fan out to all specialists in parallel (with timeout), gather outputs,
143
+ * run council-level verifier kernels on each, and reconcile.
144
+ */
145
+ runCouncil(council: Council, query: string): Promise<CouncilOutput>;
146
+ }
147
+
148
+ export { type ModelAdapter, Runner, type RunnerConfig, type RunnerEvent };
@@ -0,0 +1,148 @@
1
+ import { Agent, AgentResult } from '../agent/index.js';
2
+ import { CouncilSpecialistOutput, CouncilOutput, Council } from '../council/index.js';
3
+ import { Session } from '../session/index.js';
4
+ import { Memory } from '../memory/index.js';
5
+ import { ToolContext } from '../tools/index.js';
6
+ import { VerifierResult } from '../verifiers/index.js';
7
+ import 'zod';
8
+
9
+ /** Events the Runner emits as it executes. Subscribe via runner.on(). */
10
+ type RunnerEvent = {
11
+ type: "agent_start";
12
+ agent: string;
13
+ query: string;
14
+ } | {
15
+ type: "agent_thinking";
16
+ agent: string;
17
+ delta: string;
18
+ } | {
19
+ type: "tool_call_start";
20
+ agent: string;
21
+ tool: string;
22
+ input: unknown;
23
+ } | {
24
+ type: "tool_call_done";
25
+ agent: string;
26
+ tool: string;
27
+ output: unknown;
28
+ ms: number;
29
+ } | {
30
+ type: "verifier_run";
31
+ agent: string;
32
+ kernel: string;
33
+ result: VerifierResult;
34
+ } | {
35
+ type: "agent_output";
36
+ agent: string;
37
+ output: string;
38
+ } | {
39
+ type: "council_start";
40
+ council: string;
41
+ query: string;
42
+ } | {
43
+ type: "specialist_done";
44
+ specialist: string;
45
+ output: CouncilSpecialistOutput;
46
+ } | {
47
+ type: "council_done";
48
+ council: string;
49
+ output: CouncilOutput;
50
+ } | {
51
+ type: "error";
52
+ agent: string;
53
+ message: string;
54
+ };
55
+ /**
56
+ * Model adapter — the function the Runner calls to talk to an LLM.
57
+ *
58
+ * Implement once per provider. Three reference adapters ship in
59
+ * `examples/adapters/`: OpenRouter, OpenAI, Anthropic. Production users write
60
+ * their own adapter for their preferred endpoint (Vext-hosted Theron, AWS
61
+ * Bedrock, Cerebras, Groq, your own OSS endpoint).
62
+ */
63
+ interface ModelAdapter {
64
+ name: string;
65
+ /** Chat completion. Streams deltas via the optional `onDelta` callback. */
66
+ chat(opts: {
67
+ model: string;
68
+ messages: Array<{
69
+ role: "system" | "user" | "assistant" | "tool";
70
+ content: string;
71
+ }>;
72
+ tools?: Array<{
73
+ name: string;
74
+ description: string;
75
+ input_schema: Record<string, unknown>;
76
+ }>;
77
+ max_tokens?: number;
78
+ temperature?: number;
79
+ onDelta?: (delta: string) => void;
80
+ }): Promise<{
81
+ content: string;
82
+ tool_calls?: Array<{
83
+ name: string;
84
+ input: unknown;
85
+ }>;
86
+ tokens: {
87
+ input: number;
88
+ output: number;
89
+ };
90
+ }>;
91
+ }
92
+ interface RunnerConfig {
93
+ /** The model adapter to use. */
94
+ model: ModelAdapter;
95
+ /** Default model identifier (e.g., "gpt-4o", "claude-3-5-sonnet"). */
96
+ default_model: string;
97
+ /** Optional Memory implementation. */
98
+ memory?: Memory;
99
+ /** Optional Session for event logging. */
100
+ session?: Session;
101
+ /** Optional tool execution context (cwd, yolo, tenant). */
102
+ tool_context?: ToolContext;
103
+ }
104
+ /**
105
+ * Runner — executes Agents and Councils.
106
+ *
107
+ * Minimal usage:
108
+ * const runner = new Runner({
109
+ * model: openrouterAdapter({ apiKey: process.env.OPENROUTER_API_KEY }),
110
+ * default_model: "openai/gpt-4o-mini",
111
+ * });
112
+ * const result = await runner.run(myAgent, "What's 2+2?");
113
+ *
114
+ * With a council:
115
+ * const result = await runner.runCouncil(myCouncil, "Review this PR");
116
+ */
117
+ declare class Runner {
118
+ readonly model: ModelAdapter;
119
+ readonly default_model: string;
120
+ readonly memory: Memory | undefined;
121
+ readonly session: Session | undefined;
122
+ readonly tool_context: ToolContext;
123
+ private listeners;
124
+ constructor(config: RunnerConfig);
125
+ /** Subscribe to runner events for streaming UIs / observability. */
126
+ on(handler: (event: RunnerEvent) => void): () => void;
127
+ private emit;
128
+ /**
129
+ * Run a single agent on a query.
130
+ *
131
+ * Loop:
132
+ * 1. Send messages + tool schemas to the model
133
+ * 2. If model returns a tool call → execute the tool → append result to messages → repeat
134
+ * 3. If model returns content + end_turn → finalize
135
+ * 4. Run any registered verifier kernels on the final output
136
+ * 5. Return the AgentResult
137
+ */
138
+ run(agent: Agent, query: string): Promise<AgentResult>;
139
+ /**
140
+ * Run a Council on a query.
141
+ *
142
+ * Fan out to all specialists in parallel (with timeout), gather outputs,
143
+ * run council-level verifier kernels on each, and reconcile.
144
+ */
145
+ runCouncil(council: Council, query: string): Promise<CouncilOutput>;
146
+ }
147
+
148
+ export { type ModelAdapter, Runner, type RunnerConfig, type RunnerEvent };