@elizaos/autonomous 2.0.0-alpha.10

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 (241) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +270 -0
  3. package/src/actions/emote.ts +101 -0
  4. package/src/actions/restart.ts +101 -0
  5. package/src/actions/send-message.ts +168 -0
  6. package/src/actions/stream-control.ts +439 -0
  7. package/src/actions/switch-stream-source.ts +126 -0
  8. package/src/actions/terminal.ts +186 -0
  9. package/src/api/agent-admin-routes.ts +178 -0
  10. package/src/api/agent-lifecycle-routes.ts +129 -0
  11. package/src/api/agent-model.ts +143 -0
  12. package/src/api/agent-transfer-routes.ts +211 -0
  13. package/src/api/apps-routes.ts +210 -0
  14. package/src/api/auth-routes.ts +90 -0
  15. package/src/api/bsc-trade.ts +736 -0
  16. package/src/api/bug-report-routes.ts +161 -0
  17. package/src/api/character-routes.ts +421 -0
  18. package/src/api/cloud-billing-routes.ts +598 -0
  19. package/src/api/cloud-compat-routes.ts +192 -0
  20. package/src/api/cloud-routes.ts +529 -0
  21. package/src/api/cloud-status-routes.ts +234 -0
  22. package/src/api/compat-utils.ts +154 -0
  23. package/src/api/connector-health.ts +135 -0
  24. package/src/api/coordinator-wiring.ts +179 -0
  25. package/src/api/credit-detection.ts +47 -0
  26. package/src/api/database.ts +1357 -0
  27. package/src/api/diagnostics-routes.ts +389 -0
  28. package/src/api/drop-service.ts +205 -0
  29. package/src/api/early-logs.ts +111 -0
  30. package/src/api/http-helpers.ts +252 -0
  31. package/src/api/index.ts +85 -0
  32. package/src/api/knowledge-routes.ts +1189 -0
  33. package/src/api/knowledge-service-loader.ts +92 -0
  34. package/src/api/memory-bounds.ts +121 -0
  35. package/src/api/memory-routes.ts +349 -0
  36. package/src/api/merkle-tree.ts +239 -0
  37. package/src/api/models-routes.ts +72 -0
  38. package/src/api/nfa-routes.ts +169 -0
  39. package/src/api/nft-verify.ts +188 -0
  40. package/src/api/og-tracker.ts +72 -0
  41. package/src/api/parse-action-block.ts +145 -0
  42. package/src/api/permissions-routes.ts +222 -0
  43. package/src/api/plugin-validation.ts +355 -0
  44. package/src/api/provider-switch-config.ts +455 -0
  45. package/src/api/registry-routes.ts +165 -0
  46. package/src/api/registry-service.ts +292 -0
  47. package/src/api/route-helpers.ts +21 -0
  48. package/src/api/sandbox-routes.ts +1480 -0
  49. package/src/api/server.ts +17674 -0
  50. package/src/api/signal-routes.ts +265 -0
  51. package/src/api/stream-persistence.ts +297 -0
  52. package/src/api/stream-route-state.ts +48 -0
  53. package/src/api/stream-routes.ts +1046 -0
  54. package/src/api/stream-voice-routes.ts +208 -0
  55. package/src/api/streaming-text.ts +129 -0
  56. package/src/api/streaming-types.ts +23 -0
  57. package/src/api/subscription-routes.ts +283 -0
  58. package/src/api/terminal-run-limits.ts +31 -0
  59. package/src/api/training-backend-check.ts +40 -0
  60. package/src/api/training-routes.ts +314 -0
  61. package/src/api/training-service-like.ts +46 -0
  62. package/src/api/trajectory-routes.ts +714 -0
  63. package/src/api/trigger-routes.ts +438 -0
  64. package/src/api/twitter-verify.ts +226 -0
  65. package/src/api/tx-service.ts +193 -0
  66. package/src/api/wallet-dex-prices.ts +206 -0
  67. package/src/api/wallet-evm-balance.ts +989 -0
  68. package/src/api/wallet-routes.ts +505 -0
  69. package/src/api/wallet-rpc.ts +523 -0
  70. package/src/api/wallet-trading-profile.ts +694 -0
  71. package/src/api/wallet.ts +745 -0
  72. package/src/api/whatsapp-routes.ts +282 -0
  73. package/src/api/zip-utils.ts +130 -0
  74. package/src/auth/anthropic.ts +63 -0
  75. package/src/auth/apply-stealth.ts +38 -0
  76. package/src/auth/claude-code-stealth.ts +141 -0
  77. package/src/auth/credentials.ts +226 -0
  78. package/src/auth/index.ts +18 -0
  79. package/src/auth/openai-codex.ts +94 -0
  80. package/src/auth/types.ts +24 -0
  81. package/src/awareness/registry.ts +220 -0
  82. package/src/bin.ts +10 -0
  83. package/src/cli/index.ts +36 -0
  84. package/src/cli/parse-duration.ts +43 -0
  85. package/src/cloud/auth.test.ts +370 -0
  86. package/src/cloud/auth.ts +176 -0
  87. package/src/cloud/backup.test.ts +150 -0
  88. package/src/cloud/backup.ts +50 -0
  89. package/src/cloud/base-url.ts +45 -0
  90. package/src/cloud/bridge-client.test.ts +481 -0
  91. package/src/cloud/bridge-client.ts +307 -0
  92. package/src/cloud/cloud-manager.test.ts +223 -0
  93. package/src/cloud/cloud-manager.ts +151 -0
  94. package/src/cloud/cloud-proxy.test.ts +122 -0
  95. package/src/cloud/cloud-proxy.ts +52 -0
  96. package/src/cloud/index.ts +23 -0
  97. package/src/cloud/reconnect.test.ts +178 -0
  98. package/src/cloud/reconnect.ts +108 -0
  99. package/src/cloud/validate-url.test.ts +147 -0
  100. package/src/cloud/validate-url.ts +176 -0
  101. package/src/config/character-schema.ts +44 -0
  102. package/src/config/config.ts +149 -0
  103. package/src/config/env-vars.ts +86 -0
  104. package/src/config/includes.ts +196 -0
  105. package/src/config/index.ts +15 -0
  106. package/src/config/object-utils.ts +10 -0
  107. package/src/config/paths.ts +92 -0
  108. package/src/config/plugin-auto-enable.ts +520 -0
  109. package/src/config/schema.ts +1342 -0
  110. package/src/config/telegram-custom-commands.ts +99 -0
  111. package/src/config/types.agent-defaults.ts +342 -0
  112. package/src/config/types.agents.ts +112 -0
  113. package/src/config/types.gateway.ts +243 -0
  114. package/src/config/types.hooks.ts +124 -0
  115. package/src/config/types.messages.ts +201 -0
  116. package/src/config/types.milady.ts +791 -0
  117. package/src/config/types.tools.ts +416 -0
  118. package/src/config/types.ts +7 -0
  119. package/src/config/zod-schema.agent-runtime.ts +777 -0
  120. package/src/config/zod-schema.core.ts +778 -0
  121. package/src/config/zod-schema.hooks.ts +139 -0
  122. package/src/config/zod-schema.providers-core.ts +1126 -0
  123. package/src/config/zod-schema.session.ts +98 -0
  124. package/src/config/zod-schema.ts +865 -0
  125. package/src/contracts/apps.ts +46 -0
  126. package/src/contracts/awareness.ts +56 -0
  127. package/src/contracts/config.ts +172 -0
  128. package/src/contracts/drop.ts +21 -0
  129. package/src/contracts/index.ts +8 -0
  130. package/src/contracts/onboarding.ts +592 -0
  131. package/src/contracts/permissions.ts +52 -0
  132. package/src/contracts/verification.ts +9 -0
  133. package/src/contracts/wallet.ts +503 -0
  134. package/src/diagnostics/integration-observability.ts +132 -0
  135. package/src/emotes/catalog.ts +655 -0
  136. package/src/external-modules.d.ts +7 -0
  137. package/src/hooks/discovery.test.ts +357 -0
  138. package/src/hooks/discovery.ts +231 -0
  139. package/src/hooks/eligibility.ts +146 -0
  140. package/src/hooks/hooks.test.ts +320 -0
  141. package/src/hooks/index.ts +8 -0
  142. package/src/hooks/loader.test.ts +418 -0
  143. package/src/hooks/loader.ts +256 -0
  144. package/src/hooks/registry.test.ts +168 -0
  145. package/src/hooks/registry.ts +74 -0
  146. package/src/hooks/types.ts +121 -0
  147. package/src/index.ts +19 -0
  148. package/src/onboarding-presets.ts +828 -0
  149. package/src/plugins/custom-rtmp/index.ts +40 -0
  150. package/src/providers/admin-trust.ts +76 -0
  151. package/src/providers/session-bridge.ts +143 -0
  152. package/src/providers/session-utils.ts +42 -0
  153. package/src/providers/simple-mode.ts +113 -0
  154. package/src/providers/ui-catalog.ts +135 -0
  155. package/src/providers/workspace-provider.ts +213 -0
  156. package/src/providers/workspace.ts +497 -0
  157. package/src/runtime/agent-event-service.ts +57 -0
  158. package/src/runtime/cloud-onboarding.test.ts +489 -0
  159. package/src/runtime/cloud-onboarding.ts +408 -0
  160. package/src/runtime/core-plugins.ts +53 -0
  161. package/src/runtime/custom-actions.ts +605 -0
  162. package/src/runtime/eliza.ts +4941 -0
  163. package/src/runtime/embedding-presets.ts +73 -0
  164. package/src/runtime/index.ts +8 -0
  165. package/src/runtime/milady-plugin.ts +180 -0
  166. package/src/runtime/onboarding-names.ts +76 -0
  167. package/src/runtime/release-plugin-policy.ts +119 -0
  168. package/src/runtime/restart.ts +59 -0
  169. package/src/runtime/trajectory-persistence.ts +2584 -0
  170. package/src/runtime/version.ts +6 -0
  171. package/src/security/audit-log.ts +222 -0
  172. package/src/security/network-policy.ts +91 -0
  173. package/src/server/index.ts +6 -0
  174. package/src/services/agent-export.ts +976 -0
  175. package/src/services/app-manager.ts +755 -0
  176. package/src/services/browser-capture.ts +215 -0
  177. package/src/services/coding-agent-context.ts +355 -0
  178. package/src/services/fallback-training-service.ts +196 -0
  179. package/src/services/index.ts +17 -0
  180. package/src/services/mcp-marketplace.ts +327 -0
  181. package/src/services/plugin-manager-types.ts +185 -0
  182. package/src/services/privy-wallets.ts +352 -0
  183. package/src/services/registry-client-app-meta.ts +201 -0
  184. package/src/services/registry-client-endpoints.ts +253 -0
  185. package/src/services/registry-client-local.ts +485 -0
  186. package/src/services/registry-client-network.ts +173 -0
  187. package/src/services/registry-client-queries.ts +176 -0
  188. package/src/services/registry-client-types.ts +104 -0
  189. package/src/services/registry-client.ts +366 -0
  190. package/src/services/remote-signing-service.ts +261 -0
  191. package/src/services/sandbox-engine.ts +753 -0
  192. package/src/services/sandbox-manager.ts +503 -0
  193. package/src/services/self-updater.ts +213 -0
  194. package/src/services/signal-pairing.ts +189 -0
  195. package/src/services/signing-policy.ts +230 -0
  196. package/src/services/skill-catalog-client.ts +195 -0
  197. package/src/services/skill-marketplace.ts +909 -0
  198. package/src/services/stream-manager.ts +707 -0
  199. package/src/services/tts-stream-bridge.ts +465 -0
  200. package/src/services/update-checker.ts +163 -0
  201. package/src/services/version-compat.ts +367 -0
  202. package/src/services/whatsapp-pairing.ts +279 -0
  203. package/src/shared/ui-catalog-prompt.ts +1158 -0
  204. package/src/test-support/process-helpers.ts +35 -0
  205. package/src/test-support/route-test-helpers.ts +113 -0
  206. package/src/test-support/test-helpers.ts +304 -0
  207. package/src/testing/index.ts +3 -0
  208. package/src/triggers/action.ts +342 -0
  209. package/src/triggers/runtime.ts +432 -0
  210. package/src/triggers/scheduling.ts +472 -0
  211. package/src/triggers/types.ts +133 -0
  212. package/src/types/app-hyperscape-routes-shim.d.ts +29 -0
  213. package/src/types/external-modules.d.ts +7 -0
  214. package/src/utils/exec-safety.ts +23 -0
  215. package/src/utils/number-parsing.ts +112 -0
  216. package/src/utils/spoken-text.ts +65 -0
  217. package/src/version-resolver.ts +60 -0
  218. package/test/api/agent-admin-routes.test.ts +160 -0
  219. package/test/api/agent-lifecycle-routes.test.ts +164 -0
  220. package/test/api/agent-transfer-routes.test.ts +136 -0
  221. package/test/api/apps-routes.test.ts +140 -0
  222. package/test/api/auth-routes.test.ts +160 -0
  223. package/test/api/bug-report-routes.test.ts +88 -0
  224. package/test/api/knowledge-routes.test.ts +73 -0
  225. package/test/api/lifecycle.test.ts +342 -0
  226. package/test/api/memory-routes.test.ts +74 -0
  227. package/test/api/models-routes.test.ts +112 -0
  228. package/test/api/nfa-routes.test.ts +78 -0
  229. package/test/api/permissions-routes.test.ts +185 -0
  230. package/test/api/registry-routes.test.ts +157 -0
  231. package/test/api/signal-routes.test.ts +113 -0
  232. package/test/api/subscription-routes.test.ts +90 -0
  233. package/test/api/trigger-routes.test.ts +87 -0
  234. package/test/api/wallet-routes.observability.test.ts +191 -0
  235. package/test/api/wallet-routes.test.ts +502 -0
  236. package/test/diagnostics/integration-observability.test.ts +135 -0
  237. package/test/security/audit-log.test.ts +229 -0
  238. package/test/security/network-policy.test.ts +143 -0
  239. package/test/services/version-compat.test.ts +127 -0
  240. package/tsconfig.build.json +21 -0
  241. package/tsconfig.json +19 -0
