@nookplot/runtime 0.5.144 → 0.5.146

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 (153) hide show
  1. package/dist/__tests__/apiMarketplace.test.js +189 -2
  2. package/dist/__tests__/apiMarketplace.test.js.map +1 -1
  3. package/dist/__tests__/autonomous.dedup.test.js +11 -0
  4. package/dist/__tests__/autonomous.dedup.test.js.map +1 -1
  5. package/dist/__tests__/autonomous.getAvailableActions.test.js +13 -1
  6. package/dist/__tests__/autonomous.getAvailableActions.test.js.map +1 -1
  7. package/dist/__tests__/autonomous.goalBootstrap.test.d.ts +2 -0
  8. package/dist/__tests__/autonomous.goalBootstrap.test.d.ts.map +1 -0
  9. package/dist/__tests__/autonomous.goalBootstrap.test.js +148 -0
  10. package/dist/__tests__/autonomous.goalBootstrap.test.js.map +1 -0
  11. package/dist/__tests__/autonomous.miningTrack.test.d.ts +2 -0
  12. package/dist/__tests__/autonomous.miningTrack.test.d.ts.map +1 -0
  13. package/dist/__tests__/autonomous.miningTrack.test.js +38 -0
  14. package/dist/__tests__/autonomous.miningTrack.test.js.map +1 -0
  15. package/dist/__tests__/autonomous.payApi.test.d.ts +2 -0
  16. package/dist/__tests__/autonomous.payApi.test.d.ts.map +1 -0
  17. package/dist/__tests__/autonomous.payApi.test.js +73 -0
  18. package/dist/__tests__/autonomous.payApi.test.js.map +1 -0
  19. package/dist/__tests__/autonomous.v11OpenSignals.test.js +10 -0
  20. package/dist/__tests__/autonomous.v11OpenSignals.test.js.map +1 -1
  21. package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts +2 -0
  22. package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts.map +1 -0
  23. package/dist/__tests__/autonomous.workspaceOpportunity.test.js +212 -0
  24. package/dist/__tests__/autonomous.workspaceOpportunity.test.js.map +1 -0
  25. package/dist/__tests__/bdAgentPack.test.js +1 -1
  26. package/dist/__tests__/bdAgentPack.test.js.map +1 -1
  27. package/dist/__tests__/bounties.test.d.ts +11 -0
  28. package/dist/__tests__/bounties.test.d.ts.map +1 -0
  29. package/dist/__tests__/bounties.test.js +78 -0
  30. package/dist/__tests__/bounties.test.js.map +1 -0
  31. package/dist/__tests__/codegen-drift.test.js +3 -1
  32. package/dist/__tests__/codegen-drift.test.js.map +1 -1
  33. package/dist/__tests__/conversation/modelThresholdsParity.test.js +19 -14
  34. package/dist/__tests__/conversation/modelThresholdsParity.test.js.map +1 -1
  35. package/dist/__tests__/economy.surplusBranch.test.js +64 -0
  36. package/dist/__tests__/economy.surplusBranch.test.js.map +1 -1
  37. package/dist/__tests__/goalLoop.test.d.ts +2 -0
  38. package/dist/__tests__/goalLoop.test.d.ts.map +1 -0
  39. package/dist/__tests__/goalLoop.test.js +358 -0
  40. package/dist/__tests__/goalLoop.test.js.map +1 -0
  41. package/dist/__tests__/helpers/mockRuntime.d.ts.map +1 -1
  42. package/dist/__tests__/helpers/mockRuntime.js +7 -0
  43. package/dist/__tests__/helpers/mockRuntime.js.map +1 -1
  44. package/dist/__tests__/loadProfile.test.d.ts +8 -0
  45. package/dist/__tests__/loadProfile.test.d.ts.map +1 -0
  46. package/dist/__tests__/loadProfile.test.js +134 -0
  47. package/dist/__tests__/loadProfile.test.js.map +1 -0
  48. package/dist/__tests__/mining.test.d.ts +2 -0
  49. package/dist/__tests__/mining.test.d.ts.map +1 -0
  50. package/dist/__tests__/mining.test.js +306 -0
  51. package/dist/__tests__/mining.test.js.map +1 -0
  52. package/dist/__tests__/pack.test.js +14 -14
  53. package/dist/__tests__/packLoader.test.js +4 -4
  54. package/dist/__tests__/presetLoader.test.js +42 -42
  55. package/dist/__tests__/runtimeConstructor.test.d.ts +18 -0
  56. package/dist/__tests__/runtimeConstructor.test.d.ts.map +1 -0
  57. package/dist/__tests__/runtimeConstructor.test.js +57 -0
  58. package/dist/__tests__/runtimeConstructor.test.js.map +1 -0
  59. package/dist/__tests__/sandbox.test.js +24 -24
  60. package/dist/__tests__/signalActionMap.test.d.ts +17 -0
  61. package/dist/__tests__/signalActionMap.test.d.ts.map +1 -0
  62. package/dist/__tests__/signalActionMap.test.js +165 -0
  63. package/dist/__tests__/signalActionMap.test.js.map +1 -0
  64. package/dist/__tests__/usdcBudget.test.d.ts +2 -0
  65. package/dist/__tests__/usdcBudget.test.d.ts.map +1 -0
  66. package/dist/__tests__/usdcBudget.test.js +128 -0
  67. package/dist/__tests__/usdcBudget.test.js.map +1 -0
  68. package/dist/__tests__/x402.test.d.ts +2 -0
  69. package/dist/__tests__/x402.test.d.ts.map +1 -0
  70. package/dist/__tests__/x402.test.js +117 -0
  71. package/dist/__tests__/x402.test.js.map +1 -0
  72. package/dist/actionCatalog.d.ts.map +1 -1
  73. package/dist/actionCatalog.generated.d.ts +1 -1
  74. package/dist/actionCatalog.generated.d.ts.map +1 -1
  75. package/dist/actionCatalog.generated.js +153 -28
  76. package/dist/actionCatalog.generated.js.map +1 -1
  77. package/dist/actionCatalog.js +0 -10
  78. package/dist/actionCatalog.js.map +1 -1
  79. package/dist/api-marketplace.d.ts +146 -0
  80. package/dist/api-marketplace.d.ts.map +1 -1
  81. package/dist/api-marketplace.js +218 -0
  82. package/dist/api-marketplace.js.map +1 -1
  83. package/dist/autonomous.d.ts +16 -9
  84. package/dist/autonomous.d.ts.map +1 -1
  85. package/dist/autonomous.js +278 -59
  86. package/dist/autonomous.js.map +1 -1
  87. package/dist/bounties.d.ts +21 -1
  88. package/dist/bounties.d.ts.map +1 -1
  89. package/dist/bounties.js +5 -1
  90. package/dist/bounties.js.map +1 -1
  91. package/dist/contentSafety.d.ts +1 -1
  92. package/dist/contentSafety.d.ts.map +1 -1
  93. package/dist/contentSafety.js +6 -2
  94. package/dist/contentSafety.js.map +1 -1
  95. package/dist/conversation/modelLimits.js +17 -17
  96. package/dist/discovery.js +1 -1
  97. package/dist/discovery.js.map +1 -1
  98. package/dist/economy.d.ts +10 -15
  99. package/dist/economy.d.ts.map +1 -1
  100. package/dist/economy.js +16 -29
  101. package/dist/economy.js.map +1 -1
  102. package/dist/goal/goalLoop.d.ts +78 -0
  103. package/dist/goal/goalLoop.d.ts.map +1 -0
  104. package/dist/goal/goalLoop.js +388 -0
  105. package/dist/goal/goalLoop.js.map +1 -0
  106. package/dist/goal/goalPrompts.d.ts +20 -0
  107. package/dist/goal/goalPrompts.d.ts.map +1 -0
  108. package/dist/goal/goalPrompts.js +54 -0
  109. package/dist/goal/goalPrompts.js.map +1 -0
  110. package/dist/goal/types.d.ts +102 -0
  111. package/dist/goal/types.d.ts.map +1 -0
  112. package/dist/goal/types.js +7 -0
  113. package/dist/goal/types.js.map +1 -0
  114. package/dist/identity.d.ts +51 -0
  115. package/dist/identity.d.ts.map +1 -1
  116. package/dist/identity.js +50 -0
  117. package/dist/identity.js.map +1 -1
  118. package/dist/index.d.ts +18 -3
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +27 -2
  121. package/dist/index.js.map +1 -1
  122. package/dist/loadProfile.d.ts +100 -0
  123. package/dist/loadProfile.d.ts.map +1 -0
  124. package/dist/loadProfile.js +221 -0
  125. package/dist/loadProfile.js.map +1 -0
  126. package/dist/signalActionMap.d.ts.map +1 -1
  127. package/dist/signalActionMap.js +24 -5
  128. package/dist/signalActionMap.js.map +1 -1
  129. package/dist/swarms.d.ts +13 -0
  130. package/dist/swarms.d.ts.map +1 -1
  131. package/dist/swarms.js +4 -0
  132. package/dist/swarms.js.map +1 -1
  133. package/dist/tools.js +1 -1
  134. package/dist/tools.js.map +1 -1
  135. package/dist/types.d.ts +21 -0
  136. package/dist/types.d.ts.map +1 -1
  137. package/dist/usdcBudget.d.ts +90 -0
  138. package/dist/usdcBudget.d.ts.map +1 -0
  139. package/dist/usdcBudget.js +155 -0
  140. package/dist/usdcBudget.js.map +1 -0
  141. package/dist/x402.d.ts +69 -0
  142. package/dist/x402.d.ts.map +1 -0
  143. package/dist/x402.js +139 -0
  144. package/dist/x402.js.map +1 -0
  145. package/package.json +75 -75
  146. package/dist/__tests__/economy.frontierInference.test.d.ts +0 -2
  147. package/dist/__tests__/economy.frontierInference.test.d.ts.map +0 -1
  148. package/dist/__tests__/economy.frontierInference.test.js +0 -61
  149. package/dist/__tests__/economy.frontierInference.test.js.map +0 -1
  150. package/dist/frontierPass.d.ts +0 -30
  151. package/dist/frontierPass.d.ts.map +0 -1
  152. package/dist/frontierPass.js +0 -42
  153. package/dist/frontierPass.js.map +0 -1
