@nookplot/runtime 0.5.143 → 0.5.144

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 (169) hide show
  1. package/dist/__tests__/apiMarketplace.test.js +2 -189
  2. package/dist/__tests__/apiMarketplace.test.js.map +1 -1
  3. package/dist/__tests__/autonomous.dedup.test.js +0 -11
  4. package/dist/__tests__/autonomous.dedup.test.js.map +1 -1
  5. package/dist/__tests__/autonomous.getAvailableActions.test.js +1 -13
  6. package/dist/__tests__/autonomous.getAvailableActions.test.js.map +1 -1
  7. package/dist/__tests__/bdAgentPack.test.d.ts +2 -0
  8. package/dist/__tests__/bdAgentPack.test.d.ts.map +1 -0
  9. package/dist/__tests__/bdAgentPack.test.js +44 -0
  10. package/dist/__tests__/bdAgentPack.test.js.map +1 -0
  11. package/dist/__tests__/codegen-drift.test.js +1 -3
  12. package/dist/__tests__/codegen-drift.test.js.map +1 -1
  13. package/dist/__tests__/conversation/modelThresholdsParity.test.js +14 -19
  14. package/dist/__tests__/conversation/modelThresholdsParity.test.js.map +1 -1
  15. package/dist/__tests__/economy.frontierInference.test.d.ts +2 -0
  16. package/dist/__tests__/economy.frontierInference.test.d.ts.map +1 -0
  17. package/dist/__tests__/economy.frontierInference.test.js +61 -0
  18. package/dist/__tests__/economy.frontierInference.test.js.map +1 -0
  19. package/dist/__tests__/economy.surplusBranch.test.js +0 -64
  20. package/dist/__tests__/economy.surplusBranch.test.js.map +1 -1
  21. package/dist/__tests__/externalMcpTools.test.d.ts +2 -0
  22. package/dist/__tests__/externalMcpTools.test.d.ts.map +1 -0
  23. package/dist/__tests__/externalMcpTools.test.js +94 -0
  24. package/dist/__tests__/externalMcpTools.test.js.map +1 -0
  25. package/dist/__tests__/helpers/mockRuntime.d.ts.map +1 -1
  26. package/dist/__tests__/helpers/mockRuntime.js +0 -7
  27. package/dist/__tests__/helpers/mockRuntime.js.map +1 -1
  28. package/dist/__tests__/pack.gating.test.d.ts +2 -0
  29. package/dist/__tests__/pack.gating.test.d.ts.map +1 -0
  30. package/dist/__tests__/pack.gating.test.js +134 -0
  31. package/dist/__tests__/pack.gating.test.js.map +1 -0
  32. package/dist/__tests__/pack.test.d.ts +2 -0
  33. package/dist/__tests__/pack.test.d.ts.map +1 -0
  34. package/dist/__tests__/pack.test.js +299 -0
  35. package/dist/__tests__/pack.test.js.map +1 -0
  36. package/dist/__tests__/packLoader.test.d.ts +2 -0
  37. package/dist/__tests__/packLoader.test.d.ts.map +1 -0
  38. package/dist/__tests__/packLoader.test.js +304 -0
  39. package/dist/__tests__/packLoader.test.js.map +1 -0
  40. package/dist/__tests__/presetLoader.test.js +17 -0
  41. package/dist/__tests__/presetLoader.test.js.map +1 -1
  42. package/dist/actionCatalog.d.ts.map +1 -1
  43. package/dist/actionCatalog.generated.d.ts +1 -1
  44. package/dist/actionCatalog.generated.d.ts.map +1 -1
  45. package/dist/actionCatalog.generated.js +22 -137
  46. package/dist/actionCatalog.generated.js.map +1 -1
  47. package/dist/actionCatalog.js +13 -11
  48. package/dist/actionCatalog.js.map +1 -1
  49. package/dist/api-marketplace.d.ts +0 -146
  50. package/dist/api-marketplace.d.ts.map +1 -1
  51. package/dist/api-marketplace.js +0 -218
  52. package/dist/api-marketplace.js.map +1 -1
  53. package/dist/autonomous.d.ts +33 -17
  54. package/dist/autonomous.d.ts.map +1 -1
  55. package/dist/autonomous.js +122 -273
  56. package/dist/autonomous.js.map +1 -1
  57. package/dist/contentSafety.d.ts +1 -1
  58. package/dist/contentSafety.d.ts.map +1 -1
  59. package/dist/contentSafety.js +2 -6
  60. package/dist/contentSafety.js.map +1 -1
  61. package/dist/discovery.js +1 -1
  62. package/dist/discovery.js.map +1 -1
  63. package/dist/economy.d.ts +15 -10
  64. package/dist/economy.d.ts.map +1 -1
  65. package/dist/economy.js +29 -16
  66. package/dist/economy.js.map +1 -1
  67. package/dist/frontierPass.d.ts +30 -0
  68. package/dist/frontierPass.d.ts.map +1 -0
  69. package/dist/frontierPass.js +42 -0
  70. package/dist/frontierPass.js.map +1 -0
  71. package/dist/identity.d.ts +0 -51
  72. package/dist/identity.d.ts.map +1 -1
  73. package/dist/identity.js +0 -50
  74. package/dist/identity.js.map +1 -1
  75. package/dist/index.d.ts +8 -19
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +6 -16
  78. package/dist/index.js.map +1 -1
  79. package/dist/pack.d.ts +181 -0
  80. package/dist/pack.d.ts.map +1 -0
  81. package/dist/pack.js +379 -0
  82. package/dist/pack.js.map +1 -0
  83. package/dist/packLoader.d.ts +109 -0
  84. package/dist/packLoader.d.ts.map +1 -0
  85. package/dist/packLoader.js +236 -0
  86. package/dist/packLoader.js.map +1 -0
  87. package/dist/presetLoader.d.ts +3 -1
  88. package/dist/presetLoader.d.ts.map +1 -1
  89. package/dist/presetLoader.js +7 -1
  90. package/dist/presetLoader.js.map +1 -1
  91. package/dist/signalActionMap.d.ts +17 -1
  92. package/dist/signalActionMap.d.ts.map +1 -1
  93. package/dist/signalActionMap.js +42 -17
  94. package/dist/signalActionMap.js.map +1 -1
  95. package/dist/swarms.d.ts +0 -13
  96. package/dist/swarms.d.ts.map +1 -1
  97. package/dist/swarms.js +0 -4
  98. package/dist/swarms.js.map +1 -1
  99. package/dist/tools.d.ts +23 -7
  100. package/dist/tools.d.ts.map +1 -1
  101. package/dist/tools.js +21 -7
  102. package/dist/tools.js.map +1 -1
  103. package/dist/types.d.ts +0 -21
  104. package/dist/types.d.ts.map +1 -1
  105. package/package.json +1 -1
  106. package/dist/__tests__/autonomous.goalBootstrap.test.d.ts +0 -2
  107. package/dist/__tests__/autonomous.goalBootstrap.test.d.ts.map +0 -1
  108. package/dist/__tests__/autonomous.goalBootstrap.test.js +0 -148
  109. package/dist/__tests__/autonomous.goalBootstrap.test.js.map +0 -1
  110. package/dist/__tests__/autonomous.miningTrack.test.d.ts +0 -2
  111. package/dist/__tests__/autonomous.miningTrack.test.d.ts.map +0 -1
  112. package/dist/__tests__/autonomous.miningTrack.test.js +0 -38
  113. package/dist/__tests__/autonomous.miningTrack.test.js.map +0 -1
  114. package/dist/__tests__/autonomous.payApi.test.d.ts +0 -2
  115. package/dist/__tests__/autonomous.payApi.test.d.ts.map +0 -1
  116. package/dist/__tests__/autonomous.payApi.test.js +0 -73
  117. package/dist/__tests__/autonomous.payApi.test.js.map +0 -1
  118. package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts +0 -2
  119. package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts.map +0 -1
  120. package/dist/__tests__/autonomous.workspaceOpportunity.test.js +0 -200
  121. package/dist/__tests__/autonomous.workspaceOpportunity.test.js.map +0 -1
  122. package/dist/__tests__/goalLoop.test.d.ts +0 -2
  123. package/dist/__tests__/goalLoop.test.d.ts.map +0 -1
  124. package/dist/__tests__/goalLoop.test.js +0 -358
  125. package/dist/__tests__/goalLoop.test.js.map +0 -1
  126. package/dist/__tests__/loadProfile.test.d.ts +0 -8
  127. package/dist/__tests__/loadProfile.test.d.ts.map +0 -1
  128. package/dist/__tests__/loadProfile.test.js +0 -134
  129. package/dist/__tests__/loadProfile.test.js.map +0 -1
  130. package/dist/__tests__/mining.test.d.ts +0 -2
  131. package/dist/__tests__/mining.test.d.ts.map +0 -1
  132. package/dist/__tests__/mining.test.js +0 -306
  133. package/dist/__tests__/mining.test.js.map +0 -1
  134. package/dist/__tests__/signalActionMap.test.d.ts +0 -17
  135. package/dist/__tests__/signalActionMap.test.d.ts.map +0 -1
  136. package/dist/__tests__/signalActionMap.test.js +0 -165
  137. package/dist/__tests__/signalActionMap.test.js.map +0 -1
  138. package/dist/__tests__/usdcBudget.test.d.ts +0 -2
  139. package/dist/__tests__/usdcBudget.test.d.ts.map +0 -1
  140. package/dist/__tests__/usdcBudget.test.js +0 -128
  141. package/dist/__tests__/usdcBudget.test.js.map +0 -1
  142. package/dist/__tests__/x402.test.d.ts +0 -2
  143. package/dist/__tests__/x402.test.d.ts.map +0 -1
  144. package/dist/__tests__/x402.test.js +0 -117
  145. package/dist/__tests__/x402.test.js.map +0 -1
  146. package/dist/goal/goalLoop.d.ts +0 -78
  147. package/dist/goal/goalLoop.d.ts.map +0 -1
  148. package/dist/goal/goalLoop.js +0 -388
  149. package/dist/goal/goalLoop.js.map +0 -1
  150. package/dist/goal/goalPrompts.d.ts +0 -20
  151. package/dist/goal/goalPrompts.d.ts.map +0 -1
  152. package/dist/goal/goalPrompts.js +0 -54
  153. package/dist/goal/goalPrompts.js.map +0 -1
  154. package/dist/goal/types.d.ts +0 -102
  155. package/dist/goal/types.d.ts.map +0 -1
  156. package/dist/goal/types.js +0 -7
  157. package/dist/goal/types.js.map +0 -1
  158. package/dist/loadProfile.d.ts +0 -100
  159. package/dist/loadProfile.d.ts.map +0 -1
  160. package/dist/loadProfile.js +0 -221
  161. package/dist/loadProfile.js.map +0 -1
  162. package/dist/usdcBudget.d.ts +0 -90
  163. package/dist/usdcBudget.d.ts.map +0 -1
  164. package/dist/usdcBudget.js +0 -155
  165. package/dist/usdcBudget.js.map +0 -1
  166. package/dist/x402.d.ts +0 -69
  167. package/dist/x402.d.ts.map +0 -1
  168. package/dist/x402.js +0 -139
  169. package/dist/x402.js.map +0 -1
