@nookplot/runtime 0.5.109 → 0.5.114

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 (70) hide show
  1. package/dist/__tests__/autonomous.doomLoop.test.d.ts +2 -0
  2. package/dist/__tests__/autonomous.doomLoop.test.d.ts.map +1 -0
  3. package/dist/__tests__/autonomous.doomLoop.test.js +126 -0
  4. package/dist/__tests__/autonomous.doomLoop.test.js.map +1 -0
  5. package/dist/__tests__/autonomous.getAvailableActions.test.js +41 -0
  6. package/dist/__tests__/autonomous.getAvailableActions.test.js.map +1 -1
  7. package/dist/__tests__/autonomous.loadedSkillRefs.test.d.ts +2 -0
  8. package/dist/__tests__/autonomous.loadedSkillRefs.test.d.ts.map +1 -0
  9. package/dist/__tests__/autonomous.loadedSkillRefs.test.js +150 -0
  10. package/dist/__tests__/autonomous.loadedSkillRefs.test.js.map +1 -0
  11. package/dist/__tests__/codegen-drift.test.d.ts +23 -0
  12. package/dist/__tests__/codegen-drift.test.d.ts.map +1 -0
  13. package/dist/__tests__/codegen-drift.test.js +185 -0
  14. package/dist/__tests__/codegen-drift.test.js.map +1 -0
  15. package/dist/__tests__/doomLoop.test.d.ts +6 -0
  16. package/dist/__tests__/doomLoop.test.d.ts.map +1 -0
  17. package/dist/__tests__/doomLoop.test.js +144 -0
  18. package/dist/__tests__/doomLoop.test.js.map +1 -0
  19. package/dist/__tests__/goalLoop.test.js +111 -0
  20. package/dist/__tests__/goalLoop.test.js.map +1 -1
  21. package/dist/__tests__/hooks.test.js +4 -0
  22. package/dist/__tests__/hooks.test.js.map +1 -1
  23. package/dist/__tests__/onChainActions.parity.test.d.ts +12 -0
  24. package/dist/__tests__/onChainActions.parity.test.d.ts.map +1 -0
  25. package/dist/__tests__/onChainActions.parity.test.js +104 -0
  26. package/dist/__tests__/onChainActions.parity.test.js.map +1 -0
  27. package/dist/__tests__/presetLoader.test.js +42 -42
  28. package/dist/__tests__/sandbox.test.js +24 -24
  29. package/dist/__tests__/signing.test.d.ts +2 -0
  30. package/dist/__tests__/signing.test.d.ts.map +1 -0
  31. package/dist/__tests__/signing.test.js +260 -0
  32. package/dist/__tests__/signing.test.js.map +1 -0
  33. package/dist/actionCatalog.generated.d.ts +1 -1
  34. package/dist/actionCatalog.generated.d.ts.map +1 -1
  35. package/dist/actionCatalog.generated.js +117 -22
  36. package/dist/actionCatalog.generated.js.map +1 -1
  37. package/dist/actionCatalog.js +1 -1
  38. package/dist/actionCatalog.js.map +1 -1
  39. package/dist/autonomous.d.ts +11 -0
  40. package/dist/autonomous.d.ts.map +1 -1
  41. package/dist/autonomous.js +93 -0
  42. package/dist/autonomous.js.map +1 -1
  43. package/dist/conversation/modelLimits.js +17 -17
  44. package/dist/doomLoop.d.ts +52 -0
  45. package/dist/doomLoop.d.ts.map +1 -0
  46. package/dist/doomLoop.js +173 -0
  47. package/dist/doomLoop.js.map +1 -0
  48. package/dist/goal/goalLoop.d.ts +3 -0
  49. package/dist/goal/goalLoop.d.ts.map +1 -1
  50. package/dist/goal/goalLoop.js +30 -1
  51. package/dist/goal/goalLoop.js.map +1 -1
  52. package/dist/goal/goalPrompts.js +27 -27
  53. package/dist/hooks.d.ts +10 -0
  54. package/dist/hooks.d.ts.map +1 -1
  55. package/dist/hooks.js.map +1 -1
  56. package/dist/signalActionMap.d.ts +13 -1
  57. package/dist/signalActionMap.d.ts.map +1 -1
  58. package/dist/signalActionMap.js +25 -2
  59. package/dist/signalActionMap.js.map +1 -1
  60. package/dist/signing.js +2 -2
  61. package/dist/signing.js.map +1 -1
  62. package/package.json +60 -60
  63. package/dist/__tests__/lineage.test.d.ts +0 -2
  64. package/dist/__tests__/lineage.test.d.ts.map +0 -1
  65. package/dist/__tests__/lineage.test.js +0 -131
  66. package/dist/__tests__/lineage.test.js.map +0 -1
  67. package/dist/lineage.d.ts +0 -69
  68. package/dist/lineage.d.ts.map +0 -1
  69. package/dist/lineage.js +0 -72
  70. package/dist/lineage.js.map +0 -1
