@nookplot/runtime 0.5.144 → 0.5.145

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 (151) 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.workspaceOpportunity.test.d.ts +2 -0
  20. package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts.map +1 -0
  21. package/dist/__tests__/autonomous.workspaceOpportunity.test.js +212 -0
  22. package/dist/__tests__/autonomous.workspaceOpportunity.test.js.map +1 -0
  23. package/dist/__tests__/bdAgentPack.test.js +1 -1
  24. package/dist/__tests__/bdAgentPack.test.js.map +1 -1
  25. package/dist/__tests__/bounties.test.d.ts +11 -0
  26. package/dist/__tests__/bounties.test.d.ts.map +1 -0
  27. package/dist/__tests__/bounties.test.js +78 -0
  28. package/dist/__tests__/bounties.test.js.map +1 -0
  29. package/dist/__tests__/codegen-drift.test.js +3 -1
  30. package/dist/__tests__/codegen-drift.test.js.map +1 -1
  31. package/dist/__tests__/conversation/modelThresholdsParity.test.js +19 -14
  32. package/dist/__tests__/conversation/modelThresholdsParity.test.js.map +1 -1
  33. package/dist/__tests__/economy.surplusBranch.test.js +64 -0
  34. package/dist/__tests__/economy.surplusBranch.test.js.map +1 -1
  35. package/dist/__tests__/goalLoop.test.d.ts +2 -0
  36. package/dist/__tests__/goalLoop.test.d.ts.map +1 -0
  37. package/dist/__tests__/goalLoop.test.js +358 -0
  38. package/dist/__tests__/goalLoop.test.js.map +1 -0
  39. package/dist/__tests__/helpers/mockRuntime.d.ts.map +1 -1
  40. package/dist/__tests__/helpers/mockRuntime.js +7 -0
  41. package/dist/__tests__/helpers/mockRuntime.js.map +1 -1
  42. package/dist/__tests__/loadProfile.test.d.ts +8 -0
  43. package/dist/__tests__/loadProfile.test.d.ts.map +1 -0
  44. package/dist/__tests__/loadProfile.test.js +134 -0
  45. package/dist/__tests__/loadProfile.test.js.map +1 -0
  46. package/dist/__tests__/mining.test.d.ts +2 -0
  47. package/dist/__tests__/mining.test.d.ts.map +1 -0
  48. package/dist/__tests__/mining.test.js +306 -0
  49. package/dist/__tests__/mining.test.js.map +1 -0
  50. package/dist/__tests__/pack.test.js +14 -14
  51. package/dist/__tests__/packLoader.test.js +4 -4
  52. package/dist/__tests__/presetLoader.test.js +42 -42
  53. package/dist/__tests__/runtimeConstructor.test.d.ts +18 -0
  54. package/dist/__tests__/runtimeConstructor.test.d.ts.map +1 -0
  55. package/dist/__tests__/runtimeConstructor.test.js +57 -0
  56. package/dist/__tests__/runtimeConstructor.test.js.map +1 -0
  57. package/dist/__tests__/sandbox.test.js +24 -24
  58. package/dist/__tests__/signalActionMap.test.d.ts +17 -0
  59. package/dist/__tests__/signalActionMap.test.d.ts.map +1 -0
  60. package/dist/__tests__/signalActionMap.test.js +165 -0
  61. package/dist/__tests__/signalActionMap.test.js.map +1 -0
  62. package/dist/__tests__/usdcBudget.test.d.ts +2 -0
  63. package/dist/__tests__/usdcBudget.test.d.ts.map +1 -0
  64. package/dist/__tests__/usdcBudget.test.js +128 -0
  65. package/dist/__tests__/usdcBudget.test.js.map +1 -0
  66. package/dist/__tests__/x402.test.d.ts +2 -0
  67. package/dist/__tests__/x402.test.d.ts.map +1 -0
  68. package/dist/__tests__/x402.test.js +117 -0
  69. package/dist/__tests__/x402.test.js.map +1 -0
  70. package/dist/actionCatalog.d.ts.map +1 -1
  71. package/dist/actionCatalog.generated.d.ts +1 -1
  72. package/dist/actionCatalog.generated.d.ts.map +1 -1
  73. package/dist/actionCatalog.generated.js +154 -29
  74. package/dist/actionCatalog.generated.js.map +1 -1
  75. package/dist/actionCatalog.js +0 -10
  76. package/dist/actionCatalog.js.map +1 -1
  77. package/dist/api-marketplace.d.ts +146 -0
  78. package/dist/api-marketplace.d.ts.map +1 -1
  79. package/dist/api-marketplace.js +218 -0
  80. package/dist/api-marketplace.js.map +1 -1
  81. package/dist/autonomous.d.ts +16 -9
  82. package/dist/autonomous.d.ts.map +1 -1
  83. package/dist/autonomous.js +278 -59
  84. package/dist/autonomous.js.map +1 -1
  85. package/dist/bounties.d.ts +21 -1
  86. package/dist/bounties.d.ts.map +1 -1
  87. package/dist/bounties.js +5 -1
  88. package/dist/bounties.js.map +1 -1
  89. package/dist/contentSafety.d.ts +1 -1
  90. package/dist/contentSafety.d.ts.map +1 -1
  91. package/dist/contentSafety.js +6 -2
  92. package/dist/contentSafety.js.map +1 -1
  93. package/dist/conversation/modelLimits.js +17 -17
  94. package/dist/discovery.js +1 -1
  95. package/dist/discovery.js.map +1 -1
  96. package/dist/economy.d.ts +10 -15
  97. package/dist/economy.d.ts.map +1 -1
  98. package/dist/economy.js +16 -29
  99. package/dist/economy.js.map +1 -1
  100. package/dist/goal/goalLoop.d.ts +78 -0
  101. package/dist/goal/goalLoop.d.ts.map +1 -0
  102. package/dist/goal/goalLoop.js +388 -0
  103. package/dist/goal/goalLoop.js.map +1 -0
  104. package/dist/goal/goalPrompts.d.ts +20 -0
  105. package/dist/goal/goalPrompts.d.ts.map +1 -0
  106. package/dist/goal/goalPrompts.js +54 -0
  107. package/dist/goal/goalPrompts.js.map +1 -0
  108. package/dist/goal/types.d.ts +102 -0
  109. package/dist/goal/types.d.ts.map +1 -0
  110. package/dist/goal/types.js +7 -0
  111. package/dist/goal/types.js.map +1 -0
  112. package/dist/identity.d.ts +51 -0
  113. package/dist/identity.d.ts.map +1 -1
  114. package/dist/identity.js +50 -0
  115. package/dist/identity.js.map +1 -1
  116. package/dist/index.d.ts +18 -3
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +27 -2
  119. package/dist/index.js.map +1 -1
  120. package/dist/loadProfile.d.ts +100 -0
  121. package/dist/loadProfile.d.ts.map +1 -0
  122. package/dist/loadProfile.js +221 -0
  123. package/dist/loadProfile.js.map +1 -0
  124. package/dist/signalActionMap.d.ts.map +1 -1
  125. package/dist/signalActionMap.js +16 -5
  126. package/dist/signalActionMap.js.map +1 -1
  127. package/dist/swarms.d.ts +13 -0
  128. package/dist/swarms.d.ts.map +1 -1
  129. package/dist/swarms.js +4 -0
  130. package/dist/swarms.js.map +1 -1
  131. package/dist/tools.js +1 -1
  132. package/dist/tools.js.map +1 -1
  133. package/dist/types.d.ts +21 -0
  134. package/dist/types.d.ts.map +1 -1
  135. package/dist/usdcBudget.d.ts +90 -0
  136. package/dist/usdcBudget.d.ts.map +1 -0
  137. package/dist/usdcBudget.js +155 -0
  138. package/dist/usdcBudget.js.map +1 -0
  139. package/dist/x402.d.ts +69 -0
  140. package/dist/x402.d.ts.map +1 -0
  141. package/dist/x402.js +139 -0
  142. package/dist/x402.js.map +1 -0
  143. package/package.json +1 -1
  144. package/dist/__tests__/economy.frontierInference.test.d.ts +0 -2
  145. package/dist/__tests__/economy.frontierInference.test.d.ts.map +0 -1
  146. package/dist/__tests__/economy.frontierInference.test.js +0 -61
  147. package/dist/__tests__/economy.frontierInference.test.js.map +0 -1
  148. package/dist/frontierPass.d.ts +0 -30
  149. package/dist/frontierPass.d.ts.map +0 -1
  150. package/dist/frontierPass.js +0 -42
  151. package/dist/frontierPass.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeConstructor.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/runtimeConstructor.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Constructor-time config validation for `NookplotRuntime`.
