@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.
- package/CHANGELOG.md +59 -0
- package/LICENSE +21 -0
- package/README.md +270 -0
- package/dist/adapters/theron.cjs +92 -0
- package/dist/adapters/theron.d.cts +42 -0
- package/dist/adapters/theron.d.ts +42 -0
- package/dist/adapters/theron.js +89 -0
- package/dist/agent/index.cjs +33 -0
- package/dist/agent/index.d.cts +84 -0
- package/dist/agent/index.d.ts +84 -0
- package/dist/agent/index.js +31 -0
- package/dist/council/index.cjs +68 -0
- package/dist/council/index.d.cts +96 -0
- package/dist/council/index.d.ts +96 -0
- package/dist/council/index.js +66 -0
- package/dist/index.cjs +1288 -0
- package/dist/index.d.cts +60 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +1244 -0
- package/dist/loop/index.cjs +106 -0
- package/dist/loop/index.d.cts +285 -0
- package/dist/loop/index.d.ts +285 -0
- package/dist/loop/index.js +95 -0
- package/dist/mcp/index.cjs +153 -0
- package/dist/mcp/index.d.cts +69 -0
- package/dist/mcp/index.d.ts +69 -0
- package/dist/mcp/index.js +150 -0
- package/dist/memory/index.cjs +53 -0
- package/dist/memory/index.d.cts +73 -0
- package/dist/memory/index.d.ts +73 -0
- package/dist/memory/index.js +50 -0
- package/dist/patterns/index.cjs +159 -0
- package/dist/patterns/index.d.cts +200 -0
- package/dist/patterns/index.d.ts +200 -0
- package/dist/patterns/index.js +150 -0
- package/dist/receipts/index.cjs +151 -0
- package/dist/receipts/index.d.cts +132 -0
- package/dist/receipts/index.d.ts +132 -0
- package/dist/receipts/index.js +146 -0
- package/dist/runtime/index.cjs +205 -0
- package/dist/runtime/index.d.cts +148 -0
- package/dist/runtime/index.d.ts +148 -0
- package/dist/runtime/index.js +203 -0
- package/dist/session/index.cjs +49 -0
- package/dist/session/index.d.cts +79 -0
- package/dist/session/index.d.ts +79 -0
- package/dist/session/index.js +47 -0
- package/dist/tools/index.cjs +51 -0
- package/dist/tools/index.d.cts +52 -0
- package/dist/tools/index.d.ts +52 -0
- package/dist/tools/index.js +46 -0
- package/dist/verifiers/index.cjs +96 -0
- package/dist/verifiers/index.d.cts +63 -0
- package/dist/verifiers/index.d.ts +63 -0
- package/dist/verifiers/index.js +93 -0
- package/examples/01_code_reviewer.ts +90 -0
- package/examples/02_research_assistant.ts +85 -0
- package/examples/03_council_of_three.ts +91 -0
- package/examples/_adapters/openrouter.ts +90 -0
- package/examples/adapters/openrouter.ts +144 -0
- package/examples/adapters/theron.ts +105 -0
- package/examples/basic-agent.ts +56 -0
- package/examples/council-deliberation.ts +90 -0
- package/examples/cyber-recon-bot.ts +163 -0
- package/examples/loop-primitives.ts +50 -0
- package/examples/meeting-prep-bot.ts +172 -0
- package/examples/reasoning-patterns.ts +125 -0
- package/examples/support-triage-bot.ts +181 -0
- package/examples/verifier-kernel.ts +108 -0
- package/package.json +154 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Tool, ToolSchema } from '../tools/index.cjs';
|
|
2
|
+
import { Verifier } from '../verifiers/index.cjs';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
interface AgentInstruction {
|
|
6
|
+
/** System prompt or persona instruction. */
|
|
7
|
+
system: string;
|
|
8
|
+
/** Optional few-shot exemplars. */
|
|
9
|
+
examples?: {
|
|
10
|
+
user: string;
|
|
11
|
+
assistant: string;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
interface AgentConfig {
|
|
15
|
+
/** Display name. Used for logging + routing. */
|
|
16
|
+
name: string;
|
|
17
|
+
/** Model identifier. Defaults to whatever the Runner is configured for.
|
|
18
|
+
* Examples: "gpt-4o", "claude-3-5-sonnet", "theron-base-v8@cyber". */
|
|
19
|
+
model?: string;
|
|
20
|
+
/** Instructions to the model. Either a string (shorthand for {system: ...})
|
|
21
|
+
* or a full AgentInstruction object. */
|
|
22
|
+
instruction: string | AgentInstruction;
|
|
23
|
+
/** Tools the agent may call. */
|
|
24
|
+
tools?: Tool[];
|
|
25
|
+
/** Sub-agents the agent may delegate to. */
|
|
26
|
+
sub_agents?: Agent[];
|
|
27
|
+
/** Verifier kernels that run against the final output. Failures are
|
|
28
|
+
* reported in AgentResult.verifier_results but do not throw — callers
|
|
29
|
+
* decide how to react. Use Council if you want gating + reconciliation. */
|
|
30
|
+
verifiers?: Verifier[];
|
|
31
|
+
/** Optional max-turn cap. Defaults to runner default. */
|
|
32
|
+
max_turns?: number;
|
|
33
|
+
}
|
|
34
|
+
interface AgentResult {
|
|
35
|
+
agent: string;
|
|
36
|
+
output: string;
|
|
37
|
+
tool_calls: Array<{
|
|
38
|
+
name: string;
|
|
39
|
+
input: unknown;
|
|
40
|
+
output: unknown;
|
|
41
|
+
}>;
|
|
42
|
+
verifier_results: Array<{
|
|
43
|
+
kernel: string;
|
|
44
|
+
pass: boolean;
|
|
45
|
+
issues: unknown[];
|
|
46
|
+
ms: number;
|
|
47
|
+
}>;
|
|
48
|
+
tokens_used: {
|
|
49
|
+
input: number;
|
|
50
|
+
output: number;
|
|
51
|
+
};
|
|
52
|
+
cost_usd: number;
|
|
53
|
+
latency_ms: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The Agent primitive.
|
|
57
|
+
*
|
|
58
|
+
* Minimal usage:
|
|
59
|
+
* const a = new Agent({ name: "helper", instruction: "You are helpful." });
|
|
60
|
+
*
|
|
61
|
+
* With tools + verifiers:
|
|
62
|
+
* const a = new Agent({
|
|
63
|
+
* name: "researcher",
|
|
64
|
+
* instruction: "Answer with citations.",
|
|
65
|
+
* tools: [webSearch, fetchUrl],
|
|
66
|
+
* verifiers: [VerifierKernels.citationPresence],
|
|
67
|
+
* });
|
|
68
|
+
*/
|
|
69
|
+
declare class Agent {
|
|
70
|
+
readonly name: string;
|
|
71
|
+
readonly model: string | undefined;
|
|
72
|
+
readonly instruction: AgentInstruction;
|
|
73
|
+
readonly tools: Tool[];
|
|
74
|
+
readonly sub_agents: Agent[];
|
|
75
|
+
readonly verifiers: Verifier[];
|
|
76
|
+
readonly max_turns: number;
|
|
77
|
+
constructor(config: AgentConfig);
|
|
78
|
+
/** Render the tools as JSON schemas for the model. */
|
|
79
|
+
toolSchemas(): ToolSchema[];
|
|
80
|
+
/** True if the agent has any sub-agents (i.e., this is a supervisor). */
|
|
81
|
+
isSupervisor(): boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { Agent, type AgentConfig, type AgentInstruction, type AgentResult };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Tool, ToolSchema } from '../tools/index.js';
|
|
2
|
+
import { Verifier } from '../verifiers/index.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
interface AgentInstruction {
|
|
6
|
+
/** System prompt or persona instruction. */
|
|
7
|
+
system: string;
|
|
8
|
+
/** Optional few-shot exemplars. */
|
|
9
|
+
examples?: {
|
|
10
|
+
user: string;
|
|
11
|
+
assistant: string;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
interface AgentConfig {
|
|
15
|
+
/** Display name. Used for logging + routing. */
|
|
16
|
+
name: string;
|
|
17
|
+
/** Model identifier. Defaults to whatever the Runner is configured for.
|
|
18
|
+
* Examples: "gpt-4o", "claude-3-5-sonnet", "theron-base-v8@cyber". */
|
|
19
|
+
model?: string;
|
|
20
|
+
/** Instructions to the model. Either a string (shorthand for {system: ...})
|
|
21
|
+
* or a full AgentInstruction object. */
|
|
22
|
+
instruction: string | AgentInstruction;
|
|
23
|
+
/** Tools the agent may call. */
|
|
24
|
+
tools?: Tool[];
|
|
25
|
+
/** Sub-agents the agent may delegate to. */
|
|
26
|
+
sub_agents?: Agent[];
|
|
27
|
+
/** Verifier kernels that run against the final output. Failures are
|
|
28
|
+
* reported in AgentResult.verifier_results but do not throw — callers
|
|
29
|
+
* decide how to react. Use Council if you want gating + reconciliation. */
|
|
30
|
+
verifiers?: Verifier[];
|
|
31
|
+
/** Optional max-turn cap. Defaults to runner default. */
|
|
32
|
+
max_turns?: number;
|
|
33
|
+
}
|
|
34
|
+
interface AgentResult {
|
|
35
|
+
agent: string;
|
|
36
|
+
output: string;
|
|
37
|
+
tool_calls: Array<{
|
|
38
|
+
name: string;
|
|
39
|
+
input: unknown;
|
|
40
|
+
output: unknown;
|
|
41
|
+
}>;
|
|
42
|
+
verifier_results: Array<{
|
|
43
|
+
kernel: string;
|
|
44
|
+
pass: boolean;
|
|
45
|
+
issues: unknown[];
|
|
46
|
+
ms: number;
|
|
47
|
+
}>;
|
|
48
|
+
tokens_used: {
|
|
49
|
+
input: number;
|
|
50
|
+
output: number;
|
|
51
|
+
};
|
|
52
|
+
cost_usd: number;
|
|
53
|
+
latency_ms: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The Agent primitive.
|
|
57
|
+
*
|
|
58
|
+
* Minimal usage:
|
|
59
|
+
* const a = new Agent({ name: "helper", instruction: "You are helpful." });
|
|
60
|
+
*
|
|
61
|
+
* With tools + verifiers:
|
|
62
|
+
* const a = new Agent({
|
|
63
|
+
* name: "researcher",
|
|
64
|
+
* instruction: "Answer with citations.",
|
|
65
|
+
* tools: [webSearch, fetchUrl],
|
|
66
|
+
* verifiers: [VerifierKernels.citationPresence],
|
|
67
|
+
* });
|
|
68
|
+
*/
|
|
69
|
+
declare class Agent {
|
|
70
|
+
readonly name: string;
|
|
71
|
+
readonly model: string | undefined;
|
|
72
|
+
readonly instruction: AgentInstruction;
|
|
73
|
+
readonly tools: Tool[];
|
|
74
|
+
readonly sub_agents: Agent[];
|
|
75
|
+
readonly verifiers: Verifier[];
|
|
76
|
+
readonly max_turns: number;
|
|
77
|
+
constructor(config: AgentConfig);
|
|
78
|
+
/** Render the tools as JSON schemas for the model. */
|
|
79
|
+
toolSchemas(): ToolSchema[];
|
|
80
|
+
/** True if the agent has any sub-agents (i.e., this is a supervisor). */
|
|
81
|
+
isSupervisor(): boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { Agent, type AgentConfig, type AgentInstruction, type AgentResult };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/agent/index.ts
|
|
2
|
+
var Agent = class {
|
|
3
|
+
name;
|
|
4
|
+
model;
|
|
5
|
+
instruction;
|
|
6
|
+
tools;
|
|
7
|
+
sub_agents;
|
|
8
|
+
verifiers;
|
|
9
|
+
max_turns;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
if (!config.name) throw new Error("Agent requires a `name`.");
|
|
12
|
+
if (!config.instruction) throw new Error(`Agent "${config.name}" requires an \`instruction\`.`);
|
|
13
|
+
this.name = config.name;
|
|
14
|
+
this.model = config.model;
|
|
15
|
+
this.instruction = typeof config.instruction === "string" ? { system: config.instruction } : config.instruction;
|
|
16
|
+
this.tools = config.tools ?? [];
|
|
17
|
+
this.sub_agents = config.sub_agents ?? [];
|
|
18
|
+
this.verifiers = config.verifiers ?? [];
|
|
19
|
+
this.max_turns = config.max_turns ?? 10;
|
|
20
|
+
}
|
|
21
|
+
/** Render the tools as JSON schemas for the model. */
|
|
22
|
+
toolSchemas() {
|
|
23
|
+
return this.tools.map((t) => t.schema);
|
|
24
|
+
}
|
|
25
|
+
/** True if the agent has any sub-agents (i.e., this is a supervisor). */
|
|
26
|
+
isSupervisor() {
|
|
27
|
+
return this.sub_agents.length > 0;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { Agent };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/council/index.ts
|
|
4
|
+
var Council = class {
|
|
5
|
+
name;
|
|
6
|
+
specialists;
|
|
7
|
+
verifiers;
|
|
8
|
+
reconciler;
|
|
9
|
+
specialist_timeout_ms;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
if (!config.name) throw new Error("Council requires a `name`.");
|
|
12
|
+
if (!config.specialists || config.specialists.length === 0) {
|
|
13
|
+
throw new Error(`Council "${config.name}" requires at least one specialist.`);
|
|
14
|
+
}
|
|
15
|
+
this.name = config.name;
|
|
16
|
+
this.specialists = config.specialists;
|
|
17
|
+
this.verifiers = config.verifiers ?? [];
|
|
18
|
+
this.reconciler = config.reconciler ?? deterministicClaimMerge;
|
|
19
|
+
this.specialist_timeout_ms = config.specialist_timeout_ms ?? 3e4;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Convenience entry point — pointed at the Runner you've already constructed.
|
|
23
|
+
* Most users will call `runner.runCouncil(council, query)` directly; this
|
|
24
|
+
* exists for symmetry with `agent.run`-style ergonomics in user code.
|
|
25
|
+
*/
|
|
26
|
+
async deliberate(_query) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"Council.deliberate() requires a Runner. Use:\n const runner = new Runner({ ... });\n const result = await runner.runCouncil(council, query);\nSee: https://github.com/Vext-Labs-Inc/theron-agent-sdk/blob/main/examples/03_council_of_three.ts"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var deterministicClaimMerge = async (specialists) => {
|
|
33
|
+
if (specialists.length === 0) {
|
|
34
|
+
return { answer: "(no specialists responded)", consensus: "refuted" };
|
|
35
|
+
}
|
|
36
|
+
if (specialists.length === 1) {
|
|
37
|
+
return { answer: specialists[0].output, consensus: "ratified" };
|
|
38
|
+
}
|
|
39
|
+
const claimMap = /* @__PURE__ */ new Map();
|
|
40
|
+
for (const s of specialists) {
|
|
41
|
+
for (const c of s.claims) {
|
|
42
|
+
const key = c.text.toLowerCase().trim();
|
|
43
|
+
if (!claimMap.has(key)) claimMap.set(key, []);
|
|
44
|
+
claimMap.get(key).push(s.specialist);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const ratified = [];
|
|
48
|
+
const disagreements = [];
|
|
49
|
+
const majorityThreshold = specialists.length / 2;
|
|
50
|
+
for (const [claim, voters] of claimMap.entries()) {
|
|
51
|
+
if (voters.length === specialists.length) {
|
|
52
|
+
ratified.push(claim);
|
|
53
|
+
} else if (voters.length > majorityThreshold) {
|
|
54
|
+
ratified.push(claim);
|
|
55
|
+
} else {
|
|
56
|
+
disagreements.push({
|
|
57
|
+
claim,
|
|
58
|
+
specialists_for: voters,
|
|
59
|
+
specialists_against: specialists.map((s) => s.specialist).filter((n) => !voters.includes(n))
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const answer = ratified.join(" ") || specialists[0].output;
|
|
64
|
+
const consensus = disagreements.length > 0 ? "split" : ratified.length > 0 ? "ratified" : "refuted";
|
|
65
|
+
return { answer, consensus, disagreements: disagreements.length > 0 ? disagreements : void 0 };
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
exports.Council = Council;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Agent } from '../agent/index.cjs';
|
|
2
|
+
import { Verifier, VerifierResult } from '../verifiers/index.cjs';
|
|
3
|
+
import '../tools/index.cjs';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
/** Output of a single specialist's turn before reconciliation. */
|
|
7
|
+
interface CouncilSpecialistOutput {
|
|
8
|
+
specialist: string;
|
|
9
|
+
output: string;
|
|
10
|
+
claims: Array<{
|
|
11
|
+
text: string;
|
|
12
|
+
confidence: number;
|
|
13
|
+
type: string;
|
|
14
|
+
}>;
|
|
15
|
+
verifier_results: VerifierResult[];
|
|
16
|
+
cost_usd: number;
|
|
17
|
+
latency_ms: number;
|
|
18
|
+
}
|
|
19
|
+
/** Final synthesized output after reconciliation. */
|
|
20
|
+
interface CouncilOutput {
|
|
21
|
+
/** The synthesized final answer. */
|
|
22
|
+
answer: string;
|
|
23
|
+
/** Per-specialist outputs (visible if you want to surface deliberation). */
|
|
24
|
+
specialists: CouncilSpecialistOutput[];
|
|
25
|
+
/** Whether the council reached consensus or surfaced a disagreement. */
|
|
26
|
+
consensus: "ratified" | "split" | "refuted";
|
|
27
|
+
/** If split: what the disagreement was about. */
|
|
28
|
+
disagreements?: Array<{
|
|
29
|
+
claim: string;
|
|
30
|
+
specialists_for: string[];
|
|
31
|
+
specialists_against: string[];
|
|
32
|
+
}>;
|
|
33
|
+
/** Aggregated cost + latency. */
|
|
34
|
+
total_cost_usd: number;
|
|
35
|
+
total_latency_ms: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A reconciler synthesizes N specialist outputs into one answer.
|
|
39
|
+
*
|
|
40
|
+
* Two kinds:
|
|
41
|
+
* - Deterministic reconcilers (regex / SMT / voting) — fast, cheap, no LLM
|
|
42
|
+
* - LLM reconcilers (Theron-Reconciler-D or another model) — better synthesis,
|
|
43
|
+
* more expensive
|
|
44
|
+
*
|
|
45
|
+
* The default is a deterministic claim-merging reconciler.
|
|
46
|
+
*/
|
|
47
|
+
type Reconciler = (specialists: CouncilSpecialistOutput[]) => Promise<{
|
|
48
|
+
answer: string;
|
|
49
|
+
consensus: "ratified" | "split" | "refuted";
|
|
50
|
+
disagreements?: CouncilOutput["disagreements"];
|
|
51
|
+
}>;
|
|
52
|
+
interface CouncilConfig {
|
|
53
|
+
/** Display name for the council. */
|
|
54
|
+
name: string;
|
|
55
|
+
/** The specialists that will deliberate. Order doesn't matter. */
|
|
56
|
+
specialists: Agent[];
|
|
57
|
+
/** Verifier kernels run against each specialist output before reconciliation. */
|
|
58
|
+
verifiers?: Verifier[];
|
|
59
|
+
/** How to synthesize the specialist outputs. Defaults to deterministic claim-merge. */
|
|
60
|
+
reconciler?: Reconciler;
|
|
61
|
+
/** Optional timeout per specialist (ms). Slow specialists are dropped. */
|
|
62
|
+
specialist_timeout_ms?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The Council primitive.
|
|
66
|
+
*
|
|
67
|
+
* Minimal usage:
|
|
68
|
+
* const c = new Council({
|
|
69
|
+
* name: "engineering-review",
|
|
70
|
+
* specialists: [cyberAgent, codeAgent, archAgent],
|
|
71
|
+
* });
|
|
72
|
+
* const result = await runner.runCouncil(c, "Review this PR for security risks");
|
|
73
|
+
*
|
|
74
|
+
* With verifier kernels:
|
|
75
|
+
* const c = new Council({
|
|
76
|
+
* name: "math-proof",
|
|
77
|
+
* specialists: [mathAgent, reasoningAgent, verifierAgent],
|
|
78
|
+
* verifiers: [VerifierKernels.arithmetic, VerifierKernels.citationPresence],
|
|
79
|
+
* });
|
|
80
|
+
*/
|
|
81
|
+
declare class Council {
|
|
82
|
+
readonly name: string;
|
|
83
|
+
readonly specialists: Agent[];
|
|
84
|
+
readonly verifiers: Verifier[];
|
|
85
|
+
readonly reconciler: Reconciler;
|
|
86
|
+
readonly specialist_timeout_ms: number;
|
|
87
|
+
constructor(config: CouncilConfig);
|
|
88
|
+
/**
|
|
89
|
+
* Convenience entry point — pointed at the Runner you've already constructed.
|
|
90
|
+
* Most users will call `runner.runCouncil(council, query)` directly; this
|
|
91
|
+
* exists for symmetry with `agent.run`-style ergonomics in user code.
|
|
92
|
+
*/
|
|
93
|
+
deliberate(_query: string): Promise<CouncilOutput>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { Council, type CouncilConfig, type CouncilOutput, type CouncilSpecialistOutput, type Reconciler };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Agent } from '../agent/index.js';
|
|
2
|
+
import { Verifier, VerifierResult } from '../verifiers/index.js';
|
|
3
|
+
import '../tools/index.js';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
/** Output of a single specialist's turn before reconciliation. */
|
|
7
|
+
interface CouncilSpecialistOutput {
|
|
8
|
+
specialist: string;
|
|
9
|
+
output: string;
|
|
10
|
+
claims: Array<{
|
|
11
|
+
text: string;
|
|
12
|
+
confidence: number;
|
|
13
|
+
type: string;
|
|
14
|
+
}>;
|
|
15
|
+
verifier_results: VerifierResult[];
|
|
16
|
+
cost_usd: number;
|
|
17
|
+
latency_ms: number;
|
|
18
|
+
}
|
|
19
|
+
/** Final synthesized output after reconciliation. */
|
|
20
|
+
interface CouncilOutput {
|
|
21
|
+
/** The synthesized final answer. */
|
|
22
|
+
answer: string;
|
|
23
|
+
/** Per-specialist outputs (visible if you want to surface deliberation). */
|
|
24
|
+
specialists: CouncilSpecialistOutput[];
|
|
25
|
+
/** Whether the council reached consensus or surfaced a disagreement. */
|
|
26
|
+
consensus: "ratified" | "split" | "refuted";
|
|
27
|
+
/** If split: what the disagreement was about. */
|
|
28
|
+
disagreements?: Array<{
|
|
29
|
+
claim: string;
|
|
30
|
+
specialists_for: string[];
|
|
31
|
+
specialists_against: string[];
|
|
32
|
+
}>;
|
|
33
|
+
/** Aggregated cost + latency. */
|
|
34
|
+
total_cost_usd: number;
|
|
35
|
+
total_latency_ms: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A reconciler synthesizes N specialist outputs into one answer.
|
|
39
|
+
*
|
|
40
|
+
* Two kinds:
|
|
41
|
+
* - Deterministic reconcilers (regex / SMT / voting) — fast, cheap, no LLM
|
|
42
|
+
* - LLM reconcilers (Theron-Reconciler-D or another model) — better synthesis,
|
|
43
|
+
* more expensive
|
|
44
|
+
*
|
|
45
|
+
* The default is a deterministic claim-merging reconciler.
|
|
46
|
+
*/
|
|
47
|
+
type Reconciler = (specialists: CouncilSpecialistOutput[]) => Promise<{
|
|
48
|
+
answer: string;
|
|
49
|
+
consensus: "ratified" | "split" | "refuted";
|
|
50
|
+
disagreements?: CouncilOutput["disagreements"];
|
|
51
|
+
}>;
|
|
52
|
+
interface CouncilConfig {
|
|
53
|
+
/** Display name for the council. */
|
|
54
|
+
name: string;
|
|
55
|
+
/** The specialists that will deliberate. Order doesn't matter. */
|
|
56
|
+
specialists: Agent[];
|
|
57
|
+
/** Verifier kernels run against each specialist output before reconciliation. */
|
|
58
|
+
verifiers?: Verifier[];
|
|
59
|
+
/** How to synthesize the specialist outputs. Defaults to deterministic claim-merge. */
|
|
60
|
+
reconciler?: Reconciler;
|
|
61
|
+
/** Optional timeout per specialist (ms). Slow specialists are dropped. */
|
|
62
|
+
specialist_timeout_ms?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The Council primitive.
|
|
66
|
+
*
|
|
67
|
+
* Minimal usage:
|
|
68
|
+
* const c = new Council({
|
|
69
|
+
* name: "engineering-review",
|
|
70
|
+
* specialists: [cyberAgent, codeAgent, archAgent],
|
|
71
|
+
* });
|
|
72
|
+
* const result = await runner.runCouncil(c, "Review this PR for security risks");
|
|
73
|
+
*
|
|
74
|
+
* With verifier kernels:
|
|
75
|
+
* const c = new Council({
|
|
76
|
+
* name: "math-proof",
|
|
77
|
+
* specialists: [mathAgent, reasoningAgent, verifierAgent],
|
|
78
|
+
* verifiers: [VerifierKernels.arithmetic, VerifierKernels.citationPresence],
|
|
79
|
+
* });
|
|
80
|
+
*/
|
|
81
|
+
declare class Council {
|
|
82
|
+
readonly name: string;
|
|
83
|
+
readonly specialists: Agent[];
|
|
84
|
+
readonly verifiers: Verifier[];
|
|
85
|
+
readonly reconciler: Reconciler;
|
|
86
|
+
readonly specialist_timeout_ms: number;
|
|
87
|
+
constructor(config: CouncilConfig);
|
|
88
|
+
/**
|
|
89
|
+
* Convenience entry point — pointed at the Runner you've already constructed.
|
|
90
|
+
* Most users will call `runner.runCouncil(council, query)` directly; this
|
|
91
|
+
* exists for symmetry with `agent.run`-style ergonomics in user code.
|
|
92
|
+
*/
|
|
93
|
+
deliberate(_query: string): Promise<CouncilOutput>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { Council, type CouncilConfig, type CouncilOutput, type CouncilSpecialistOutput, type Reconciler };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/council/index.ts
|
|
2
|
+
var Council = class {
|
|
3
|
+
name;
|
|
4
|
+
specialists;
|
|
5
|
+
verifiers;
|
|
6
|
+
reconciler;
|
|
7
|
+
specialist_timeout_ms;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
if (!config.name) throw new Error("Council requires a `name`.");
|
|
10
|
+
if (!config.specialists || config.specialists.length === 0) {
|
|
11
|
+
throw new Error(`Council "${config.name}" requires at least one specialist.`);
|
|
12
|
+
}
|
|
13
|
+
this.name = config.name;
|
|
14
|
+
this.specialists = config.specialists;
|
|
15
|
+
this.verifiers = config.verifiers ?? [];
|
|
16
|
+
this.reconciler = config.reconciler ?? deterministicClaimMerge;
|
|
17
|
+
this.specialist_timeout_ms = config.specialist_timeout_ms ?? 3e4;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Convenience entry point — pointed at the Runner you've already constructed.
|
|
21
|
+
* Most users will call `runner.runCouncil(council, query)` directly; this
|
|
22
|
+
* exists for symmetry with `agent.run`-style ergonomics in user code.
|
|
23
|
+
*/
|
|
24
|
+
async deliberate(_query) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
"Council.deliberate() requires a Runner. Use:\n const runner = new Runner({ ... });\n const result = await runner.runCouncil(council, query);\nSee: https://github.com/Vext-Labs-Inc/theron-agent-sdk/blob/main/examples/03_council_of_three.ts"
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var deterministicClaimMerge = async (specialists) => {
|
|
31
|
+
if (specialists.length === 0) {
|
|
32
|
+
return { answer: "(no specialists responded)", consensus: "refuted" };
|
|
33
|
+
}
|
|
34
|
+
if (specialists.length === 1) {
|
|
35
|
+
return { answer: specialists[0].output, consensus: "ratified" };
|
|
36
|
+
}
|
|
37
|
+
const claimMap = /* @__PURE__ */ new Map();
|
|
38
|
+
for (const s of specialists) {
|
|
39
|
+
for (const c of s.claims) {
|
|
40
|
+
const key = c.text.toLowerCase().trim();
|
|
41
|
+
if (!claimMap.has(key)) claimMap.set(key, []);
|
|
42
|
+
claimMap.get(key).push(s.specialist);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const ratified = [];
|
|
46
|
+
const disagreements = [];
|
|
47
|
+
const majorityThreshold = specialists.length / 2;
|
|
48
|
+
for (const [claim, voters] of claimMap.entries()) {
|
|
49
|
+
if (voters.length === specialists.length) {
|
|
50
|
+
ratified.push(claim);
|
|
51
|
+
} else if (voters.length > majorityThreshold) {
|
|
52
|
+
ratified.push(claim);
|
|
53
|
+
} else {
|
|
54
|
+
disagreements.push({
|
|
55
|
+
claim,
|
|
56
|
+
specialists_for: voters,
|
|
57
|
+
specialists_against: specialists.map((s) => s.specialist).filter((n) => !voters.includes(n))
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const answer = ratified.join(" ") || specialists[0].output;
|
|
62
|
+
const consensus = disagreements.length > 0 ? "split" : ratified.length > 0 ? "ratified" : "refuted";
|
|
63
|
+
return { answer, consensus, disagreements: disagreements.length > 0 ? disagreements : void 0 };
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export { Council };
|