@@ -47,6 +47,9 @@ import { WakeUpStack } from "./wakeUpStack.js";
47
47
  import { GoalLoop } from "./goal/goalLoop.js";
48
48
  import { hooks as defaultHooks } from "./hooks.js";
49
49
  import { guardrails as defaultGuardrails, GuardrailTripped, InputGuardrailTripped, } from "./guardrails.js";
50
+ import { buildCorrectivePrompt, checkForDoomLoopFromSignatures, makeSignature, } from "./doomLoop.js";
51
+ const AUTONOMOUS_DOOM_LOOP_MAX_TRIGGERS = 3;
52
+ const AUTONOMOUS_DOOM_LOOP_SIGNATURE_WINDOW = 30;
50
53
  // ----------------------------------------------------------------
51
54
  // AutonomousAgent
52
55
  // ----------------------------------------------------------------
@@ -150,6 +153,17 @@ export class AutonomousAgent {
150
153
  processedSignals = new Map();
151
154
  /** Dynamic tool browsing: categories loaded via browse_tools. */
152
155
  loadedCategories = new Set();
156
+ /** Doom-loop detector state: ring buffer of recent action signatures + trigger counter. */
157
+ toolSignatures = [];
158
+ doomLoopTriggers = 0;
159
+ /**
160
+ * SRA Phase 4b — rolling buffer of skills loaded via nookplot_load_skill.
161
+ * Captured post-dispatch; flushed into args.loadedSkillRefs pre-dispatch on
162
+ * submit_reasoning_trace / submit_subtask_trace. Cap 64 (drop oldest); the
163
+ * gateway intersects against (agent, used=true, last 24h) signals so stale
164
+ * or unauthorized refs are dropped silently. Format on the wire: `${kind}:${ref}`.
165
+ */
166
+ loadedSkillRefs = [];
153
167
  /** Tiered knowledge context: L0 identity + L1 essentials (session-cached) + L2 on-demand. */
154
168
  wakeUpStack;
155
169
  constructor(runtime, options = {}) {
@@ -2790,6 +2804,58 @@ export class AutonomousAgent {
2790
2804
  }
2791
2805
  const startTime = Date.now();
2792
2806
  hooks.emitFireAndForget("action_start", { actionType, args, actionId, agentAddress });
2807
+ // ── Doom-loop detection (parity with goal_loop.ts) ──
2808
+ // Push the action signature into a 30-slot ring; if the recent tail
2809
+ // shows 3+ identical calls or an A,B,A,B-style cycle, increment the
2810
+ // trigger counter. After 3 triggers in this agent's lifetime, abort
2811
+ // the current cycle so the outer proactive scheduler can re-route —
2812
+ // keeping the autonomous loop from spending NOOK on a stuck bug.
2813
+ this.toolSignatures.push(makeSignature(actionType, args));
2814
+ if (this.toolSignatures.length > AUTONOMOUS_DOOM_LOOP_SIGNATURE_WINDOW) {
2815
+ this.toolSignatures = this.toolSignatures.slice(-AUTONOMOUS_DOOM_LOOP_SIGNATURE_WINDOW);
2816
+ }
2817
+ const doomOffender = checkForDoomLoopFromSignatures(this.toolSignatures);
2818
+ if (doomOffender) {
2819
+ this.doomLoopTriggers++;
2820
+ hooks.emitFireAndForget("doom_loop_detected", {
2821
+ offender: doomOffender,
2822
+ triggers: this.doomLoopTriggers,
2823
+ actionType,
2824
+ });
2825
+ // Track C.2: also push to gateway as fire-and-forget telemetry so
2826
+ // ops dashboards can answer "which tools most often misbehave?"
2827
+ // and "is this agent stuck right now?" — see
2828
+ // gateway/src/services/doomLoopMetrics.ts. Errors are swallowed so
2829
+ // a backend outage never blocks the runtime's recovery path.
2830
+ void this.runtime.connection
2831
+ .request("POST", "/v1/agents/me/doom-loop-event", {
2832
+ offender: doomOffender,
2833
+ triggers: this.doomLoopTriggers,
2834
+ actionType,
2835
+ })
2836
+ .catch(() => { });
2837
+ if (this.doomLoopTriggers >= AUTONOMOUS_DOOM_LOOP_MAX_TRIGGERS) {
2838
+ if (this.verbose) {
2839
+ console.warn(`[autonomous] ✗ doom loop on '${doomOffender}' (${this.doomLoopTriggers} triggers) — aborting cycle`);
2840
+ }
2841
+ const reason = buildCorrectivePrompt(doomOffender);
2842
+ hooks.emitFireAndForget("action_error", {
2843
+ actionType, args, error: new Error(reason),
2844
+ durationMs: Date.now() - startTime, actionId,
2845
+ });
2846
+ if (actionId) {
2847
+ try {
2848
+ await this.runtime.proactive.rejectDelegatedAction(actionId, reason);
2849
+ }
2850
+ catch { /* best-effort */ }
2851
+ }
2852
+ // Reset trigger counter so the agent can recover after the outer
2853
+ // scheduler reroutes — but keep the signature ring so a doom loop
2854
+ // resurfacing on the next action still trips the detector.
2855
+ this.doomLoopTriggers = 0;
2856
+ return;
2857
+ }
2858
+ }
2793
2859
  // ── Input guardrails (Phase 3) ──
2794
2860
  // Run BEFORE the action body so a tripped guardrail prevents dispatch.
2795
2861
  // `tool_input` fires regardless so observability sees the call attempt;
@@ -2885,10 +2951,37 @@ export class AutonomousAgent {
2885
2951
  ...args,
2886
2952
  ...(suggestedContent ? { suggestedContent } : {}),
2887
2953
  };
2954
+ // SRA Phase 4b — flush loaded-skill buffer into submit traces. If the
2955
+ // caller already set loadedSkillRefs explicitly, respect it; otherwise
2956
+ // populate from the buffer. Drop entries older than 24h client-side
2957
+ // (server enforces the same window via intersection, but this keeps the
2958
+ // payload small).
2959
+ if ((actionType === "submit_reasoning_trace" || actionType === "submit_subtask_trace") &&
2960
+ dispatchPayload.loadedSkillRefs == null &&
2961
+ this.loadedSkillRefs.length > 0) {
2962
+ const cutoff = Date.now() - 24 * 60 * 60 * 1000;
2963
+ this.loadedSkillRefs = this.loadedSkillRefs.filter(e => e.loadedAt >= cutoff);
2964
+ if (this.loadedSkillRefs.length > 0) {
2965
+ dispatchPayload.loadedSkillRefs = this.loadedSkillRefs.map(e => `${e.kind}:${e.ref}`);
2966
+ }
2967
+ }
2888
2968
  const dispatchResult = await this.runtime.connection.request("POST", "/v1/actions/execute", { toolName, payload: dispatchPayload });
2889
2969
  switch (dispatchResult.status) {
2890
2970
  case "completed": {
2891
2971
  result = (dispatchResult.result ?? {});
2972
+ // SRA Phase 4b — capture canonical_ref from load_skill responses so
2973
+ // the next submit_reasoning_trace can declare them. The gateway's
2974
+ // recordLoad() already flips used=true on its skill_utility_signals
2975
+ // row; we just need to remember the ref for the trace-linkage step.
2976
+ if (actionType === "load_skill" && typeof result.canonical_ref === "string") {
2977
+ const kind = args.kind;
2978
+ if (typeof kind === "string") {
2979
+ this.loadedSkillRefs.push({ kind, ref: result.canonical_ref, loadedAt: Date.now() });
2980
+ if (this.loadedSkillRefs.length > 64) {
2981
+ this.loadedSkillRefs = this.loadedSkillRefs.slice(-64);
2982
+ }
2983
+ }
2984
+ }
2892
2985
  break;
2893
2986
  }
2894
2987
  case "sign_required": {