3
+ *
4
+ * The surplus guard mirrors the CLI's `validateConfig` startup check: surplus
5
+ * inference signs each x402 payment with the agent's OWN key, so an
6
+ * API-key-only agent would construct fine but throw a keyless `SurplusError`
7
+ * on the FIRST inference call — deep in the autonomous loop, easily mistaken
8
+ * for an outage. Failing fast at construction turns that into an actionable
9
+ * startup error.
10
+ *
11
+ * Note: the call-time keyless behavior is covered separately in
12
+ * economy.surplusBranch.test.ts (which constructs ConnectionManager/
13
+ * EconomyManager directly, BELOW this top-level guard — so a per-call
14
+ * `inferenceSource: "surplus"` override on a platform-default runtime still
15
+ * validates at call time, unaffected by this constructor check).
16
+ */
17
+ import { describe, it, expect } from "vitest";
18
+ import { NookplotRuntime } from "../index.js";
19
+ const GATEWAY = "https://gateway.example.com";
20
+ const API_KEY = "nk_test";
21
+ const PK = "0x" + "1".repeat(64);
22
+ describe("NookplotRuntime constructor — required config", () => {
23
+ it("throws when gatewayUrl is missing", () => {
24
+ expect(() => new NookplotRuntime({ gatewayUrl: "", apiKey: API_KEY })).toThrow(/gatewayUrl is required/);
25
+ });
26
+ it("throws when apiKey is missing", () => {
27
+ expect(() => new NookplotRuntime({ gatewayUrl: GATEWAY, apiKey: "" })).toThrow(/apiKey is required/);
28
+ });
29
+ });
30
+ describe("NookplotRuntime constructor — surplus fail-fast (mirrors CLI validateConfig)", () => {
31
+ it("throws when inferenceSource='surplus' and no privateKey", () => {
32
+ expect(() => new NookplotRuntime({
33
+ gatewayUrl: GATEWAY,
34
+ apiKey: API_KEY,
35
+ inferenceSource: "surplus",
36
+ })).toThrow(/inferenceSource is 'surplus' but no privateKey/);
37
+ });
38
+ it("constructs when inferenceSource='surplus' WITH a privateKey", () => {
39
+ expect(() => new NookplotRuntime({
40
+ gatewayUrl: GATEWAY,
41
+ apiKey: API_KEY,
42
+ inferenceSource: "surplus",
43
+ privateKey: PK,
44
+ })).not.toThrow();
45
+ });
46
+ it("constructs with inferenceSource='platform' and no privateKey (gateway-metered agent)", () => {
47
+ expect(() => new NookplotRuntime({
48
+ gatewayUrl: GATEWAY,
49
+ apiKey: API_KEY,
50
+ inferenceSource: "platform",
51
+ })).not.toThrow();
52
+ });
53
+ it("constructs with no inferenceSource set (defaults to platform)", () => {
54
+ expect(() => new NookplotRuntime({ gatewayUrl: GATEWAY, apiKey: API_KEY })).not.toThrow();
55
+ });
56
+ });
57
+ //# sourceMappingURL=runtimeConstructor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeConstructor.test.js","sourceRoot":"","sources":["../../src/__tests__/runtimeConstructor.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,OAAO,GAAG,6BAA6B,CAAC;AAC9C,MAAM,OAAO,GAAG,SAAS,CAAC;AAC1B,MAAM,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAEjC,QAAQ,CAAC,+CAA+C,EAAE,GAAG,EAAE;IAC7D,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAC5E,wBAAwB,CACzB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAC5E,oBAAoB,CACrB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,8EAA8E,EAAE,GAAG,EAAE;IAC5F,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CACJ,GAAG,EAAE,CACH,IAAI,eAAe,CAAC;YAClB,UAAU,EAAE,OAAO;YACnB,MAAM,EAAE,OAAO;YACf,eAAe,EAAE,SAAS;SAC3B,CAAC,CACL,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,CACJ,GAAG,EAAE,CACH,IAAI,eAAe,CAAC;YAClB,UAAU,EAAE,OAAO;YACnB,MAAM,EAAE,OAAO;YACf,eAAe,EAAE,SAAS;YAC1B,UAAU,EAAE,EAAE;SACf,CAAC,CACL,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC9F,MAAM,CACJ,GAAG,EAAE,CACH,IAAI,eAAe,CAAC;YAClB,UAAU,EAAE,OAAO;YACnB,MAAM,EAAE,OAAO;YACf,eAAe,EAAE,UAAU;SAC5B,CAAC,CACL,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC5F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -299,30 +299,30 @@ describe("DockerSandbox lifecycle (via fake docker shim)", () => {
299
299
  // `exec id sh -c CMD` -> exec sh -c CMD