@@ -40,12 +40,12 @@
40
40
  * @module autonomous
41
41
  */
42
42
  import { prepareSignRelay } from "./signing.js";
43
- import { formatUsdc } from "./usdcBudget.js";
43
+ import { runFrontierPass } from "./frontierPass.js";
44
44
  import { wrapUntrusted, sanitizeForPrompt, UNTRUSTED_CONTENT_INSTRUCTION } from "./contentSafety.js";
45
- import { getAvailableActionsFromMap } from "./signalActionMap.js";
45
+ import { getAvailableActionsFromMap, resolveDispatchToolName, CORE_ACTIONS } from "./signalActionMap.js";
46
+ import { resolvePackActions } from "./pack.js";
46
47
  import { getCategoryListing, getToolsInCategory } from "./actionCatalog.js";
47
48
  import { WakeUpStack } from "./wakeUpStack.js";
48
- import { GoalLoop } from "./goal/goalLoop.js";
49
49
  import { hooks as defaultHooks } from "./hooks.js";
50
50
  import { guardrails as defaultGuardrails, GuardrailTripped, InputGuardrailTripped, } from "./guardrails.js";
51
51
  import { buildCorrectivePrompt, checkForDoomLoopFromSignatures, makeSignature, } from "./doomLoop.js";
@@ -63,7 +63,7 @@ const ON_CHAIN_ACTIONS = new Set([
63
63
  "cancel_bounty", "unclaim_bounty",
64
64
  "expire_disputed_bounty", "sweep_treasury_fees", // V8
65
65
  "sweep_creator_refund", // V9 H4 admin recovery
66
- "create_listing", "list_service", "update_service", "create_agreement", "api_onboard",
66
+ "create_listing", "list_service", "update_service", "create_agreement",
67
67
  "deliver_work", "settle_agreement", "dispute_agreement", "cancel_agreement",
68
68
  "expire_dispute", "expire_delivered",
69
69
  "deploy_preview",
@@ -101,6 +101,7 @@ const ON_CHAIN_ACTIONS = new Set([
101
101
  // roadmap §1h Decision 11. Approval-gates the submission before the gateway
102
102
  // pipes the artifact through /submit-solution → submitRlmTrajectory.
103
103
  "submit_rlm",
104
+ // API marketplace on-chain actions use existing service/* prepare endpoints
104
105
  // Social (missing)
105
106
  "remove_vote", "revoke_attestation",
106
107
  // Bounty lifecycle (missing)
@@ -124,9 +125,6 @@ const ON_CHAIN_ACTIONS = new Set([
124
125
  "claim_pending_guild_mining_treasury",
125
126
  // Ecosystem partner protocols (raw-tx, agent pays own gas)
126
127
  "ecosystem_stake_tokens", "ecosystem_claim_rewards",
127
- // Per-call x402 API purchase (client-signed EIP-3009 USDC auth, spends real
128
- // USDC) — gated for the same reason as ecosystem_*: it moves the agent's money.
129
- "pay_api",
130
128
  // V11: Multi-payout Open bounties (6 actions — creator + submitter + recovery)
131
129
  "create_open_bounty", "submit_open_bounty", "approve_open_submission",
132
130
  "top_up_open_bounty", "close_open_bounty", "sweep_worker_payout",
@@ -148,8 +146,8 @@ const ON_CHAIN_ACTIONS = new Set([
148
146
  * // → "- reply: Send a text reply in the current context. Params: content (string)\n..."
149
147
  * ```
150
148
  */
151
- export function getAvailableActions(signalType, loadedCategories) {
152
- return getAvailableActionsFromMap(signalType, loadedCategories ?? new Set());
149
+ export function getAvailableActions(signalType, loadedCategories, externalActions, packActions) {
150
+ return getAvailableActionsFromMap(signalType, loadedCategories ?? new Set(), externalActions, packActions);
153
151
  }
154
152
  /**
155
153
  * Maps a `mining_opportunity` signal's optional `track` field to the
@@ -210,6 +208,29 @@ export class AutonomousAgent {
210
208
  this.approvalHandler = options.onApproval;
211
209
  this.cooldownSec = options.responseCooldown ?? 120;
212
210
  this.wakeUpStack = new WakeUpStack(runtime);
211
+ this.packActions = options.pack ? resolvePackActions(options.pack) : null;
212
+ }
213
+ /** External MCP tools (mounted servers) exposed as `mcp__<server>__<tool>` actions. */
214
+ externalMcpActions = [];
215
+ /**
216
+ * Resolved action set of the loaded pack (null = no pack, ungated).
217
+ * Set once at construction from options.pack — packs are immutable per load.
218
+ */
219
+ packActions;
220
+ /**
221
+ * Fetch the agent's mounted external MCP tools and expose them as
222
+ * available actions (ROADMAP_external-mcp-connectors Phase 1). Runs at
223
+ * start(); call again after connectMcpServer to pick up new mounts.
224
+ */
225
+ async refreshExternalMcpActions() {
226
+ const tools = await this.runtime.tools.listMcpTools();
227
+ // The gateway computes the provider-safe wire name at discovery; tools
228
+ // without one (unmappable names) are visible in listMcpTools but can
229
+ // never be dispatched, so they don't surface as actions.
230
+ this.externalMcpActions = tools
231
+ .map((t) => t.wireName)
232
+ .filter((n) => typeof n === "string" && n.startsWith("mcp__"));
233
+ return this.externalMcpActions;
213
234
  }
214
235
  /** Start listening for proactive signals and action requests. */
215
236
  start() {
@@ -246,19 +267,11 @@ export class AutonomousAgent {
246
267
  if (this.verbose) {
247
268
  console.log("[autonomous] AutonomousAgent started — handling signals + actions");
248
269
  }
249
- // Pre-load tool categories so the LLM always has web_search +
250
- // search_knowledge visible without a browse_tools cold-start.
251
- // Saves ~1 LLM turn per goal-driven agent and simplifies the first
252
- // step for reactive agents too (CLAUDE.md rule — small code change).
253
- this.loadedCategories.add("tools");
254
- this.loadedCategories.add("discovery");
255
- this.loadedCategories.add("knowledge");
256
- // Goal bootstrap — run the GoalLoop in background if this agent was
257
- // forged with initial_goal set (L1 swarm auto-deploy). Failures are
258
- // non-fatal: the agent continues in normal reactive mode.
259
- this.maybeBootstrapGoal().catch((err) => {
270
+ // External MCP tools loaded once at boot so mounted servers' tools
271
+ // surface in getAvailableActions and dispatch as `mcp:<server>:<tool>`.
272
+ this.refreshExternalMcpActions().catch((err) => {
260
273
  if (this.verbose) {
261
- console.error("[autonomous] Goal bootstrap failed:", err);
274
+ console.error("[autonomous] External MCP tool load failed:", err);
262
275
  }
263
276
  });
264
277
  }
@@ -270,157 +283,6 @@ export class AutonomousAgent {
270
283
  }
271
284
  }
272
285
  // ================================================================
273
- // Goal bootstrap (L3 — migration 247)
274
- // ================================================================
275
- /**
276
- * Check whether this agent has an initial_goal + pending status, and
277
- * if so, spin up a GoalLoop in the background. Non-blocking — start()
278
- * returns immediately so WebSocket signal subscriptions are live even
279
- * while the goal loop is running its first step.
280
- */
281
- async maybeBootstrapGoal() {
282
- let goalConfig;
283
- try {
284
- goalConfig = await this.runtime.identity.getGoal();
285
- }
286
- catch (err) {
287
- if (this.verbose) {
288
- console.error("[autonomous] getGoal failed — treating as no goal:", err);
289
- }
290
- return;
291
- }
292
- if (!goalConfig || !goalConfig.initialGoal)
293
- return;
294
- if (goalConfig.goalStatus !== "pending") {
295
- if (this.verbose) {
296
- console.log(`[autonomous] Skipping goal bootstrap — status is ${goalConfig.goalStatus}, not 'pending'`);
297
- }
298
- return;
299
- }
300
- if (this.verbose) {
301
- console.log(`[autonomous] Goal bootstrap: "${goalConfig.initialGoal.slice(0, 80)}..." budget=${goalConfig.goalBudgetNook ?? "unlimited"}`);
302
- }
303
- // Transition status atomically before running the loop. If the
304
- // gateway rejects (network, permissions), bail out — we do not want
305
- // to run the loop with a mismatched DB state.
306
- try {
307
- await this.runtime.identity.updateGoalStatus("in_progress");
308
- }
309
- catch (err) {
310
- if (this.verbose) {
311
- console.error("[autonomous] Failed to transition goal → in_progress:", err);
312
- }
313
- return;
314
- }
315
- const budgetNook = goalConfig.goalBudgetNook ? BigInt(goalConfig.goalBudgetNook) : 0n;
316
- const loopOptions = {
317
- runtime: this.runtime,
318
- goal: goalConfig.initialGoal,
319
- budgetNook,
320
- parentSwarmId: goalConfig.goalParentSwarmId,
321
- verbose: this.verbose,
322
- };
323
- const loop = new GoalLoop(loopOptions);
324
- let result;
325
- try {
326
- result = await loop.run();
327
- }
328
- catch (err) {
329
- const msg = err instanceof Error ? err.message : String(err);
330
- if (this.verbose) {
331
- console.error("[autonomous] GoalLoop threw:", err);
332
- }
333
- // Transition to failed so the UI reflects the error.
334
- await this.runtime.identity.updateGoalStatus("failed").catch(() => { });
335
- await this.runtime.identity.createPendingTask({
336
- reason: "unclear_goal",
337
- description: `Goal loop crashed: ${msg.slice(0, 400)}`,
338
- parentSwarmId: goalConfig.goalParentSwarmId,
339
- }).catch(() => { });
340
- return;
341
- }
342
- await this.handleGoalResult(result, goalConfig);
343
- }
344
- /**
345
- * Dispatch on the terminal state of a GoalLoop run:
346
- * - complete → store artifact in private KG, pause agent
347
- * - blocked_budget → create pending task (budget_exhausted), pause
348
- * - blocked_stuck → create pending task (stuck_3x), pause
349
- * - blocked_capability → create pending task (needs_capability), pause
350
- */
351
- async handleGoalResult(result, goalConfig) {
352
- if (result.outcome === "complete") {
353
- // Store deliverable in private KG
354
- let artifactId = null;
355
- try {
356
- const storeResult = (await this.runtime.connection.request("POST", "/v1/agents/me/knowledge", {
357
- contentText: result.artifact.body,
358
- title: result.artifact.title,
359
- domain: result.artifact.domain,
360
- visibility: "private",
361
- knowledgeType: "fact",
362
- sourceType: "import",
363
- metadata: {
364
- goal: goalConfig.initialGoal,
365
- parentSwarmId: goalConfig.goalParentSwarmId,
366
- stepsExecuted: result.stepsExecuted,
367
- spentNook: result.spentNook.toString(),
368
- },
369
- }));
370
- artifactId = storeResult?.id ?? null;
371
- }
372
- catch (err) {
373
- if (this.verbose) {
374
- console.error("[autonomous] Failed to store goal artifact:", err);
375
- }
376
- }
377
- try {
378
- await this.runtime.identity.completeGoal(artifactId ?? "unknown");
379
- }
380
- catch (err) {
381
- if (this.verbose) {
382
- console.error("[autonomous] completeGoal failed:", err);
383
- }
384
- }
385
- // Q3: agent pauses after completion, does not stay reactive
386
- this.stop();
387
- return;
388
- }
389
- if (result.outcome === "blocked_budget") {
390
- // initialGoal is non-null here — maybeBootstrapGoal returned early otherwise.
391
- const goalText = goalConfig.initialGoal ?? "(unknown)";
392
- await this.runtime.identity.createPendingTask({
393
- reason: "budget_exhausted",
394
- description: `Needs top-off to continue goal: ${goalText.slice(0, 300)}`,
395
- parentSwarmId: goalConfig.goalParentSwarmId,
396
- }).catch(() => { });
397
- await this.runtime.identity.updateGoalStatus("paused_awaiting_topoff").catch(() => { });
398
- this.stop();
399
- return;
400
- }
401
- if (result.outcome === "blocked_stuck") {
402
- await this.runtime.identity.createPendingTask({
403
- reason: "stuck_3x",
404
- description: result.stuckReason,
405
- parentSwarmId: goalConfig.goalParentSwarmId,
406
- }).catch(() => { });
407
- await this.runtime.identity.updateGoalStatus("blocked_needs_decision").catch(() => { });
408
- this.stop();
409
- return;
410
- }
411
- if (result.outcome === "blocked_capability") {
412
- await this.runtime.identity.createPendingTask({
413
- reason: "needs_capability",
414
- description: result.capabilityNeeded,
415
- suggestedPresetId: result.suggestedPreset,
416
- parentSwarmId: goalConfig.goalParentSwarmId,
417
- }).catch(() => { });
418
- await this.runtime.identity.updateGoalStatus("blocked_needs_decision").catch(() => { });
419
- this.stop();
420
- return;
421
- }
422
- }
423
- // ================================================================
424
286
  // Signal handling (proactive.signal)
425
287
  // ================================================================
426
288
  /**
@@ -502,8 +364,6 @@ export class AutonomousAgent {
502
364
  return `proj_bounty_done:${data.bountyId ?? ""}`;
503
365
  case "guild_opportunity":
504
366
  return `guild:${data.guildId ?? ""}:${addr}`;
505
- case "workspace_opportunity":
506
- return `workspace:${data.workspaceId ?? data.sourceId ?? ""}:${addr}`;
507
367
  case "team_assembly_suggested":
508
368
  return `team_suggest:${data.txHash ?? ""}`;
509
369
  case "team_invitation":
@@ -653,7 +513,9 @@ export class AutonomousAgent {
653
513
  case "community_gap":
654
514
  await this.handleCommunityGap(data);
655
515
  break;
656
- // DD-7: directive case removed — swarm coordination uses DMs
516
+ case "directive":
517
+ await this.handleDirective(data);
518
+ break;
657
519
  case "files_committed":
658
520
  await this.handleFilesCommitted(data);
659
521
  break;
@@ -766,10 +628,6 @@ export class AutonomousAgent {
766
628
  case "guild_opportunity":
767
629
  await this.handleGuildOpportunity(data);
768
630
  break;
769
- // ── Open Cognitive Workspaces (P3): discoverable/open workspace to join ──
770
- case "workspace_opportunity":
771
- await this.handleWorkspaceOpportunity(data);
772
- break;
773
631
  // ── Mining signals ──
774
632
  case "mining_opportunity":
775
633
  await this.handleMiningOpportunity(data);
@@ -1296,51 +1154,6 @@ export class AutonomousAgent {
1296
1154
  console.error("[autonomous] Guild opportunity handling failed:", err);
1297
1155
  }
1298
1156
  }
1299
- async handleWorkspaceOpportunity(data) {
1300
- const meta = data;
1301
- const name = meta.name ?? meta.title ?? "Unknown Workspace";
1302
- const workspaceId = meta.workspaceId ?? meta.sourceId ?? "";
1303
- const visibility = meta.visibility ?? "discoverable";
1304
- const description = meta.description ?? data.messagePreview ?? "";
1305
- const memberCount = meta.memberCount ?? 0;
1306
- const regionCounts = meta.regionCounts ?? {};
1307
- const openJoinRole = meta.openJoinRole ?? 0;
1308
- try {
1309
- const isOpen = visibility === "open";
1310
- const joinNote = isOpen
1311
- ? `You can self-join instantly (you would join as ${openJoinRole === 1 ? "editor" : "viewer"}).`
1312
- : "You can request to join; the owner approves.";
1313
- const regionSummary = Object.entries(regionCounts)
1314
- .map(([r, c]) => `${r}: ${c}`)
1315
- .join(", ") || "no cognitive state yet";
1316
- const prompt = `${UNTRUSTED_CONTENT_INSTRUCTION}\n\n` +
1317
- "A cognitive workspace opportunity was found on Nookplot.\n" +
1318
- `Workspace: ${sanitizeForPrompt(name)}\n` +
1319
- `Description: ${wrapUntrusted(description, "workspace description")}\n` +
1320
- `Visibility: ${visibility}\n` +
1321
- `Members: ${memberCount}\n` +
1322
- `Cognitive state: ${sanitizeForPrompt(regionSummary)}\n` +
1323
- `ID: ${workspaceId}\n` +
1324
- `${joinNote}\n\n` +
1325
- "Should you join this workspace to collaborate on its shared reasoning state? Respond with INTERESTED or SKIP.\n" +
1326
- "If interested, briefly explain why (under 200 chars).\n\n" +
1327
- "Format:\nDECISION: INTERESTED or SKIP\nREASON: why you want to join";
1328
- const response = await this.generateResponse(prompt);
1329
- const text = response?.trim() ?? "";
1330
- if (text.toUpperCase().includes("INTERESTED")) {
1331
- if (this.verbose) {
1332
- console.log(`[autonomous] ✓ Interested in workspace "${name}" (supervised — join surfaced as an action)`);
1333
- }
1334
- // Joining is surfaced as an available action (join_workspace /
1335
- // request_workspace_join) for the agent's decision loop — not
1336
- // auto-executed here (adversarial default: no auto-join).
1337
- }
1338
- }
1339
- catch (err) {
1340
- if (this.verbose)
1341
- console.error("[autonomous] Workspace opportunity handling failed:", err);
1342
- }
1343
- }
1344
1157
  async handleMiningOpportunity(data) {
1345
1158
  const meta = data;
1346
1159
  const opportunityType = meta.opportunityType ?? "unknown";
@@ -1625,7 +1438,11 @@ export class AutonomousAgent {
1625
1438
  console.log("[autonomous] No action parsed from response");
1626
1439
  return;
1627
1440
  }
1628
- const actionType = actionMatch[1].toLowerCase();
1441
+ // Wire names (`mcp__<server>__<tool>`) are case-sensitive registry keys —
1442
+ // lowercasing a mixed-case server/tool segment would dispatch a name that
1443
+ // doesn't exist (and trip pack gating with a misleading refusal).
1444
+ const rawAction = actionMatch[1];
1445
+ const actionType = rawAction.startsWith("mcp__") ? rawAction : rawAction.toLowerCase();
1629
1446
  let payload = {};
1630
1447
  const paramsMatch = text.match(/PARAMS:\s*(\{[\s\S]*\})/i);
1631
1448
  if (paramsMatch) {
@@ -2233,7 +2050,40 @@ export class AutonomousAgent {
2233
2050
  console.error("[autonomous] Community gap handling failed:", err);
2234
2051
  }
2235
2052
  }
2236
- // DD-7: handleDirective removed — swarm coordination uses DMs exclusively
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
+ }
2237
2087
  // ================================================================
2238
2088
  // Project collaboration signal handlers
2239
2089
  // ================================================================
@@ -2867,6 +2717,16 @@ export class AutonomousAgent {
2867
2717
  // ================================================================
2868
2718
  // Action request handling (proactive.action.request)
2869
2719
  // ================================================================
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
+ }
2870
2730
  async handleActionRequest(event) {
2871
2731
  if (!this.isRunning)
2872
2732
  return;
@@ -2882,6 +2742,29 @@ export class AutonomousAgent {
2882
2742
  if (this.verbose) {
2883
2743
  console.log(`[autonomous] Action request: ${actionType}${actionId ? ` (${actionId})` : ""}`);
2884
2744
  }
2745
+ // Pack gating (ROADMAP_external-mcp-connectors Phase 3): with a pack
2746
+ // loaded, refuse anything outside CORE ∪ pack ∪ mounted-MCP. Gated
2747
+ // prompts never offer these — this guard catches handler-injected or
2748
+ // hallucinated action types.
2749
+ if (this.packActions) {
2750
+ const allowed = actionType.startsWith("mcp__")
2751
+ ? this.externalMcpActions.includes(actionType)
2752
+ : CORE_ACTIONS.includes(actionType) || this.packActions.includes(actionType);
2753
+ if (!allowed) {
2754
+ if (this.verbose)
2755
+ console.log(`[autonomous] Action denied by pack gating: ${actionType}`);
2756
+ hooks.emitFireAndForget("action_rejected", {
2757
+ actionType, args, reason: "Not allowed by loaded pack", actionId,
2758
+ });
2759
+ if (actionId) {
2760
+ try {
2761
+ await this.runtime.proactive.rejectDelegatedAction(actionId, "Not allowed by loaded pack");
2762
+ }
2763
+ catch { /* best-effort */ }
2764
+ }
2765
+ return;
2766
+ }
2767
+ }
2885
2768
  // Approval gate: on-chain actions require approval when handler is set
2886
2769
  if (this.approvalHandler && ON_CHAIN_ACTIONS.has(actionType)) {
2887
2770
  const approved = await this.approvalHandler(actionType, args);
@@ -2920,18 +2803,6 @@ export class AutonomousAgent {
2920
2803
  triggers: this.doomLoopTriggers,
2921
2804
  actionType,
2922
2805
  });
2923
- // Track C.2: also push to gateway as fire-and-forget telemetry so
2924
- // ops dashboards can answer "which tools most often misbehave?"
2925
- // and "is this agent stuck right now?" — see
2926
- // gateway/src/services/doomLoopMetrics.ts. Errors are swallowed so
2927
- // a backend outage never blocks the runtime's recovery path.
2928
- void this.runtime.connection
2929
- .request("POST", "/v1/agents/me/doom-loop-event", {
2930
- offender: doomOffender,
2931
- triggers: this.doomLoopTriggers,
2932
- actionType,
2933
- })
2934
- .catch(() => { });
2935
2806
  if (this.doomLoopTriggers >= AUTONOMOUS_DOOM_LOOP_MAX_TRIGGERS) {
2936
2807
  if (this.verbose) {
2937
2808
  console.warn(`[autonomous] ✗ doom loop on '${doomOffender}' (${this.doomLoopTriggers} triggers) — aborting cycle`);
@@ -3037,43 +2908,21 @@ export class AutonomousAgent {
3037
2908
  });
3038
2909
  return;
3039
2910
  }
3040
- // ── Intercept pay_api (per-call x402 purchase) ──
3041
- // Client-side-signed like ecosystem_*: payAndCall signs an EIP-3009 USDC
3042
- // authorization with the agent's own key (the gateway is non-custodial), so
3043
- // it CANNOT route through the unified dispatch. The hard USDC spend cap
3044
- // (budgetUsdc) is enforced HERE, at the point of spend.
3045
- if (actionType === "pay_api") {
3046
- const privateKey = this.runtime.connection.privateKey;
3047
- if (!privateKey)
3048
- throw new Error("pay_api requires the agent's private key (none configured)");
3049
- const budget = this.runtime.usdcBudget;
3050
- const maxForCall = budget.maxForNextCall(); // bigint | undefined; 0n = exhausted
3051
- if (maxForCall === 0n) {
3052
- result = { paid: false, blocked: "usdc_budget_exhausted", reason: `USDC daily spend cap exhausted (spent ${formatUsdc(budget.spent)})` };
3053
- }
3054
- else {
3055
- const { ethers } = await import("ethers");
3056
- const wallet = new ethers.Wallet(privateKey);
3057
- const payResult = await this.runtime.x402.payAndCall({
3058
- listingId: args.listingId,
3059
- path: String(args.path ?? ""),
3060
- method: args.method,
3061
- body: args.body,
3062
- ...(maxForCall !== undefined ? { maxAmountBaseUnits: maxForCall } : {}),
3063
- }, wallet);
3064
- if (payResult.paid && payResult.amountPaidBaseUnits)
3065
- budget.record(BigInt(payResult.amountPaidBaseUnits));
3066
- result = { ...payResult };
3067
- }
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);
3068
2918
  result = await guardrails.runOutput(actionType, result);
3069
2919
  hooks.emitFireAndForget("tool_output", { toolName: actionType, args, result });
3070
- // No txHash — the settlement tx is internal to the facilitator.
3071
2920
  if (actionId)
3072
2921
  await this.runtime.proactive.completeAction(actionId, undefined, result);
3073
2922
  if (this.verbose)
3074
- console.log(`[autonomous] ✓ pay_api listing=${String(args.listingId)} paid=${result.paid}`);
2923
+ console.log(`[autonomous] ✓ use_frontier_model`);
3075
2924
  hooks.emitFireAndForget("action_end", {
3076
- actionType, args, result, durationMs: Date.now() - startTime, actionId, txHash: undefined,
2925
+ actionType, args, result, durationMs: Date.now() - startTime, actionId,
3077
2926
  });
3078
2927
  return;
3079
2928
  }
@@ -3084,7 +2933,7 @@ export class AutonomousAgent {
3084
2933
  // the original `payload` — otherwise input-guardrail mutations
3085
2934
  // (lowercase normalization, content sanitization, etc.) silently get
3086
2935
  // dropped on the way to the gateway.
3087
- const toolName = `nookplot_${actionType}`;
2936
+ const toolName = resolveDispatchToolName(actionType);
3088
2937
  const dispatchPayload = {
3089
2938
  ...args,
3090
2939
  ...(suggestedContent ? { suggestedContent } : {}),
@@ -3653,7 +3502,7 @@ export class AutonomousAgent {
3653
3502
  if (!this.generateResponse)
3654
3503
  return;
3655
3504
  try {
3656
- const actions = getAvailableActionsFromMap("swarm_subtask_available", this.loadedCategories);
3505
+ const actions = getAvailableActionsFromMap("swarm_subtask_available", this.loadedCategories, this.externalMcpActions, this.packActions);
3657
3506
  const prompt = `A swarm "${sanitizeForPrompt(swarmTitle.slice(0, 100))}" has open subtasks matching your skills.\n` +
3658
3507
  `Swarm ID: ${swarmId}\n` +
3659
3508
  `Matching skills: ${skillTags.slice(0, 10).join(", ")}\n\n` +
@@ -3681,7 +3530,7 @@ export class AutonomousAgent {
3681
3530
  if (!this.generateResponse)
3682
3531
  return;
3683
3532
  try {
3684
- const actions = getAvailableActionsFromMap("swarm_result_submitted", this.loadedCategories);
3533
+ const actions = getAvailableActionsFromMap("swarm_result_submitted", this.loadedCategories, this.externalMcpActions, this.packActions);
3685
3534
  const prompt = `A result was submitted for subtask "${sanitizeForPrompt(subtaskTitle.slice(0, 100))}" in your swarm.\n` +
3686
3535
  `Swarm ID: ${swarmId}\n` +
3687
3536
  `Submitted by: ${sanitizeForPrompt((submittedBy).slice(0, 12))}...\n\n` +
@@ -3731,7 +3580,7 @@ export class AutonomousAgent {
3731
3580
  searchResult = await this.parseAndExecuteAction(searchText);
3732
3581
  }
3733
3582
  // Step 2: Based on search results, store an insight
3734
- const actions = getAvailableActionsFromMap("dream_prompt", this.loadedCategories);
3583
+ const actions = getAvailableActionsFromMap("dream_prompt", this.loadedCategories, this.externalMcpActions, this.packActions);
3735
3584
  const searchContext = searchResult
3736
3585
  ? `\nSearch results (summarized): ${JSON.stringify(searchResult).slice(0, 1500)}\n`
3737
3586
  : "\nNo search results found — consider creating foundational knowledge for these domains.\n";