@@ -0,0 +1,112 @@
1
+ export interface ParseClampedIntegerOptions {
2
+ min?: number;
3
+ max?: number;
4
+ fallback?: number;
5
+ }
6
+
7
+ export interface ParseClampedNumberOptions {
8
+ min?: number;
9
+ max?: number;
10
+ fallback?: number;
11
+ }
12
+
13
+ export interface ParsePositiveNumberOptions {
14
+ fallback?: number;
15
+ floor?: boolean;
16
+ }
17
+
18
+ function sanitizeNumericText(value: string | null | undefined): string {
19
+ return value == null ? "" : value.trim();
20
+ }
21
+
22
+ function normalizeFallback(fallback: number | undefined): number | undefined {
23
+ return Number.isFinite(fallback) ? fallback : undefined;
24
+ }
25
+
26
+ export function parseClampedInteger(
27
+ value: string | null | undefined,
28
+ options: ParseClampedIntegerOptions & { fallback: number },
29
+ ): number;
30
+ export function parseClampedInteger(
31
+ value: string | null | undefined,
32
+ options?: ParseClampedIntegerOptions,
33
+ ): number | undefined;
34
+ export function parseClampedInteger(
35
+ value: string | null | undefined,
36
+ options: ParseClampedIntegerOptions = {},
37
+ ): number | undefined {
38
+ const raw = sanitizeNumericText(value);
39
+ if (!raw) return normalizeFallback(options.fallback);
40
+
41
+ const parsed = Number.parseInt(raw, 10);
42
+ if (!Number.isFinite(parsed)) return normalizeFallback(options.fallback);
43
+
44
+ const min = options.min;
45
+ if (min !== undefined && parsed < min) return min;
46
+
47
+ const max = options.max;
48
+ if (max !== undefined && parsed > max) return max;
49
+
50
+ return parsed;
51
+ }
52
+
53
+ export function parsePositiveInteger(
54
+ value: string | null | undefined,
55
+ fallback: number,
56
+ ): number;
57
+ export function parsePositiveInteger(
58
+ value: string | null | undefined,
59
+ fallback?: number,
60
+ ): number | undefined;
61
+ export function parsePositiveInteger(
62
+ value: string | null | undefined,
63
+ fallback?: number,
64
+ ): number | undefined {
65
+ const raw = sanitizeNumericText(value);
66
+ if (!raw) return normalizeFallback(fallback);
67
+
68
+ const parsed = Number(raw);
69
+ if (!Number.isFinite(parsed) || parsed <= 0) {
70
+ return normalizeFallback(fallback);
71
+ }
72
+
73
+ return Math.max(1, Math.floor(parsed));
74
+ }
75
+
76
+ export function parsePositiveFloat(
77
+ value: string | null | undefined,
78
+ options?: ParsePositiveNumberOptions,
79
+ ): number | undefined {
80
+ const raw = sanitizeNumericText(value);
81
+ if (!raw) return normalizeFallback(options?.fallback);
82
+
83
+ const parsed = Number(raw);
84
+ if (!Number.isFinite(parsed) || parsed <= 0) {
85
+ return normalizeFallback(options?.fallback);
86
+ }
87
+
88
+ return options?.floor ? Math.floor(parsed) : parsed;
89
+ }
90
+
91
+ export function parseClampedFloat(
92
+ value: string | null | undefined,
93
+ options: ParseClampedNumberOptions & { fallback: number },
94
+ ): number;
95
+ export function parseClampedFloat(
96
+ value: string | null | undefined,
97
+ options?: ParseClampedNumberOptions,
98
+ ): number | undefined;
99
+ export function parseClampedFloat(
100
+ value: string | null | undefined,
101
+ options: ParseClampedNumberOptions = {},
102
+ ): number | undefined {
103
+ const raw = sanitizeNumericText(value);
104
+ if (!raw) return normalizeFallback(options.fallback);
105
+
106
+ const parsed = Number(raw);
107
+ if (!Number.isFinite(parsed)) return normalizeFallback(options.fallback);
108
+
109
+ const min = options.min ?? -Infinity;
110
+ const max = options.max ?? Infinity;
111
+ return Math.max(min, Math.min(max, parsed));
112
+ }
@@ -0,0 +1,65 @@
1
+ function collapseWhitespace(input: string): string {
2
+ return input.replace(/\s+/g, " ").trim();
3
+ }
4
+
5
+ function stripUrls(input: string): string {
6
+ return input.replace(/\bhttps?:\/\/\S+/gi, " ");
7
+ }
8
+
9
+ function stripThinkingAndMarkup(input: string): string {
10
+ let text = input;
11
+ text = text.replace(/<think\b[^>]*>[\s\S]*?<\/think>/gi, " ");
12
+ text = text.replace(
13
+ /<(analysis|reasoning|scratchpad|tool_calls?|tools?)\b[^>]*>[\s\S]*?<\/\1>/gi,
14
+ " ",
15
+ );
16
+ text = text.replace(/```[\s\S]*?```/g, " ");
17
+ text = text.replace(/`([^`]+)`/g, "$1");
18
+ text = text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
19
+ text = text.replace(/<[^>\n]+>/g, " ");
20
+ text = stripUrls(text);
21
+ return text;
22
+ }
23
+
24
+ const NON_SPEECH_SEGMENT_PATTERNS = [
25
+ /\*{1,2}[^*\n]+\*{1,2}/g,
26
+ /\([^()]*\)/g,
27
+ /\[[^[\]]*\]/g,
28
+ /\{[^{}]*\}/g,
29
+ ];
30
+
31
+ function stripNonSpeechDirections(input: string): string {
32
+ let text = input;
33
+ while (true) {
34
+ const previous = text;
35
+ for (const pattern of NON_SPEECH_SEGMENT_PATTERNS) {
36
+ text = text.replace(pattern, " ");
37
+ }
38
+ if (text === previous) {
39
+ break;
40
+ }
41
+ }
42
+ return text.replace(/[*()[\]{}]+/g, " ");
43
+ }
44
+
45
+ function sanitizeSpeechPunctuation(input: string): string {
46
+ let text = input;
47
+ text = text.replace(/[•·■▪◦]/g, " ");
48
+ text = text.replace(/[“”]/g, '"');
49
+ text = text.replace(/[‘’]/g, "'");
50
+ text = text.replace(/[…]/g, "...");
51
+ text = text.replace(/[–—]/g, ", ");
52
+ text = text.replace(/\s*([,;:])\s*/g, "$1 ");
53
+ text = text.replace(/\s*([.!?])\s*/g, "$1 ");
54
+ text = text.replace(/[^\p{L}\p{N}\s.,!?'"%/$:+-]/gu, " ");
55
+ text = text.replace(/([,.!?])\1+/g, "$1");
56
+ text = text.replace(/^[,;:.!?]+/g, " ");
57
+ return text;
58
+ }
59
+
60
+ export function sanitizeSpeechText(input: string): string {
61
+ const normalized = input.normalize("NFKC");
62
+ const stripped = stripThinkingAndMarkup(normalized);
63
+ const withoutDirections = stripNonSpeechDirections(stripped);
64
+ return collapseWhitespace(sanitizeSpeechPunctuation(withoutDirections));
65
+ }
@@ -0,0 +1,60 @@
1
+ import { createRequire } from "node:module";
2
+ import process from "node:process";
3
+
4
+ declare const __MILADY_VERSION__: string | undefined;
5
+
6
+ const PACKAGE_JSON_CANDIDATE = "../../package.json";
7
+ const BUILD_INFO_CANDIDATES = [
8
+ "../../build-info.json",
9
+ "../build-info.json",
10
+ "./build-info.json",
11
+ ] as const;
12
+
13
+ function isModuleNotFound(err: unknown): boolean {
14
+ return (
15
+ typeof err === "object" &&
16
+ err !== null &&
17
+ "code" in err &&
18
+ (err as NodeJS.ErrnoException).code === "MODULE_NOT_FOUND"
19
+ );
20
+ }
21
+
22
+ function readVersionFromPackageJson(requireFn: NodeRequire): string | null {
23
+ try {
24
+ const pkg = requireFn(PACKAGE_JSON_CANDIDATE) as { version?: string };
25
+ return pkg.version ?? null;
26
+ } catch (err) {
27
+ if (isModuleNotFound(err)) {
28
+ return null;
29
+ }
30
+ throw err;
31
+ }
32
+ }
33
+
34
+ function readVersionFromBuildInfo(requireFn: NodeRequire): string | null {
35
+ for (const candidate of BUILD_INFO_CANDIDATES) {
36
+ try {
37
+ const info = requireFn(candidate) as { version?: string };
38
+ if (info.version) {
39
+ return info.version;
40
+ }
41
+ } catch (err) {
42
+ if (!isModuleNotFound(err)) {
43
+ throw err;
44
+ }
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+
50
+ export function resolveMiladyVersion(moduleUrl: string): string {
51
+ const requireFn = createRequire(moduleUrl);
52
+
53
+ return (
54
+ (typeof __MILADY_VERSION__ === "string" && __MILADY_VERSION__) ||
55
+ process.env.MILADY_BUNDLED_VERSION ||
56
+ readVersionFromPackageJson(requireFn) ||
57
+ readVersionFromBuildInfo(requireFn) ||
58
+ "0.0.0"
59
+ );
60
+ }
@@ -0,0 +1,160 @@
1
+ import { describe, test, expect, vi } from "vitest";
2
+ import {
3
+ createMockIncomingMessage,
4
+ createMockHttpResponse,
5
+ } from "../../src/test-support/test-helpers";
6
+ import { handleAgentAdminRoutes } from "../../src/api/agent-admin-routes";
7
+ import type { AgentAdminRouteContext } from "../../src/api/agent-admin-routes";
8
+
9
+ function buildCtx(
10
+ method: string,
11
+ pathname: string,
12
+ overrides?: Partial<AgentAdminRouteContext>,
13
+ ): AgentAdminRouteContext & { getStatus: () => number; getJson: () => unknown } {
14
+ const { res, getStatus, getJson } = createMockHttpResponse();
15
+ const req = createMockIncomingMessage({ method, url: pathname });
16
+ const ctx = {
17
+ req,
18
+ res,
19
+ method,
20
+ pathname,
21
+ json: vi.fn((r, data, status = 200) => {
22
+ r.writeHead(status);
23
+ r.end(JSON.stringify(data));
24
+ }),
25
+ error: vi.fn((r, msg, status = 500) => {
26
+ r.writeHead(status);
27
+ r.end(JSON.stringify({ error: msg }));
28
+ }),
29
+ state: {
30
+ runtime: null,
31
+ config: {},
32
+ agentState: "running" as const,
33
+ agentName: "TestAgent",
34
+ model: "gpt-4",
35
+ startedAt: Date.now(),
36
+ chatRoomId: null,
37
+ chatUserId: null,
38
+ chatConnectionReady: null,
39
+ chatConnectionPromise: null,
40
+ pendingRestartReasons: [],
41
+ },
42
+ onRestart: undefined,
43
+ onRuntimeSwapped: undefined,
44
+ resolveStateDir: () => "/tmp/milady-state",
45
+ resolvePath: (value: string) => value,
46
+ getHomeDir: () => "/home/test",
47
+ isSafeResetStateDir: () => true,
48
+ stateDirExists: () => false,
49
+ removeStateDir: vi.fn(),
50
+ logWarn: vi.fn(),
51
+ ...overrides,
52
+ } as AgentAdminRouteContext & {
53
+ getStatus: () => number;
54
+ getJson: () => unknown;
55
+ };
56
+ (ctx as any).getStatus = getStatus;
57
+ (ctx as any).getJson = getJson;
58
+ return ctx;
59
+ }
60
+
61
+ describe("agent-admin-routes", () => {
62
+ describe("POST /api/agent/restart", () => {
63
+ test("returns 501 when no onRestart handler is registered", async () => {
64
+ const ctx = buildCtx("POST", "/api/agent/restart");
65
+ const handled = await handleAgentAdminRoutes(ctx);
66
+ expect(handled).toBe(true);
67
+ expect(ctx.error).toHaveBeenCalledOnce();
68
+ const args = (ctx.error as ReturnType<typeof vi.fn>).mock.calls[0];
69
+ expect(args[2]).toBe(501);
70
+ expect(args[1]).toContain("not supported");
71
+ });
72
+
73
+ test("returns 409 when agent is already restarting", async () => {
74
+ const ctx = buildCtx("POST", "/api/agent/restart", {
75
+ onRestart: vi.fn(async () => null),
76
+ state: {
77
+ runtime: null,
78
+ config: {},
79
+ agentState: "restarting",
80
+ agentName: "TestAgent",
81
+ model: "gpt-4",
82
+ startedAt: Date.now(),
83
+ chatRoomId: null,
84
+ chatUserId: null,
85
+ chatConnectionReady: null,
86
+ chatConnectionPromise: null,
87
+ pendingRestartReasons: [],
88
+ },
89
+ });
90
+ const handled = await handleAgentAdminRoutes(ctx);
91
+ expect(handled).toBe(true);
92
+ expect(ctx.error).toHaveBeenCalledOnce();
93
+ const args = (ctx.error as ReturnType<typeof vi.fn>).mock.calls[0];
94
+ expect(args[2]).toBe(409);
95
+ expect(args[1]).toContain("already in progress");
96
+ });
97
+
98
+ test("returns success when onRestart returns a new runtime", async () => {
99
+ const mockRuntime = {
100
+ character: { name: "NewAgent" },
101
+ getSetting: () => undefined,
102
+ modelProvider: "openai",
103
+ } as any;
104
+ const onRestart = vi.fn(async () => mockRuntime);
105
+ const onRuntimeSwapped = vi.fn();
106
+ const ctx = buildCtx("POST", "/api/agent/restart", {
107
+ onRestart,
108
+ onRuntimeSwapped,
109
+ });
110
+ const handled = await handleAgentAdminRoutes(ctx);
111
+ expect(handled).toBe(true);
112
+ expect(ctx.json).toHaveBeenCalledOnce();
113
+ const payload = (ctx.json as ReturnType<typeof vi.fn>).mock.calls[0][1];
114
+ expect(payload.ok).toBe(true);
115
+ expect(payload.pendingRestart).toBe(false);
116
+ expect(ctx.state.agentState).toBe("running");
117
+ expect(onRuntimeSwapped).toHaveBeenCalled();
118
+ });
119
+
120
+ test("returns 500 when onRestart returns null", async () => {
121
+ const ctx = buildCtx("POST", "/api/agent/restart", {
122
+ onRestart: vi.fn(async () => null),
123
+ });
124
+ const handled = await handleAgentAdminRoutes(ctx);
125
+ expect(handled).toBe(true);
126
+ expect(ctx.error).toHaveBeenCalledOnce();
127
+ const args = (ctx.error as ReturnType<typeof vi.fn>).mock.calls[0];
128
+ expect(args[2]).toBe(500);
129
+ expect(args[1]).toContain("failed to re-initialize");
130
+ expect(ctx.state.agentState).toBe("running");
131
+ });
132
+
133
+ test("returns 500 when onRestart throws", async () => {
134
+ const ctx = buildCtx("POST", "/api/agent/restart", {
135
+ onRestart: vi.fn(async () => {
136
+ throw new Error("boom");
137
+ }),
138
+ });
139
+ const handled = await handleAgentAdminRoutes(ctx);
140
+ expect(handled).toBe(true);
141
+ expect(ctx.error).toHaveBeenCalledOnce();
142
+ const args = (ctx.error as ReturnType<typeof vi.fn>).mock.calls[0];
143
+ expect(args[2]).toBe(500);
144
+ expect(args[1]).toContain("boom");
145
+ expect(ctx.state.agentState).toBe("running");
146
+ });
147
+ });
148
+
149
+ describe("routing", () => {
150
+ test("unrelated path returns false", async () => {
151
+ const ctx = buildCtx("GET", "/api/other");
152
+ expect(await handleAgentAdminRoutes(ctx)).toBe(false);
153
+ });
154
+
155
+ test("GET to restart endpoint returns false (wrong method)", async () => {
156
+ const ctx = buildCtx("GET", "/api/agent/restart");
157
+ expect(await handleAgentAdminRoutes(ctx)).toBe(false);
158
+ });
159
+ });
160
+ });
@@ -0,0 +1,164 @@
1
+ import { describe, test, expect, vi } from "vitest";
2
+ import {
3
+ createMockIncomingMessage,
4
+ createMockHttpResponse,
5
+ } from "../../src/test-support/test-helpers";
6
+ import { handleAgentLifecycleRoutes } from "../../src/api/agent-lifecycle-routes";
7
+ import type {
8
+ AgentLifecycleRouteContext,
9
+ AgentLifecycleRouteState,
10
+ } from "../../src/api/agent-lifecycle-routes";
11
+
12
+ function buildState(
13
+ overrides?: Partial<AgentLifecycleRouteState>,
14
+ ): AgentLifecycleRouteState {
15
+ return {
16
+ runtime: null,
17
+ agentState: "not_started",
18
+ agentName: "test-agent",
19
+ model: undefined,
20
+ startedAt: undefined,
21
+ ...overrides,
22
+ };
23
+ }
24
+
25
+ function buildCtx(
26
+ method: string,
27
+ pathname: string,
28
+ state: AgentLifecycleRouteState,
29
+ overrides?: Partial<AgentLifecycleRouteContext>,
30
+ ): AgentLifecycleRouteContext {
31
+ const { res } = createMockHttpResponse();
32
+ return {
33
+ req: createMockIncomingMessage({ method, url: pathname }),
34
+ res,
35
+ method,
36
+ pathname,
37
+ json: vi.fn((r, data, status = 200) => {
38
+ r.writeHead(status);
39
+ r.end(JSON.stringify(data));
40
+ }),
41
+ error: vi.fn((r, msg, status = 500) => {
42
+ r.writeHead(status);
43
+ r.end(JSON.stringify({ error: msg }));
44
+ }),
45
+ readJsonBody: vi.fn(async () => ({})),
46
+ state,
47
+ ...overrides,
48
+ } as AgentLifecycleRouteContext;
49
+ }
50
+
51
+ describe("agent-lifecycle-routes", () => {
52
+ describe("POST /api/agent/start", () => {
53
+ test("transitions to paused and sets startedAt", async () => {
54
+ const state = buildState({ agentState: "not_started" });
55
+ const ctx = buildCtx("POST", "/api/agent/start", state);
56
+ const handled = await handleAgentLifecycleRoutes(ctx);
57
+ expect(handled).toBe(true);
58
+ expect(state.agentState).toBe("paused");
59
+ expect(state.startedAt).toBeDefined();
60
+ expect(ctx.json).toHaveBeenCalled();
61
+ const payload = (ctx.json as ReturnType<typeof vi.fn>).mock.calls[0][1];
62
+ expect(payload.ok).toBe(true);
63
+ expect(payload.status.state).toBe("paused");
64
+ });
65
+ });
66
+
67
+ describe("POST /api/agent/stop", () => {
68
+ test("transitions to stopped and clears model/startedAt", async () => {
69
+ const state = buildState({
70
+ agentState: "running",
71
+ startedAt: Date.now(),
72
+ model: "gpt-4",
73
+ });
74
+ const ctx = buildCtx("POST", "/api/agent/stop", state);
75
+ await handleAgentLifecycleRoutes(ctx);
76
+ expect(state.agentState).toBe("stopped");
77
+ expect(state.startedAt).toBeUndefined();
78
+ expect(state.model).toBeUndefined();
79
+ });
80
+ });
81
+
82
+ describe("POST /api/agent/pause", () => {
83
+ test("transitions to paused and preserves startedAt", async () => {
84
+ const started = Date.now() - 5000;
85
+ const state = buildState({
86
+ agentState: "running",
87
+ startedAt: started,
88
+ model: "gpt-4",
89
+ });
90
+ const ctx = buildCtx("POST", "/api/agent/pause", state);
91
+ await handleAgentLifecycleRoutes(ctx);
92
+ expect(state.agentState).toBe("paused");
93
+ expect(state.startedAt).toBe(started);
94
+ });
95
+ });
96
+
97
+ describe("POST /api/agent/resume", () => {
98
+ test("transitions to running", async () => {
99
+ const state = buildState({ agentState: "paused" });
100
+ const ctx = buildCtx("POST", "/api/agent/resume", state);
101
+ await handleAgentLifecycleRoutes(ctx);
102
+ expect(state.agentState).toBe("running");
103
+ });
104
+ });
105
+
106
+ describe("GET /api/agent/autonomy", () => {
107
+ test("returns enabled false when no runtime", async () => {
108
+ const state = buildState();
109
+ const ctx = buildCtx("GET", "/api/agent/autonomy", state);
110
+ await handleAgentLifecycleRoutes(ctx);
111
+ expect(ctx.json).toHaveBeenCalled();
112
+ const payload = (ctx.json as ReturnType<typeof vi.fn>).mock.calls[0][1];
113
+ expect(payload.enabled).toBe(false);
114
+ });
115
+
116
+ test("returns enabled true when runtime has autonomy", async () => {
117
+ const state = buildState({
118
+ runtime: { enableAutonomy: true } as any,
119
+ });
120
+ const ctx = buildCtx("GET", "/api/agent/autonomy", state);
121
+ await handleAgentLifecycleRoutes(ctx);
122
+ const payload = (ctx.json as ReturnType<typeof vi.fn>).mock.calls[0][1];
123
+ expect(payload.enabled).toBe(true);
124
+ });
125
+ });
126
+
127
+ describe("POST /api/agent/autonomy", () => {
128
+ test("sets autonomy on runtime", async () => {
129
+ const runtime = { enableAutonomy: false } as any;
130
+ const state = buildState({ runtime });
131
+ const ctx = buildCtx("POST", "/api/agent/autonomy", state, {
132
+ readJsonBody: vi.fn(async () => ({ enabled: true })),
133
+ });
134
+ await handleAgentLifecycleRoutes(ctx);
135
+ expect(runtime.enableAutonomy).toBe(true);
136
+ });
137
+
138
+ test("rejects non-boolean enabled", async () => {
139
+ const state = buildState({ runtime: {} as any });
140
+ const ctx = buildCtx("POST", "/api/agent/autonomy", state, {
141
+ readJsonBody: vi.fn(async () => ({ enabled: "yes" })),
142
+ });
143
+ await handleAgentLifecycleRoutes(ctx);
144
+ expect(ctx.error).toHaveBeenCalled();
145
+ });
146
+
147
+ test("rejects when no runtime", async () => {
148
+ const state = buildState();
149
+ const ctx = buildCtx("POST", "/api/agent/autonomy", state, {
150
+ readJsonBody: vi.fn(async () => ({ enabled: true })),
151
+ });
152
+ await handleAgentLifecycleRoutes(ctx);
153
+ expect(ctx.error).toHaveBeenCalled();
154
+ });
155
+ });
156
+
157
+ describe("routing", () => {
158
+ test("unrelated path returns false", async () => {
159
+ const state = buildState();
160
+ const ctx = buildCtx("GET", "/api/other", state);
161
+ expect(await handleAgentLifecycleRoutes(ctx)).toBe(false);
162
+ });
163
+ });
164
+ });
@@ -0,0 +1,136 @@
1
+ import { describe, test, expect, vi } from "vitest";
2
+ import {
3
+ createMockIncomingMessage,
4
+ createMockHttpResponse,
5
+ } from "../../src/test-support/test-helpers";
6
+ import { handleAgentTransferRoutes } from "../../src/api/agent-transfer-routes";
7
+ import type { AgentTransferRouteContext } from "../../src/api/agent-transfer-routes";
8
+
9
+ function buildCtx(
10
+ method: string,
11
+ pathname: string,
12
+ overrides?: Partial<AgentTransferRouteContext>,
13
+ ): AgentTransferRouteContext & {
14
+ getStatus: () => number;
15
+ getJson: () => unknown;
16
+ } {
17
+ const { res, getStatus, getJson } = createMockHttpResponse();
18
+ const req = createMockIncomingMessage({ method, url: pathname });
19
+ const ctx = {
20
+ req,
21
+ res,
22
+ method,
23
+ pathname,
24
+ json: vi.fn((r, data, status = 200) => {
25
+ r.writeHead(status);
26
+ r.end(JSON.stringify(data));
27
+ }),
28
+ error: vi.fn((r, msg, status = 500) => {
29
+ r.writeHead(status);
30
+ r.end(JSON.stringify({ error: msg }));
31
+ }),
32
+ readJsonBody: vi.fn(async () => null),
33
+ state: {
34
+ runtime: null,
35
+ },
36
+ exportAgent: vi.fn(async () => Buffer.from("exported")),
37
+ estimateExportSize: vi.fn(async () => ({ estimatedBytes: 1024 })),
38
+ importAgent: vi.fn(async () => ({ ok: true })),
39
+ isAgentExportError: () => false,
40
+ ...overrides,
41
+ } as AgentTransferRouteContext & {
42
+ getStatus: () => number;
43
+ getJson: () => unknown;
44
+ };
45
+ (ctx as any).getStatus = getStatus;
46
+ (ctx as any).getJson = getJson;
47
+ return ctx;
48
+ }
49
+
50
+ describe("agent-transfer-routes", () => {
51
+ describe("POST /api/agent/export", () => {
52
+ test("returns 503 when no runtime", async () => {
53
+ const ctx = buildCtx("POST", "/api/agent/export");
54
+ const handled = await handleAgentTransferRoutes(ctx);
55
+ expect(handled).toBe(true);
56
+ expect(ctx.error).toHaveBeenCalledOnce();
57
+ const args = (ctx.error as ReturnType<typeof vi.fn>).mock.calls[0];
58
+ expect(args[2]).toBe(503);
59
+ expect(args[1]).toContain("not running");
60
+ });
61
+
62
+ test("returns 400 when password is missing", async () => {
63
+ const mockRuntime = { character: { name: "Agent" } } as any;
64
+ const ctx = buildCtx("POST", "/api/agent/export", {
65
+ state: { runtime: mockRuntime },
66
+ readJsonBody: vi.fn(async () => ({})),
67
+ });
68
+ const handled = await handleAgentTransferRoutes(ctx);
69
+ expect(handled).toBe(true);
70
+ expect(ctx.error).toHaveBeenCalledOnce();
71
+ const args = (ctx.error as ReturnType<typeof vi.fn>).mock.calls[0];
72
+ expect(args[2]).toBe(400);
73
+ });
74
+
75
+ test("exports successfully with valid password", async () => {
76
+ const mockRuntime = { character: { name: "Agent" } } as any;
77
+ const ctx = buildCtx("POST", "/api/agent/export", {
78
+ state: { runtime: mockRuntime },
79
+ readJsonBody: vi.fn(async () => ({
80
+ password: "secret1234",
81
+ includeLogs: false,
82
+ })),
83
+ });
84
+ const handled = await handleAgentTransferRoutes(ctx);
85
+ expect(handled).toBe(true);
86
+ expect(ctx.exportAgent).toHaveBeenCalledOnce();
87
+ expect(ctx.error).not.toHaveBeenCalled();
88
+ });
89
+ });
90
+
91
+ describe("GET /api/agent/export/estimate", () => {
92
+ test("returns 503 when no runtime", async () => {
93
+ const ctx = buildCtx("GET", "/api/agent/export/estimate");
94
+ const handled = await handleAgentTransferRoutes(ctx);
95
+ expect(handled).toBe(true);
96
+ expect(ctx.error).toHaveBeenCalledOnce();
97
+ const args = (ctx.error as ReturnType<typeof vi.fn>).mock.calls[0];
98
+ expect(args[2]).toBe(503);
99
+ });
100
+
101
+ test("returns estimate when runtime is available", async () => {
102
+ const mockRuntime = { character: { name: "Agent" } } as any;
103
+ const ctx = buildCtx("GET", "/api/agent/export/estimate", {
104
+ state: { runtime: mockRuntime },
105
+ });
106
+ const handled = await handleAgentTransferRoutes(ctx);
107
+ expect(handled).toBe(true);
108
+ expect(ctx.json).toHaveBeenCalledOnce();
109
+ expect(ctx.estimateExportSize).toHaveBeenCalledWith(mockRuntime);
110
+ });
111
+ });
112
+
113
+ describe("POST /api/agent/import", () => {
114
+ test("returns 503 when no runtime", async () => {
115
+ const ctx = buildCtx("POST", "/api/agent/import");
116
+ const handled = await handleAgentTransferRoutes(ctx);
117
+ expect(handled).toBe(true);
118
+ expect(ctx.error).toHaveBeenCalledOnce();
119
+ const args = (ctx.error as ReturnType<typeof vi.fn>).mock.calls[0];
120
+ expect(args[2]).toBe(503);
121
+ expect(args[1]).toContain("not running");
122
+ });
123
+ });
124
+
125
+ describe("routing", () => {
126
+ test("unrelated path returns false", async () => {
127
+ const ctx = buildCtx("GET", "/api/other");
128
+ expect(await handleAgentTransferRoutes(ctx)).toBe(false);
129
+ });
130
+
131
+ test("wrong method for export returns false", async () => {
132
+ const ctx = buildCtx("GET", "/api/agent/export");
133
+ expect(await handleAgentTransferRoutes(ctx)).toBe(false);
134
+ });
135
+ });
136
+ });