300
300
  // `cp SRC DST` -> cp SRC DST
301
301
  // `rm -f id` -> exit 0
302
- const script = `#!/bin/sh
303
- case "$1" in
304
- run) echo "fake-container-abc123"; exit 0 ;;
305
- port) echo "0.0.0.0:54321"; exit 0 ;;
306
- inspect) echo "true"; exit 0 ;;
307
- rm) exit 0 ;;
308
- exec)
309
- # Skip "exec", any flags (-i, -w X, -e K=V), and the container id.
310
- shift
311
- while [ "$1" = "-i" ] || [ "$1" = "-w" ] || [ "$1" = "-e" ]; do
312
- if [ "$1" = "-w" ] || [ "$1" = "-e" ]; then shift; fi
313
- shift
314
- done
315
- shift # container id
316
- exec "$@"
317
- ;;
318
- cp)
319
- shift
320
- src=\${1#*:}
321
- dst=\${2#*:}
322
- cp "$src" "$dst"
323
- ;;
324
- *) echo "shim: unknown $1" 1>&2; exit 2 ;;
325
- esac
302
+ const script = `#!/bin/sh
303
+ case "$1" in
304
+ run) echo "fake-container-abc123"; exit 0 ;;
305
+ port) echo "0.0.0.0:54321"; exit 0 ;;
306
+ inspect) echo "true"; exit 0 ;;
307
+ rm) exit 0 ;;
308
+ exec)
309
+ # Skip "exec", any flags (-i, -w X, -e K=V), and the container id.
310
+ shift
311
+ while [ "$1" = "-i" ] || [ "$1" = "-w" ] || [ "$1" = "-e" ]; do
312
+ if [ "$1" = "-w" ] || [ "$1" = "-e" ]; then shift; fi
313
+ shift
314
+ done
315
+ shift # container id
316
+ exec "$@"
317
+ ;;
318
+ cp)
319
+ shift
320
+ src=\${1#*:}
321
+ dst=\${2#*:}
322
+ cp "$src" "$dst"
323
+ ;;
324
+ *) echo "shim: unknown $1" 1>&2; exit 2 ;;
325
+ esac
326
326
  `;
327
327
  await fs.writeFile(shimBin, script, { mode: 0o755 });
328
328
  });
@@ -0,0 +1,17 @@
1
+ /**
2
+ * SignalActionMap — unit tests for getAvailableActionsFromMap + TS↔Py parity.
3
+ *
4
+ * Asserts (post-§4.3 collapse):
5
+ * - api_listings appears for buyer-side "I have work to do" signals
6
+ * (task_assigned, bounty_claimed, mining_opportunity) without browse_tools.
7
+ * - api_listings does NOT leak into unrelated signals (e.g. xmtp_message).
8
+ * - Provider-only api actions (api_endpoint, api_usage) stay category-gated —
9
+ * only reachable via browse_tools("marketplace").
10
+ *
11
+ * Cross-language parity (Phase 6.0):
12
+ * Parses SIGNAL_CONTEXT_ACTIONS from runtime-py/.../signal_action_map.py and
13
+ * diffs against this file's TS export. Catches future drift where one side
14
+ * adds a signal key or action without the other.
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=signalActionMap.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signalActionMap.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/signalActionMap.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * SignalActionMap — unit tests for getAvailableActionsFromMap + TS↔Py parity.
3
+ *
4
+ * Asserts (post-§4.3 collapse):
5
+ * - api_listings appears for buyer-side "I have work to do" signals
6
+ * (task_assigned, bounty_claimed, mining_opportunity) without browse_tools.
7
+ * - api_listings does NOT leak into unrelated signals (e.g. xmtp_message).
8
+ * - Provider-only api actions (api_endpoint, api_usage) stay category-gated —
9
+ * only reachable via browse_tools("marketplace").
10
+ *
11
+ * Cross-language parity (Phase 6.0):
12
+ * Parses SIGNAL_CONTEXT_ACTIONS from runtime-py/.../signal_action_map.py and
13
+ * diffs against this file's TS export. Catches future drift where one side
14
+ * adds a signal key or action without the other.
15
+ */
16
+ import { describe, it, expect } from "vitest";
17
+ import { readFileSync } from "node:fs";
18
+ import { fileURLToPath } from "node:url";
19
+ import { dirname, join } from "node:path";
20
+ import { CORE_ACTIONS, SIGNAL_CONTEXT_ACTIONS, getAvailableActionsFromMap, } from "../signalActionMap.js";
21
+ const __dirname = dirname(fileURLToPath(import.meta.url));
22
+ const REPO_ROOT = join(__dirname, "..", "..", "..");
23
+ const PROVIDER_ONLY_API_ACTIONS = [
24
+ "api_endpoint",
25
+ "api_usage",
26
+ ];
27
+ describe("getAvailableActionsFromMap — api_listings discoverability (G1)", () => {
28
+ it("includes api_listings on task_assigned without loading marketplace category", () => {
29
+ const actions = getAvailableActionsFromMap("task_assigned", new Set());
30
+ expect(actions).toContain("api_listings");
31
+ });
32
+ it("includes api_listings on bounty_claimed without loading marketplace category", () => {
33
+ const actions = getAvailableActionsFromMap("bounty_claimed", new Set());
34
+ expect(actions).toContain("api_listings");
35
+ });
36
+ it("includes api_listings on mining_opportunity without loading marketplace category", () => {
37
+ const actions = getAvailableActionsFromMap("mining_opportunity", new Set());
38
+ expect(actions).toContain("api_listings");
39
+ });
40
+ it("does NOT include api_listings on xmtp_message (unrelated signal)", () => {
41
+ const actions = getAvailableActionsFromMap("xmtp_message", new Set());
42
+ expect(actions).not.toContain("api_listings");
43
+ });
44
+ it("provider-only api actions stay category-gated (not in any SIGNAL_CONTEXT_ACTIONS entry)", () => {
45
+ for (const action of PROVIDER_ONLY_API_ACTIONS) {
46
+ for (const [signal, list] of Object.entries(SIGNAL_CONTEXT_ACTIONS)) {
47
+ expect(list, `${action} leaked into ${signal}`).not.toContain(action);
48
+ }
49
+ expect(CORE_ACTIONS, `${action} must not be in CORE_ACTIONS`).not.toContain(action);
50
+ }
51
+ });
52
+ it("api_listings itself is not in CORE_ACTIONS (avoids bloating every prompt)", () => {
53
+ expect(CORE_ACTIONS).not.toContain("api_listings");
54
+ });
55
+ it("includes pay_api alongside api_listings on the 3 work-bearing signals", () => {
56
+ for (const signal of ["task_assigned", "bounty_claimed", "mining_opportunity"]) {
57
+ const actions = getAvailableActionsFromMap(signal, new Set());
58
+ expect(actions, `${signal} should offer pay_api`).toContain("pay_api");
59
+ }
60
+ });
61
+ it("does NOT include pay_api on xmtp_message (money action stays scoped)", () => {
62
+ expect(getAvailableActionsFromMap("xmtp_message", new Set())).not.toContain("pay_api");
63
+ });
64
+ it("pay_api is not in CORE_ACTIONS (signal-scoped, not always-on)", () => {
65
+ expect(CORE_ACTIONS).not.toContain("pay_api");
66
+ });
67
+ it("includes api_onboard on the service signal (agent-first listing creation)", () => {
68
+ const actions = getAvailableActionsFromMap("service", new Set());
69
+ expect(actions).toContain("api_onboard");
70
+ });
71
+ it("does NOT include api_onboard on unrelated signals (xmtp_message)", () => {
72
+ expect(getAvailableActionsFromMap("xmtp_message", new Set())).not.toContain("api_onboard");
73
+ });
74
+ it("api_onboard is not in CORE_ACTIONS (signal-scoped seller action)", () => {
75
+ expect(CORE_ACTIONS).not.toContain("api_onboard");
76
+ });
77
+ });
78
+ // ──────────────────────────────────────────────────────────────────────────
79
+ // TS ↔ Python SIGNAL_CONTEXT_ACTIONS parity (Phase 6.0)
80
+ // ──────────────────────────────────────────────────────────────────────────
81
+ /**
82
+ * Parse SIGNAL_CONTEXT_ACTIONS dict from the Python source file.
83
+ *
84
+ * Pattern: each top-level entry follows
85
+ * "<signal_name>": [<comma-separated string list, possibly multi-line>],
86
+ *
87
+ * Lightweight regex-based parser — same approach as codegen-drift.test.ts.
88
+ * Tolerates trailing commas, inline `# ...` comments, and multi-line lists.
89
+ */
90
+ function loadPythonSignalContextActions() {
91
+ const path = join(REPO_ROOT, "runtime-py", "nookplot_runtime", "signal_action_map.py");
92
+ const raw = readFileSync(path, "utf-8").replace(/\r\n/g, "\n");
93
+ // Slice out the SIGNAL_CONTEXT_ACTIONS body between its opening `{` and the
94
+ // line that closes it (`}` at column 0). The first match of
95
+ // `SIGNAL_CONTEXT_ACTIONS: dict[str, list[str]] = {` is the start anchor.
96
+ const start = raw.indexOf("SIGNAL_CONTEXT_ACTIONS:");
97
+ if (start === -1)
98
+ throw new Error("SIGNAL_CONTEXT_ACTIONS not found in Python source");
99
+ const braceStart = raw.indexOf("{", start);
100
+ if (braceStart === -1)
101
+ throw new Error("Opening brace not found");
102
+ // Find matching closing brace by depth-tracking.
103
+ let depth = 0;
104
+ let braceEnd = -1;
105
+ for (let i = braceStart; i < raw.length; i++) {
106
+ const ch = raw[i];
107
+ if (ch === "{")
108
+ depth++;
109
+ else if (ch === "}") {
110
+ depth--;
111
+ if (depth === 0) {
112
+ braceEnd = i;
113
+ break;
114
+ }
115
+ }
116
+ }
117
+ if (braceEnd === -1)
118
+ throw new Error("Closing brace not found");
119
+ const body = raw.slice(braceStart + 1, braceEnd);
120
+ // Strip Python `#` comments to end of line — they could otherwise contain
121
+ // stray punctuation that confuses the entry regex.
122
+ const noComments = body.replace(/(^|\s)#[^\n]*/g, "$1");
123
+ const map = new Map();
124
+ // Match "key": [ ... ] across multi-line lists.
125
+ const entryRe = /"([a-z_][a-z0-9_]*)"\s*:\s*\[([\s\S]*?)\]\s*,/g;
126
+ let match;
127
+ while ((match = entryRe.exec(noComments)) !== null) {
128
+ const key = match[1];
129
+ const listBody = match[2];
130
+ const actions = [];
131
+ const actionRe = /"([^"]+)"/g;
132
+ let am;
133
+ while ((am = actionRe.exec(listBody)) !== null) {
134
+ actions.push(am[1]);
135
+ }
136
+ map.set(key, actions);
137
+ }
138
+ return map;
139
+ }
140
+ describe("SIGNAL_CONTEXT_ACTIONS — TS ↔ Python parity (Phase 6.0)", () => {
141
+ const pyMap = loadPythonSignalContextActions();
142
+ const tsKeys = new Set(Object.keys(SIGNAL_CONTEXT_ACTIONS));
143
+ const pyKeys = new Set(pyMap.keys());
144
+ it("both sides export the same set of signal keys", () => {
145
+ const tsOnly = [...tsKeys].filter((k) => !pyKeys.has(k)).sort();
146
+ const pyOnly = [...pyKeys].filter((k) => !tsKeys.has(k)).sort();
147
+ expect({ tsOnly, pyOnly }).toEqual({ tsOnly: [], pyOnly: [] });
148
+ });
149
+ it("each signal key has the same action set on both sides", () => {
150
+ const drifts = [];
151
+ for (const key of tsKeys) {
152
+ if (!pyKeys.has(key))
153
+ continue; // covered by previous test
154
+ const tsList = new Set(SIGNAL_CONTEXT_ACTIONS[key]);
155
+ const pyList = new Set(pyMap.get(key));
156
+ const tsOnly = [...tsList].filter((a) => !pyList.has(a)).sort();
157
+ const pyOnly = [...pyList].filter((a) => !tsList.has(a)).sort();
158
+ if (tsOnly.length || pyOnly.length) {
159
+ drifts.push({ signal: key, tsOnly, pyOnly });
160
+ }
161
+ }
162
+ expect(drifts).toEqual([]);
163
+ });
164
+ });
165
+ //# sourceMappingURL=signalActionMap.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signalActionMap.test.js","sourceRoot":"","sources":["../../src/__tests__/signalActionMap.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=usdcBudget.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usdcBudget.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/usdcBudget.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Unit tests for the UsdcBudget primitive (the hard-stop x402 spend cap).
3
+ */
4
+ import { describe, it, expect } from "vitest";
5
+ import { UsdcBudget, createUsdcBudget, parseUsdcToBaseUnits, formatUsdc, DEFAULT_DAILY_USDC_CAP_BASE_UNITS, } from "../usdcBudget.js";
6
+ const USDC = 1000000n; // 1 USDC in base units
7
+ describe("parseUsdcToBaseUnits", () => {
8
+ it("parses whole and fractional USDC", () => {
9
+ expect(parseUsdcToBaseUnits("10")).toBe(10n * USDC);
10
+ expect(parseUsdcToBaseUnits("0.5")).toBe(500000n);
11
+ expect(parseUsdcToBaseUnits("1.234567")).toBe(1234567n);
12
+ });
13
+ it("truncates beyond 6 decimals", () => {
14
+ expect(parseUsdcToBaseUnits("1.2345678")).toBe(1234567n);
15
+ });
16
+ it("returns 0n (unlimited) for empty / invalid input", () => {
17
+ expect(parseUsdcToBaseUnits(undefined)).toBe(0n);
18
+ expect(parseUsdcToBaseUnits("")).toBe(0n);
19
+ expect(parseUsdcToBaseUnits("abc")).toBe(0n);
20
+ expect(parseUsdcToBaseUnits("-5")).toBe(0n);
21
+ });
22
+ });
23
+ describe("formatUsdc", () => {
24
+ it("formats base units to a $ string", () => {
25
+ expect(formatUsdc(10n * USDC)).toBe("$10");
26
+ expect(formatUsdc(500000n)).toBe("$0.5");
27
+ expect(formatUsdc(1230000n)).toBe("$1.23");
28
+ });
29
+ });
30
+ describe("UsdcBudget", () => {
31
+ it("unlimited (cap 0n) never blocks or exhausts", () => {
32
+ const b = new UsdcBudget({ dailyCapBaseUnits: 0n });
33
+ expect(b.check(1000n * USDC).allowed).toBe(true);
34
+ expect(b.maxForNextCall()).toBeUndefined();
35
+ b.record(1000n * USDC);
36
+ expect(b.isExhausted()).toBe(false);
37
+ expect(b.remaining()).toBe(-1n);
38
+ });
39
+ it("enforces the daily cap with zero overshoot via maxForNextCall", () => {
40
+ const b = new UsdcBudget({ dailyCapBaseUnits: 2n * USDC });
41
+ expect(b.maxForNextCall()).toBe(2n * USDC);
42
+ // first $1.50 call fits
43
+ expect(b.check(1500000n).allowed).toBe(true);
44
+ b.record(1500000n);
45
+ // remaining is $0.50 → a second $1.50 call is refused by the ceiling
46
+ expect(b.maxForNextCall()).toBe(500000n);
47
+ expect(b.check(1500000n).allowed).toBe(false);
48
+ expect(b.remaining()).toBe(500000n);
49
+ });
50
+ it("isExhausted flips once the cap is fully consumed", () => {
51
+ const b = new UsdcBudget({ dailyCapBaseUnits: 1n * USDC });
52
+ expect(b.isExhausted()).toBe(false);
53
+ b.record(1n * USDC);
54
+ expect(b.isExhausted()).toBe(true);
55
+ expect(b.maxForNextCall()).toBe(0n);
56
+ });
57
+ it("honors a per-call ceiling independent of the daily cap", () => {
58
+ const b = new UsdcBudget({ dailyCapBaseUnits: 100n * USDC, perCallCapBaseUnits: 2n * USDC });
59
+ expect(b.check(3n * USDC).allowed).toBe(false); // exceeds per-call ceiling
60
+ expect(b.check(2n * USDC).allowed).toBe(true);
61
+ // maxForNextCall = min(perCall 2, remaining 100) = 2
62
+ expect(b.maxForNextCall()).toBe(2n * USDC);
63
+ });
64
+ it("rolls the window after the period elapses", () => {
65
+ let t = 1_000_000;
66
+ const b = new UsdcBudget({ dailyCapBaseUnits: 1n * USDC, windowMs: 1000, now: () => t });
67
+ b.record(1n * USDC);
68
+ expect(b.isExhausted()).toBe(true);
69
+ t += 1001; // advance past the window
70
+ expect(b.isExhausted()).toBe(false);
71
+ expect(b.spent).toBe(0n);
72
+ });
73
+ });
74
+ describe("createUsdcBudget", () => {
75
+ it("defaults to the conservative daily cap when nothing is configured", () => {
76
+ const prevDaily = process.env.X402_DAILY_USDC_CAP;
77
+ const prevPer = process.env.X402_PER_CALL_USDC_CAP;
78
+ delete process.env.X402_DAILY_USDC_CAP;
79
+ delete process.env.X402_PER_CALL_USDC_CAP;
80
+ try {
81
+ const b = createUsdcBudget();
82
+ expect(b.dailyCapBaseUnits).toBe(DEFAULT_DAILY_USDC_CAP_BASE_UNITS);
83
+ expect(b.perCallCapBaseUnits).toBe(0n);
84
+ }
85
+ finally {
86
+ if (prevDaily !== undefined)
87
+ process.env.X402_DAILY_USDC_CAP = prevDaily;
88
+ if (prevPer !== undefined)
89
+ process.env.X402_PER_CALL_USDC_CAP = prevPer;
90
+ }
91
+ });
92
+ it("reads caps from env (USDC display units)", () => {
93
+ const prevDaily = process.env.X402_DAILY_USDC_CAP;
94
+ const prevPer = process.env.X402_PER_CALL_USDC_CAP;
95
+ process.env.X402_DAILY_USDC_CAP = "25";
96
+ process.env.X402_PER_CALL_USDC_CAP = "0.5";
97
+ try {
98
+ const b = createUsdcBudget();
99
+ expect(b.dailyCapBaseUnits).toBe(25n * USDC);
100
+ expect(b.perCallCapBaseUnits).toBe(500000n);
101
+ }
102
+ finally {
103
+ if (prevDaily !== undefined)
104
+ process.env.X402_DAILY_USDC_CAP = prevDaily;
105
+ else
106
+ delete process.env.X402_DAILY_USDC_CAP;
107
+ if (prevPer !== undefined)
108
+ process.env.X402_PER_CALL_USDC_CAP = prevPer;
109
+ else
110
+ delete process.env.X402_PER_CALL_USDC_CAP;
111
+ }
112
+ });
113
+ it("explicit options win over env", () => {
114
+ const prev = process.env.X402_DAILY_USDC_CAP;
115
+ process.env.X402_DAILY_USDC_CAP = "25";
116
+ try {
117
+ const b = createUsdcBudget({ dailyCapBaseUnits: 0n });
118
+ expect(b.dailyCapBaseUnits).toBe(0n); // explicit unlimited beats env
119
+ }
120
+ finally {
121
+ if (prev !== undefined)
122
+ process.env.X402_DAILY_USDC_CAP = prev;
123
+ else
124
+ delete process.env.X402_DAILY_USDC_CAP;
125
+ }
126
+ });
127
+ });
128
+ //# sourceMappingURL=usdcBudget.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usdcBudget.test.js","sourceRoot":"","sources":["../../src/__tests__/usdcBudget.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=x402.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x402.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/x402.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,117 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { ethers } from "ethers";
3
+ import { X402Manager } from "../x402.js";
4
+ const SPLITTER = "0x5555555555555555555555555555555555555555";
5
+ const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
6
+ const GATEWAY = "https://gw.example.com";
7
+ const RECEIVE_TYPES = {
8
+ ReceiveWithAuthorization: [
9
+ { name: "from", type: "address" },
10
+ { name: "to", type: "address" },
11
+ { name: "value", type: "uint256" },
12
+ { name: "validAfter", type: "uint256" },
13
+ { name: "validBefore", type: "uint256" },
14
+ { name: "nonce", type: "bytes32" },
15
+ ],
16
+ };
17
+ function challenge() {
18
+ return {
19
+ x402Version: 2,
20
+ accepts: [
21
+ {
22
+ scheme: "exact",
23
+ network: "eip155:8453",
24
+ asset: USDC,
25
+ payTo: SPLITTER,
26
+ maxAmountRequired: "1000000",
27
+ maxTimeoutSeconds: 600,
28
+ extra: { listingId: "42" },
29
+ },
30
+ ],
31
+ };
32
+ }
33
+ function res(status, body) {
34
+ return new Response(typeof body === "string" ? body : JSON.stringify(body), {
35
+ status,
36
+ headers: { "content-type": "application/json" },
37
+ });
38
+ }
39
+ describe("X402Manager.payAndCall", () => {
40
+ afterEach(() => vi.unstubAllGlobals());
41
+ it("probes, signs EIP-3009, and re-sends a gateway-recoverable authorization", async () => {
42
+ const wallet = ethers.Wallet.createRandom();
43
+ const fetchFn = vi
44
+ .fn()
45
+ .mockResolvedValueOnce(res(402, challenge()))
46
+ .mockResolvedValueOnce(res(200, { ok: "upstream" }));
47
+ vi.stubGlobal("fetch", fetchFn);
48
+ const mgr = new X402Manager(GATEWAY);
49
+ const result = await mgr.payAndCall({ listingId: 42, path: "chat", method: "POST", body: { q: "hi" } }, wallet);
50
+ expect(result.paid).toBe(true);
51
+ expect(result.status).toBe(200);
52
+ expect(result.body).toEqual({ ok: "upstream" });
53
+ expect(fetchFn).toHaveBeenCalledTimes(2);
54
+ expect(fetchFn.mock.calls[0][0]).toBe(`${GATEWAY}/v1/api-x402/42/chat`);
55
+ // The re-send carries the encoded payment header.
56
+ const init2 = fetchFn.mock.calls[1][1];
57
+ const header = init2.headers["PAYMENT-SIGNATURE"];
58
+ expect(header).toBeTruthy();
59
+ // Decode it and confirm the EIP-3009 signature recovers to the buyer — i.e.
60
+ // exactly what the gateway facilitator's verify() will check.
61
+ const decoded = JSON.parse(Buffer.from(header, "base64").toString("utf8"));
62
+ const auth = decoded.payload.authorization;
63
+ expect(auth.from).toBe(wallet.address);
64
+ expect(auth.to).toBe(SPLITTER);
65
+ expect(auth.value).toBe("1000000");
66
+ const recovered = ethers.verifyTypedData({ name: "USD Coin", version: "2", chainId: 8453, verifyingContract: USDC }, RECEIVE_TYPES, auth, decoded.payload.signature);
67
+ expect(recovered).toBe(wallet.address);
68
+ });
69
+ it("returns immediately (no signing) when the gateway does not answer 402", async () => {
70
+ const wallet = ethers.Wallet.createRandom();
71
+ const fetchFn = vi.fn().mockResolvedValueOnce(res(404, { error: "listing not available for x402" }));
72
+ vi.stubGlobal("fetch", fetchFn);
73
+ const result = await new X402Manager(GATEWAY).payAndCall({ listingId: 9, path: "x" }, wallet);
74
+ expect(result.paid).toBe(false);
75
+ expect(result.status).toBe(404);
76
+ expect(fetchFn).toHaveBeenCalledTimes(1);
77
+ });
78
+ it("throws when the 402 offers no supported 'exact' scheme", async () => {
79
+ const wallet = ethers.Wallet.createRandom();
80
+ const fetchFn = vi.fn().mockResolvedValueOnce(res(402, { x402Version: 2, accepts: [{ scheme: "weird" }] }));
81
+ vi.stubGlobal("fetch", fetchFn);
82
+ await expect(new X402Manager(GATEWAY).payAndCall({ listingId: 1, path: "x" }, wallet)).rejects.toThrow(/exact/);
83
+ });
84
+ it("aborts BEFORE signing when the quote exceeds maxAmountBaseUnits (spend cap)", async () => {
85
+ const wallet = ethers.Wallet.createRandom();
86
+ const fetchFn = vi.fn().mockResolvedValueOnce(res(402, challenge())); // quote = 1_000_000
87
+ vi.stubGlobal("fetch", fetchFn);
88
+ await expect(new X402Manager(GATEWAY).payAndCall({ listingId: 42, path: "x", maxAmountBaseUnits: 500000n }, // ceiling below quote
89
+ wallet)).rejects.toThrow(/spend blocked/i);
90
+ // Only the probe ran — no payment was signed or submitted.
91
+ expect(fetchFn).toHaveBeenCalledTimes(1);
92
+ });
93
+ it("returns amountPaidBaseUnits on a paid call within the ceiling", async () => {
94
+ const wallet = ethers.Wallet.createRandom();
95
+ const fetchFn = vi.fn().mockResolvedValueOnce(res(402, challenge())).mockResolvedValueOnce(res(200, {}));
96
+ vi.stubGlobal("fetch", fetchFn);
97
+ const result = await new X402Manager(GATEWAY).payAndCall({ listingId: 42, path: "x", maxAmountBaseUnits: 1000000n }, // == quote, allowed
98
+ wallet);
99
+ expect(result.paid).toBe(true);
100
+ expect(result.amountPaidBaseUnits).toBe("1000000");
101
+ expect(fetchFn).toHaveBeenCalledTimes(2);
102
+ });
103
+ it("derives chainId from the 402 network and honors an override", async () => {
104
+ const wallet = ethers.Wallet.createRandom();
105
+ const ch = challenge();
106
+ ch.accepts[0].network = "base-sepolia";
107
+ const fetchFn = vi.fn().mockResolvedValueOnce(res(402, ch)).mockResolvedValueOnce(res(200, {}));
108
+ vi.stubGlobal("fetch", fetchFn);
109
+ await new X402Manager(GATEWAY).payAndCall({ listingId: 42, path: "x" }, wallet);
110
+ const header = fetchFn.mock.calls[1][1].headers;
111
+ const decoded = JSON.parse(Buffer.from(header["PAYMENT-SIGNATURE"], "base64").toString("utf8"));
112
+ // Signature recovers only under chainId 84532 (base-sepolia).
113
+ const recovered = ethers.verifyTypedData({ name: "USD Coin", version: "2", chainId: 84532, verifyingContract: USDC }, RECEIVE_TYPES, decoded.payload.authorization, decoded.payload.signature);
114
+ expect(recovered).toBe(wallet.address);
115
+ });
116
+ });
117
+ //# sourceMappingURL=x402.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x402.test.js","sourceRoot":"","sources":["../../src/__tests__/x402.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"actionCatalog.d.ts","sourceRoot":"","sources":["../src/actionCatalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAgJD;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAGrD,CAAC;AAIF;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAQtE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAI7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAiBhE"}
1
+ {"version":3,"file":"actionCatalog.d.ts","sourceRoot":"","sources":["../src/actionCatalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAoID;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAGrD,CAAC;AAIF;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAQtE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAI7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAiBhE"}
@@ -1,4 +1,4 @@
1
1
  import type { ActionInfo } from "./actionCatalog.js";
2
- /** MCP-derived action catalog entries (476 tools). */
2
+ /** MCP-derived action catalog entries (501 tools). */
3
3
  export declare const GENERATED_CATALOG: Record<string, ActionInfo>;
4
4
  //# sourceMappingURL=actionCatalog.generated.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"actionCatalog.generated.d.ts","sourceRoot":"","sources":["../src/actionCatalog.generated.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,sDAAsD;AACtD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAuyExD,CAAC"}
1
+ {"version":3,"file":"actionCatalog.generated.d.ts","sourceRoot":"","sources":["../src/actionCatalog.generated.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,sDAAsD;AACtD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAo6ExD,CAAC"}