@nookplot/runtime 0.5.138 → 0.5.140

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 (115) hide show
  1. package/dist/__tests__/autonomous.dedup.test.js +11 -0
  2. package/dist/__tests__/autonomous.dedup.test.js.map +1 -1
  3. package/dist/__tests__/autonomous.getAvailableActions.test.js +13 -1
  4. package/dist/__tests__/autonomous.getAvailableActions.test.js.map +1 -1
  5. package/dist/__tests__/autonomous.goalBootstrap.test.d.ts +2 -0
  6. package/dist/__tests__/autonomous.goalBootstrap.test.d.ts.map +1 -0
  7. package/dist/__tests__/autonomous.goalBootstrap.test.js +148 -0
  8. package/dist/__tests__/autonomous.goalBootstrap.test.js.map +1 -0
  9. package/dist/__tests__/autonomous.miningTrack.test.d.ts +2 -0
  10. package/dist/__tests__/autonomous.miningTrack.test.d.ts.map +1 -0
  11. package/dist/__tests__/autonomous.miningTrack.test.js +38 -0
  12. package/dist/__tests__/autonomous.miningTrack.test.js.map +1 -0
  13. package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts +2 -0
  14. package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts.map +1 -0
  15. package/dist/__tests__/autonomous.workspaceOpportunity.test.js +200 -0
  16. package/dist/__tests__/autonomous.workspaceOpportunity.test.js.map +1 -0
  17. package/dist/__tests__/codegen-drift.test.js +3 -1
  18. package/dist/__tests__/codegen-drift.test.js.map +1 -1
  19. package/dist/__tests__/conversation/modelThresholdsParity.test.js +6 -11
  20. package/dist/__tests__/conversation/modelThresholdsParity.test.js.map +1 -1
  21. package/dist/__tests__/economy.surplusBranch.test.d.ts +17 -0
  22. package/dist/__tests__/economy.surplusBranch.test.d.ts.map +1 -0
  23. package/dist/__tests__/economy.surplusBranch.test.js +95 -0
  24. package/dist/__tests__/economy.surplusBranch.test.js.map +1 -0
  25. package/dist/__tests__/goalLoop.test.d.ts +2 -0
  26. package/dist/__tests__/goalLoop.test.d.ts.map +1 -0
  27. package/dist/__tests__/goalLoop.test.js +335 -0
  28. package/dist/__tests__/goalLoop.test.js.map +1 -0
  29. package/dist/__tests__/helpers/mockRuntime.d.ts.map +1 -1
  30. package/dist/__tests__/helpers/mockRuntime.js +7 -0
  31. package/dist/__tests__/helpers/mockRuntime.js.map +1 -1
  32. package/dist/__tests__/loadProfile.test.d.ts +8 -0
  33. package/dist/__tests__/loadProfile.test.d.ts.map +1 -0
  34. package/dist/__tests__/loadProfile.test.js +134 -0
  35. package/dist/__tests__/loadProfile.test.js.map +1 -0
  36. package/dist/__tests__/mining.test.d.ts +2 -0
  37. package/dist/__tests__/mining.test.d.ts.map +1 -0
  38. package/dist/__tests__/mining.test.js +306 -0
  39. package/dist/__tests__/mining.test.js.map +1 -0
  40. package/dist/__tests__/presetLoader.test.d.ts +2 -0
  41. package/dist/__tests__/presetLoader.test.d.ts.map +1 -0
  42. package/dist/__tests__/presetLoader.test.js +749 -0
  43. package/dist/__tests__/presetLoader.test.js.map +1 -0
  44. package/dist/__tests__/sandbox.test.js +24 -24
  45. package/dist/__tests__/surplusInference.test.d.ts +11 -0
  46. package/dist/__tests__/surplusInference.test.d.ts.map +1 -0
  47. package/dist/__tests__/surplusInference.test.js +346 -0
  48. package/dist/__tests__/surplusInference.test.js.map +1 -0
  49. package/dist/actionCatalog.generated.d.ts +1 -1
  50. package/dist/actionCatalog.generated.d.ts.map +1 -1
  51. package/dist/actionCatalog.generated.js +168 -19
  52. package/dist/actionCatalog.generated.js.map +1 -1
  53. package/dist/autonomous.d.ts +16 -1
  54. package/dist/autonomous.d.ts.map +1 -1
  55. package/dist/autonomous.js +232 -37
  56. package/dist/autonomous.js.map +1 -1
  57. package/dist/connection.d.ts +5 -1
  58. package/dist/connection.d.ts.map +1 -1
  59. package/dist/connection.js +4 -0
  60. package/dist/connection.js.map +1 -1
  61. package/dist/contentSafety.d.ts +1 -1
  62. package/dist/contentSafety.d.ts.map +1 -1
  63. package/dist/contentSafety.js +6 -2
  64. package/dist/contentSafety.js.map +1 -1
  65. package/dist/conversation/modelLimits.js +17 -17
  66. package/dist/discovery.js +1 -1
  67. package/dist/discovery.js.map +1 -1
  68. package/dist/economy.d.ts +19 -1
  69. package/dist/economy.d.ts.map +1 -1
  70. package/dist/economy.js +35 -1
  71. package/dist/economy.js.map +1 -1
  72. package/dist/goal/goalLoop.d.ts +78 -0
  73. package/dist/goal/goalLoop.d.ts.map +1 -0
  74. package/dist/goal/goalLoop.js +376 -0
  75. package/dist/goal/goalLoop.js.map +1 -0
  76. package/dist/goal/goalPrompts.d.ts +20 -0
  77. package/dist/goal/goalPrompts.d.ts.map +1 -0
  78. package/dist/goal/goalPrompts.js +54 -0
  79. package/dist/goal/goalPrompts.js.map +1 -0
  80. package/dist/goal/types.d.ts +98 -0
  81. package/dist/goal/types.d.ts.map +1 -0
  82. package/dist/goal/types.js +7 -0
  83. package/dist/goal/types.js.map +1 -0
  84. package/dist/identity.d.ts +51 -0
  85. package/dist/identity.d.ts.map +1 -1
  86. package/dist/identity.js +50 -0
  87. package/dist/identity.js.map +1 -1
  88. package/dist/index.d.ts +12 -2
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +6 -0
  91. package/dist/index.js.map +1 -1
  92. package/dist/inference/surplusInference.d.ts +171 -0
  93. package/dist/inference/surplusInference.d.ts.map +1 -0
  94. package/dist/inference/surplusInference.js +455 -0
  95. package/dist/inference/surplusInference.js.map +1 -0
  96. package/dist/loadProfile.d.ts +100 -0
  97. package/dist/loadProfile.d.ts.map +1 -0
  98. package/dist/loadProfile.js +221 -0
  99. package/dist/loadProfile.js.map +1 -0
  100. package/dist/presetLoader.d.ts +130 -0
  101. package/dist/presetLoader.d.ts.map +1 -0
  102. package/dist/presetLoader.js +734 -0
  103. package/dist/presetLoader.js.map +1 -0
  104. package/dist/signalActionMap.d.ts.map +1 -1
  105. package/dist/signalActionMap.js +9 -0
  106. package/dist/signalActionMap.js.map +1 -1
  107. package/dist/swarms.d.ts +13 -0
  108. package/dist/swarms.d.ts.map +1 -1
  109. package/dist/swarms.js +4 -0
  110. package/dist/swarms.js.map +1 -1
  111. package/dist/tools.js +1 -1
  112. package/dist/tools.js.map +1 -1
  113. package/dist/types.d.ts +50 -0
  114. package/dist/types.d.ts.map +1 -1
  115. package/package.json +17 -2
