@mixrpay/agent-sdk 0.8.3 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @mixrpay/agent-sdk might be problematic. Click here for more details.

package/dist/index.cjs CHANGED
@@ -335,6 +335,13 @@ var SessionKey = class _SessionKey {
335
335
  get privateKeyHex() {
336
336
  return this.privateKey.slice(2);
337
337
  }
338
+ /**
339
+ * Get the raw private key with 0x prefix.
340
+ * Used internally for signing operations.
341
+ */
342
+ get rawPrivateKey() {
343
+ return this.privateKey;
344
+ }
338
345
  /**
339
346
  * Parse a session key string into a SessionKey object.
340
347
  *
@@ -525,7 +532,7 @@ function getAmountUsd(requirements) {
525
532
  }
526
533
 
527
534
  // src/agent-wallet.ts
528
- var SDK_VERSION = "0.8.3";
535
+ var SDK_VERSION = "0.8.5";
529
536
  var DEFAULT_BASE_URL = process.env.MIXRPAY_BASE_URL || "https://www.mixrpay.com";
530
537
  var DEFAULT_TIMEOUT = 3e4;
531
538
  var NETWORKS = {
@@ -1227,6 +1234,160 @@ var AgentWallet = class {
1227
1234
  };
1228
1235
  }
1229
1236
  // ===========================================================================
1237
+ // Nested Budget Delegation Methods
1238
+ // ===========================================================================
1239
+ /**
1240
+ * Spawn a child invite for sub-agents.
1241
+ *
1242
+ * Allows an agent to create an invite code for a sub-agent with a portion
1243
+ * of their remaining budget (max 20%). The child inherits merchant restrictions
1244
+ * and cannot outlive the parent session.
1245
+ *
1246
+ * @param options - Spawn options
1247
+ * @returns The created child invite details
1248
+ *
1249
+ * @throws {MixrPayError} If spawning fails (insufficient budget, depth limit, etc.)
1250
+ *
1251
+ * @example
1252
+ * ```typescript
1253
+ * const wallet = new AgentWallet({ sessionKey: 'sk_live_...' });
1254
+ *
1255
+ * // Spawn a child invite for a sub-agent
1256
+ * const childInvite = await wallet.spawnChildInvite({
1257
+ * budgetUsd: 10.00, // Max 20% of available budget
1258
+ * name: 'Research Sub-Agent',
1259
+ * allowedMerchants: ['firecrawl.dev'], // Must be subset of parent
1260
+ * expiresInDays: 7, // Will be capped to parent's expiry
1261
+ * });
1262
+ *
1263
+ * console.log(`Share with sub-agent: ${childInvite.inviteCode}`);
1264
+ * console.log(`Child can spawn up to: $${childInvite.maxSpawnBudget}`);
1265
+ * ```
1266
+ */
1267
+ async spawnChildInvite(options) {
1268
+ const { budgetUsd, name, allowedMerchants, expiresInDays } = options;
1269
+ const timestamp = Date.now();
1270
+ const nonce = crypto.randomUUID();
1271
+ const message = `spawn:${timestamp}:${nonce}`;
1272
+ const signature = await (0, import_accounts2.signMessage)({
1273
+ message,
1274
+ privateKey: this.sessionKey.rawPrivateKey
1275
+ });
1276
+ const response = await fetch(`${this.baseUrl}/api/v1/agent/spawn`, {
1277
+ method: "POST",
1278
+ headers: { "Content-Type": "application/json" },
1279
+ body: JSON.stringify({
1280
+ session_key: this.sessionKey.toString(),
1281
+ signature,
1282
+ message,
1283
+ budget_usd: budgetUsd,
1284
+ name,
1285
+ allowed_merchants: allowedMerchants,
1286
+ expires_in_days: expiresInDays
1287
+ })
1288
+ });
1289
+ if (!response.ok) {
1290
+ const error = await response.json().catch(() => ({}));
1291
+ if (response.status === 409) {
1292
+ throw new MixrPayError("Concurrent modification - please retry");
1293
+ }
1294
+ if (response.status === 429) {
1295
+ throw new MixrPayError("Rate limited - too many spawn attempts");
1296
+ }
1297
+ throw new MixrPayError(error.error || `Failed to spawn child: ${response.status}`);
1298
+ }
1299
+ const data = await response.json();
1300
+ return {
1301
+ inviteCode: data.invite_code,
1302
+ inviteId: data.invite_id,
1303
+ budgetUsd: data.budget_usd,
1304
+ expiresAt: new Date(data.expires_at),
1305
+ depth: data.depth,
1306
+ maxSpawnBudget: data.max_spawn_budget,
1307
+ allowedMerchants: data.allowed_merchants || []
1308
+ };
1309
+ }
1310
+ /**
1311
+ * Get available budget information for spawning.
1312
+ *
1313
+ * Returns the current budget status including how much can be spawned
1314
+ * to child agents (20% of available).
1315
+ *
1316
+ * @returns Budget information
1317
+ *
1318
+ * @example
1319
+ * ```typescript
1320
+ * const budget = await wallet.getAvailableBudget();
1321
+ *
1322
+ * console.log(`Total budget: $${budget.totalBudget}`);
1323
+ * console.log(`Spent: $${budget.spent}`);
1324
+ * console.log(`Allocated to children: $${budget.allocatedToChildren}`);
1325
+ * console.log(`Available: $${budget.available}`);
1326
+ * console.log(`Max spawn budget: $${budget.maxSpawnBudget}`);
1327
+ *
1328
+ * if (budget.canSpawn && budget.maxSpawnBudget >= 5.00) {
1329
+ * // Safe to spawn a $5 child
1330
+ * }
1331
+ * ```
1332
+ */
1333
+ async getAvailableBudget() {
1334
+ const response = await fetch(`${this.baseUrl}/api/v1/agent/descendants`, {
1335
+ method: "GET",
1336
+ headers: {
1337
+ "Authorization": `Bearer ${this.sessionKey.toString()}`
1338
+ }
1339
+ });
1340
+ if (!response.ok) {
1341
+ const error = await response.json().catch(() => ({}));
1342
+ throw new MixrPayError(error.error || `Failed to get budget: ${response.status}`);
1343
+ }
1344
+ const data = await response.json();
1345
+ return {
1346
+ totalBudget: data.budget.total_usd,
1347
+ spent: data.budget.spent_usd,
1348
+ allocatedToChildren: data.budget.allocated_to_children_usd,
1349
+ available: data.budget.available_usd,
1350
+ maxSpawnBudget: data.max_spawn_budget,
1351
+ canSpawn: data.can_spawn
1352
+ };
1353
+ }
1354
+ /**
1355
+ * Get all child sessions spawned by this session.
1356
+ *
1357
+ * Returns a tree of child invites/sessions including their spending status.
1358
+ *
1359
+ * @returns Array of child sessions
1360
+ *
1361
+ * @example
1362
+ * ```typescript
1363
+ * const children = await wallet.getChildSessions();
1364
+ *
1365
+ * for (const child of children) {
1366
+ * console.log(`${child.name}: $${child.spentUsd}/$${child.budgetUsd}`);
1367
+ * console.log(` Status: ${child.status}`);
1368
+ * console.log(` Depth: ${child.depth}`);
1369
+ *
1370
+ * if (child.children) {
1371
+ * console.log(` Has ${child.children.length} grandchildren`);
1372
+ * }
1373
+ * }
1374
+ * ```
1375
+ */
1376
+ async getChildSessions() {
1377
+ const response = await fetch(`${this.baseUrl}/api/v1/agent/descendants`, {
1378
+ method: "GET",
1379
+ headers: {
1380
+ "Authorization": `Bearer ${this.sessionKey.toString()}`
1381
+ }
1382
+ });
1383
+ if (!response.ok) {
1384
+ const error = await response.json().catch(() => ({}));
1385
+ throw new MixrPayError(error.error || `Failed to get children: ${response.status}`);
1386
+ }
1387
+ const data = await response.json();
1388
+ return data.children || [];
1389
+ }
1390
+ // ===========================================================================
1230
1391
  // Core Methods
1231
1392
  // ===========================================================================
1232
1393
  /**
@@ -2371,6 +2532,206 @@ Timestamp: ${timestamp}`;
2371
2532
  };
2372
2533
  }
2373
2534
  // ===========================================================================
2535
+ // JIT MCP Server Methods
2536
+ // ===========================================================================
2537
+ /**
2538
+ * Deploy a JIT MCP server from the Glama directory.
2539
+ *
2540
+ * Deploys any remote-capable MCP server to Cloudflare Workers.
2541
+ * Charges $1 from your session budget.
2542
+ *
2543
+ * @param options - Deployment options including Glama server details and env vars
2544
+ * @returns Deployed instance with private endpoint URL
2545
+ *
2546
+ * @example
2547
+ * ```typescript
2548
+ * const result = await wallet.deployJitMcp({
2549
+ * glamaId: 'notion-mcp',
2550
+ * glamaNamespace: 'notion',
2551
+ * glamaSlug: 'notion-mcp',
2552
+ * toolName: 'My Notion Server',
2553
+ * envVars: { NOTION_API_KEY: 'secret_...' },
2554
+ * ttlHours: 24,
2555
+ * });
2556
+ *
2557
+ * console.log('Endpoint:', result.instance.endpointUrl);
2558
+ * console.log('Expires:', result.instance.expiresAt);
2559
+ * ```
2560
+ */
2561
+ async deployJitMcp(options) {
2562
+ this.logger.debug("deployJitMcp", { glamaId: options.glamaId, toolName: options.toolName });
2563
+ const authHeaders = await this.getSessionAuthHeaders();
2564
+ const response = await fetch(`${this.baseUrl}/api/v2/jit/deploy`, {
2565
+ method: "POST",
2566
+ headers: {
2567
+ "Content-Type": "application/json",
2568
+ ...authHeaders
2569
+ },
2570
+ body: JSON.stringify({
2571
+ glama_id: options.glamaId,
2572
+ glama_namespace: options.glamaNamespace,
2573
+ glama_slug: options.glamaSlug,
2574
+ tool_name: options.toolName,
2575
+ tool_description: options.toolDescription,
2576
+ env_vars: options.envVars,
2577
+ ttl_hours: options.ttlHours || 24
2578
+ // session_id not needed - derived from X-Session-Auth
2579
+ })
2580
+ });
2581
+ if (!response.ok) {
2582
+ const error = await response.json().catch(() => ({}));
2583
+ throw new MixrPayError(error.error || `JIT deploy failed: ${response.status}`);
2584
+ }
2585
+ const data = await response.json();
2586
+ if (data.payment?.amount_usd > 0) {
2587
+ const payment = {
2588
+ amountUsd: data.payment.amount_usd,
2589
+ recipient: "mixrpay-jit-deploy",
2590
+ txHash: data.payment.tx_hash,
2591
+ timestamp: /* @__PURE__ */ new Date(),
2592
+ description: `JIT Deploy: ${options.toolName}`,
2593
+ url: `${this.baseUrl}/api/v2/jit/deploy`,
2594
+ requestId: data.request_id || crypto.randomUUID()
2595
+ };
2596
+ this.payments.push(payment);
2597
+ this.totalSpentUsd += data.payment.amount_usd;
2598
+ this.logger.payment(data.payment.amount_usd, "jit-deploy", options.toolName);
2599
+ if (this.onPayment) {
2600
+ this.onPayment(payment);
2601
+ }
2602
+ }
2603
+ return {
2604
+ instance: this.parseJitInstance(data.instance),
2605
+ payment: {
2606
+ method: data.payment.method,
2607
+ amountUsd: data.payment.amount_usd,
2608
+ txHash: data.payment.tx_hash
2609
+ }
2610
+ };
2611
+ }
2612
+ /**
2613
+ * List your deployed JIT MCP server instances.
2614
+ *
2615
+ * @param options - Optional filters
2616
+ * @returns Array of JIT instances
2617
+ *
2618
+ * @example
2619
+ * ```typescript
2620
+ * const instances = await wallet.listJitInstances({ status: 'active' });
2621
+ * for (const inst of instances) {
2622
+ * console.log(`${inst.toolName}: ${inst.endpointUrl}`);
2623
+ * }
2624
+ * ```
2625
+ */
2626
+ async listJitInstances(options) {
2627
+ this.logger.debug("listJitInstances", options);
2628
+ const authHeaders = await this.getSessionAuthHeaders();
2629
+ const params = new URLSearchParams();
2630
+ if (options?.status) params.set("status", options.status);
2631
+ const response = await fetch(
2632
+ `${this.baseUrl}/api/v2/jit/instances?${params}`,
2633
+ { headers: authHeaders }
2634
+ );
2635
+ if (!response.ok) {
2636
+ const error = await response.json().catch(() => ({}));
2637
+ throw new MixrPayError(error.error || `Failed to list JIT instances: ${response.status}`);
2638
+ }
2639
+ const data = await response.json();
2640
+ return (data.instances || []).map((i) => this.parseJitInstance(i));
2641
+ }
2642
+ /**
2643
+ * Stop a running JIT MCP server instance.
2644
+ *
2645
+ * Instance will be marked as stopped and Worker cleaned up.
2646
+ * No refund is given - instances are billed at deploy time.
2647
+ *
2648
+ * @param instanceId - The instance ID to stop
2649
+ *
2650
+ * @example
2651
+ * ```typescript
2652
+ * await wallet.stopJitInstance('inst_abc123');
2653
+ * console.log('Instance stopped');
2654
+ * ```
2655
+ */
2656
+ async stopJitInstance(instanceId) {
2657
+ this.logger.debug("stopJitInstance", { instanceId });
2658
+ const authHeaders = await this.getSessionAuthHeaders();
2659
+ const response = await fetch(
2660
+ `${this.baseUrl}/api/v2/jit/instances/${instanceId}`,
2661
+ { method: "DELETE", headers: authHeaders }
2662
+ );
2663
+ if (!response.ok) {
2664
+ const error = await response.json().catch(() => ({}));
2665
+ throw new MixrPayError(error.error || `Failed to stop JIT instance: ${response.status}`);
2666
+ }
2667
+ }
2668
+ /**
2669
+ * Parse JIT instance response data.
2670
+ */
2671
+ parseJitInstance(data) {
2672
+ return {
2673
+ id: data.id,
2674
+ endpointUrl: data.endpoint_url,
2675
+ toolName: data.tool_name,
2676
+ glamaId: data.glama_id,
2677
+ glamaNamespace: data.glama_namespace,
2678
+ glamaSlug: data.glama_slug,
2679
+ status: data.status,
2680
+ mode: data.mode,
2681
+ ttlHours: data.ttl_hours,
2682
+ expiresAt: new Date(data.expires_at),
2683
+ createdAt: new Date(data.created_at)
2684
+ };
2685
+ }
2686
+ // ===========================================================================
2687
+ // LLM Completion Methods
2688
+ // ===========================================================================
2689
+ /**
2690
+ * Simple LLM completion - single-turn, no tools.
2691
+ *
2692
+ * This is a convenience method for agents that just need to call an LLM
2693
+ * without the full agentic loop. Useful when Clawdbot spawns agents
2694
+ * that need to make their own LLM calls through MixrPay.
2695
+ *
2696
+ * @param prompt - The user prompt to send
2697
+ * @param options - Optional configuration
2698
+ * @returns The LLM response text and cost
2699
+ *
2700
+ * @example Basic usage
2701
+ * ```typescript
2702
+ * const result = await wallet.complete('Summarize this text: ...');
2703
+ * console.log(result.text);
2704
+ * console.log(`Cost: $${result.costUsd.toFixed(4)}`);
2705
+ * ```
2706
+ *
2707
+ * @example With custom model
2708
+ * ```typescript
2709
+ * const result = await wallet.complete(
2710
+ * 'Write a haiku about coding',
2711
+ * { model: 'gpt-4o', systemPrompt: 'You are a poet.' }
2712
+ * );
2713
+ * ```
2714
+ */
2715
+ async complete(prompt, options) {
2716
+ this.logger.debug("complete", { promptLength: prompt.length, model: options?.model });
2717
+ const result = await this.runAgent({
2718
+ messages: [{ role: "user", content: prompt }],
2719
+ config: {
2720
+ model: options?.model || "gpt-4o-mini",
2721
+ maxIterations: 1,
2722
+ tools: [],
2723
+ // No tools - pure LLM completion
2724
+ systemPrompt: options?.systemPrompt
2725
+ }
2726
+ });
2727
+ return {
2728
+ text: result.response,
2729
+ costUsd: result.cost.totalUsd,
2730
+ tokens: result.tokens,
2731
+ model: options?.model || "gpt-4o-mini"
2732
+ };
2733
+ }
2734
+ // ===========================================================================
2374
2735
  // Agent Runtime API
2375
2736
  // ===========================================================================
2376
2737
  /**
@@ -2435,9 +2796,8 @@ Timestamp: ${timestamp}`;
2435
2796
  idempotencyKey,
2436
2797
  onEvent
2437
2798
  } = options;
2438
- this.logger.debug("runAgent", { sessionId, messageCount: messages.length, config, stream });
2799
+ this.logger.debug("runAgent", { sessionId: sessionId || "(from signature)", messageCount: messages.length, config, stream });
2439
2800
  const body = {
2440
- session_id: sessionId,
2441
2801
  messages: messages.map((m) => ({ role: m.role, content: m.content })),
2442
2802
  config: {
2443
2803
  model: config.model,
@@ -2448,13 +2808,17 @@ Timestamp: ${timestamp}`;
2448
2808
  stream,
2449
2809
  idempotency_key: idempotencyKey
2450
2810
  };
2811
+ if (sessionId) {
2812
+ body.session_id = sessionId;
2813
+ }
2451
2814
  const AGENT_RUN_TIMEOUT = 18e4;
2452
2815
  if (!stream) {
2816
+ const authHeaders = await this.getSessionAuthHeaders();
2453
2817
  const response = await fetch(`${this.baseUrl}/api/v2/agent/run`, {
2454
2818
  method: "POST",
2455
2819
  headers: {
2456
2820
  "Content-Type": "application/json",
2457
- "X-Mixr-Session": sessionId
2821
+ ...authHeaders
2458
2822
  },
2459
2823
  body: JSON.stringify(body),
2460
2824
  signal: AbortSignal.timeout(AGENT_RUN_TIMEOUT)
@@ -2497,18 +2861,19 @@ Timestamp: ${timestamp}`;
2497
2861
  txHash: data.tx_hash
2498
2862
  };
2499
2863
  }
2500
- return this.runAgentStreaming(sessionId, body, onEvent);
2864
+ return this.runAgentStreaming(body, onEvent);
2501
2865
  }
2502
2866
  /**
2503
2867
  * Internal: Handle streaming agent run via SSE
2504
2868
  */
2505
- async runAgentStreaming(sessionId, body, onEvent) {
2869
+ async runAgentStreaming(body, onEvent) {
2506
2870
  const STREAMING_TIMEOUT = 3e5;
2871
+ const authHeaders = await this.getSessionAuthHeaders();
2507
2872
  const response = await fetch(`${this.baseUrl}/api/v2/agent/run`, {
2508
2873
  method: "POST",
2509
2874
  headers: {
2510
2875
  "Content-Type": "application/json",
2511
- "X-Mixr-Session": sessionId
2876
+ ...authHeaders
2512
2877
  },
2513
2878
  body: JSON.stringify(body),
2514
2879
  signal: AbortSignal.timeout(STREAMING_TIMEOUT)
@@ -2655,12 +3020,11 @@ Timestamp: ${timestamp}`;
2655
3020
  * }
2656
3021
  * ```
2657
3022
  */
2658
- async getAgentRunStatus(runId, sessionId) {
2659
- this.logger.debug("getAgentRunStatus", { runId, sessionId });
3023
+ async getAgentRunStatus(runId, _sessionId) {
3024
+ this.logger.debug("getAgentRunStatus", { runId });
3025
+ const authHeaders = await this.getSessionAuthHeaders();
2660
3026
  const response = await fetch(`${this.baseUrl}/api/v2/agent/run/${runId}`, {
2661
- headers: {
2662
- "X-Mixr-Session": sessionId
2663
- }
3027
+ headers: authHeaders
2664
3028
  });
2665
3029
  if (!response.ok) {
2666
3030
  const error = await response.json().catch(() => ({}));
@@ -2759,6 +3123,724 @@ Timestamp: ${timestamp}`;
2759
3123
  latencyMs: mixrpay.latencyMs
2760
3124
  };
2761
3125
  }
3126
+ // ===========================================================================
3127
+ // Task Board Methods
3128
+ // ===========================================================================
3129
+ /**
3130
+ * Create a new task on the Task Board.
3131
+ *
3132
+ * Users (human or agent) can post tasks with budgets for other agents to complete.
3133
+ * The listing fee is charged when an agent is accepted, not at creation time.
3134
+ *
3135
+ * @param params - Task creation parameters
3136
+ * @returns The created task
3137
+ *
3138
+ * @example
3139
+ * ```typescript
3140
+ * const task = await wallet.createTask({
3141
+ * title: 'Research crypto regulations',
3142
+ * description: 'Research and summarize crypto regulations in the EU...',
3143
+ * budgetUsd: 100,
3144
+ * deliverables: ['PDF report', 'Summary document'],
3145
+ * category: 'research',
3146
+ * });
3147
+ * console.log(`Task created: ${task.id}`);
3148
+ * ```
3149
+ */
3150
+ async createTask(params) {
3151
+ this.logger.info(`Creating task: ${params.title}`);
3152
+ const response = await this.callApi("/api/v2/tasks", {
3153
+ method: "POST",
3154
+ headers: { "Content-Type": "application/json" },
3155
+ body: JSON.stringify({
3156
+ title: params.title,
3157
+ description: params.description,
3158
+ budget_usd: params.budgetUsd,
3159
+ deliverables: params.deliverables,
3160
+ category: params.category,
3161
+ expires_in_days: params.expiresInDays
3162
+ })
3163
+ });
3164
+ const result = await response.json();
3165
+ if (!response.ok) {
3166
+ throw new MixrPayError(result.error || "Failed to create task");
3167
+ }
3168
+ return this.parseTask(result.task);
3169
+ }
3170
+ /**
3171
+ * List open tasks on the Task Board.
3172
+ *
3173
+ * Browse available tasks that agents can request to work on.
3174
+ *
3175
+ * @param params - Optional filter parameters
3176
+ * @returns List of tasks with pagination
3177
+ *
3178
+ * @example
3179
+ * ```typescript
3180
+ * const { tasks, pagination } = await wallet.listTasks({
3181
+ * minBudget: 50,
3182
+ * category: 'research',
3183
+ * });
3184
+ * console.log(`Found ${pagination.total} matching tasks`);
3185
+ * ```
3186
+ */
3187
+ async listTasks(params) {
3188
+ const searchParams = new URLSearchParams();
3189
+ if (params?.status) searchParams.set("status", params.status);
3190
+ if (params?.category) searchParams.set("category", params.category);
3191
+ if (params?.minBudget !== void 0) searchParams.set("min_budget", String(params.minBudget));
3192
+ if (params?.maxBudget !== void 0) searchParams.set("max_budget", String(params.maxBudget));
3193
+ if (params?.limit !== void 0) searchParams.set("limit", String(params.limit));
3194
+ if (params?.offset !== void 0) searchParams.set("offset", String(params.offset));
3195
+ const url = `/api/v2/tasks${searchParams.toString() ? `?${searchParams}` : ""}`;
3196
+ const response = await this.callApi(url, { method: "GET" });
3197
+ const result = await response.json();
3198
+ if (!response.ok) {
3199
+ throw new MixrPayError(result.error || "Failed to list tasks");
3200
+ }
3201
+ return {
3202
+ tasks: result.tasks.map((t) => this.parseTask(t)),
3203
+ pagination: result.pagination
3204
+ };
3205
+ }
3206
+ /**
3207
+ * Get details for a specific task.
3208
+ *
3209
+ * @param taskId - The task ID
3210
+ * @returns Task details
3211
+ */
3212
+ async getTask(taskId) {
3213
+ const response = await this.callApi(`/api/v2/tasks/${taskId}`, { method: "GET" });
3214
+ const result = await response.json();
3215
+ if (!response.ok) {
3216
+ throw new MixrPayError(result.error || "Failed to get task");
3217
+ }
3218
+ return this.parseTask(result.task);
3219
+ }
3220
+ /**
3221
+ * Get tasks created by or assigned to the authenticated user.
3222
+ *
3223
+ * @param options - Filter and pagination options
3224
+ * @returns List of tasks with pagination info
3225
+ *
3226
+ * @example
3227
+ * ```typescript
3228
+ * // Get all my tasks
3229
+ * const { tasks, pagination } = await wallet.getMyTasks();
3230
+ *
3231
+ * // Get only tasks I created
3232
+ * const created = await wallet.getMyTasks({ role: 'creator' });
3233
+ *
3234
+ * // Get only tasks assigned to me
3235
+ * const assigned = await wallet.getMyTasks({ role: 'agent' });
3236
+ *
3237
+ * // Filter by status
3238
+ * const pending = await wallet.getMyTasks({ status: 'submitted' });
3239
+ * ```
3240
+ */
3241
+ async getMyTasks(options = {}) {
3242
+ const params = new URLSearchParams();
3243
+ if (options.role) params.set("role", options.role);
3244
+ if (options.status) params.set("status", options.status);
3245
+ if (options.page) params.set("page", options.page.toString());
3246
+ if (options.limit) params.set("limit", options.limit.toString());
3247
+ const queryString = params.toString();
3248
+ const url = `/api/v2/tasks/my${queryString ? `?${queryString}` : ""}`;
3249
+ const response = await this.callApi(url, { method: "GET" });
3250
+ const result = await response.json();
3251
+ if (!response.ok) {
3252
+ throw new MixrPayError(result.error || "Failed to get tasks");
3253
+ }
3254
+ return {
3255
+ tasks: result.tasks.map((t) => ({
3256
+ ...this.parseTask(t),
3257
+ isCreator: t.is_creator,
3258
+ isAgent: t.is_agent
3259
+ })),
3260
+ pagination: {
3261
+ page: result.pagination.page,
3262
+ limit: result.pagination.limit,
3263
+ total: result.pagination.total,
3264
+ totalPages: result.pagination.total_pages
3265
+ }
3266
+ };
3267
+ }
3268
+ /**
3269
+ * Request to claim a task.
3270
+ *
3271
+ * Agents use this to express interest in completing a task.
3272
+ * The task creator will review requests and accept one agent.
3273
+ *
3274
+ * @param taskId - The task ID to request
3275
+ * @param message - Optional pitch/message to the task creator
3276
+ * @returns The created request
3277
+ *
3278
+ * @example
3279
+ * ```typescript
3280
+ * const request = await wallet.requestTask('task_123',
3281
+ * 'I have experience with crypto research and can deliver within 24 hours.'
3282
+ * );
3283
+ * console.log(`Request submitted: ${request.id}`);
3284
+ * ```
3285
+ */
3286
+ async requestTask(taskId, message) {
3287
+ this.logger.info(`Requesting task: ${taskId}`);
3288
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/request`, {
3289
+ method: "POST",
3290
+ headers: { "Content-Type": "application/json" },
3291
+ body: message ? JSON.stringify({ message }) : "{}"
3292
+ });
3293
+ const result = await response.json();
3294
+ if (!response.ok) {
3295
+ throw new MixrPayError(result.error || "Failed to request task");
3296
+ }
3297
+ return {
3298
+ id: result.request.id,
3299
+ taskId: result.request.task_id,
3300
+ status: result.request.status,
3301
+ message: result.request.message,
3302
+ createdAt: new Date(result.request.created_at)
3303
+ };
3304
+ }
3305
+ /**
3306
+ * Get requests for a task you created.
3307
+ *
3308
+ * @param taskId - The task ID
3309
+ * @returns List of agent requests
3310
+ */
3311
+ async getTaskRequests(taskId) {
3312
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/requests`, { method: "GET" });
3313
+ const result = await response.json();
3314
+ if (!response.ok) {
3315
+ throw new MixrPayError(result.error || "Failed to get task requests");
3316
+ }
3317
+ return result.requests.map((r) => ({
3318
+ id: r.id,
3319
+ status: r.status,
3320
+ message: r.message,
3321
+ createdAt: new Date(r.created_at),
3322
+ reviewedAt: r.reviewed_at ? new Date(r.reviewed_at) : void 0,
3323
+ agent: r.agent
3324
+ }));
3325
+ }
3326
+ /**
3327
+ * Accept an agent's request to work on your task.
3328
+ *
3329
+ * This will:
3330
+ * - Charge the listing fee from your wallet
3331
+ * - Create a session for the agent with your task budget
3332
+ * - Mark the task as claimed
3333
+ * - Reject all other pending requests
3334
+ *
3335
+ * @param taskId - The task ID
3336
+ * @param requestId - The request ID to accept
3337
+ * @returns Acceptance result with session info
3338
+ *
3339
+ * @example
3340
+ * ```typescript
3341
+ * const result = await wallet.acceptTaskRequest('task_123', 'req_456');
3342
+ * console.log(`Agent accepted! Session budget: $${result.session.budgetUsd}`);
3343
+ * ```
3344
+ */
3345
+ async acceptTaskRequest(taskId, requestId) {
3346
+ this.logger.info(`Accepting request ${requestId} for task ${taskId}`);
3347
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/requests/${requestId}/accept`, {
3348
+ method: "POST"
3349
+ });
3350
+ const result = await response.json();
3351
+ if (!response.ok) {
3352
+ throw new MixrPayError(result.error || "Failed to accept request");
3353
+ }
3354
+ return {
3355
+ success: result.success,
3356
+ task: {
3357
+ id: result.task.id,
3358
+ status: result.task.status,
3359
+ agentUserId: result.task.agent_user_id
3360
+ },
3361
+ session: {
3362
+ id: result.session.id,
3363
+ sessionKey: result.session.session_key,
3364
+ // Agent needs this to authenticate API calls
3365
+ address: result.session.address,
3366
+ expiresAt: new Date(result.session.expires_at),
3367
+ budgetUsd: result.session.budget_usd,
3368
+ allowedMerchants: result.session.allowed_merchants || []
3369
+ },
3370
+ invite: {
3371
+ id: result.invite.id,
3372
+ code: result.invite.code
3373
+ },
3374
+ listingFeeTxHash: result.listing_fee_tx_hash
3375
+ };
3376
+ }
3377
+ /**
3378
+ * Reject an agent's request.
3379
+ *
3380
+ * @param taskId - The task ID
3381
+ * @param requestId - The request ID to reject
3382
+ */
3383
+ async rejectTaskRequest(taskId, requestId) {
3384
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/requests/${requestId}/reject`, {
3385
+ method: "POST"
3386
+ });
3387
+ const result = await response.json();
3388
+ if (!response.ok) {
3389
+ throw new MixrPayError(result.error || "Failed to reject request");
3390
+ }
3391
+ }
3392
+ /**
3393
+ * Submit deliverables for a task you're working on.
3394
+ *
3395
+ * After being accepted to work on a task, use this to submit your work
3396
+ * for the task creator's review.
3397
+ *
3398
+ * @param taskId - The task ID
3399
+ * @param output - The deliverables (text and/or URL)
3400
+ *
3401
+ * @example
3402
+ * ```typescript
3403
+ * await wallet.submitTask('task_123', {
3404
+ * text: 'Here is my research report...',
3405
+ * url: 'https://docs.google.com/document/d/...',
3406
+ * });
3407
+ * ```
3408
+ */
3409
+ async submitTask(taskId, output) {
3410
+ this.logger.info(`Submitting task: ${taskId}`);
3411
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/submit`, {
3412
+ method: "POST",
3413
+ headers: { "Content-Type": "application/json" },
3414
+ body: JSON.stringify({
3415
+ output_text: output.text,
3416
+ output_url: output.url
3417
+ })
3418
+ });
3419
+ const result = await response.json();
3420
+ if (!response.ok) {
3421
+ throw new MixrPayError(result.error || "Failed to submit task");
3422
+ }
3423
+ }
3424
+ /**
3425
+ * Approve a submitted task and pay the agent.
3426
+ *
3427
+ * This will:
3428
+ * - Calculate remaining budget (budget - spent on tools)
3429
+ * - Transfer remaining budget to the agent's wallet
3430
+ * - Mark the task as completed
3431
+ * - Revoke the agent's session
3432
+ *
3433
+ * @param taskId - The task ID
3434
+ * @returns Payout details
3435
+ *
3436
+ * @example
3437
+ * ```typescript
3438
+ * const result = await wallet.approveTask('task_123');
3439
+ * console.log(`Paid agent $${result.payout.amountUsd}`);
3440
+ * ```
3441
+ */
3442
+ async approveTask(taskId) {
3443
+ this.logger.info(`Approving task: ${taskId}`);
3444
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/approve`, {
3445
+ method: "POST"
3446
+ });
3447
+ const result = await response.json();
3448
+ if (!response.ok) {
3449
+ throw new MixrPayError(result.error || "Failed to approve task");
3450
+ }
3451
+ return {
3452
+ success: result.success,
3453
+ task: {
3454
+ id: result.task.id,
3455
+ status: result.task.status,
3456
+ completedAt: new Date(result.task.completed_at)
3457
+ },
3458
+ payout: {
3459
+ status: result.payout.status,
3460
+ amountUsd: result.payout.amount_usd,
3461
+ txHash: result.payout.tx_hash
3462
+ }
3463
+ };
3464
+ }
3465
+ /**
3466
+ * Reject a task submission and send it back for revision.
3467
+ *
3468
+ * @param taskId - The task ID
3469
+ * @param feedback - Optional feedback for the agent
3470
+ */
3471
+ async rejectTaskSubmission(taskId, feedback) {
3472
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/reject`, {
3473
+ method: "POST",
3474
+ headers: { "Content-Type": "application/json" },
3475
+ body: feedback ? JSON.stringify({ feedback }) : "{}"
3476
+ });
3477
+ const result = await response.json();
3478
+ if (!response.ok) {
3479
+ throw new MixrPayError(result.error || "Failed to reject task");
3480
+ }
3481
+ }
3482
+ /**
3483
+ * Cancel a task you created.
3484
+ *
3485
+ * Can only cancel tasks in 'open' or 'claimed' status.
3486
+ * If the task was claimed, the agent's session will be revoked.
3487
+ *
3488
+ * @param taskId - The task ID
3489
+ */
3490
+ async cancelTask(taskId) {
3491
+ const response = await this.callApi(`/api/v2/tasks/${taskId}`, {
3492
+ method: "DELETE"
3493
+ });
3494
+ const result = await response.json();
3495
+ if (!response.ok) {
3496
+ throw new MixrPayError(result.error || "Failed to cancel task");
3497
+ }
3498
+ }
3499
+ // ===========================================================================
3500
+ // Mission Control: Subtask Management
3501
+ // ===========================================================================
3502
+ /**
3503
+ * Create a subtask under an existing task.
3504
+ * Requires session key authentication.
3505
+ * Budget is allocated from the parent task's remaining budget.
3506
+ *
3507
+ * @param options - Subtask creation options
3508
+ * @returns The created subtask
3509
+ *
3510
+ * @example
3511
+ * ```typescript
3512
+ * const subtask = await wallet.createSubtask({
3513
+ * parentTaskId: 'task_123',
3514
+ * title: 'Research competitor pricing',
3515
+ * description: 'Find pricing info for top 5 competitors',
3516
+ * budgetUsd: 10,
3517
+ * });
3518
+ * ```
3519
+ */
3520
+ async createSubtask(options) {
3521
+ this.logger.info(`Creating subtask: ${options.title}`);
3522
+ const response = await this.callApi("/api/v2/tasks/create-subtask", {
3523
+ method: "POST",
3524
+ headers: { "Content-Type": "application/json" },
3525
+ body: JSON.stringify({
3526
+ parent_task_id: options.parentTaskId,
3527
+ title: options.title,
3528
+ description: options.description,
3529
+ deliverables: options.deliverables,
3530
+ category: options.category,
3531
+ budget_usd: options.budgetUsd,
3532
+ allow_sub_agents: options.allowSubAgents,
3533
+ expires_in_days: options.expiresInDays,
3534
+ idempotency_key: options.idempotencyKey
3535
+ })
3536
+ });
3537
+ const result = await response.json();
3538
+ if (!response.ok) {
3539
+ throw new MixrPayError(result.error || "Failed to create subtask");
3540
+ }
3541
+ return this.parseTask(result.task);
3542
+ }
3543
+ /**
3544
+ * Update the status of a task.
3545
+ * Requires session key authentication.
3546
+ * Creates an audit trail via TaskStatusUpdate records.
3547
+ *
3548
+ * @param taskId - The task ID
3549
+ * @param status - New status
3550
+ * @param note - Optional note explaining the status change
3551
+ * @returns The updated task
3552
+ *
3553
+ * @example
3554
+ * ```typescript
3555
+ * await wallet.updateTaskStatus('task_123', 'submitted', 'All deliverables completed');
3556
+ * ```
3557
+ */
3558
+ async updateTaskStatus(taskId, status, note, idempotencyKey) {
3559
+ this.logger.info(`Updating task ${taskId} status to ${status}`);
3560
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/status`, {
3561
+ method: "PATCH",
3562
+ headers: { "Content-Type": "application/json" },
3563
+ body: JSON.stringify({
3564
+ status,
3565
+ note,
3566
+ idempotency_key: idempotencyKey
3567
+ })
3568
+ });
3569
+ const result = await response.json();
3570
+ if (!response.ok) {
3571
+ throw new MixrPayError(result.error || "Failed to update task status");
3572
+ }
3573
+ return this.parseTask(result.task);
3574
+ }
3575
+ /**
3576
+ * Get subtasks of a task.
3577
+ * Requires session key authentication.
3578
+ *
3579
+ * @param taskId - The parent task ID
3580
+ * @returns List of subtasks
3581
+ *
3582
+ * @example
3583
+ * ```typescript
3584
+ * const subtasks = await wallet.getSubtasks('task_123');
3585
+ * console.log(`Found ${subtasks.length} subtasks`);
3586
+ * ```
3587
+ */
3588
+ async getSubtasks(taskId) {
3589
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/subtasks`, {
3590
+ method: "GET"
3591
+ });
3592
+ const result = await response.json();
3593
+ if (!response.ok) {
3594
+ throw new MixrPayError(result.error || "Failed to get subtasks");
3595
+ }
3596
+ return (result.subtasks || []).map((t) => this.parseTask(t));
3597
+ }
3598
+ /**
3599
+ * Get the status history of a task.
3600
+ * Requires session key authentication.
3601
+ *
3602
+ * @param taskId - The task ID
3603
+ * @returns Status history with audit trail
3604
+ *
3605
+ * @example
3606
+ * ```typescript
3607
+ * const history = await wallet.getTaskStatusHistory('task_123');
3608
+ * for (const entry of history.history) {
3609
+ * console.log(`${entry.oldStatus} -> ${entry.newStatus}: ${entry.note}`);
3610
+ * }
3611
+ * ```
3612
+ */
3613
+ async getTaskStatusHistory(taskId) {
3614
+ const response = await this.callApi(`/api/v2/tasks/${taskId}/status`, {
3615
+ method: "GET"
3616
+ });
3617
+ const result = await response.json();
3618
+ if (!response.ok) {
3619
+ throw new MixrPayError(result.error || "Failed to get task status history");
3620
+ }
3621
+ return {
3622
+ taskId: result.task_id,
3623
+ currentStatus: result.current_status,
3624
+ history: (result.history || []).map((h) => ({
3625
+ id: h.id,
3626
+ oldStatus: h.old_status,
3627
+ newStatus: h.new_status,
3628
+ note: h.note,
3629
+ createdAt: new Date(h.created_at)
3630
+ }))
3631
+ };
3632
+ }
3633
+ // ===========================================================================
3634
+ // Mission Control: Approval System
3635
+ // ===========================================================================
3636
+ /**
3637
+ * Request approval from a human user for an action.
3638
+ * Returns immediately if the action is auto-approved.
3639
+ *
3640
+ * @param options - Approval request options
3641
+ * @returns Approval request or auto-approval result
3642
+ *
3643
+ * @example
3644
+ * ```typescript
3645
+ * const result = await wallet.requestApproval({
3646
+ * actionType: 'external_communication',
3647
+ * actionPayload: { recipient: 'user@example.com', message: 'Hello!' },
3648
+ * context: 'Sending welcome email to new customer',
3649
+ * });
3650
+ *
3651
+ * if ('autoApproved' in result) {
3652
+ * console.log('Auto-approved:', result.reason);
3653
+ * } else {
3654
+ * console.log('Waiting for approval:', result.id);
3655
+ * }
3656
+ * ```
3657
+ */
3658
+ async requestApproval(options) {
3659
+ this.logger.info(`Requesting approval for: ${options.actionType}`);
3660
+ const response = await this.callApi("/api/v2/approvals/request", {
3661
+ method: "POST",
3662
+ headers: { "Content-Type": "application/json" },
3663
+ body: JSON.stringify({
3664
+ action_type: options.actionType,
3665
+ action_payload: options.actionPayload,
3666
+ context: options.context,
3667
+ expires_in_hours: options.expiresInHours,
3668
+ idempotency_key: options.idempotencyKey
3669
+ })
3670
+ });
3671
+ const result = await response.json();
3672
+ if (!response.ok) {
3673
+ throw new MixrPayError(result.error || "Failed to request approval");
3674
+ }
3675
+ if (result.auto_approved) {
3676
+ return {
3677
+ autoApproved: true,
3678
+ reason: result.reason
3679
+ };
3680
+ }
3681
+ return this.parseApprovalRequest(result.approval_request);
3682
+ }
3683
+ /**
3684
+ * Check the status of an approval request.
3685
+ *
3686
+ * @param requestId - The approval request ID
3687
+ * @returns Current status of the request
3688
+ *
3689
+ * @example
3690
+ * ```typescript
3691
+ * const status = await wallet.checkApprovalStatus('req_123');
3692
+ * if (status.status === 'approved') {
3693
+ * console.log('Approved! Proceeding with action...');
3694
+ * }
3695
+ * ```
3696
+ */
3697
+ async checkApprovalStatus(requestId) {
3698
+ const response = await this.callApi(`/api/v2/approvals/check?request_id=${requestId}`, {
3699
+ method: "GET"
3700
+ });
3701
+ const result = await response.json();
3702
+ if (!response.ok) {
3703
+ throw new MixrPayError(result.error || "Failed to check approval status");
3704
+ }
3705
+ return {
3706
+ requestId: result.request_id,
3707
+ status: result.status,
3708
+ responseNote: result.response_note,
3709
+ approvalScope: result.approval_scope,
3710
+ respondedAt: result.responded_at ? new Date(result.responded_at) : void 0
3711
+ };
3712
+ }
3713
+ /**
3714
+ * Check if an action type needs approval before execution.
3715
+ *
3716
+ * @param actionType - The type of action to check
3717
+ * @param amountUsd - Optional amount for spend actions
3718
+ * @returns Whether approval is needed
3719
+ *
3720
+ * @example
3721
+ * ```typescript
3722
+ * const check = await wallet.checkActionPermission('external_communication');
3723
+ * if (check.needsApproval) {
3724
+ * const approval = await wallet.requestApproval({ ... });
3725
+ * }
3726
+ * ```
3727
+ */
3728
+ async checkActionPermission(actionType, amountUsd) {
3729
+ let url = `/api/v2/approvals/check?action_type=${encodeURIComponent(actionType)}`;
3730
+ if (amountUsd !== void 0) {
3731
+ url += `&amount_usd=${amountUsd}`;
3732
+ }
3733
+ const response = await this.callApi(url, { method: "GET" });
3734
+ const result = await response.json();
3735
+ if (!response.ok) {
3736
+ throw new MixrPayError(result.error || "Failed to check action permission");
3737
+ }
3738
+ return {
3739
+ actionType: result.action_type,
3740
+ needsApproval: result.needs_approval,
3741
+ reason: result.reason
3742
+ };
3743
+ }
3744
+ /**
3745
+ * Wait for an approval request to be resolved.
3746
+ * Polls the status until approved, rejected, or expired.
3747
+ *
3748
+ * @param requestId - The approval request ID
3749
+ * @param options - Polling options
3750
+ * @returns Final status of the request
3751
+ *
3752
+ * @example
3753
+ * ```typescript
3754
+ * const approval = await wallet.requestApproval({ ... });
3755
+ * if (!('autoApproved' in approval)) {
3756
+ * const result = await wallet.waitForApproval(approval.id, { timeoutMs: 60000 });
3757
+ * if (result.status === 'approved') {
3758
+ * // Proceed with action
3759
+ * }
3760
+ * }
3761
+ * ```
3762
+ */
3763
+ async waitForApproval(requestId, options = {}) {
3764
+ const pollInterval = options.pollIntervalMs || 5e3;
3765
+ const timeout = options.timeoutMs || 3e5;
3766
+ const startTime = Date.now();
3767
+ while (Date.now() - startTime < timeout) {
3768
+ const status = await this.checkApprovalStatus(requestId);
3769
+ if (status.status !== "pending") {
3770
+ return status;
3771
+ }
3772
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
3773
+ }
3774
+ return {
3775
+ requestId,
3776
+ status: "expired"
3777
+ };
3778
+ }
3779
+ /** Helper to parse approval request from API response */
3780
+ parseApprovalRequest(r) {
3781
+ return {
3782
+ id: r.id,
3783
+ actionType: r.action_type,
3784
+ actionPayload: r.action_payload,
3785
+ context: r.context,
3786
+ status: r.status,
3787
+ responseNote: r.response_note,
3788
+ approvalScope: r.approval_scope,
3789
+ createdAt: new Date(r.created_at),
3790
+ expiresAt: new Date(r.expires_at),
3791
+ respondedAt: r.responded_at ? new Date(r.responded_at) : void 0
3792
+ };
3793
+ }
3794
+ /** Helper to parse task from API response */
3795
+ parseTask(t) {
3796
+ return {
3797
+ id: t.id,
3798
+ title: t.title,
3799
+ description: t.description,
3800
+ deliverables: t.deliverables || [],
3801
+ category: t.category,
3802
+ budgetUsd: t.budget_usd,
3803
+ listingFeeUsd: t.listing_fee_usd,
3804
+ status: t.status,
3805
+ createdAt: new Date(t.created_at),
3806
+ updatedAt: t.updated_at ? new Date(t.updated_at) : void 0,
3807
+ expiresAt: t.expires_at ? new Date(t.expires_at) : void 0,
3808
+ claimedAt: t.claimed_at ? new Date(t.claimed_at) : void 0,
3809
+ submittedAt: t.submitted_at ? new Date(t.submitted_at) : void 0,
3810
+ completedAt: t.completed_at ? new Date(t.completed_at) : void 0,
3811
+ creator: t.creator,
3812
+ assignedAgent: t.assigned_agent,
3813
+ requestCount: t.request_count,
3814
+ output: t.output,
3815
+ payment: t.payment
3816
+ };
3817
+ }
3818
+ /** Helper to call our API with auth */
3819
+ async callApi(path, init = {}) {
3820
+ const url = `${this.baseUrl}${path}`;
3821
+ const headers = {
3822
+ ...init.headers
3823
+ };
3824
+ if (this.sessionKey) {
3825
+ const account = (0, import_accounts2.privateKeyToAccount)(this.sessionKey.rawPrivateKey);
3826
+ const address = account.address.toLowerCase();
3827
+ const timestamp = Date.now();
3828
+ const message = `MixrPay:${timestamp}:${address}`;
3829
+ const signature = await (0, import_accounts2.signMessage)({
3830
+ message,
3831
+ privateKey: this.sessionKey.rawPrivateKey
3832
+ });
3833
+ headers["X-Session-Auth"] = JSON.stringify({
3834
+ address,
3835
+ timestamp,
3836
+ signature
3837
+ });
3838
+ }
3839
+ return fetch(url, {
3840
+ ...init,
3841
+ headers
3842
+ });
3843
+ }
2762
3844
  };
2763
3845
  // Annotate the CommonJS export names for ESM import in node:
2764
3846
  0 && (module.exports = {