kompass-sdk 0.14.0 → 0.15.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 (53) hide show
  1. package/dist/cli.js +0 -0
  2. package/dist/matching.d.ts.map +1 -1
  3. package/dist/matching.js +8 -0
  4. package/dist/matching.js.map +1 -1
  5. package/dist/sources/acp.js +1 -1
  6. package/dist/sources/acp.js.map +1 -1
  7. package/dist/sources/bankr.d.ts +8 -0
  8. package/dist/sources/bankr.d.ts.map +1 -0
  9. package/dist/sources/bankr.js +79 -0
  10. package/dist/sources/bankr.js.map +1 -0
  11. package/dist/sources/index.d.ts.map +1 -1
  12. package/dist/sources/index.js +4 -0
  13. package/dist/sources/index.js.map +1 -1
  14. package/dist/sources/locus.d.ts +12 -0
  15. package/dist/sources/locus.d.ts.map +1 -0
  16. package/dist/sources/locus.js +134 -0
  17. package/dist/sources/locus.js.map +1 -0
  18. package/dist/sources/openserv.d.ts +8 -0
  19. package/dist/sources/openserv.d.ts.map +1 -0
  20. package/dist/sources/openserv.js +76 -0
  21. package/dist/sources/openserv.js.map +1 -0
  22. package/dist/sources/types.d.ts +1 -1
  23. package/dist/sources/types.d.ts.map +1 -1
  24. package/dist/wallet/bridge.d.ts +1 -0
  25. package/dist/wallet/bridge.d.ts.map +1 -1
  26. package/dist/wallet/bridge.js +31 -5
  27. package/dist/wallet/bridge.js.map +1 -1
  28. package/dist/wallet/handlers/acp.d.ts.map +1 -1
  29. package/dist/wallet/handlers/acp.js +59 -3
  30. package/dist/wallet/handlers/acp.js.map +1 -1
  31. package/dist/wallet/handlers/bankr.d.ts +13 -0
  32. package/dist/wallet/handlers/bankr.d.ts.map +1 -0
  33. package/dist/wallet/handlers/bankr.js +88 -0
  34. package/dist/wallet/handlers/bankr.js.map +1 -0
  35. package/dist/wallet/handlers/locus.d.ts +21 -0
  36. package/dist/wallet/handlers/locus.d.ts.map +1 -0
  37. package/dist/wallet/handlers/locus.js +144 -0
  38. package/dist/wallet/handlers/locus.js.map +1 -0
  39. package/dist/wallet/payment-router.d.ts.map +1 -1
  40. package/dist/wallet/payment-router.js +12 -0
  41. package/dist/wallet/payment-router.js.map +1 -1
  42. package/package.json +1 -1
  43. package/src/matching.ts +9 -0
  44. package/src/sources/acp.ts +1 -1
  45. package/src/sources/bankr.ts +87 -0
  46. package/src/sources/index.ts +4 -0
  47. package/src/sources/locus.ts +156 -0
  48. package/src/sources/types.ts +3 -1
  49. package/src/wallet/bridge.ts +33 -6
  50. package/src/wallet/handlers/acp.ts +69 -3
  51. package/src/wallet/handlers/bankr.ts +108 -0
  52. package/src/wallet/handlers/locus.ts +170 -0
  53. package/src/wallet/payment-router.ts +14 -0
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Locus Wrapped API Source Adapter
3
+ * Fetches Locus's live API catalog and indexes 50+ pay-per-use providers
4
+ * as discoverable capabilities. Each wrapped API becomes a searchable agent.
5
+ * Agents pay per-call via USDC on Base — no upstream API keys needed.
6
+ *
7
+ * Docs: https://beta.paywithlocus.com/skill.md
8
+ * Live index: https://beta.paywithlocus.com/wapi/index.md
9
+ */
10
+
11
+ import type { SourceAdapter, UnifiedAgent, SourceSearchOptions } from "./types.js";
12
+
13
+ const LOCUS_API = "https://beta-api.paywithlocus.com/api";
14
+ const WAPI_INDEX_URL = "https://beta.paywithlocus.com/wapi/index.md";
15
+
16
+ interface LocusProvider {
17
+ name: string;
18
+ slug: string;
19
+ category: string;
20
+ endpoints: string;
21
+ description: string;
22
+ }
23
+
24
+ // Cache fetched providers — refresh every 10 minutes
25
+ let providerCache: LocusProvider[] | null = null;
26
+ let cacheTime = 0;
27
+ const CACHE_TTL = 10 * 60 * 1000;
28
+
29
+ /**
30
+ * Parse the live markdown index from Locus into structured provider data.
31
+ * The index is a markdown table with columns: Provider, Category, Endpoints, Description
32
+ */
33
+ function parseMarkdownIndex(md: string): LocusProvider[] {
34
+ const providers: LocusProvider[] = [];
35
+ const lines = md.split("\n");
36
+
37
+ for (const line of lines) {
38
+ // Match table rows: | [Provider Name](url) | Category | Endpoints | Description |
39
+ const match = line.match(/^\| \[([^\]]+)\]\(https:\/\/[^)]*\/wapi\/([^.]+)\.md\)\s*\|\s*([^|]+)\|\s*([^|]+)\|\s*([^|]+)\|/);
40
+ if (match) {
41
+ providers.push({
42
+ name: match[1].trim(),
43
+ slug: match[2].trim(),
44
+ category: match[3].trim().toLowerCase(),
45
+ endpoints: match[4].trim(),
46
+ description: match[5].trim(),
47
+ });
48
+ }
49
+ }
50
+
51
+ return providers;
52
+ }
53
+
54
+ async function fetchProviders(timeout: number): Promise<LocusProvider[]> {
55
+ if (providerCache && Date.now() - cacheTime < CACHE_TTL) {
56
+ return providerCache;
57
+ }
58
+
59
+ try {
60
+ const res = await fetch(WAPI_INDEX_URL, {
61
+ signal: AbortSignal.timeout(timeout),
62
+ });
63
+
64
+ if (!res.ok) return providerCache ?? [];
65
+
66
+ const md = await res.text();
67
+ const parsed = parseMarkdownIndex(md);
68
+
69
+ if (parsed.length > 0) {
70
+ providerCache = parsed;
71
+ cacheTime = Date.now();
72
+ }
73
+
74
+ return providerCache ?? [];
75
+ } catch {
76
+ return providerCache ?? [];
77
+ }
78
+ }
79
+
80
+ export const locusAdapter: SourceAdapter = {
81
+ name: "locus" as any,
82
+ displayName: "Locus Wrapped APIs",
83
+
84
+ async search(query: string, options?: SourceSearchOptions): Promise<UnifiedAgent[]> {
85
+ const limit = options?.limit ?? 20;
86
+ const timeout = options?.timeout ?? 10000;
87
+ const lower = query.toLowerCase();
88
+ const terms = lower.split(/\s+/).filter(t => t.length > 2);
89
+
90
+ const providers = await fetchProviders(timeout);
91
+
92
+ // Score each provider by relevance
93
+ const scored = providers.map(provider => {
94
+ let score = 0;
95
+ const searchText = `${provider.name} ${provider.description} ${provider.category} ${provider.endpoints}`.toLowerCase();
96
+
97
+ for (const term of terms) {
98
+ if (provider.name.toLowerCase().includes(term)) score += 5;
99
+ if (provider.description.toLowerCase().includes(term)) score += 3;
100
+ if (provider.endpoints.toLowerCase().includes(term)) score += 2;
101
+ if (provider.category.includes(term)) score += 4;
102
+ if (searchText.includes(term)) score += 1;
103
+ }
104
+
105
+ return { provider, score };
106
+ })
107
+ .filter(s => s.score > 0)
108
+ .sort((a, b) => b.score - a.score)
109
+ .slice(0, limit);
110
+
111
+ return scored.map(s => mapLocusProvider(s.provider));
112
+ },
113
+
114
+ async ping(): Promise<boolean> {
115
+ try {
116
+ const res = await fetch(WAPI_INDEX_URL, {
117
+ signal: AbortSignal.timeout(5000),
118
+ });
119
+ return res.ok;
120
+ } catch {
121
+ return false;
122
+ }
123
+ },
124
+ };
125
+
126
+ function mapLocusProvider(provider: LocusProvider): UnifiedAgent {
127
+ const endpointList = provider.endpoints.split(",").map(e => e.trim()).slice(0, 5);
128
+
129
+ return {
130
+ id: `locus:${provider.slug}`,
131
+ nativeId: provider.slug,
132
+ name: `${provider.name} (via Locus)`,
133
+ description: `${provider.description} Pay-per-use via USDC — no API key needed.`,
134
+ categories: [provider.category.split(" / ")[0].toLowerCase(), "api", "pay-per-use"],
135
+ capabilities: endpointList.map(e => `${provider.name}: ${e}`),
136
+ source: "locus" as any,
137
+ protocol: "http",
138
+ endpoints: {
139
+ http: `${LOCUS_API}/wrapped/${provider.slug}`,
140
+ },
141
+ pricing: {
142
+ model: "per-call",
143
+ amount: "0.01",
144
+ currency: "USDC",
145
+ },
146
+ verified: true,
147
+ lastSeen: Date.now(),
148
+ raw: {
149
+ type: "locus-wrapped-api",
150
+ provider: provider.slug,
151
+ category: provider.category,
152
+ endpoints: endpointList,
153
+ allEndpoints: provider.endpoints,
154
+ },
155
+ };
156
+ }
@@ -14,7 +14,9 @@ export type AgentSource =
14
14
  | "adp"