@@ -44,6 +44,7 @@ import { wrapUntrusted, sanitizeForPrompt, UNTRUSTED_CONTENT_INSTRUCTION } from
44
44
  import { getAvailableActionsFromMap } from "./signalActionMap.js";
45
45
  import { getCategoryListing, getToolsInCategory } from "./actionCatalog.js";
46
46
  import { WakeUpStack } from "./wakeUpStack.js";
47
+ import { GoalLoop } from "./goal/goalLoop.js";
47
48
  import { hooks as defaultHooks } from "./hooks.js";
48
49
  import { guardrails as defaultGuardrails, GuardrailTripped, InputGuardrailTripped, } from "./guardrails.js";
49
50
  import { buildCorrectivePrompt, checkForDoomLoopFromSignatures, makeSignature, } from "./doomLoop.js";
@@ -242,6 +243,21 @@ export class AutonomousAgent {
242
243
  if (this.verbose) {
243
244
  console.log("[autonomous] AutonomousAgent started — handling signals + actions");
244
245
  }
246
+ // Pre-load tool categories so the LLM always has web_search +
247
+ // search_knowledge visible without a browse_tools cold-start.
248
+ // Saves ~1 LLM turn per goal-driven agent and simplifies the first
249
+ // step for reactive agents too (CLAUDE.md rule — small code change).
250
+ this.loadedCategories.add("tools");
251
+ this.loadedCategories.add("discovery");
252
+ this.loadedCategories.add("knowledge");
253
+ // Goal bootstrap — run the GoalLoop in background if this agent was
254
+ // forged with initial_goal set (L1 swarm auto-deploy). Failures are
255
+ // non-fatal: the agent continues in normal reactive mode.
256
+ this.maybeBootstrapGoal().catch((err) => {
257
+ if (this.verbose) {
258
+ console.error("[autonomous] Goal bootstrap failed:", err);
259
+ }
260
+ });
245
261
  }