@@ -40,12 +40,13 @@
40
40
  * @module autonomous
41
41
  */
42
42
  import { prepareSignRelay } from "./signing.js";
43
- import { runFrontierPass } from "./frontierPass.js";
43
+ import { formatUsdc } from "./usdcBudget.js";
44
44
  import { wrapUntrusted, sanitizeForPrompt, UNTRUSTED_CONTENT_INSTRUCTION } from "./contentSafety.js";
45
45
  import { getAvailableActionsFromMap, resolveDispatchToolName, CORE_ACTIONS } from "./signalActionMap.js";
46
46
  import { resolvePackActions } from "./pack.js";
47
47
  import { getCategoryListing, getToolsInCategory } from "./actionCatalog.js";
48
48
  import { WakeUpStack } from "./wakeUpStack.js";
49
+ import { GoalLoop } from "./goal/goalLoop.js";
49
50
  import { hooks as defaultHooks } from "./hooks.js";
50
51
  import { guardrails as defaultGuardrails, GuardrailTripped, InputGuardrailTripped, } from "./guardrails.js";
51
52
  import { buildCorrectivePrompt, checkForDoomLoopFromSignatures, makeSignature, } from "./doomLoop.js";
@@ -63,7 +64,7 @@ const ON_CHAIN_ACTIONS = new Set([
63
64
  "cancel_bounty", "unclaim_bounty",
64
65
  "expire_disputed_bounty", "sweep_treasury_fees", // V8
65
66
  "sweep_creator_refund", // V9 H4 admin recovery
66
- "create_listing", "list_service", "update_service", "create_agreement",
67
+ "create_listing", "list_service", "update_service", "create_agreement", "api_onboard",
67
68
  "deliver_work", "settle_agreement", "dispute_agreement", "cancel_agreement",
68
69
  "expire_dispute", "expire_delivered",
69
70
  "deploy_preview",
@@ -101,7 +102,6 @@ const ON_CHAIN_ACTIONS = new Set([
101
102
  // roadmap §1h Decision 11. Approval-gates the submission before the gateway
102
103
  // pipes the artifact through /submit-solution → submitRlmTrajectory.
103
104
  "submit_rlm",
104
- // API marketplace on-chain actions use existing service/* prepare endpoints
105
105
  // Social (missing)
106
106
  "remove_vote", "revoke_attestation",
107
107
  // Bounty lifecycle (missing)
@@ -125,9 +125,14 @@ const ON_CHAIN_ACTIONS = new Set([
125
125
  "claim_pending_guild_mining_treasury",
126
126
  // Ecosystem partner protocols (raw-tx, agent pays own gas)
127
127
  "ecosystem_stake_tokens", "ecosystem_claim_rewards",
128
+ // Per-call x402 API purchase (client-signed EIP-3009 USDC auth, spends real
129
+ // USDC) — gated for the same reason as ecosystem_*: it moves the agent's money.
130
+ "pay_api",
128
131
  // V11: Multi-payout Open bounties (6 actions — creator + submitter + recovery)
129
132
  "create_open_bounty", "submit_open_bounty", "approve_open_submission",
130
133
  "top_up_open_bounty", "close_open_bounty", "sweep_worker_payout",
134
+ // V12 (Unit D): team-split approve + claimable pull-payment withdraw
135
+ "approve_open_submission_split", "withdraw_split_payout",
131
136
  ]);
132
137
  /**
133
138
  * Get the list of available actions for a given signal type.
@@ -267,6 +272,13 @@ export class AutonomousAgent {
267
272
  if (this.verbose) {
268
273
  console.log("[autonomous] AutonomousAgent started — handling signals + actions");
269
274
  }
275
+ // Pre-load tool categories so the LLM always has web_search +
276
+ // search_knowledge visible without a browse_tools cold-start.
277
+ // Saves ~1 LLM turn per goal-driven agent and simplifies the first
278
+ // step for reactive agents too (CLAUDE.md rule — small code change).
279
+ this.loadedCategories.add("tools");
280
+ this.loadedCategories.add("discovery");
281
+ this.loadedCategories.add("knowledge");
270
282
  // External MCP tools — loaded once at boot so mounted servers' tools
271
283
  // surface in getAvailableActions and dispatch as `mcp:<server>:<tool>`.
272
284
  this.refreshExternalMcpActions().catch((err) => {
@@ -274,6 +286,14 @@ export class AutonomousAgent {
274
286
  console.error("[autonomous] External MCP tool load failed:", err);
275
287
  }
276
288
  });
289
+ // Goal bootstrap — run the GoalLoop in background if this agent was
290
+ // forged with initial_goal set (L1 swarm auto-deploy). Failures are
291
+ // non-fatal: the agent continues in normal reactive mode.
292
+ this.maybeBootstrapGoal().catch((err) => {
293
+ if (this.verbose) {
294
+ console.error("[autonomous] Goal bootstrap failed:", err);
295
+ }
296
+ });
277
297
  }
278
298
  /** Stop the autonomous agent. */
279
299
  stop() {
@@ -283,6 +303,157 @@ export class AutonomousAgent {
283
303
  }
284
304
  }
285
305
  // ================================================================
306
+ // Goal bootstrap (L3 — migration 247)
307
+ // ================================================================
308
+ /**
309
+ * Check whether this agent has an initial_goal + pending status, and
310
+ * if so, spin up a GoalLoop in the background. Non-blocking — start()
311
+ * returns immediately so WebSocket signal subscriptions are live even
312
+ * while the goal loop is running its first step.
313
+ */
314
+ async maybeBootstrapGoal() {
315
+ let goalConfig;
316
+ try {
317
+ goalConfig = await this.runtime.identity.getGoal();
318
+ }
319
+ catch (err) {
320
+ if (this.verbose) {
321
+ console.error("[autonomous] getGoal failed — treating as no goal:", err);
322
+ }
323
+ return;
324
+ }
325
+ if (!goalConfig || !goalConfig.initialGoal)
326
+ return;
327
+ if (goalConfig.goalStatus !== "pending") {
328
+ if (this.verbose) {
329
+ console.log(`[autonomous] Skipping goal bootstrap — status is ${goalConfig.goalStatus}, not 'pending'`);
330
+ }
331
+ return;
332
+ }
333
+ if (this.verbose) {
334
+ console.log(`[autonomous] Goal bootstrap: "${goalConfig.initialGoal.slice(0, 80)}..." budget=${goalConfig.goalBudgetNook ?? "unlimited"}`);
335
+ }
336
+ // Transition status atomically before running the loop. If the
337
+ // gateway rejects (network, permissions), bail out — we do not want
338
+ // to run the loop with a mismatched DB state.
339
+ try {
340
+ await this.runtime.identity.updateGoalStatus("in_progress");
341
+ }
342
+ catch (err) {
343
+ if (this.verbose) {
344
+ console.error("[autonomous] Failed to transition goal → in_progress:", err);
345
+ }
346
+ return;
347
+ }
348
+ const budgetNook = goalConfig.goalBudgetNook ? BigInt(goalConfig.goalBudgetNook) : 0n;
349
+ const loopOptions = {
350
+ runtime: this.runtime,
351
+ goal: goalConfig.initialGoal,
352
+ budgetNook,
353
+ parentSwarmId: goalConfig.goalParentSwarmId,
354
+ verbose: this.verbose,
355
+ };
356
+ const loop = new GoalLoop(loopOptions);
357
+ let result;
358
+ try {
359
+ result = await loop.run();
360
+ }
361
+ catch (err) {
362
+ const msg = err instanceof Error ? err.message : String(err);
363
+ if (this.verbose) {
364
+ console.error("[autonomous] GoalLoop threw:", err);
365
+ }
366
+ // Transition to failed so the UI reflects the error.
367
+ await this.runtime.identity.updateGoalStatus("failed").catch(() => { });
368
+ await this.runtime.identity.createPendingTask({
369
+ reason: "unclear_goal",
370
+ description: `Goal loop crashed: ${msg.slice(0, 400)}`,
371
+ parentSwarmId: goalConfig.goalParentSwarmId,
372
+ }).catch(() => { });
373
+ return;
374
+ }
375
+ await this.handleGoalResult(result, goalConfig);
376
+ }
377
+ /**
378
+ * Dispatch on the terminal state of a GoalLoop run:
379
+ * - complete → store artifact in private KG, pause agent
380
+ * - blocked_budget → create pending task (budget_exhausted), pause
381
+ * - blocked_stuck → create pending task (stuck_3x), pause
382
+ * - blocked_capability → create pending task (needs_capability), pause
383
+ */
384
+ async handleGoalResult(result, goalConfig) {
385
+ if (result.outcome === "complete") {
386
+ // Store deliverable in private KG
387
+ let artifactId = null;
388
+ try {
389
+ const storeResult = (await this.runtime.connection.request("POST", "/v1/agents/me/knowledge", {
390
+ contentText: result.artifact.body,
391
+ title: result.artifact.title,
392
+ domain: result.artifact.domain,
393
+ visibility: "private",
394
+ knowledgeType: "fact",
395
+ sourceType: "import",
396
+ metadata: {
397
+ goal: goalConfig.initialGoal,
398
+ parentSwarmId: goalConfig.goalParentSwarmId,
399
+ stepsExecuted: result.stepsExecuted,
400
+ spentNook: result.spentNook.toString(),
401
+ },
402
+ }));
403
+ artifactId = storeResult?.id ?? null;
404
+ }
405
+ catch (err) {
406
+ if (this.verbose) {
407
+ console.error("[autonomous] Failed to store goal artifact:", err);
408
+ }
409
+ }
410
+ try {
411
+ await this.runtime.identity.completeGoal(artifactId ?? "unknown");
412
+ }
413
+ catch (err) {
414
+ if (this.verbose) {
415
+ console.error("[autonomous] completeGoal failed:", err);
416
+ }
417
+ }
418
+ // Q3: agent pauses after completion, does not stay reactive
419
+ this.stop();
420
+ return;
421
+ }
422
+ if (result.outcome === "blocked_budget") {
423
+ // initialGoal is non-null here — maybeBootstrapGoal returned early otherwise.
424
+ const goalText = goalConfig.initialGoal ?? "(unknown)";
425
+ await this.runtime.identity.createPendingTask({
426
+ reason: "budget_exhausted",
427
+ description: `Needs top-off to continue goal: ${goalText.slice(0, 300)}`,
428
+ parentSwarmId: goalConfig.goalParentSwarmId,
429
+ }).catch(() => { });
430
+ await this.runtime.identity.updateGoalStatus("paused_awaiting_topoff").catch(() => { });
431
+ this.stop();
432
+ return;
433
+ }
434
+ if (result.outcome === "blocked_stuck") {
435
+ await this.runtime.identity.createPendingTask({
436
+ reason: "stuck_3x",
437
+ description: result.stuckReason,
438
+ parentSwarmId: goalConfig.goalParentSwarmId,
439
+ }).catch(() => { });
440
+ await this.runtime.identity.updateGoalStatus("blocked_needs_decision").catch(() => { });
441
+ this.stop();
442
+ return;
443
+ }
444
+ if (result.outcome === "blocked_capability") {
445
+ await this.runtime.identity.createPendingTask({
446
+ reason: "needs_capability",
447
+ description: result.capabilityNeeded,
448
+ suggestedPresetId: result.suggestedPreset,
449
+ parentSwarmId: goalConfig.goalParentSwarmId,
450
+ }).catch(() => { });
451
+ await this.runtime.identity.updateGoalStatus("blocked_needs_decision").catch(() => { });
452
+ this.stop();
453
+ return;
454
+ }
455
+ }
456
+ // ================================================================
286
457
  // Signal handling (proactive.signal)
287
458
  // ================================================================
288
459
  /**
@@ -364,6 +535,8 @@ export class AutonomousAgent {
364
535
  return `proj_bounty_done:${data.bountyId ?? ""}`;
365
536
  case "guild_opportunity":
366
537
  return `guild:${data.guildId ?? ""}:${addr}`;
538
+ case "workspace_opportunity":
539
+ return `workspace:${data.workspaceId ?? data.sourceId ?? ""}:${addr}`;
367
540
  case "team_assembly_suggested":
368
541
  return `team_suggest:${data.txHash ?? ""}`;
369
542
  case "team_invitation":
@@ -513,9 +686,7 @@ export class AutonomousAgent {
513
686
  case "community_gap":
514
687
  await this.handleCommunityGap(data);
515
688
  break;
516
- case "directive":
517
- await this.handleDirective(data);
518
- break;
689
+ // DD-7: directive case removed — swarm coordination uses DMs
519
690
  case "files_committed":
520
691
  await this.handleFilesCommitted(data);
521
692
  break;
@@ -628,6 +799,10 @@ export class AutonomousAgent {
628
799
  case "guild_opportunity":
629
800
  await this.handleGuildOpportunity(data);
630
801
  break;
802
+ // ── Open Cognitive Workspaces (P3): discoverable/open workspace to join ──
803
+ case "workspace_opportunity":
804
+ await this.handleWorkspaceOpportunity(data);
805
+ break;
631
806
  // ── Mining signals ──
632
807
  case "mining_opportunity":
633
808
  await this.handleMiningOpportunity(data);
@@ -1154,6 +1329,59 @@ export class AutonomousAgent {
1154
1329
  console.error("[autonomous] Guild opportunity handling failed:", err);
1155
1330
  }
1156
1331
  }
1332
+ async handleWorkspaceOpportunity(data) {
1333
+ const meta = data;
1334
+ const name = meta.name ?? meta.title ?? "Unknown Workspace";
1335
+ const workspaceId = meta.workspaceId ?? meta.sourceId ?? "";
1336
+ const visibility = meta.visibility ?? "discoverable";
1337
+ const description = meta.description ?? data.messagePreview ?? "";
1338
+ const memberCount = meta.memberCount ?? 0;
1339
+ const regionCounts = meta.regionCounts ?? {};
1340
+ const openJoinRole = meta.openJoinRole ?? 0;
1341
+ const sourceType = meta.sourceType ?? "";
1342
+ const sourceRef = meta.sourceRef ?? "";
1343
+ try {
1344
+ const isOpen = visibility === "open";
1345
+ const joinNote = isOpen
1346
+ ? `You can self-join instantly (you would join as ${openJoinRole === 1 ? "editor" : "viewer"}).`
1347
+ : "You can request to join; the owner approves.";
1348
+ const regionSummary = Object.entries(regionCounts)
1349
+ .map(([r, c]) => `${r}: ${c}`)
1350
+ .join(", ") || "no cognitive state yet";
1351
+ // Unit A (A3): surface the bounty linkage so the agent knows this is a
1352
+ // team forming to compete for an open bounty, not a generic workspace.
1353
+ const bountyLine = sourceType === "bounty" && sourceRef
1354
+ ? `Bounty: this team is forming to compete for open bounty #${sanitizeForPrompt(sourceRef)}.\n`
1355
+ : "";
1356
+ const prompt = `${UNTRUSTED_CONTENT_INSTRUCTION}\n\n` +
1357
+ "A cognitive workspace opportunity was found on Nookplot.\n" +
1358
+ `Workspace: ${sanitizeForPrompt(name)}\n` +
1359
+ `Description: ${wrapUntrusted(description, "workspace description")}\n` +
1360
+ bountyLine +
1361
+ `Visibility: ${visibility}\n` +
1362
+ `Members: ${memberCount}\n` +
1363
+ `Cognitive state: ${sanitizeForPrompt(regionSummary)}\n` +
1364
+ `ID: ${workspaceId}\n` +
1365
+ `${joinNote}\n\n` +
1366
+ "Should you join this workspace to collaborate on its shared reasoning state? Respond with INTERESTED or SKIP.\n" +
1367
+ "If interested, briefly explain why (under 200 chars).\n\n" +
1368
+ "Format:\nDECISION: INTERESTED or SKIP\nREASON: why you want to join";
1369
+ const response = await this.generateResponse(prompt);
1370
+ const text = response?.trim() ?? "";
1371
+ if (text.toUpperCase().includes("INTERESTED")) {
1372
+ if (this.verbose) {
1373
+ console.log(`[autonomous] ✓ Interested in workspace "${name}" (supervised — join surfaced as an action)`);
1374
+ }
1375
+ // Joining is surfaced as an available action (join_workspace /
1376
+ // request_workspace_join) for the agent's decision loop — not
1377
+ // auto-executed here (adversarial default: no auto-join).
1378
+ }
1379
+ }
1380
+ catch (err) {
1381
+ if (this.verbose)
1382
+ console.error("[autonomous] Workspace opportunity handling failed:", err);
1383
+ }
1384
+ }
1157
1385
  async handleMiningOpportunity(data) {
1158
1386
  const meta = data;
1159
1387
  const opportunityType = meta.opportunityType ?? "unknown";
@@ -2050,40 +2278,7 @@ export class AutonomousAgent {
2050
2278
  console.error("[autonomous] Community gap handling failed:", err);
2051
2279
  }
2052
2280
  }
2053
- async handleDirective(data) {
2054
- const directiveContent = data.messagePreview ?? "";
2055
- const channelId = data.channelId;
2056
- const community = data.community ?? "general";
2057
- try {
2058
- const prompt = `${UNTRUSTED_CONTENT_INSTRUCTION}\n\n` +
2059
- "You received a directive on Nookplot.\n" +
2060
- `Directive:\n${wrapUntrusted(directiveContent, "directive")}\n\n` +
2061
- "Follow the directive and compose your response.\n" +
2062
- "If it asks you to post, write the post content.\n" +
2063
- "If it asks you to discuss, write a discussion message.\n" +
2064
- "If you can't follow this directive, respond with exactly: [SKIP]\n\n" +
2065
- "Your response (under 500 chars):";
2066
- const response = await this.generateResponse(prompt);
2067
- const content = response?.trim() ?? "";
2068
- if (content && content !== "[SKIP]") {
2069
- if (channelId) {
2070
- await this.runtime.channels.send(channelId, content);
2071
- if (this.verbose)
2072
- console.log(`[autonomous] ✓ Directive response sent to channel ${channelId.slice(0, 12)}`);
2073
- }
2074
- else {
2075
- const title = content.slice(0, 100);
2076
- await this.runtime.memory.publishKnowledge({ title, body: content, community });
2077
- if (this.verbose)
2078
- console.log(`[autonomous] ✓ Directive response posted in ${community}`);
2079
- }
2080
- }
2081
- }
2082
- catch (err) {
2083
- if (this.verbose)
2084
- console.error("[autonomous] Directive handling failed:", err);
2085
- }
2086
- }
2281
+ // DD-7: handleDirective removed — swarm coordination uses DMs exclusively
2087
2282
  // ================================================================
2088
2283
  // Project collaboration signal handlers
2089
2284
  // ================================================================
@@ -2717,16 +2912,6 @@ export class AutonomousAgent {
2717
2912
  // ================================================================
2718
2913
  // Action request handling (proactive.action.request)
2719
2914
  // ================================================================
2720
- /**
2721
- * Execute a `use_frontier_model` action — delegates to the shared
2722
- * {@link runFrontierPass} so the runtime + CLI dispatch paths can't drift on
2723
- * this money path. Consumes an owner-reserved pass, runs a one-shot Surplus
2724
- * completion paid by the agent's OWN x402 key, finalizes (or reverts on
2725
- * failure). Returns the frontier answer, or an `{ error }` tool result.
2726
- */
2727
- async executeFrontierPass(args, agentAddress) {
2728
- return runFrontierPass(this.runtime.connection, this.runtime.economy, args, agentAddress);
2729
- }
2730
2915
  async handleActionRequest(event) {
2731
2916
  if (!this.isRunning)
2732
2917
  return;
@@ -2803,6 +2988,18 @@ export class AutonomousAgent {
2803
2988
  triggers: this.doomLoopTriggers,
2804
2989
  actionType,
2805
2990
  });
2991
+ // Track C.2: also push to gateway as fire-and-forget telemetry so
2992
+ // ops dashboards can answer "which tools most often misbehave?"
2993
+ // and "is this agent stuck right now?" — see
2994
+ // gateway/src/services/doomLoopMetrics.ts. Errors are swallowed so
2995
+ // a backend outage never blocks the runtime's recovery path.
2996
+ void this.runtime.connection
2997
+ .request("POST", "/v1/agents/me/doom-loop-event", {
2998
+ offender: doomOffender,
2999
+ triggers: this.doomLoopTriggers,
3000
+ actionType,
3001
+ })
3002
+ .catch(() => { });
2806
3003
  if (this.doomLoopTriggers >= AUTONOMOUS_DOOM_LOOP_MAX_TRIGGERS) {
2807
3004
  if (this.verbose) {
2808
3005
  console.warn(`[autonomous] ✗ doom loop on '${doomOffender}' (${this.doomLoopTriggers} triggers) — aborting cycle`);
@@ -2908,21 +3105,43 @@ export class AutonomousAgent {
2908
3105
  });
2909
3106
  return;
2910
3107
  }
2911
- // ── Intercept use_frontier_model (client-executed frontier pass) ──
2912
- // Not a gateway tool: consume an owner-reserved pass, run a one-shot
2913
- // Surplus completion paid by the agent's OWN client-signed x402 key, then
2914
- // finalize (or revert on failure). The gateway can't run this
2915
- // non-custodially. See ROADMAP_frontier-passes.md.
2916
- if (actionType === "use_frontier_model") {
2917
- result = await this.executeFrontierPass(args, agentAddress);
3108
+ // ── Intercept pay_api (per-call x402 purchase) ──
3109
+ // Client-side-signed like ecosystem_*: payAndCall signs an EIP-3009 USDC
3110
+ // authorization with the agent's own key (the gateway is non-custodial), so
3111
+ // it CANNOT route through the unified dispatch. The hard USDC spend cap
3112
+ // (budgetUsdc) is enforced HERE, at the point of spend.
3113
+ if (actionType === "pay_api") {
3114
+ const privateKey = this.runtime.connection.privateKey;
3115
+ if (!privateKey)
3116
+ throw new Error("pay_api requires the agent's private key (none configured)");
3117
+ const budget = this.runtime.usdcBudget;
3118
+ const maxForCall = budget.maxForNextCall(); // bigint | undefined; 0n = exhausted
3119
+ if (maxForCall === 0n) {
3120
+ result = { paid: false, blocked: "usdc_budget_exhausted", reason: `USDC daily spend cap exhausted (spent ${formatUsdc(budget.spent)})` };
3121
+ }
3122
+ else {
3123
+ const { ethers } = await import("ethers");
3124
+ const wallet = new ethers.Wallet(privateKey);
3125
+ const payResult = await this.runtime.x402.payAndCall({
3126
+ listingId: args.listingId,
3127
+ path: String(args.path ?? ""),
3128
+ method: args.method,
3129
+ body: args.body,
3130
+ ...(maxForCall !== undefined ? { maxAmountBaseUnits: maxForCall } : {}),
3131
+ }, wallet);
3132
+ if (payResult.paid && payResult.amountPaidBaseUnits)
3133
+ budget.record(BigInt(payResult.amountPaidBaseUnits));
3134
+ result = { ...payResult };
3135
+ }
2918
3136
  result = await guardrails.runOutput(actionType, result);
2919
3137
  hooks.emitFireAndForget("tool_output", { toolName: actionType, args, result });
3138
+ // No txHash — the settlement tx is internal to the facilitator.
2920
3139
  if (actionId)
2921
3140
  await this.runtime.proactive.completeAction(actionId, undefined, result);
2922
3141
  if (this.verbose)
2923
- console.log(`[autonomous] ✓ use_frontier_model`);
3142
+ console.log(`[autonomous] ✓ pay_api listing=${String(args.listingId)} paid=${result.paid}`);
2924
3143
  hooks.emitFireAndForget("action_end", {
2925
- actionType, args, result, durationMs: Date.now() - startTime, actionId,
3144
+ actionType, args, result, durationMs: Date.now() - startTime, actionId, txHash: undefined,
2926
3145
  });
2927
3146
  return;
2928
3147
  }