15
15
  | "a2a-wellknown"
16
16
  | "l402-directory"
17
- | "skills-registry";
17
+ | "skills-registry"
18
+ | "bankr"
19
+ | "locus";
18
20
 
19
21
  export type AgentProtocol = "acp" | "mcp" | "x402" | "a2a" | "http" | "l402" | "onchain" | "skill";
20
22
 
@@ -19,6 +19,10 @@ const USDC_ADDRESSES: Record<string, Address> = {
19
19
  "base-sepolia": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
20
20
  };
21
21
 
22
+ // Kompass treasury address — receives platform fee on payments
23
+ const KOMPASS_TREASURY: Address = "0x2aB73f8CCee4ABE601cE71eCC82Bd9eC375FeBbA";
24
+ const PLATFORM_FEE_BPS = 200; // 2% = 200 basis points
25
+
22
26
  // ── ACP Wallet Topup ─────────────────────────────────────
23
27
 
24
28
  /**
@@ -29,12 +33,17 @@ export async function topupACPWallet(
29
33
  wallet: KompassWallet,
30
34
  acpWalletAddress: Address,
31
35
  amountUSDC: number
32
- ): Promise<{ txHash: Hex; amount: string }> {
36
+ ): Promise<{ txHash: Hex; amount: string; fee?: string }> {
33
37
  const network = wallet.getNetwork();
34
38
  const usdcAddress = USDC_ADDRESSES[network];
35
39
  if (!usdcAddress) throw new Error(`No USDC address for network: ${network}`);
36
40
 
37
- const amount = parseUnits(String(amountUSDC), 6);
41
+ // Calculate platform fee (2%)
42
+ const feeUSDC = amountUSDC * PLATFORM_FEE_BPS / 10000;
43
+ const netAmount = amountUSDC - feeUSDC;
44
+ const totalNeeded = parseUnits(String(amountUSDC), 6);
45
+ const netAmountRaw = parseUnits(String(netAmount.toFixed(6)), 6);
46
+ const feeAmountRaw = totalNeeded - netAmountRaw;
38
47
 
39
48
  // Check balance first
40
49
  const balance = await wallet.getPublicClient().readContract({
@@ -44,25 +53,43 @@ export async function topupACPWallet(
44
53
  args: [wallet.getAddress()],
45
54
  });
46
55
 
47
- if (balance < amount) {
56
+ if (balance < totalNeeded) {
48
57
  throw new Error(
49
58
  `Insufficient USDC: have ${formatUnits(balance, 6)}, need ${amountUSDC}. ` +
50
59
  `Fund your Kompass wallet at ${wallet.getAddress()} on ${network}.`
51
60
  );
52
61
  }
53
62
 
63
+ // Transfer net amount to ACP wallet
54
64
  const txHash = await (wallet.getWalletClient() as any).writeContract({
55
65
  address: usdcAddress,
56
66
  abi: USDC_ABI,
57
67
  functionName: "transfer",
58
- args: [acpWalletAddress, amount],
68
+ args: [acpWalletAddress, netAmountRaw],
59
69
  });
60
70
 
61
71
  await wallet.getPublicClient().waitForTransactionReceipt({ hash: txHash });
62
72
 
63
- console.log(`[Kompass Bridge] Transferred ${amountUSDC} USDC to ACP wallet ${acpWalletAddress.slice(0, 10)}... | tx: ${txHash}`);
73
+ // Transfer platform fee to Kompass treasury (skip if treasury == ACP wallet)
74
+ if (feeAmountRaw > 0n && KOMPASS_TREASURY.toLowerCase() !== acpWalletAddress.toLowerCase()) {
75
+ try {
76
+ const feeTx = await (wallet.getWalletClient() as any).writeContract({
77
+ address: usdcAddress,
78
+ abi: USDC_ABI,
79
+ functionName: "transfer",
80
+ args: [KOMPASS_TREASURY, feeAmountRaw],
81
+ });
82
+ await wallet.getPublicClient().waitForTransactionReceipt({ hash: feeTx });
83
+ console.log(`[Kompass Bridge] Platform fee: $${feeUSDC.toFixed(4)} USDC → treasury | tx: ${feeTx}`);
84
+ } catch (feeErr) {
85
+ // Fee transfer failure shouldn't block the job
86
+ console.log(`[Kompass Bridge] Fee transfer failed (non-blocking): ${feeErr instanceof Error ? feeErr.message : feeErr}`);
87
+ }
88
+ }
89
+
90
+ console.log(`[Kompass Bridge] Transferred ${netAmount.toFixed(4)} USDC to ACP wallet ${acpWalletAddress.slice(0, 10)}... | tx: ${txHash}`);
64
91
 
65
- return { txHash, amount: String(amountUSDC) };
92
+ return { txHash, amount: String(netAmount.toFixed(4)), fee: feeUSDC.toFixed(4) };
66
93
  }
67
94
 
68
95
  // ── Lightning Swap (USDC → sats) ─────────────────────────
@@ -40,9 +40,10 @@ export async function handleACPPayment(
40
40
  // Job creation (REQUEST) and acceptance (NEGOTIATION) are FREE.
41
41
  // We topup only after confirming an agent accepted.
42
42
 
43
- // Use ACP CLI with execFile (not execSync) to avoid shell injection
44
- // Arguments passed as array, not concatenated string
45
- const requirements = JSON.stringify({ description: task });
43
+ // Format requirements to match offering's expected schema.
44
+ // If the offering has a requirement template/schema, try to conform to it.
45
+ // Otherwise fall back to {description: task}.
46
+ const requirements = JSON.stringify(formatRequirements(task, offering));
46
47
 
47
48
  const { stdout } = await execFileAsync(
48
49
  "npx",
@@ -157,3 +158,68 @@ export async function handleACPPayment(
157
158
  };
158
159
  }
159
160
  }
161
+
162
+ /**
163
+ * Format task requirements to match the offering's expected schema.
164
+ *
165
+ * ACP agents reject jobs when requirements don't match their offering's
166
+ * expected input format. This function:
167
+ * 1. Checks if the offering has a requirement schema/template
168
+ * 2. Tries to extract relevant fields from the natural language task
169
+ * 3. Falls back to {description: task} if no schema exists
170
+ */
171
+ function formatRequirements(task: string, offering: any): Record<string, unknown> {
172
+ const schema = offering.requirement ?? offering.requirementSchema ?? offering.inputSchema;
173
+
174
+ // No schema defined — send description (most flexible)
175
+ if (!schema || typeof schema === "string") {
176
+ return { description: task };
177
+ }
178
+
179
+ // Schema is an object — try to populate its fields from the task
180
+ if (typeof schema === "object") {
181
+ const formatted: Record<string, unknown> = {};
182
+ const taskLower = task.toLowerCase();
183
+
184
+ for (const [key, spec] of Object.entries(schema)) {
185
+ const keyLower = key.toLowerCase();
186
+
187
+ // Try to extract Ethereum addresses for address-type fields
188
+ if (keyLower.includes("address") || keyLower.includes("token") || keyLower.includes("contract")) {
189
+ const addrMatch = task.match(/0x[a-fA-F0-9]{40}/);
190
+ if (addrMatch) {
191
+ formatted[key] = addrMatch[0];
192
+ continue;
193
+ }
194
+ }
195
+
196
+ // Try to extract chain/network
197
+ if (keyLower.includes("chain") || keyLower.includes("network")) {
198
+ const chains = ["base", "ethereum", "polygon", "arbitrum", "optimism", "bsc"];
199
+ const found = chains.find((c) => taskLower.includes(c));
200
+ formatted[key] = found ?? "base";
201
+ continue;
202
+ }
203
+
204
+ // Try to extract numeric amounts
205
+ if (keyLower.includes("amount") || keyLower.includes("quantity") || keyLower.includes("limit")) {
206
+ const numMatch = task.match(/\d+\.?\d*/);
207
+ if (numMatch) {
208
+ formatted[key] = parseFloat(numMatch[0]);
209
+ continue;
210
+ }
211
+ }
212
+
213
+ // For any other field, use the task description as the value
214
+ if (typeof spec === "string") {
215
+ formatted[key] = task; // Use full task as the value
216
+ }
217
+ }
218
+
219
+ // Always include description for context
220
+ formatted.description = task;
221
+ return formatted;
222
+ }
223
+
224
+ return { description: task };
225
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Bankr Payment Handler
3
+ * Routes trading tasks through Bankr's LLM Gateway
4
+ * POST prompt → poll for result
5
+ */
6
+
7
+ import type { KompassWallet } from "../index.js";
8
+ import type { UnifiedAgent } from "../../sources/types.js";
9
+
10
+ const BANKR_API = "https://api.bankr.bot";
11
+
12
+ export async function handleBankrPayment(
13
+ wallet: KompassWallet,
14
+ agent: UnifiedAgent,
15
+ task: string,
16
+ ): Promise<{ success: boolean; deliverable?: unknown; jobId?: string }> {
17
+ const apiKey = process.env.BANKR_API_KEY;
18
+ if (!apiKey) {
19
+ return {
20
+ success: false,
21
+ deliverable: {
22
+ error: "BANKR_API_KEY not configured. Set the BANKR_API_KEY environment variable.",
23
+ hint: "Get an API key from https://bankr.bot",
24
+ },
25
+ };
26
+ }
27
+
28
+ const headers: Record<string, string> = {
29
+ "Content-Type": "application/json",
30
+ "X-API-Key": apiKey,
31
+ };
32
+
33
+ try {
34
+ // Step 1: Submit prompt
35
+ const submitRes = await fetch(`${BANKR_API}/agent/prompt`, {
36
+ method: "POST",
37
+ headers,
38
+ body: JSON.stringify({ prompt: task }),
39
+ signal: AbortSignal.timeout(30000),
40
+ });
41
+
42
+ if (!submitRes.ok) {
43
+ const errBody = await submitRes.text().catch(() => "");
44
+ return {
45
+ success: false,
46
+ deliverable: { error: `Bankr API error: ${submitRes.status}`, details: errBody },
47
+ };
48
+ }
49
+
50
+ const submitData = await submitRes.json();
51
+ const jobId = submitData.jobId ?? submitData.job_id ?? submitData.id;
52
+
53
+ // If the response is immediate (no job polling needed)
54
+ if (!jobId || submitData.result || submitData.response) {
55
+ return {
56
+ success: true,
57
+ deliverable: submitData.result ?? submitData.response ?? submitData,
58
+ };
59
+ }
60
+
61
+ // Step 2: Poll for result
62
+ const maxAttempts = 30;
63
+ const pollInterval = 2000;
64
+
65
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
66
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
67
+
68
+ const pollRes = await fetch(`${BANKR_API}/agent/job/${jobId}`, {
69
+ headers,
70
+ signal: AbortSignal.timeout(10000),
71
+ });
72
+
73
+ if (!pollRes.ok) continue;
74
+
75
+ const pollData = await pollRes.json();
76
+ const status = pollData.status?.toLowerCase();
77
+
78
+ if (status === "completed" || status === "done" || status === "success") {
79
+ return {
80
+ success: true,
81
+ deliverable: pollData.result ?? pollData.response ?? pollData,
82
+ jobId,
83
+ };
84
+ }
85
+
86
+ if (status === "failed" || status === "error") {
87
+ return {
88
+ success: false,
89
+ deliverable: { error: pollData.error ?? "Job failed", details: pollData },
90
+ jobId,
91
+ };
92
+ }
93
+
94
+ // Still pending, continue polling
95
+ }
96
+
97
+ return {
98
+ success: false,
99
+ deliverable: { error: "Job timed out after polling", jobId },
100
+ jobId,
101
+ };
102
+ } catch (err) {
103
+ return {
104
+ success: false,
105
+ deliverable: { error: err instanceof Error ? err.message : String(err) },
106
+ };
107
+ }
108
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Locus Payment Handler
3
+ * Calls Locus wrapped APIs — pay-per-use via USDC on Base.
4
+ * No upstream API keys needed — Locus proxies the call and charges the wallet.
5
+ *
6
+ * Flow:
7
+ * 1. Get or create Locus API key (self-register if needed)
8
+ * 2. Wait for wallet deployment
9
+ * 3. Call the wrapped API endpoint
10
+ * 4. Locus charges USDC from wallet, returns response
11
+ *
12
+ * Docs: https://beta.paywithlocus.com/skill.md
13
+ */
14
+
15
+ import type { KompassWallet } from "../index.js";
16
+ import type { UnifiedAgent } from "../../sources/types.js";
17
+
18
+ const LOCUS_API = "https://beta-api.paywithlocus.com/api";
19
+
20
+ export async function handleLocusPayment(
21
+ wallet: KompassWallet,
22
+ agent: UnifiedAgent,
23
+ task: string,
24
+ ): Promise<{ success: boolean; deliverable?: unknown; txHash?: string }> {
25
+ let apiKey = process.env.LOCUS_API_KEY;
26
+
27
+ try {
28
+ // Step 1: Self-register if no API key
29
+ if (!apiKey) {
30
+ const walletAddress = wallet.getAddress();
31
+ const registerRes = await fetch(`${LOCUS_API}/register`, {
32
+ method: "POST",
33
+ headers: { "Content-Type": "application/json" },
34
+ body: JSON.stringify({
35
+ wallet_address: walletAddress,
36
+ agent_name: "kompass-agent",
37
+ }),
38
+ signal: AbortSignal.timeout(15000),
39
+ });
40
+
41
+ if (!registerRes.ok) {
42
+ const errBody = await registerRes.text().catch(() => "");
43
+ return {
44
+ success: false,
45
+ deliverable: {
46
+ error: `Locus registration failed: ${registerRes.status}`,
47
+ details: errBody,
48
+ hint: "Set LOCUS_API_KEY env var or fund a Locus wallet at paywithlocus.com",
49
+ },
50
+ };
51
+ }
52
+
53
+ const registerData = await registerRes.json();
54
+ apiKey = registerData.data?.apiKey;
55
+
56
+ if (!apiKey) {
57
+ return {
58
+ success: false,
59
+ deliverable: { error: "Locus registration did not return an API key" },
60
+ };
61
+ }
62
+
63
+ // Wait for wallet deployment
64
+ for (let i = 0; i < 10; i++) {
65
+ const statusRes = await fetch(`${LOCUS_API}/status`, {
66
+ headers: { "Authorization": `Bearer ${apiKey}` },
67
+ signal: AbortSignal.timeout(5000),
68
+ }).catch(() => null);
69
+
70
+ if (statusRes?.ok) {
71
+ const statusData = await statusRes.json();
72
+ if (statusData.data?.walletStatus === "deployed") break;
73
+ }
74
+ await new Promise((r) => setTimeout(r, 3000));
75
+ }
76
+ }
77
+
78
+ const headers: Record<string, string> = {
79
+ "Content-Type": "application/json",
80
+ "Authorization": `Bearer ${apiKey}`,
81
+ };
82
+
83
+ // Step 2: Determine if this is a wrapped API call or a direct payment
84
+ const rawData = agent.raw as any;
85
+ const isWrappedApi = rawData?.type === "locus-wrapped-api";
86
+
87
+ if (isWrappedApi) {
88
+ // Call the wrapped API directly
89
+ // The endpoint URL is like: /api/wrapped/{provider}/{endpoint}
90
+ // We need to figure out the best endpoint for the task
91
+ const provider = rawData.provider;
92
+ const availableEndpoints = rawData.endpoints as string[] || [];
93
+
94
+ // Pick the first/most relevant endpoint
95
+ // For now use the first endpoint — the provider slug is already in the URL
96
+ const endpoint = availableEndpoints[0]?.toLowerCase().replace(/\s+/g, "-") || "search";
97
+
98
+ const callUrl = `${LOCUS_API}/wrapped/${provider}/${endpoint}`;
99
+
100
+ const callRes = await fetch(callUrl, {
101
+ method: "POST",
102
+ headers,
103
+ body: JSON.stringify({ query: task, prompt: task, q: task }),
104
+ signal: AbortSignal.timeout(30000),
105
+ });
106
+
107
+ if (!callRes.ok) {
108
+ const errBody = await callRes.text().catch(() => "");
109
+ return {
110
+ success: false,
111
+ deliverable: {
112
+ error: `Locus wrapped API call failed: ${callRes.status}`,
113
+ provider,
114
+ endpoint,
115
+ details: errBody,
116
+ },
117
+ };
118
+ }
119
+
120
+ const result = await callRes.json();
121
+ return {
122
+ success: true,
123
+ deliverable: result,
124
+ };
125
+ }
126
+
127
+ // Step 3: Direct USDC payment to agent address
128
+ const toAddress = agent.endpoints.acp?.walletAddress ?? agent.endpoints.http;
129
+ const amount = agent.pricing?.amount ?? "0.01";
130
+
131
+ if (!toAddress || !toAddress.startsWith("0x")) {
132
+ return {
133
+ success: false,
134
+ deliverable: { error: "No valid target address for Locus payment" },
135
+ };
136
+ }
137
+
138
+ const sendRes = await fetch(`${LOCUS_API}/pay/send`, {
139
+ method: "POST",
140
+ headers,
141
+ body: JSON.stringify({
142
+ to_address: toAddress,
143
+ amount: parseFloat(amount),
144
+ memo: `Kompass task: ${task.slice(0, 100)}`,
145
+ }),
146
+ signal: AbortSignal.timeout(30000),
147
+ });
148
+
149
+ if (!sendRes.ok) {
150
+ const errBody = await sendRes.text().catch(() => "");
151
+ return {
152
+ success: false,
153
+ deliverable: { error: `Locus payment failed: ${sendRes.status}`, details: errBody },
154
+ };
155
+ }
156
+
157
+ const sendData = await sendRes.json();
158
+
159
+ return {
160
+ success: true,
161
+ deliverable: sendData,
162
+ txHash: sendData.data?.tx_hash ?? sendData.data?.transaction_id,
163
+ };
164
+ } catch (err) {
165
+ return {
166
+ success: false,
167
+ deliverable: { error: err instanceof Error ? err.message : String(err) },
168
+ };
169
+ }
170
+ }
@@ -11,6 +11,8 @@ import { handleL402Payment } from "./handlers/l402.js";
11
11
  import { handleMPPPayment } from "./handlers/mpp.js";
12
12
  import { handleNanopayment } from "./handlers/nanopayments.js";
13
13
  import { handleFreePayment } from "./handlers/free.js";
14
+ import { handleBankrPayment } from "./handlers/bankr.js";
15
+ import { handleLocusPayment } from "./handlers/locus.js";
14
16
  import { ensureSufficientBalance } from "./bridge.js";
15
17
 
16
18
  export interface PaymentResult {
@@ -72,6 +74,16 @@ export async function routePayment(
72
74
  return { ...result, protocol: "circle-nanopayments", costUSDC: parseFloat(agent.pricing?.amount ?? "0") };
73
75
  }
74
76
 
77
+ case "bankr": {
78
+ const result = await handleBankrPayment(wallet, agent, task);
79
+ return { ...result, protocol: "bankr", costUSDC: parseFloat(agent.pricing?.amount ?? "0") };
80
+ }
81
+
82
+ case "locus": {
83
+ const result = await handleLocusPayment(wallet, agent, task);
84
+ return { ...result, protocol: "locus", costUSDC: parseFloat(agent.pricing?.amount ?? "0") };
85
+ }
86
+
75
87
  case "free":
76
88
  case "mcp": {
77
89
  // MCP servers and free tools — try to execute directly
@@ -130,6 +142,8 @@ function determinePaymentProtocol(agent: UnifiedAgent): string {
130
142
  case "l402-directory": return "l402";
131
143
  case "mcp-registry": return "free";
132
144
  case "skills-registry": return "free";
145
+ case "bankr": return "bankr";
146
+ case "locus": return "locus";
133
147
  default: return agent.protocol === "onchain" ? "acp" : "free";
134
148
  }
135
149
  }