246
262
  /** Stop the autonomous agent. */
247
263
  stop() {
@@ -251,6 +267,157 @@ export class AutonomousAgent {
251
267
  }
252
268
  }
253
269
  // ================================================================
270
+ // Goal bootstrap (L3 — migration 247)
271
+ // ================================================================
272
+ /**
273
+ * Check whether this agent has an initial_goal + pending status, and
274
+ * if so, spin up a GoalLoop in the background. Non-blocking — start()
275
+ * returns immediately so WebSocket signal subscriptions are live even
276
+ * while the goal loop is running its first step.
277
+ */
278
+ async maybeBootstrapGoal() {
279
+ let goalConfig;
280
+ try {
281
+ goalConfig = await this.runtime.identity.getGoal();
282
+ }
283
+ catch (err) {
284
+ if (this.verbose) {
285
+ console.error("[autonomous] getGoal failed — treating as no goal:", err);
286
+ }
287
+ return;
288
+ }
289
+ if (!goalConfig || !goalConfig.initialGoal)
290
+ return;
291
+ if (goalConfig.goalStatus !== "pending") {
292
+ if (this.verbose) {
293
+ console.log(`[autonomous] Skipping goal bootstrap — status is ${goalConfig.goalStatus}, not 'pending'`);
294
+ }
295
+ return;
296
+ }
297
+ if (this.verbose) {
298
+ console.log(`[autonomous] Goal bootstrap: "${goalConfig.initialGoal.slice(0, 80)}..." budget=${goalConfig.goalBudgetNook ?? "unlimited"}`);
299
+ }
300
+ // Transition status atomically before running the loop. If the
301
+ // gateway rejects (network, permissions), bail out — we do not want
302
+ // to run the loop with a mismatched DB state.
303
+ try {
304
+ await this.runtime.identity.updateGoalStatus("in_progress");
305
+ }
306
+ catch (err) {
307
+ if (this.verbose) {
308
+ console.error("[autonomous] Failed to transition goal → in_progress:", err);
309
+ }
310
+ return;
311
+ }
312
+ const budgetNook = goalConfig.goalBudgetNook ? BigInt(goalConfig.goalBudgetNook) : 0n;
313
+ const loopOptions = {
314
+ runtime: this.runtime,
315
+ goal: goalConfig.initialGoal,
316
+ budgetNook,
317
+ parentSwarmId: goalConfig.goalParentSwarmId,
318
+ verbose: this.verbose,
319
+ };
320
+ const loop = new GoalLoop(loopOptions);
321
+ let result;
322
+ try {
323
+ result = await loop.run();
324
+ }
325
+ catch (err) {
326
+ const msg = err instanceof Error ? err.message : String(err);
327
+ if (this.verbose) {
328
+ console.error("[autonomous] GoalLoop threw:", err);
329
+ }
330
+ // Transition to failed so the UI reflects the error.
331
+ await this.runtime.identity.updateGoalStatus("failed").catch(() => { });
332
+ await this.runtime.identity.createPendingTask({
333
+ reason: "unclear_goal",
334
+ description: `Goal loop crashed: ${msg.slice(0, 400)}`,
335
+ parentSwarmId: goalConfig.goalParentSwarmId,
336
+ }).catch(() => { });
337
+ return;
338
+ }
339
+ await this.handleGoalResult(result, goalConfig);
340
+ }
341
+ /**
342
+ * Dispatch on the terminal state of a GoalLoop run:
343
+ * - complete → store artifact in private KG, pause agent
344
+ * - blocked_budget → create pending task (budget_exhausted), pause
345
+ * - blocked_stuck → create pending task (stuck_3x), pause
346
+ * - blocked_capability → create pending task (needs_capability), pause
347
+ */
348
+ async handleGoalResult(result, goalConfig) {
349
+ if (result.outcome === "complete") {
350
+ // Store deliverable in private KG
351
+ let artifactId = null;
352
+ try {
353
+ const storeResult = (await this.runtime.connection.request("POST", "/v1/agents/me/knowledge", {
354
+ contentText: result.artifact.body,
355
+ title: result.artifact.title,
356
+ domain: result.artifact.domain,
357
+ visibility: "private",
358
+ knowledgeType: "fact",
359
+ sourceType: "import",
360
+ metadata: {
361
+ goal: goalConfig.initialGoal,
362
+ parentSwarmId: goalConfig.goalParentSwarmId,
363
+ stepsExecuted: result.stepsExecuted,
364
+ spentNook: result.spentNook.toString(),
365
+ },
366
+ }));
367
+ artifactId = storeResult?.id ?? null;
368
+ }
369
+ catch (err) {
370
+ if (this.verbose) {
371
+ console.error("[autonomous] Failed to store goal artifact:", err);
372
+ }
373
+ }
374
+ try {
375
+ await this.runtime.identity.completeGoal(artifactId ?? "unknown");
376
+ }
377
+ catch (err) {
378
+ if (this.verbose) {
379
+ console.error("[autonomous] completeGoal failed:", err);
380
+ }
381
+ }
382
+ // Q3: agent pauses after completion, does not stay reactive
383
+ this.stop();
384
+ return;
385
+ }
386
+ if (result.outcome === "blocked_budget") {
387
+ // initialGoal is non-null here — maybeBootstrapGoal returned early otherwise.
388
+ const goalText = goalConfig.initialGoal ?? "(unknown)";
389
+ await this.runtime.identity.createPendingTask({
390
+ reason: "budget_exhausted",
391
+ description: `Needs top-off to continue goal: ${goalText.slice(0, 300)}`,
392
+ parentSwarmId: goalConfig.goalParentSwarmId,
393
+ }).catch(() => { });
394
+ await this.runtime.identity.updateGoalStatus("paused_awaiting_topoff").catch(() => { });
395
+ this.stop();
396
+ return;
397
+ }
398
+ if (result.outcome === "blocked_stuck") {
399
+ await this.runtime.identity.createPendingTask({
400
+ reason: "stuck_3x",
401
+ description: result.stuckReason,
402
+ parentSwarmId: goalConfig.goalParentSwarmId,
403
+ }).catch(() => { });
404
+ await this.runtime.identity.updateGoalStatus("blocked_needs_decision").catch(() => { });
405
+ this.stop();
406
+ return;
407
+ }
408
+ if (result.outcome === "blocked_capability") {
409
+ await this.runtime.identity.createPendingTask({
410
+ reason: "needs_capability",
411
+ description: result.capabilityNeeded,
412
+ suggestedPresetId: result.suggestedPreset,
413
+ parentSwarmId: goalConfig.goalParentSwarmId,
414
+ }).catch(() => { });
415
+ await this.runtime.identity.updateGoalStatus("blocked_needs_decision").catch(() => { });
416
+ this.stop();
417
+ return;
418
+ }
419
+ }
420
+ // ================================================================
254
421
  // Signal handling (proactive.signal)
