kompass-sdk 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/dist/a2a/agent-card.d.ts +13 -0
- package/dist/a2a/agent-card.d.ts.map +1 -0
- package/dist/a2a/agent-card.js +52 -0
- package/dist/a2a/agent-card.js.map +1 -0
- package/dist/a2a/bridge.d.ts +52 -0
- package/dist/a2a/bridge.d.ts.map +1 -0
- package/dist/a2a/bridge.js +123 -0
- package/dist/a2a/bridge.js.map +1 -0
- package/dist/a2a/client.d.ts +34 -0
- package/dist/a2a/client.d.ts.map +1 -0
- package/dist/a2a/client.js +65 -0
- package/dist/a2a/client.js.map +1 -0
- package/dist/a2a/server.d.ts +17 -0
- package/dist/a2a/server.d.ts.map +1 -0
- package/dist/a2a/server.js +194 -0
- package/dist/a2a/server.js.map +1 -0
- package/dist/abi.d.ts +1068 -0
- package/dist/abi.d.ts.map +1 -0
- package/dist/abi.js +1372 -0
- package/dist/abi.js.map +1 -0
- package/dist/adapters/agentkit.d.ts +41 -0
- package/dist/adapters/agentkit.d.ts.map +1 -0
- package/dist/adapters/agentkit.js +67 -0
- package/dist/adapters/agentkit.js.map +1 -0
- package/dist/adapters/generic.d.ts +35 -0
- package/dist/adapters/generic.d.ts.map +1 -0
- package/dist/adapters/generic.js +47 -0
- package/dist/adapters/generic.js.map +1 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +5 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/langchain.d.ts +26 -0
- package/dist/adapters/langchain.d.ts.map +1 -0
- package/dist/adapters/langchain.js +228 -0
- package/dist/adapters/langchain.js.map +1 -0
- package/dist/adapters/openclaw.d.ts +18 -0
- package/dist/adapters/openclaw.d.ts.map +1 -0
- package/dist/adapters/openclaw.js +168 -0
- package/dist/adapters/openclaw.js.map +1 -0
- package/dist/aggregator.d.ts +36 -0
- package/dist/aggregator.d.ts.map +1 -0
- package/dist/aggregator.js +168 -0
- package/dist/aggregator.js.map +1 -0
- package/dist/backends/acp.d.ts +29 -0
- package/dist/backends/acp.d.ts.map +1 -0
- package/dist/backends/acp.js +126 -0
- package/dist/backends/acp.js.map +1 -0
- package/dist/backends/types.d.ts +59 -0
- package/dist/backends/types.d.ts.map +1 -0
- package/dist/backends/types.js +2 -0
- package/dist/backends/types.js.map +1 -0
- package/dist/bridge.d.ts +35 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +192 -0
- package/dist/bridge.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +331 -0
- package/dist/cli.js.map +1 -0
- package/dist/discover.d.ts +15 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +163 -0
- package/dist/discover.js.map +1 -0
- package/dist/escrow.d.ts +45 -0
- package/dist/escrow.d.ts.map +1 -0
- package/dist/escrow.js +243 -0
- package/dist/escrow.js.map +1 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +145 -0
- package/dist/index.js.map +1 -0
- package/dist/intents.d.ts +28 -0
- package/dist/intents.d.ts.map +1 -0
- package/dist/intents.js +111 -0
- package/dist/intents.js.map +1 -0
- package/dist/matching.d.ts +29 -0
- package/dist/matching.d.ts.map +1 -0
- package/dist/matching.js +147 -0
- package/dist/matching.js.map +1 -0
- package/dist/pipelineAbi.d.ts +113 -0
- package/dist/pipelineAbi.d.ts.map +1 -0
- package/dist/pipelineAbi.js +74 -0
- package/dist/pipelineAbi.js.map +1 -0
- package/dist/pipelines.d.ts +42 -0
- package/dist/pipelines.d.ts.map +1 -0
- package/dist/pipelines.js +185 -0
- package/dist/pipelines.js.map +1 -0
- package/dist/registry.d.ts +36 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +187 -0
- package/dist/registry.js.map +1 -0
- package/dist/reputation.d.ts +10 -0
- package/dist/reputation.d.ts.map +1 -0
- package/dist/reputation.js +33 -0
- package/dist/reputation.js.map +1 -0
- package/dist/router.d.ts +72 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +190 -0
- package/dist/router.js.map +1 -0
- package/dist/simple.d.ts +160 -0
- package/dist/simple.d.ts.map +1 -0
- package/dist/simple.js +237 -0
- package/dist/simple.js.map +1 -0
- package/dist/sources/a2a-wellknown.d.ts +8 -0
- package/dist/sources/a2a-wellknown.d.ts.map +1 -0
- package/dist/sources/a2a-wellknown.js +104 -0
- package/dist/sources/a2a-wellknown.js.map +1 -0
- package/dist/sources/acp.d.ts +7 -0
- package/dist/sources/acp.d.ts.map +1 -0
- package/dist/sources/acp.js +86 -0
- package/dist/sources/acp.js.map +1 -0
- package/dist/sources/adp.d.ts +7 -0
- package/dist/sources/adp.d.ts.map +1 -0
- package/dist/sources/adp.js +59 -0
- package/dist/sources/adp.js.map +1 -0
- package/dist/sources/erc8004.d.ts +7 -0
- package/dist/sources/erc8004.d.ts.map +1 -0
- package/dist/sources/erc8004.js +150 -0
- package/dist/sources/erc8004.js.map +1 -0
- package/dist/sources/index.d.ts +17 -0
- package/dist/sources/index.d.ts.map +1 -0
- package/dist/sources/index.js +35 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/sources/kompass-registry.d.ts +8 -0
- package/dist/sources/kompass-registry.d.ts.map +1 -0
- package/dist/sources/kompass-registry.js +62 -0
- package/dist/sources/kompass-registry.js.map +1 -0
- package/dist/sources/l402-directory.d.ts +7 -0
- package/dist/sources/l402-directory.d.ts.map +1 -0
- package/dist/sources/l402-directory.js +42 -0
- package/dist/sources/l402-directory.js.map +1 -0
- package/dist/sources/mcp-registry.d.ts +8 -0
- package/dist/sources/mcp-registry.d.ts.map +1 -0
- package/dist/sources/mcp-registry.js +85 -0
- package/dist/sources/mcp-registry.js.map +1 -0
- package/dist/sources/skills.d.ts +8 -0
- package/dist/sources/skills.d.ts.map +1 -0
- package/dist/sources/skills.js +153 -0
- package/dist/sources/skills.js.map +1 -0
- package/dist/sources/types.d.ts +72 -0
- package/dist/sources/types.d.ts.map +1 -0
- package/dist/sources/types.js +8 -0
- package/dist/sources/types.js.map +1 -0
- package/dist/sources/x402-ecosystem.d.ts +7 -0
- package/dist/sources/x402-ecosystem.d.ts.map +1 -0
- package/dist/sources/x402-ecosystem.js +78 -0
- package/dist/sources/x402-ecosystem.js.map +1 -0
- package/dist/types.d.ts +133 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/unified.d.ts +90 -0
- package/dist/unified.d.ts.map +1 -0
- package/dist/unified.js +107 -0
- package/dist/unified.js.map +1 -0
- package/dist/x402.d.ts +30 -0
- package/dist/x402.d.ts.map +1 -0
- package/dist/x402.js +79 -0
- package/dist/x402.js.map +1 -0
- package/package.json +61 -0
- package/scripts/bootstrap-agents.mjs +246 -0
- package/src/.gitkeep +0 -0
- package/src/a2a/agent-card.ts +66 -0
- package/src/a2a/bridge.ts +168 -0
- package/src/a2a/client.ts +92 -0
- package/src/a2a/server.ts +234 -0
- package/src/abi.ts +1373 -0
- package/src/adapters/agentkit.ts +83 -0
- package/src/adapters/generic.ts +62 -0
- package/src/adapters/index.ts +4 -0
- package/src/adapters/langchain.ts +282 -0
- package/src/adapters/openclaw.ts +203 -0
- package/src/aggregator.ts +203 -0
- package/src/backends/acp.ts +199 -0
- package/src/backends/types.ts +78 -0
- package/src/bridge.ts +263 -0
- package/src/cli.ts +397 -0
- package/src/discover.ts +187 -0
- package/src/escrow.ts +284 -0
- package/src/index.ts +245 -0
- package/src/intents.ts +166 -0
- package/src/matching.ts +192 -0
- package/src/pipelineAbi.ts +74 -0
- package/src/pipelines.ts +253 -0
- package/src/registry.ts +232 -0
- package/src/reputation.ts +43 -0
- package/src/router.ts +279 -0
- package/src/simple.ts +366 -0
- package/src/sources/a2a-wellknown.ts +120 -0
- package/src/sources/acp.ts +91 -0
- package/src/sources/adp.ts +64 -0
- package/src/sources/erc8004.ts +166 -0
- package/src/sources/index.ts +52 -0
- package/src/sources/kompass-registry.ts +67 -0
- package/src/sources/l402-directory.ts +51 -0
- package/src/sources/mcp-registry.ts +104 -0
- package/src/sources/skills.ts +161 -0
- package/src/sources/types.ts +82 -0
- package/src/sources/x402-ecosystem.ts +86 -0
- package/src/types.ts +147 -0
- package/src/unified.ts +155 -0
- package/src/x402.ts +122 -0
- package/tests/pipelineFlow.test.ts +239 -0
- package/tsconfig.json +20 -0
package/src/router.ts
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kompass Capability Router
|
|
3
|
+
*
|
|
4
|
+
* The core product logic. Given a task, figure out the best way to get it done.
|
|
5
|
+
*
|
|
6
|
+
* kompass.do("get DeFi yield data") →
|
|
7
|
+
* 1. Parse intent → what capability is needed
|
|
8
|
+
* 2. Search all sources → where can I get it
|
|
9
|
+
* 3. Rank options → cost, speed, reliability, reputation
|
|
10
|
+
* 4. Execute via right protocol → ACP, x402, MCP, skill, A2A
|
|
11
|
+
* 5. Return result
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { UnifiedAgent } from "./sources/types.js";
|
|
15
|
+
import { Aggregator } from "./aggregator.js";
|
|
16
|
+
import { rankAgents, parseIntent, type MatchResult } from "./matching.js";
|
|
17
|
+
import { ProtocolBridge, type BridgeHireResult } from "./bridge.js";
|
|
18
|
+
|
|
19
|
+
export type CostPreference = "cheapest" | "balanced" | "best-quality";
|
|
20
|
+
|
|
21
|
+
export interface DoOptions {
|
|
22
|
+
/** What you want done — natural language */
|
|
23
|
+
task: string;
|
|
24
|
+
/** Cost preference */
|
|
25
|
+
costPreference?: CostPreference;
|
|
26
|
+
/** Max budget in USD (e.g., 1.0 = $1) */
|
|
27
|
+
maxBudgetUSD?: number;
|
|
28
|
+
/** Prefer specific protocol */
|
|
29
|
+
preferProtocol?: string;
|
|
30
|
+
/** Prefer specific source */
|
|
31
|
+
preferSource?: string;
|
|
32
|
+
/** Timeout per capability in ms */
|
|
33
|
+
timeout?: number;
|
|
34
|
+
/** Return options without executing (dry run) */
|
|
35
|
+
dryRun?: boolean;
|
|
36
|
+
/** Additional payload to send to the capability */
|
|
37
|
+
payload?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface CapabilityOption {
|
|
41
|
+
agent: UnifiedAgent;
|
|
42
|
+
scores: MatchResult["scores"];
|
|
43
|
+
estimatedCost: number | null;
|
|
44
|
+
protocol: string;
|
|
45
|
+
source: string;
|
|
46
|
+
why: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface DoResult {
|
|
50
|
+
success: boolean;
|
|
51
|
+
/** The task that was requested */
|
|
52
|
+
task: string;
|
|
53
|
+
/** All options found across protocols */
|
|
54
|
+
options: CapabilityOption[];
|
|
55
|
+
/** The option that was selected and executed */
|
|
56
|
+
selected: CapabilityOption | null;
|
|
57
|
+
/** Result from executing the selected option */
|
|
58
|
+
execution: BridgeHireResult | null;
|
|
59
|
+
/** Discovery metadata */
|
|
60
|
+
discovery: {
|
|
61
|
+
sourcesQueried: number;
|
|
62
|
+
totalFound: number;
|
|
63
|
+
queryTimeMs: number;
|
|
64
|
+
};
|
|
65
|
+
/** Why this option was selected */
|
|
66
|
+
reasoning: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class CapabilityRouter {
|
|
70
|
+
private aggregator: Aggregator;
|
|
71
|
+
private bridge: ProtocolBridge;
|
|
72
|
+
|
|
73
|
+
constructor(aggregator: Aggregator, bridge: ProtocolBridge) {
|
|
74
|
+
this.aggregator = aggregator;
|
|
75
|
+
this.bridge = bridge;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async do(options: DoOptions): Promise<DoResult> {
|
|
79
|
+
const { task, dryRun = false } = options;
|
|
80
|
+
|
|
81
|
+
// 1. Search all sources
|
|
82
|
+
const searchResult = await this.aggregator.search(task, {
|
|
83
|
+
limit: 30,
|
|
84
|
+
timeout: options.timeout ?? 15000,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (searchResult.agents.length === 0) {
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
task,
|
|
91
|
+
options: [],
|
|
92
|
+
selected: null,
|
|
93
|
+
execution: null,
|
|
94
|
+
discovery: {
|
|
95
|
+
sourcesQueried: searchResult.sources.length,
|
|
96
|
+
totalFound: 0,
|
|
97
|
+
queryTimeMs: searchResult.queryTimeMs,
|
|
98
|
+
},
|
|
99
|
+
reasoning: "No capabilities found for this task across any registry.",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 2. Rank with multi-layer matching
|
|
104
|
+
const ranked = rankAgents(searchResult.agents, task);
|
|
105
|
+
|
|
106
|
+
// 3. Build options with cost estimates
|
|
107
|
+
const capabilityOptions = ranked.map((r) => this.buildOption(r, options));
|
|
108
|
+
|
|
109
|
+
// 4. Filter by budget
|
|
110
|
+
const affordable = options.maxBudgetUSD
|
|
111
|
+
? capabilityOptions.filter(
|
|
112
|
+
(o) => o.estimatedCost === null || o.estimatedCost <= options.maxBudgetUSD!
|
|
113
|
+
)
|
|
114
|
+
: capabilityOptions;
|
|
115
|
+
|
|
116
|
+
// 5. Apply preference-based reranking
|
|
117
|
+
const reranked = this.applyPreferences(affordable.length > 0 ? affordable : capabilityOptions, options);
|
|
118
|
+
|
|
119
|
+
// 6. Select best option
|
|
120
|
+
const selected = reranked[0] ?? null;
|
|
121
|
+
|
|
122
|
+
if (!selected) {
|
|
123
|
+
return {
|
|
124
|
+
success: false,
|
|
125
|
+
task,
|
|
126
|
+
options: capabilityOptions,
|
|
127
|
+
selected: null,
|
|
128
|
+
execution: null,
|
|
129
|
+
discovery: {
|
|
130
|
+
sourcesQueried: searchResult.sources.length,
|
|
131
|
+
totalFound: searchResult.totalFound,
|
|
132
|
+
queryTimeMs: searchResult.queryTimeMs,
|
|
133
|
+
},
|
|
134
|
+
reasoning: options.maxBudgetUSD
|
|
135
|
+
? `Found ${capabilityOptions.length} options but none within budget ($${options.maxBudgetUSD}).`
|
|
136
|
+
: "No suitable options found after ranking.",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 7. Execute (unless dry run)
|
|
141
|
+
let execution: BridgeHireResult | null = null;
|
|
142
|
+
|
|
143
|
+
if (!dryRun) {
|
|
144
|
+
execution = await this.bridge.hire(selected.agent, {
|
|
145
|
+
task,
|
|
146
|
+
timeout: options.timeout,
|
|
147
|
+
payload: options.payload,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const reasoning = this.explainChoice(selected, reranked, options);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
success: dryRun ? true : (execution?.success ?? false),
|
|
155
|
+
task,
|
|
156
|
+
options: capabilityOptions.slice(0, 10), // Top 10 options
|
|
157
|
+
selected,
|
|
158
|
+
execution,
|
|
159
|
+
discovery: {
|
|
160
|
+
sourcesQueried: searchResult.sources.length,
|
|
161
|
+
totalFound: searchResult.totalFound,
|
|
162
|
+
queryTimeMs: searchResult.queryTimeMs,
|
|
163
|
+
},
|
|
164
|
+
reasoning,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private buildOption(match: MatchResult, options: DoOptions): CapabilityOption {
|
|
169
|
+
const agent = match.agent;
|
|
170
|
+
let estimatedCost: number | null = null;
|
|
171
|
+
|
|
172
|
+
if (agent.pricing?.amount) {
|
|
173
|
+
estimatedCost = parseFloat(agent.pricing.amount);
|
|
174
|
+
} else if (agent.pricing?.model === "free") {
|
|
175
|
+
estimatedCost = 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let why = "";
|
|
179
|
+
if (match.scores.structured > 10) why += "Strong capability match. ";
|
|
180
|
+
if (match.scores.bm25 > 5) why += "High text relevance. ";
|
|
181
|
+
if (match.scores.multiCriteria > 15) why += "Good reputation/trust signals. ";
|
|
182
|
+
if (estimatedCost === 0) why += "Free. ";
|
|
183
|
+
if (estimatedCost !== null && estimatedCost > 0 && estimatedCost < 0.05) why += "Very affordable. ";
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
agent,
|
|
187
|
+
scores: match.scores,
|
|
188
|
+
estimatedCost,
|
|
189
|
+
protocol: agent.protocol,
|
|
190
|
+
source: agent.source,
|
|
191
|
+
why: why.trim() || "General match.",
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private applyPreferences(options: CapabilityOption[], doOptions: DoOptions): CapabilityOption[] {
|
|
196
|
+
let sorted = [...options];
|
|
197
|
+
|
|
198
|
+
// Protocol preference
|
|
199
|
+
if (doOptions.preferProtocol) {
|
|
200
|
+
sorted.sort((a, b) => {
|
|
201
|
+
const aMatch = a.protocol === doOptions.preferProtocol ? 1 : 0;
|
|
202
|
+
const bMatch = b.protocol === doOptions.preferProtocol ? 1 : 0;
|
|
203
|
+
return bMatch - aMatch;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Source preference
|
|
208
|
+
if (doOptions.preferSource) {
|
|
209
|
+
sorted.sort((a, b) => {
|
|
210
|
+
const aMatch = a.source === doOptions.preferSource ? 1 : 0;
|
|
211
|
+
const bMatch = b.source === doOptions.preferSource ? 1 : 0;
|
|
212
|
+
return bMatch - aMatch;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Cost preference reranking
|
|
217
|
+
switch (doOptions.costPreference) {
|
|
218
|
+
case "cheapest":
|
|
219
|
+
sorted.sort((a, b) => {
|
|
220
|
+
const aCost = a.estimatedCost ?? Infinity;
|
|
221
|
+
const bCost = b.estimatedCost ?? Infinity;
|
|
222
|
+
return aCost - bCost;
|
|
223
|
+
});
|
|
224
|
+
break;
|
|
225
|
+
|
|
226
|
+
case "best-quality":
|
|
227
|
+
// Already sorted by score, but boost high-reputation agents
|
|
228
|
+
sorted.sort((a, b) => {
|
|
229
|
+
const aRep = a.agent.reputation?.score ?? 0;
|
|
230
|
+
const bRep = b.agent.reputation?.score ?? 0;
|
|
231
|
+
return bRep - aRep || b.scores.total - a.scores.total;
|
|
232
|
+
});
|
|
233
|
+
break;
|
|
234
|
+
|
|
235
|
+
case "balanced":
|
|
236
|
+
default:
|
|
237
|
+
// Default: score-based with cost as tiebreaker
|
|
238
|
+
sorted.sort((a, b) => {
|
|
239
|
+
if (Math.abs(b.scores.total - a.scores.total) > 5) {
|
|
240
|
+
return b.scores.total - a.scores.total;
|
|
241
|
+
}
|
|
242
|
+
const aCost = a.estimatedCost ?? 1;
|
|
243
|
+
const bCost = b.estimatedCost ?? 1;
|
|
244
|
+
return aCost - bCost;
|
|
245
|
+
});
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return sorted;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private explainChoice(
|
|
253
|
+
selected: CapabilityOption,
|
|
254
|
+
alternatives: CapabilityOption[],
|
|
255
|
+
options: DoOptions
|
|
256
|
+
): string {
|
|
257
|
+
const parts: string[] = [];
|
|
258
|
+
|
|
259
|
+
parts.push(
|
|
260
|
+
`Selected "${selected.agent.name}" via ${selected.protocol.toUpperCase()} from ${selected.source}.`
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
if (selected.estimatedCost !== null) {
|
|
264
|
+
parts.push(
|
|
265
|
+
selected.estimatedCost === 0
|
|
266
|
+
? "This option is free."
|
|
267
|
+
: `Estimated cost: $${selected.estimatedCost}.`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
parts.push(selected.why);
|
|
272
|
+
|
|
273
|
+
if (alternatives.length > 1) {
|
|
274
|
+
parts.push(`${alternatives.length - 1} other options available.`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return parts.join(" ");
|
|
278
|
+
}
|
|
279
|
+
}
|
package/src/simple.ts
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kompass Simplified API
|
|
3
|
+
*
|
|
4
|
+
* Dead-simple interface for agent-to-agent commerce.
|
|
5
|
+
* Wraps ACP escrow + Kompass intelligence (reputation, x402, intent routing).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const kompass = await KompassSimple.create({ privateKey, network: "base-sepolia" });
|
|
9
|
+
* const result = await kompass.hireAgent({ task: "Find best yield on Base", maxBudget: 1_000_000n });
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Address, Hex } from "viem";
|
|
13
|
+
import { createPublicClient, http } from "viem";
|
|
14
|
+
import { baseSepolia, base } from "viem/chains";
|
|
15
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
16
|
+
import { AcpEscrowBackend, type AcpBackendConfig } from "./backends/acp.js";
|
|
17
|
+
import type {
|
|
18
|
+
EscrowBackend,
|
|
19
|
+
AgentSearchResult,
|
|
20
|
+
JobResult,
|
|
21
|
+
IncomingJob,
|
|
22
|
+
} from "./backends/types.js";
|
|
23
|
+
import { getReputation } from "./reputation.js";
|
|
24
|
+
import type { KompassConfig, ReputationSummary } from "./types.js";
|
|
25
|
+
|
|
26
|
+
// ── Config ───────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
export interface KompassSimpleConfig {
|
|
29
|
+
/** Agent's private key */
|
|
30
|
+
privateKey: string;
|
|
31
|
+
/** Network: "base-sepolia" or "base" */
|
|
32
|
+
network?: "base-sepolia" | "base";
|
|
33
|
+
/** ACP entity/session key address (from ACP registration) */
|
|
34
|
+
acpEntityAddress?: string;
|
|
35
|
+
/** ACP wallet address */
|
|
36
|
+
acpWalletAddress?: string;
|
|
37
|
+
/** Kompass indexer URL for ENS discovery + reputation */
|
|
38
|
+
indexerUrl?: string;
|
|
39
|
+
/** ERC-8004 reputation registry address */
|
|
40
|
+
reputationRegistryAddress?: Address;
|
|
41
|
+
/** Custom RPC URL */
|
|
42
|
+
rpcUrl?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── Hire Options ─────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
export interface HireOptions {
|
|
48
|
+
/** What you want done — natural language task description */
|
|
49
|
+
task: string;
|
|
50
|
+
/** Max budget in USDC base units (6 decimals). 1_000_000 = $1 */
|
|
51
|
+
maxBudget?: bigint;
|
|
52
|
+
/** Minimum ERC-8004 reputation score to trust an agent */
|
|
53
|
+
minReputation?: number;
|
|
54
|
+
/** Search query override (defaults to task) */
|
|
55
|
+
searchQuery?: string;
|
|
56
|
+
/** Specific offering ID if known */
|
|
57
|
+
offeringId?: string;
|
|
58
|
+
/** Additional requirements to send to the agent */
|
|
59
|
+
requirements?: Record<string, unknown>;
|
|
60
|
+
/** Timeout in ms for waiting for deliverable (default 5 min) */
|
|
61
|
+
timeoutMs?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface HireResult {
|
|
65
|
+
agent: AgentSearchResult;
|
|
66
|
+
jobId: string;
|
|
67
|
+
deliverable: unknown;
|
|
68
|
+
phase: string;
|
|
69
|
+
reputationChecked: boolean;
|
|
70
|
+
txHash?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Accept Options ───────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
export interface AcceptJobsOptions {
|
|
76
|
+
/** What capabilities this agent offers */
|
|
77
|
+
capabilities: string[];
|
|
78
|
+
/** Handler called when a job comes in — return the deliverable */
|
|
79
|
+
handler: (job: IncomingJob) => Promise<unknown>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Orchestrate Options ──────────────────────────────────
|
|
83
|
+
|
|
84
|
+
export interface OrchestrateOptions {
|
|
85
|
+
/** High-level intent — will be decomposed into multiple agent hires */
|
|
86
|
+
intent: string;
|
|
87
|
+
/** Risk tolerance for DeFi intents */
|
|
88
|
+
riskTolerance?: "conservative" | "balanced" | "aggressive";
|
|
89
|
+
/** Total budget across all steps */
|
|
90
|
+
budget?: bigint;
|
|
91
|
+
/** Minimum reputation for any hired agent */
|
|
92
|
+
minReputation?: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface OrchestrateResult {
|
|
96
|
+
intent: string;
|
|
97
|
+
steps: {
|
|
98
|
+
role: string;
|
|
99
|
+
agent: AgentSearchResult | null;
|
|
100
|
+
result: HireResult | null;
|
|
101
|
+
skipped: boolean;
|
|
102
|
+
reason?: string;
|
|
103
|
+
}[];
|
|
104
|
+
totalCostUSDC: number;
|
|
105
|
+
completedSteps: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── Main Class ───────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
export class KompassSimple {
|
|
111
|
+
private backend: EscrowBackend;
|
|
112
|
+
private config: KompassSimpleConfig;
|
|
113
|
+
private kompassConfig: KompassConfig | null = null;
|
|
114
|
+
|
|
115
|
+
private constructor(config: KompassSimpleConfig, backend: EscrowBackend) {
|
|
116
|
+
this.config = config;
|
|
117
|
+
this.backend = backend;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Create and initialize a Kompass instance.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* const kompass = await KompassSimple.create({
|
|
125
|
+
* privateKey: "0x...",
|
|
126
|
+
* network: "base-sepolia",
|
|
127
|
+
* acpEntityAddress: "0x...",
|
|
128
|
+
* acpWalletAddress: "0x...",
|
|
129
|
+
* });
|
|
130
|
+
*/
|
|
131
|
+
static async create(config: KompassSimpleConfig): Promise<KompassSimple> {
|
|
132
|
+
const account = privateKeyToAccount(config.privateKey as Hex);
|
|
133
|
+
const chain = config.network === "base" ? base : baseSepolia;
|
|
134
|
+
const rpcUrl = config.rpcUrl ?? (config.network === "base" ? "https://mainnet.base.org" : "https://sepolia.base.org");
|
|
135
|
+
|
|
136
|
+
// Create ACP backend
|
|
137
|
+
const acpConfig: AcpBackendConfig = {
|
|
138
|
+
privateKey: config.privateKey,
|
|
139
|
+
walletAddress: (config.acpWalletAddress ?? account.address) as Address,
|
|
140
|
+
entityAddress: (config.acpEntityAddress ?? account.address) as Address,
|
|
141
|
+
network: config.network ?? "base-sepolia",
|
|
142
|
+
rpcUrl,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const backend = new AcpEscrowBackend(acpConfig);
|
|
146
|
+
await backend.init();
|
|
147
|
+
|
|
148
|
+
const instance = new KompassSimple(config, backend);
|
|
149
|
+
|
|
150
|
+
// Set up Kompass config for reputation queries if registry address provided
|
|
151
|
+
if (config.reputationRegistryAddress) {
|
|
152
|
+
const publicClient = createPublicClient({
|
|
153
|
+
chain,
|
|
154
|
+
transport: http(rpcUrl),
|
|
155
|
+
});
|
|
156
|
+
instance.kompassConfig = {
|
|
157
|
+
publicClient,
|
|
158
|
+
escrowAddress: "0x0000000000000000000000000000000000000000" as Address,
|
|
159
|
+
registryAddress: "0x0000000000000000000000000000000000000000" as Address,
|
|
160
|
+
reputationRegistryAddress: config.reputationRegistryAddress,
|
|
161
|
+
indexerUrl: config.indexerUrl ?? "",
|
|
162
|
+
} as KompassConfig;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return instance;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Hire an agent to do a task.
|
|
170
|
+
*
|
|
171
|
+
* Under the hood:
|
|
172
|
+
* 1. Searches ACP marketplace for matching agents
|
|
173
|
+
* 2. Checks ERC-8004 reputation (if configured)
|
|
174
|
+
* 3. Creates an ACP job and waits for delivery
|
|
175
|
+
* 4. Returns the deliverable
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* const result = await kompass.hireAgent({
|
|
179
|
+
* task: "Find the best yield for 1000 USDC on Base",
|
|
180
|
+
* maxBudget: 1_000_000n,
|
|
181
|
+
* });
|
|
182
|
+
* console.log(result.deliverable);
|
|
183
|
+
*/
|
|
184
|
+
async hireAgent(options: HireOptions): Promise<HireResult> {
|
|
185
|
+
const query = options.searchQuery ?? options.task;
|
|
186
|
+
|
|
187
|
+
// 1. Discover agents on ACP
|
|
188
|
+
const agents = await this.backend.browseAgents(query, {
|
|
189
|
+
limit: 10,
|
|
190
|
+
sortBy: "successRate",
|
|
191
|
+
onlineOnly: true,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
if (agents.length === 0) {
|
|
195
|
+
throw new Error(`No agents found for: "${query}"`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 2. Check reputation (if configured)
|
|
199
|
+
let reputationChecked = false;
|
|
200
|
+
let filteredAgents = agents;
|
|
201
|
+
|
|
202
|
+
if (this.kompassConfig && options.minReputation !== undefined) {
|
|
203
|
+
filteredAgents = [];
|
|
204
|
+
for (const agent of agents) {
|
|
205
|
+
try {
|
|
206
|
+
const rep = await this.checkReputation(agent.address);
|
|
207
|
+
const score = Number(rep.summaryValue);
|
|
208
|
+
if (score >= options.minReputation || Number(rep.count) === 0) {
|
|
209
|
+
filteredAgents.push(agent);
|
|
210
|
+
}
|
|
211
|
+
} catch {
|
|
212
|
+
// If reputation check fails, include the agent (permissive)
|
|
213
|
+
filteredAgents.push(agent);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
reputationChecked = true;
|
|
217
|
+
|
|
218
|
+
if (filteredAgents.length === 0) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`No agents meet reputation threshold (${options.minReputation}). Found ${agents.length} agents but none passed.`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// 3. Select best agent (first match after filtering)
|
|
226
|
+
const agent = filteredAgents[0];
|
|
227
|
+
const offering = agent.offerings[0];
|
|
228
|
+
|
|
229
|
+
if (!offering) {
|
|
230
|
+
throw new Error(`Agent "${agent.name}" has no offerings`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 4. Create job on ACP
|
|
234
|
+
const requirements = {
|
|
235
|
+
description: options.task,
|
|
236
|
+
...options.requirements,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const job = await this.backend.hireAgent(
|
|
240
|
+
agent,
|
|
241
|
+
options.offeringId ?? offering.id,
|
|
242
|
+
requirements,
|
|
243
|
+
options.maxBudget
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
// 5. Wait for result
|
|
247
|
+
const result = await this.backend.waitForResult(
|
|
248
|
+
job.jobId,
|
|
249
|
+
options.timeoutMs ?? 300_000
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
agent,
|
|
254
|
+
jobId: result.jobId,
|
|
255
|
+
deliverable: result.deliverable,
|
|
256
|
+
phase: result.phase,
|
|
257
|
+
reputationChecked,
|
|
258
|
+
txHash: result.txHash,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Start accepting jobs from other agents.
|
|
264
|
+
*
|
|
265
|
+
* Registers on ACP and listens for incoming jobs matching your capabilities.
|
|
266
|
+
* When a job arrives, your handler is called. Return the deliverable and
|
|
267
|
+
* the SDK handles ACP delivery automatically.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* await kompass.acceptJobs({
|
|
271
|
+
* capabilities: ["defi-research", "yield-analysis"],
|
|
272
|
+
* handler: async (job) => {
|
|
273
|
+
* const report = await doResearch(job.description);
|
|
274
|
+
* return report;
|
|
275
|
+
* },
|
|
276
|
+
* });
|
|
277
|
+
*/
|
|
278
|
+
async acceptJobs(options: AcceptJobsOptions): Promise<void> {
|
|
279
|
+
await this.backend.acceptJobs(options.capabilities, options.handler);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Orchestrate a multi-agent pipeline for a complex intent.
|
|
284
|
+
*
|
|
285
|
+
* Decomposes the intent into specialist roles (research, data, risk, execution),
|
|
286
|
+
* hires an agent for each role via ACP, chains results, and returns the combined output.
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* const result = await kompass.orchestrate({
|
|
290
|
+
* intent: "Find best yield for 1000 USDC on Base",
|
|
291
|
+
* riskTolerance: "balanced",
|
|
292
|
+
* budget: 5_000_000n,
|
|
293
|
+
* });
|
|
294
|
+
*/
|
|
295
|
+
async orchestrate(options: OrchestrateOptions): Promise<OrchestrateResult> {
|
|
296
|
+
const roles = ["research", "premium-data", "risk", "execution"];
|
|
297
|
+
const perStepBudget = options.budget
|
|
298
|
+
? options.budget / BigInt(roles.length)
|
|
299
|
+
: 1_000_000n;
|
|
300
|
+
|
|
301
|
+
const steps: OrchestrateResult["steps"] = [];
|
|
302
|
+
let totalCost = 0;
|
|
303
|
+
|
|
304
|
+
for (const role of roles) {
|
|
305
|
+
try {
|
|
306
|
+
const result = await this.hireAgent({
|
|
307
|
+
task: `[${role}] ${options.intent}`,
|
|
308
|
+
searchQuery: role === "premium-data" ? "data analytics" : role,
|
|
309
|
+
maxBudget: perStepBudget,
|
|
310
|
+
minReputation: options.minReputation,
|
|
311
|
+
timeoutMs: 120_000,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
steps.push({
|
|
315
|
+
role,
|
|
316
|
+
agent: result.agent,
|
|
317
|
+
result,
|
|
318
|
+
skipped: false,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
totalCost += Number(perStepBudget) / 1e6;
|
|
322
|
+
} catch (err) {
|
|
323
|
+
steps.push({
|
|
324
|
+
role,
|
|
325
|
+
agent: null,
|
|
326
|
+
result: null,
|
|
327
|
+
skipped: true,
|
|
328
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
intent: options.intent,
|
|
335
|
+
steps,
|
|
336
|
+
totalCostUSDC: totalCost,
|
|
337
|
+
completedSteps: steps.filter((s) => !s.skipped).length,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Check an agent's ERC-8004 on-chain reputation.
|
|
343
|
+
*/
|
|
344
|
+
async checkReputation(agentAddress: Address): Promise<ReputationSummary> {
|
|
345
|
+
if (!this.kompassConfig) {
|
|
346
|
+
throw new Error("Reputation checking requires reputationRegistryAddress in config");
|
|
347
|
+
}
|
|
348
|
+
// Use agent address as ID (simplified — in production, look up erc8004Id)
|
|
349
|
+
return getReputation(this.kompassConfig, 0n);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Search for agents on ACP marketplace.
|
|
354
|
+
*/
|
|
355
|
+
async discover(query: string, options?: { limit?: number }): Promise<AgentSearchResult[]> {
|
|
356
|
+
return this.backend.browseAgents(query, {
|
|
357
|
+
limit: options?.limit ?? 20,
|
|
358
|
+
sortBy: "successRate",
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Get the backend name */
|
|
363
|
+
get backendName(): string {
|
|
364
|
+
return this.backend.name();
|
|
365
|
+
}
|
|
366
|
+
}
|