@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,213 @@
1
+ /**
2
+ * Reads workspace bootstrap files and injects them into agent context.
3
+ *
4
+ * Also provides coding agent context enrichment: when coding-agent metadata
5
+ * is present on the inbound message, the provider appends a summary of the
6
+ * current coding session state (active iteration, recent errors, pending
7
+ * feedback) so the LLM has full awareness during the autonomous coding loop.
8
+ */
9
+
10
+ import {
11
+ ChannelType,
12
+ type IAgentRuntime,
13
+ logger,
14
+ type Memory,
15
+ type Provider,
16
+ type ProviderResult,
17
+ type State,
18
+ } from "@elizaos/core";
19
+ import type { CodingAgentContext } from "../services/coding-agent-context";
20
+ import {
21
+ DEFAULT_AGENT_WORKSPACE_DIR,
22
+ filterBootstrapFilesForSession,
23
+ loadWorkspaceBootstrapFiles,
24
+ type WorkspaceBootstrapFile,
25
+ } from "./workspace";
26
+
27
+ const DEFAULT_MAX_CHARS = 20_000;
28
+ /** Hard cap on total workspace context to prevent prompt explosion. */
29
+ const MAX_TOTAL_WORKSPACE_CHARS = 100_000;
30
+ const CACHE_TTL_MS = 60_000;
31
+
32
+ // Per-workspace cache so multi-agent doesn't thrash.
33
+ const cache = new Map<
34
+ string,
35
+ { files: WorkspaceBootstrapFile[]; at: number }
36
+ >();
37
+ /** Maximum number of workspace directories to cache simultaneously. */
38
+ const MAX_CACHE_ENTRIES = 20;
39
+
40
+ async function getFiles(dir: string): Promise<WorkspaceBootstrapFile[]> {
41
+ const now = Date.now();
42
+ const entry = cache.get(dir);
43
+ if (entry && now - entry.at < CACHE_TTL_MS) return entry.files;
44
+
45
+ // Evict expired entries and enforce size cap before inserting
46
+ for (const [key, val] of cache) {
47
+ if (now - val.at >= CACHE_TTL_MS) cache.delete(key);
48
+ }
49
+ if (cache.size >= MAX_CACHE_ENTRIES) {
50
+ // Remove the oldest entry
51
+ const oldest = cache.keys().next().value;
52
+ if (oldest) cache.delete(oldest);
53
+ }
54
+
55
+ const files = await loadWorkspaceBootstrapFiles(dir);
56
+ cache.set(dir, { files, at: now });
57
+ return files;
58
+ }
59
+
60
+ /** @internal Exported for testing. */
61
+ export function truncate(content: string, max: number): string {
62
+ if (content.length <= max) return content;
63
+ return `${content.slice(0, max)}\n\n[... truncated at ${max.toLocaleString()} chars]`;
64
+ }
65
+
66
+ /** @internal Exported for testing. */
67
+ export function buildContext(
68
+ files: WorkspaceBootstrapFile[],
69
+ maxChars: number,
70
+ ): string {
71
+ const sections: string[] = [];
72
+ let totalChars = 0;
73
+ for (const f of files) {
74
+ if (f.missing || !f.content?.trim()) continue;
75
+ const trimmed = f.content.trim();
76
+ // Per-file truncation
77
+ const text = truncate(trimmed, maxChars);
78
+ const tag = text.length > trimmed.length ? " [TRUNCATED]" : "";
79
+ const section = `### ${f.name}${tag}\n\n${text}`;
80
+ // Stop adding files if the total would exceed the hard cap
81
+ if (
82
+ totalChars + section.length > MAX_TOTAL_WORKSPACE_CHARS &&
83
+ sections.length > 0
84
+ ) {
85
+ break;
86
+ }
87
+ sections.push(section);
88
+ totalChars += section.length;
89
+ }
90
+ if (sections.length === 0) return "";
91
+ return `## Project Context (Workspace)\n\n${sections.join("\n\n---\n\n")}`;
92
+ }
93
+
94
+ /** @internal Exported for testing. Builds a summary of the coding agent session. */
95
+ export function buildCodingAgentSummary(ctx: CodingAgentContext): string {
96
+ const lines: string[] = [];
97
+
98
+ lines.push("## Coding Agent Session");
99
+ lines.push("");
100
+ lines.push(`**Task:** ${ctx.taskDescription}`);
101
+ lines.push(`**Working Directory:** ${ctx.workingDirectory}`);
102
+ lines.push(`**Connector:** ${ctx.connector.type}`);
103
+ lines.push(`**Mode:** ${ctx.interactionMode}`);
104
+ lines.push(`**Active:** ${ctx.active ? "yes" : "no"}`);
105
+
106
+ if (!ctx.connector.available) {
107
+ lines.push(`**Connector Status:** unavailable`);
108
+ }
109
+
110
+ // Errors from the last iteration
111
+ const lastIteration = ctx.iterations[ctx.iterations.length - 1];
112
+ if (lastIteration && lastIteration.errors.length > 0) {
113
+ lines.push("");
114
+ lines.push("### Errors to Resolve");
115
+ for (const err of lastIteration.errors) {
116
+ const loc = err.filePath
117
+ ? err.line
118
+ ? ` (${err.filePath}:${err.line})`
119
+ : ` (${err.filePath})`
120
+ : "";
121
+ lines.push(`- [${err.category}]${loc}: ${err.message}`);
122
+ }
123
+ }
124
+
125
+ // Human feedback
126
+ const pendingFeedback = ctx.allFeedback.filter(
127
+ (fb) => !fb.iterationRef || fb.iterationRef >= ctx.iterations.length - 1,
128
+ );
129
+ if (pendingFeedback.length > 0) {
130
+ lines.push("");
131
+ lines.push("### Human Feedback");
132
+ for (const fb of pendingFeedback) {
133
+ lines.push(`- [${fb.type}]: ${fb.text}`);
134
+ }
135
+ }
136
+
137
+ // Recent commands from the last iteration
138
+ if (lastIteration && lastIteration.commandResults.length > 0) {
139
+ lines.push("");
140
+ lines.push("### Recent Commands");
141
+ for (const cmd of lastIteration.commandResults.slice(-5)) {
142
+ const status = cmd.success ? "OK" : `FAIL(${cmd.exitCode})`;
143
+ lines.push(`- \`${cmd.command}\` → ${status}`);
144
+ if (cmd.stdout?.trim()) {
145
+ lines.push(` stdout: ${truncate(cmd.stdout.trim(), 200)}`);
146
+ }
147
+ if (cmd.stderr?.trim()) {
148
+ lines.push(` stderr: ${truncate(cmd.stderr.trim(), 200)}`);
149
+ }
150
+ }
151
+ }
152
+
153
+ return lines.join("\n");
154
+ }
155
+
156
+ export function createWorkspaceProvider(options?: {
157
+ workspaceDir?: string;
158
+ maxCharsPerFile?: number;
159
+ }): Provider {
160
+ const dir = options?.workspaceDir ?? DEFAULT_AGENT_WORKSPACE_DIR;
161
+ const maxChars = options?.maxCharsPerFile ?? DEFAULT_MAX_CHARS;
162
+
163
+ return {
164
+ name: "workspaceContext",
165
+ description:
166
+ "Workspace bootstrap files (AGENTS.md, TOOLS.md, IDENTITY.md, etc.) and coding agent context",
167
+ position: 10,
168
+
169
+ async get(
170
+ _runtime: IAgentRuntime,
171
+ message: Memory,
172
+ _state: State,
173
+ ): Promise<ProviderResult> {
174
+ const channelType = message.content?.channelType;
175
+ if (
176
+ channelType === ChannelType.VOICE_DM ||
177
+ channelType === ChannelType.VOICE_GROUP
178
+ ) {
179
+ return {
180
+ text: "",
181
+ data: {
182
+ workspaceDir: dir,
183
+ skipped: "voice_channel",
184
+ },
185
+ };
186
+ }
187
+
188
+ try {
189
+ const allFiles = await getFiles(dir);
190
+ const meta = message.metadata as Record<string, unknown> | undefined;
191
+ const sessionKey =
192
+ typeof meta?.sessionKey === "string" ? meta.sessionKey : undefined;
193
+ const files = filterBootstrapFilesForSession(allFiles, sessionKey);
194
+ const text = buildContext(files, maxChars);
195
+
196
+ return {
197
+ text,
198
+ data: {
199
+ workspaceDir: dir,
200
+ },
201
+ };
202
+ } catch (err) {
203
+ logger.warn(
204
+ `[workspace-provider] Failed to load workspace context: ${err instanceof Error ? err.message : err}`,
205
+ );
206
+ return {
207
+ text: `[Workspace context unavailable: ${err instanceof Error ? err.message : err}]`,
208
+ data: {},
209
+ };
210
+ }
211
+ },
212
+ };
213
+ }
@@ -0,0 +1,497 @@
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import * as elizaCore from "@elizaos/core";
6
+ import { resolveUserPath } from "../config/paths";
7
+
8
+ export interface RunCommandResult {
9
+ code: number;
10
+ stdout: string;
11
+ stderr: string;
12
+ }
13
+
14
+ export interface RunCommandOptions {
15
+ cwd?: string;
16
+ timeoutMs?: number;
17
+ env?: NodeJS.ProcessEnv;
18
+ }
19
+
20
+ /**
21
+ * Runs a command with an optional timeout.
22
+ * Returns { code, stdout, stderr }.
23
+ * Rejects if the process cannot be spawned or the timeout fires.
24
+ */
25
+ export function runCommandWithTimeout(
26
+ argv: string[],
27
+ opts: RunCommandOptions = {},
28
+ ): Promise<RunCommandResult> {
29
+ const [cmd, ...args] = argv;
30
+ if (!cmd) {
31
+ return Promise.reject(new Error("runCommandWithTimeout: empty argv"));
32
+ }
33
+
34
+ return new Promise<RunCommandResult>((resolve, reject) => {
35
+ const child = spawn(cmd, args, {
36
+ cwd: opts.cwd,
37
+ env: opts.env ?? process.env,
38
+ stdio: ["ignore", "pipe", "pipe"],
39
+ });
40
+
41
+ const stdoutChunks: Buffer[] = [];
42
+ const stderrChunks: Buffer[] = [];
43
+
44
+ child.stdout.on("data", (chunk: Buffer) => stdoutChunks.push(chunk));
45
+ child.stderr.on("data", (chunk: Buffer) => stderrChunks.push(chunk));
46
+
47
+ let timedOut = false;
48
+ let timer: ReturnType<typeof setTimeout> | undefined;
49
+
50
+ if (opts.timeoutMs && opts.timeoutMs > 0) {
51
+ timer = setTimeout(() => {
52
+ timedOut = true;
53
+ child.kill("SIGKILL");
54
+ }, opts.timeoutMs);
55
+ }
56
+
57
+ child.on("error", (err) => {
58
+ if (timer) clearTimeout(timer);
59
+ reject(err);
60
+ });
61
+
62
+ child.on("close", (exitCode) => {
63
+ if (timer) clearTimeout(timer);
64
+
65
+ if (timedOut) {
66
+ reject(
67
+ new Error(
68
+ `Command timed out after ${opts.timeoutMs}ms: ${argv.join(" ")}`,
69
+ ),
70
+ );
71
+ return;
72
+ }
73
+
74
+ resolve({
75
+ code: exitCode ?? 1,
76
+ stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
77
+ stderr: Buffer.concat(stderrChunks).toString("utf-8"),
78
+ });
79
+ });
80
+ });
81
+ }
82
+
83
+ export function resolveDefaultAgentWorkspaceDir(
84
+ env: NodeJS.ProcessEnv = process.env,
85
+ homedir: () => string = os.homedir,
86
+ ): string {
87
+ const profile = env.MILADY_PROFILE?.trim();
88
+ if (profile && profile.toLowerCase() !== "default") {
89
+ return path.join(homedir(), ".milady", `workspace-${profile}`);
90
+ }
91
+ return path.join(homedir(), ".milady", "workspace");
92
+ }
93
+
94
+ export const DEFAULT_AGENT_WORKSPACE_DIR = resolveDefaultAgentWorkspaceDir();
95
+ const DEFAULT_AGENTS_FILENAME = "AGENTS.md";
96
+ const DEFAULT_TOOLS_FILENAME = "TOOLS.md";
97
+ const DEFAULT_IDENTITY_FILENAME = "IDENTITY.md";
98
+ const DEFAULT_USER_FILENAME = "USER.md";
99
+ const DEFAULT_HEARTBEAT_FILENAME = "HEARTBEAT.md";
100
+ const DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md";
101
+ const DEFAULT_MEMORY_FILENAME = "MEMORY.md";
102
+ const DEFAULT_MEMORY_ALT_FILENAME = "memory.md";
103
+
104
+ /** Inline workspace bootstrap templates — no external files needed. */
105
+ const WORKSPACE_TEMPLATES: Record<string, string> = {
106
+ [DEFAULT_AGENTS_FILENAME]: `# Agents
107
+
108
+ You are an autonomous AI agent powered by elizaOS.
109
+
110
+ ## Capabilities
111
+
112
+ - Respond to user messages conversationally
113
+ - Execute actions and use available tools
114
+ - Access and manage knowledge from your workspace
115
+ - Maintain context across conversations
116
+
117
+ ## Guidelines
118
+
119
+ - Be helpful, concise, and accurate
120
+ - Ask for clarification when instructions are ambiguous
121
+ - Use tools when they would help accomplish the user's goal
122
+ - Respect the user's preferences and communication style
123
+ `,
124
+ [DEFAULT_TOOLS_FILENAME]: `# Tools
125
+
126
+ Available tools and capabilities for the agent.
127
+
128
+ ## Built-in Tools
129
+
130
+ The agent has access to tools provided by enabled plugins.
131
+ Each plugin may register actions, providers, and evaluators
132
+ that extend the agent's capabilities.
133
+
134
+ ## Usage
135
+
136
+ Tools are invoked automatically when the agent determines
137
+ they would help accomplish the user's goal. No manual
138
+ configuration is required.
139
+ `,
140
+ [DEFAULT_IDENTITY_FILENAME]: `# Identity
141
+
142
+ Your character and personality settings.
143
+
144
+ Customize this file to define your agent's personality,
145
+ tone, and behavior style.
146
+ `,
147
+ [DEFAULT_USER_FILENAME]: `# User
148
+
149
+ User context and preferences.
150
+
151
+ This file stores information about the user to help
152
+ personalize interactions.
153
+ `,
154
+ [DEFAULT_HEARTBEAT_FILENAME]: `# Heartbeat
155
+
156
+ The heartbeat system enables autonomous agent behavior.
157
+
158
+ ## Scheduling
159
+
160
+ When autonomy is enabled, the agent periodically evaluates
161
+ whether to take proactive actions based on its goals,
162
+ pending tasks, and environmental changes.
163
+
164
+ ## Triggers
165
+
166
+ - Scheduled intervals (configurable)
167
+ - External events from connected channels
168
+ - System notifications and alerts
169
+ `,
170
+ [DEFAULT_BOOTSTRAP_FILENAME]: `# Bootstrap
171
+
172
+ Initial workspace setup for a new agent.
173
+
174
+ ## Getting Started
175
+
176
+ This workspace was automatically created for your agent.
177
+ You can customize it by editing the markdown files in this
178
+ directory:
179
+
180
+ - **AGENTS.md** — Agent behavior and capabilities
181
+ - **TOOLS.md** — Available tools and plugins
182
+ - **IDENTITY.md** — Character and personality
183
+ - **USER.md** — User context and preferences
184
+ - **HEARTBEAT.md** — Autonomous behavior settings
185
+
186
+ ## Configuration
187
+
188
+ Agent configuration is managed through \`~/.milady/milady.json\`
189
+ or the Milady Control UI.
190
+ `,
191
+ };
192
+
193
+ export type WorkspaceBootstrapFileName =
194
+ | typeof DEFAULT_AGENTS_FILENAME
195
+ | typeof DEFAULT_TOOLS_FILENAME
196
+ | typeof DEFAULT_IDENTITY_FILENAME
197
+ | typeof DEFAULT_USER_FILENAME
198
+ | typeof DEFAULT_HEARTBEAT_FILENAME
199
+ | typeof DEFAULT_BOOTSTRAP_FILENAME
200
+ | typeof DEFAULT_MEMORY_FILENAME
201
+ | typeof DEFAULT_MEMORY_ALT_FILENAME;
202
+
203
+ export type WorkspaceBootstrapFile = {
204
+ name: WorkspaceBootstrapFileName;
205
+ path: string;
206
+ content?: string;
207
+ missing: boolean;
208
+ };
209
+
210
+ type ElizaCoreWorkspaceHelpers = {
211
+ isSubagentSessionKey?: (key: string) => boolean;
212
+ logger?: {
213
+ warn: (message: string) => void;
214
+ };
215
+ };
216
+
217
+ const coreWorkspaceHelpers = elizaCore as ElizaCoreWorkspaceHelpers;
218
+
219
+ function isSubagentSessionKey(sessionKey: string): boolean {
220
+ if (typeof coreWorkspaceHelpers.isSubagentSessionKey === "function") {
221
+ return coreWorkspaceHelpers.isSubagentSessionKey(sessionKey);
222
+ }
223
+ // Older @elizaos/core versions do not expose subagent helpers.
224
+ // Treat all sessions as primary sessions in that case.
225
+ return false;
226
+ }
227
+
228
+ function logWarn(message: string): void {
229
+ if (
230
+ coreWorkspaceHelpers.logger &&
231
+ typeof coreWorkspaceHelpers.logger.warn === "function"
232
+ ) {
233
+ coreWorkspaceHelpers.logger.warn(message);
234
+ return;
235
+ }
236
+ console.warn(message);
237
+ }
238
+
239
+ async function writeFileIfMissing(filePath: string, content: string) {
240
+ try {
241
+ await fs.writeFile(filePath, content, {
242
+ encoding: "utf-8",
243
+ flag: "wx",
244
+ });
245
+ } catch (err) {
246
+ if ((err as NodeJS.ErrnoException).code !== "EEXIST") {
247
+ throw err;
248
+ }
249
+ }
250
+ }
251
+
252
+ async function hasGitRepo(dir: string): Promise<boolean> {
253
+ try {
254
+ await fs.stat(path.join(dir, ".git"));
255
+ return true;
256
+ } catch (err) {
257
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") {
258
+ return false;
259
+ }
260
+ throw err;
261
+ }
262
+ }
263
+
264
+ async function isGitAvailable(): Promise<boolean> {
265
+ try {
266
+ const result = await runCommandWithTimeout(["git", "--version"], {
267
+ timeoutMs: 2_000,
268
+ });
269
+ return result.code === 0;
270
+ } catch {
271
+ return false;
272
+ }
273
+ }
274
+
275
+ async function ensureGitRepo(dir: string, isBrandNewWorkspace: boolean) {
276
+ if (!isBrandNewWorkspace) {
277
+ return;
278
+ }
279
+ if (await hasGitRepo(dir)) {
280
+ return;
281
+ }
282
+ if (!(await isGitAvailable())) {
283
+ return;
284
+ }
285
+ try {
286
+ await runCommandWithTimeout(["git", "init"], {
287
+ cwd: dir,
288
+ timeoutMs: 10_000,
289
+ });
290
+ } catch (err) {
291
+ logWarn(
292
+ `[workspace] git init failed: ${err instanceof Error ? err.message : err}`,
293
+ );
294
+ }
295
+ }
296
+
297
+ export async function ensureAgentWorkspace(params?: {
298
+ dir?: string;
299
+ ensureBootstrapFiles?: boolean;
300
+ }): Promise<{
301
+ dir: string;
302
+ agentsPath?: string;
303
+ toolsPath?: string;
304
+ identityPath?: string;
305
+ userPath?: string;
306
+ heartbeatPath?: string;
307
+ bootstrapPath?: string;
308
+ }> {
309
+ const rawDir = params?.dir?.trim()
310
+ ? params.dir.trim()
311
+ : DEFAULT_AGENT_WORKSPACE_DIR;
312
+ const dir = resolveUserPath(rawDir);
313
+ await fs.mkdir(dir, { recursive: true });
314
+
315
+ if (!params?.ensureBootstrapFiles) {
316
+ return { dir };
317
+ }
318
+
319
+ const agentsPath = path.join(dir, DEFAULT_AGENTS_FILENAME);
320
+ const toolsPath = path.join(dir, DEFAULT_TOOLS_FILENAME);
321
+ const identityPath = path.join(dir, DEFAULT_IDENTITY_FILENAME);
322
+ const userPath = path.join(dir, DEFAULT_USER_FILENAME);
323
+ const heartbeatPath = path.join(dir, DEFAULT_HEARTBEAT_FILENAME);
324
+ const bootstrapPath = path.join(dir, DEFAULT_BOOTSTRAP_FILENAME);
325
+
326
+ const isBrandNewWorkspace = await (async () => {
327
+ const paths = [
328
+ agentsPath,
329
+ toolsPath,
330
+ identityPath,
331
+ userPath,
332
+ heartbeatPath,
333
+ ];
334
+ const existing = await Promise.all(
335
+ paths.map(async (p) => {
336
+ try {
337
+ await fs.access(p);
338
+ return true;
339
+ } catch (err) {
340
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") {
341
+ return false;
342
+ }
343
+ throw err;
344
+ }
345
+ }),
346
+ );
347
+ return existing.every((v) => !v);
348
+ })();
349
+
350
+ const agentsTemplate = WORKSPACE_TEMPLATES[DEFAULT_AGENTS_FILENAME];
351
+ const toolsTemplate = WORKSPACE_TEMPLATES[DEFAULT_TOOLS_FILENAME];
352
+ const identityTemplate = WORKSPACE_TEMPLATES[DEFAULT_IDENTITY_FILENAME];
353
+ const userTemplate = WORKSPACE_TEMPLATES[DEFAULT_USER_FILENAME];
354
+ const heartbeatTemplate = WORKSPACE_TEMPLATES[DEFAULT_HEARTBEAT_FILENAME];
355
+ const bootstrapTemplate = WORKSPACE_TEMPLATES[DEFAULT_BOOTSTRAP_FILENAME];
356
+
357
+ const writeOps = [
358
+ writeFileIfMissing(agentsPath, agentsTemplate),
359
+ writeFileIfMissing(toolsPath, toolsTemplate),
360
+ writeFileIfMissing(identityPath, identityTemplate),
361
+ writeFileIfMissing(userPath, userTemplate),
362
+ writeFileIfMissing(heartbeatPath, heartbeatTemplate),
363
+ ];
364
+ if (isBrandNewWorkspace) {
365
+ writeOps.push(writeFileIfMissing(bootstrapPath, bootstrapTemplate));
366
+ }
367
+ await Promise.all(writeOps);
368
+ await ensureGitRepo(dir, isBrandNewWorkspace);
369
+
370
+ return {
371
+ dir,
372
+ agentsPath,
373
+ toolsPath,
374
+ identityPath,
375
+ userPath,
376
+ heartbeatPath,
377
+ bootstrapPath,
378
+ };
379
+ }
380
+
381
+ async function resolveMemoryBootstrapEntries(
382
+ resolvedDir: string,
383
+ ): Promise<Array<{ name: WorkspaceBootstrapFileName; filePath: string }>> {
384
+ const candidates: WorkspaceBootstrapFileName[] = [
385
+ DEFAULT_MEMORY_FILENAME,
386
+ DEFAULT_MEMORY_ALT_FILENAME,
387
+ ];
388
+ const entries: Array<{ name: WorkspaceBootstrapFileName; filePath: string }> =
389
+ [];
390
+ for (const name of candidates) {
391
+ const filePath = path.join(resolvedDir, name);
392
+ try {
393
+ await fs.access(filePath);
394
+ entries.push({ name, filePath });
395
+ } catch (err) {
396
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
397
+ throw err;
398
+ }
399
+ }
400
+ }
401
+ if (entries.length <= 1) {
402
+ return entries;
403
+ }
404
+
405
+ const seen = new Set<string>();
406
+ const deduped: Array<{ name: WorkspaceBootstrapFileName; filePath: string }> =
407
+ [];
408
+ for (const entry of entries) {
409
+ let key = entry.filePath;
410
+ try {
411
+ key = await fs.realpath(entry.filePath);
412
+ } catch (err) {
413
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
414
+ throw err;
415
+ }
416
+ }
417
+ if (seen.has(key)) {
418
+ continue;
419
+ }
420
+ seen.add(key);
421
+ deduped.push(entry);
422
+ }
423
+ return deduped;
424
+ }
425
+
426
+ export async function loadWorkspaceBootstrapFiles(
427
+ dir: string,
428
+ ): Promise<WorkspaceBootstrapFile[]> {
429
+ const resolvedDir = resolveUserPath(dir);
430
+
431
+ const entries: Array<{
432
+ name: WorkspaceBootstrapFileName;
433
+ filePath: string;
434
+ }> = [
435
+ {
436
+ name: DEFAULT_AGENTS_FILENAME,
437
+ filePath: path.join(resolvedDir, DEFAULT_AGENTS_FILENAME),
438
+ },
439
+ {
440
+ name: DEFAULT_TOOLS_FILENAME,
441
+ filePath: path.join(resolvedDir, DEFAULT_TOOLS_FILENAME),
442
+ },
443
+ {
444
+ name: DEFAULT_IDENTITY_FILENAME,
445
+ filePath: path.join(resolvedDir, DEFAULT_IDENTITY_FILENAME),
446
+ },
447
+ {
448
+ name: DEFAULT_USER_FILENAME,
449
+ filePath: path.join(resolvedDir, DEFAULT_USER_FILENAME),
450
+ },
451
+ {
452
+ name: DEFAULT_HEARTBEAT_FILENAME,
453
+ filePath: path.join(resolvedDir, DEFAULT_HEARTBEAT_FILENAME),
454
+ },
455
+ {
456
+ name: DEFAULT_BOOTSTRAP_FILENAME,
457
+ filePath: path.join(resolvedDir, DEFAULT_BOOTSTRAP_FILENAME),
458
+ },
459
+ ];
460
+
461
+ entries.push(...(await resolveMemoryBootstrapEntries(resolvedDir)));
462
+
463
+ const result = await Promise.all(
464
+ entries.map(async (entry): Promise<WorkspaceBootstrapFile> => {
465
+ try {
466
+ const content = await fs.readFile(entry.filePath, "utf-8");
467
+ return {
468
+ name: entry.name,
469
+ path: entry.filePath,
470
+ content,
471
+ missing: false,
472
+ };
473
+ } catch (err) {
474
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") {
475
+ return { name: entry.name, path: entry.filePath, missing: true };
476
+ }
477
+ throw err;
478
+ }
479
+ }),
480
+ );
481
+ return result;
482
+ }
483
+
484
+ const SUBAGENT_BOOTSTRAP_ALLOWLIST = new Set([
485
+ DEFAULT_AGENTS_FILENAME,
486
+ DEFAULT_TOOLS_FILENAME,
487
+ ]);
488
+
489
+ export function filterBootstrapFilesForSession(
490
+ files: WorkspaceBootstrapFile[],
491
+ sessionKey?: string,
492
+ ): WorkspaceBootstrapFile[] {
493
+ if (!sessionKey || !isSubagentSessionKey(sessionKey)) {
494
+ return files;
495
+ }
496
+ return files.filter((file) => SUBAGENT_BOOTSTRAP_ALLOWLIST.has(file.name));
497
+ }