255
422
  // ================================================================
256
423
  /**
@@ -332,6 +499,8 @@ export class AutonomousAgent {
332
499
  return `proj_bounty_done:${data.bountyId ?? ""}`;
333
500
  case "guild_opportunity":
334
501
  return `guild:${data.guildId ?? ""}:${addr}`;
502
+ case "workspace_opportunity":
503
+ return `workspace:${data.workspaceId ?? data.sourceId ?? ""}:${addr}`;
335
504
  case "team_assembly_suggested":
336
505
  return `team_suggest:${data.txHash ?? ""}`;
337
506
  case "team_invitation":
@@ -481,9 +650,7 @@ export class AutonomousAgent {
481
650
  case "community_gap":
482
651
  await this.handleCommunityGap(data);
483
652
  break;
484
- case "directive":
485
- await this.handleDirective(data);
486
- break;
653
+ // DD-7: directive case removed — swarm coordination uses DMs
487
654
  case "files_committed":
488
655
  await this.handleFilesCommitted(data);
489
656
  break;
@@ -596,6 +763,10 @@ export class AutonomousAgent {
596
763
  case "guild_opportunity":
597
764
  await this.handleGuildOpportunity(data);
598
765
  break;
766
+ // ── Open Cognitive Workspaces (P3): discoverable/open workspace to join ──
767
+ case "workspace_opportunity":
768
+ await this.handleWorkspaceOpportunity(data);
769
+ break;
599
770
  // ── Mining signals ──
600
771
  case "mining_opportunity":
601
772
  await this.handleMiningOpportunity(data);
@@ -1122,6 +1293,51 @@ export class AutonomousAgent {
1122
1293
  console.error("[autonomous] Guild opportunity handling failed:", err);
1123
1294
  }
1124
1295
  }
1296
+ async handleWorkspaceOpportunity(data) {
1297
+ const meta = data;
1298
+ const name = meta.name ?? meta.title ?? "Unknown Workspace";
1299
+ const workspaceId = meta.workspaceId ?? meta.sourceId ?? "";
1300
+ const visibility = meta.visibility ?? "discoverable";
1301
+ const description = meta.description ?? data.messagePreview ?? "";
1302
+ const memberCount = meta.memberCount ?? 0;
1303
+ const regionCounts = meta.regionCounts ?? {};
1304
+ const openJoinRole = meta.openJoinRole ?? 0;
1305
+ try {
1306
+ const isOpen = visibility === "open";
1307
+ const joinNote = isOpen
1308
+ ? `You can self-join instantly (you would join as ${openJoinRole === 1 ? "editor" : "viewer"}).`
1309
+ : "You can request to join; the owner approves.";
1310
+ const regionSummary = Object.entries(regionCounts)
1311
+ .map(([r, c]) => `${r}: ${c}`)
1312
+ .join(", ") || "no cognitive state yet";
1313
+ const prompt = `${UNTRUSTED_CONTENT_INSTRUCTION}\n\n` +
1314
+ "A cognitive workspace opportunity was found on Nookplot.\n" +
1315
+ `Workspace: ${sanitizeForPrompt(name)}\n` +
1316
+ `Description: ${wrapUntrusted(description, "workspace description")}\n` +
1317
+ `Visibility: ${visibility}\n` +
1318
+ `Members: ${memberCount}\n` +
1319
+ `Cognitive state: ${sanitizeForPrompt(regionSummary)}\n` +
1320
+ `ID: ${workspaceId}\n` +
1321
+ `${joinNote}\n\n` +
1322
+ "Should you join this workspace to collaborate on its shared reasoning state? Respond with INTERESTED or SKIP.\n" +
1323
+ "If interested, briefly explain why (under 200 chars).\n\n" +
1324
+ "Format:\nDECISION: INTERESTED or SKIP\nREASON: why you want to join";
1325
+ const response = await this.generateResponse(prompt);
1326
+ const text = response?.trim() ?? "";
1327
+ if (text.toUpperCase().includes("INTERESTED")) {
1328
+ if (this.verbose) {
1329
+ console.log(`[autonomous] ✓ Interested in workspace "${name}" (supervised — join surfaced as an action)`);
1330
+ }
1331
+ // Joining is surfaced as an available action (join_workspace /
1332
+ // request_workspace_join) for the agent's decision loop — not
1333
+ // auto-executed here (adversarial default: no auto-join).
1334
+ }
1335
+ }
1336
+ catch (err) {
1337
+ if (this.verbose)
1338
+ console.error("[autonomous] Workspace opportunity handling failed:", err);
1339
+ }
1340
+ }
1125
1341
  async handleMiningOpportunity(data) {
1126
1342
  const meta = data;
1127
1343
  const opportunityType = meta.opportunityType ?? "unknown";
@@ -2014,40 +2230,7 @@ export class AutonomousAgent {
2014
2230
  console.error("[autonomous] Community gap handling failed:", err);
2015
2231
  }
2016
2232
  }
2017
- async handleDirective(data) {
2018
- const directiveContent = data.messagePreview ?? "";
2019
- const channelId = data.channelId;
2020
- const community = data.community ?? "general";
2021
- try {
2022
- const prompt = `${UNTRUSTED_CONTENT_INSTRUCTION}\n\n` +
2023
- "You received a directive on Nookplot.\n" +
2024
- `Directive:\n${wrapUntrusted(directiveContent, "directive")}\n\n` +
2025
- "Follow the directive and compose your response.\n" +
2026
- "If it asks you to post, write the post content.\n" +
2027
- "If it asks you to discuss, write a discussion message.\n" +
2028
- "If you can't follow this directive, respond with exactly: [SKIP]\n\n" +
2029
- "Your response (under 500 chars):";
2030
- const response = await this.generateResponse(prompt);
2031
- const content = response?.trim() ?? "";
2032
- if (content && content !== "[SKIP]") {
2033
- if (channelId) {
2034
- await this.runtime.channels.send(channelId, content);
2035
- if (this.verbose)
2036
- console.log(`[autonomous] ✓ Directive response sent to channel ${channelId.slice(0, 12)}`);
2037
- }
2038
- else {
2039
- const title = content.slice(0, 100);
2040
- await this.runtime.memory.publishKnowledge({ title, body: content, community });
2041
- if (this.verbose)
2042
- console.log(`[autonomous] ✓ Directive response posted in ${community}`);
2043
- }
2044
- }
2045
- }
2046
- catch (err) {
2047
- if (this.verbose)
2048
- console.error("[autonomous] Directive handling failed:", err);
2049
- }
2050
- }
2233
+ // DD-7: handleDirective removed — swarm coordination uses DMs exclusively
2051
2234
  // ================================================================
2052
2235
  // Project collaboration signal handlers
2053
2236
  // ================================================================
@@ -2734,6 +2917,18 @@ export class AutonomousAgent {
2734
2917
  triggers: this.doomLoopTriggers,
2735
2918
  actionType,
2736
2919
  });
2920
+ // Track C.2: also push to gateway as fire-and-forget telemetry so
2921
+ // ops dashboards can answer "which tools most often misbehave?"
2922
+ // and "is this agent stuck right now?" — see
2923
+ // gateway/src/services/doomLoopMetrics.ts. Errors are swallowed so
2924
+ // a backend outage never blocks the runtime's recovery path.
2925
+ void this.runtime.connection
2926
+ .request("POST", "/v1/agents/me/doom-loop-event", {
2927
+ offender: doomOffender,
2928
+ triggers: this.doomLoopTriggers,
2929
+ actionType,
2930
+ })
2931
+ .catch(() => { });
2737
2932
  if (this.doomLoopTriggers >= AUTONOMOUS_DOOM_LOOP_MAX_TRIGGERS) {
2738
2933
  if (this.verbose) {
2739
2934
  console.warn(`[autonomous] ✗ doom loop on '${doomOffender}' (${this.doomLoopTriggers} triggers) — aborting cycle`);