agent-world 0.11.1 → 0.12.0

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 (267) hide show
  1. package/README.md +17 -7
  2. package/dist/cli/commands.d.ts +109 -0
  3. package/dist/cli/commands.js +2024 -0
  4. package/dist/cli/display.d.ts +124 -0
  5. package/dist/cli/display.js +381 -0
  6. package/dist/cli/hitl.d.ts +33 -0
  7. package/dist/cli/hitl.js +81 -0
  8. package/dist/cli/index.d.ts +2 -0
  9. package/dist/cli/stream.d.ts +41 -0
  10. package/dist/cli/stream.js +222 -0
  11. package/dist/core/activity-tracker.d.ts +16 -0
  12. package/dist/core/activity-tracker.d.ts.map +1 -0
  13. package/dist/core/activity-tracker.js +91 -0
  14. package/dist/core/activity-tracker.js.map +1 -0
  15. package/dist/core/ai-commands.d.ts +16 -0
  16. package/dist/core/ai-commands.d.ts.map +1 -0
  17. package/dist/core/ai-commands.js +24 -0
  18. package/dist/core/ai-commands.js.map +1 -0
  19. package/dist/core/ai-sdk-patch.d.ts +24 -0
  20. package/dist/core/ai-sdk-patch.d.ts.map +1 -0
  21. package/dist/core/ai-sdk-patch.js +169 -0
  22. package/dist/core/ai-sdk-patch.js.map +1 -0
  23. package/dist/core/anthropic-direct.d.ts +52 -0
  24. package/dist/core/anthropic-direct.d.ts.map +1 -0
  25. package/dist/core/anthropic-direct.js +301 -0
  26. package/dist/core/anthropic-direct.js.map +1 -0
  27. package/dist/core/approval-cache.d.ts +104 -0
  28. package/dist/core/approval-cache.d.ts.map +1 -0
  29. package/dist/core/approval-cache.js +150 -0
  30. package/dist/core/approval-cache.js.map +1 -0
  31. package/dist/core/chat-constants.d.ts +20 -0
  32. package/dist/core/chat-constants.d.ts.map +1 -0
  33. package/dist/core/chat-constants.js +22 -0
  34. package/dist/core/chat-constants.js.map +1 -0
  35. package/dist/core/create-agent-tool.d.ts +66 -0
  36. package/dist/core/create-agent-tool.d.ts.map +1 -0
  37. package/dist/core/create-agent-tool.js +212 -0
  38. package/dist/core/create-agent-tool.js.map +1 -0
  39. package/dist/core/events/approval-checker.d.ts +61 -0
  40. package/dist/core/events/approval-checker.d.ts.map +1 -0
  41. package/dist/core/events/approval-checker.js +226 -0
  42. package/dist/core/events/approval-checker.js.map +1 -0
  43. package/dist/core/events/index.d.ts +25 -0
  44. package/dist/core/events/index.d.ts.map +1 -0
  45. package/dist/core/events/index.js +30 -0
  46. package/dist/core/events/index.js.map +1 -0
  47. package/dist/core/events/memory-manager.d.ts +73 -0
  48. package/dist/core/events/memory-manager.d.ts.map +1 -0
  49. package/dist/core/events/memory-manager.js +1218 -0
  50. package/dist/core/events/memory-manager.js.map +1 -0
  51. package/dist/core/events/mention-logic.d.ts +39 -0
  52. package/dist/core/events/mention-logic.d.ts.map +1 -0
  53. package/dist/core/events/mention-logic.js +163 -0
  54. package/dist/core/events/mention-logic.js.map +1 -0
  55. package/dist/core/events/orchestrator.d.ts +69 -0
  56. package/dist/core/events/orchestrator.d.ts.map +1 -0
  57. package/dist/core/events/orchestrator.js +883 -0
  58. package/dist/core/events/orchestrator.js.map +1 -0
  59. package/dist/core/events/persistence.d.ts +41 -0
  60. package/dist/core/events/persistence.d.ts.map +1 -0
  61. package/dist/core/events/persistence.js +296 -0
  62. package/dist/core/events/persistence.js.map +1 -0
  63. package/dist/core/events/publishers.d.ts +81 -0
  64. package/dist/core/events/publishers.d.ts.map +1 -0
  65. package/dist/core/events/publishers.js +272 -0
  66. package/dist/core/events/publishers.js.map +1 -0
  67. package/dist/core/events/subscribers.d.ts +45 -0
  68. package/dist/core/events/subscribers.d.ts.map +1 -0
  69. package/dist/core/events/subscribers.js +288 -0
  70. package/dist/core/events/subscribers.js.map +1 -0
  71. package/dist/core/events/tool-bridge-logging.d.ts +28 -0
  72. package/dist/core/events/tool-bridge-logging.d.ts.map +1 -0
  73. package/dist/core/events/tool-bridge-logging.js +94 -0
  74. package/dist/core/events/tool-bridge-logging.js.map +1 -0
  75. package/dist/core/events-metadata.d.ts +72 -0
  76. package/dist/core/events-metadata.d.ts.map +1 -0
  77. package/dist/core/events-metadata.js +167 -0
  78. package/dist/core/events-metadata.js.map +1 -0
  79. package/dist/core/events.d.ts +186 -0
  80. package/dist/core/events.d.ts.map +1 -0
  81. package/dist/core/events.js +1248 -0
  82. package/dist/core/events.js.map +1 -0
  83. package/dist/core/export.d.ts +106 -0
  84. package/dist/core/export.d.ts.map +1 -0
  85. package/dist/core/export.js +705 -0
  86. package/dist/core/export.js.map +1 -0
  87. package/dist/core/file-tools.d.ts +114 -0
  88. package/dist/core/file-tools.d.ts.map +1 -0
  89. package/dist/core/file-tools.js +370 -0
  90. package/dist/core/file-tools.js.map +1 -0
  91. package/dist/core/google-direct.d.ts +58 -0
  92. package/dist/core/google-direct.d.ts.map +1 -0
  93. package/dist/core/google-direct.js +298 -0
  94. package/dist/core/google-direct.js.map +1 -0
  95. package/dist/core/hitl.d.ts +54 -0
  96. package/dist/core/hitl.d.ts.map +1 -0
  97. package/dist/core/hitl.js +153 -0
  98. package/dist/core/hitl.js.map +1 -0
  99. package/dist/core/index.d.ts +59 -0
  100. package/dist/core/index.d.ts.map +1 -0
  101. package/dist/core/index.js +70 -0
  102. package/dist/core/index.js.map +1 -0
  103. package/dist/core/llm-config.d.ts +128 -0
  104. package/dist/core/llm-config.d.ts.map +1 -0
  105. package/dist/core/llm-config.js +164 -0
  106. package/dist/core/llm-config.js.map +1 -0
  107. package/dist/core/llm-manager.d.ts +163 -0
  108. package/dist/core/llm-manager.d.ts.map +1 -0
  109. package/dist/core/llm-manager.js +669 -0
  110. package/dist/core/llm-manager.js.map +1 -0
  111. package/dist/core/load-skill-tool.d.ts +55 -0
  112. package/dist/core/load-skill-tool.d.ts.map +1 -0
  113. package/dist/core/load-skill-tool.js +468 -0
  114. package/dist/core/load-skill-tool.js.map +1 -0
  115. package/dist/core/logger.d.ts +88 -0
  116. package/dist/core/logger.d.ts.map +1 -0
  117. package/dist/core/logger.js +358 -0
  118. package/dist/core/logger.js.map +1 -0
  119. package/dist/core/managers.d.ts +131 -0
  120. package/dist/core/managers.d.ts.map +1 -0
  121. package/dist/core/managers.js +1223 -0
  122. package/dist/core/managers.js.map +1 -0
  123. package/dist/core/mcp-server-registry.d.ts +304 -0
  124. package/dist/core/mcp-server-registry.d.ts.map +1 -0
  125. package/dist/core/mcp-server-registry.js +1769 -0
  126. package/dist/core/mcp-server-registry.js.map +1 -0
  127. package/dist/core/mcp-tools.d.ts +56 -0
  128. package/dist/core/mcp-tools.d.ts.map +1 -0
  129. package/dist/core/mcp-tools.js +186 -0
  130. package/dist/core/mcp-tools.js.map +1 -0
  131. package/dist/core/message-prep.d.ts +81 -0
  132. package/dist/core/message-prep.d.ts.map +1 -0
  133. package/dist/core/message-prep.js +223 -0
  134. package/dist/core/message-prep.js.map +1 -0
  135. package/dist/core/message-processing-control.d.ts +54 -0
  136. package/dist/core/message-processing-control.d.ts.map +1 -0
  137. package/dist/core/message-processing-control.js +139 -0
  138. package/dist/core/message-processing-control.js.map +1 -0
  139. package/dist/core/openai-direct.d.ts +80 -0
  140. package/dist/core/openai-direct.d.ts.map +1 -0
  141. package/dist/core/openai-direct.js +374 -0
  142. package/dist/core/openai-direct.js.map +1 -0
  143. package/dist/core/shell-cmd-tool.d.ts +235 -0
  144. package/dist/core/shell-cmd-tool.d.ts.map +1 -0
  145. package/dist/core/shell-cmd-tool.js +1157 -0
  146. package/dist/core/shell-cmd-tool.js.map +1 -0
  147. package/dist/core/shell-process-registry.d.ts +88 -0
  148. package/dist/core/shell-process-registry.d.ts.map +1 -0
  149. package/dist/core/shell-process-registry.js +309 -0
  150. package/dist/core/shell-process-registry.js.map +1 -0
  151. package/dist/core/skill-registry.d.ts +75 -0
  152. package/dist/core/skill-registry.d.ts.map +1 -0
  153. package/dist/core/skill-registry.js +369 -0
  154. package/dist/core/skill-registry.js.map +1 -0
  155. package/dist/core/skill-script-runner.d.ts +89 -0
  156. package/dist/core/skill-script-runner.d.ts.map +1 -0
  157. package/dist/core/skill-script-runner.js +274 -0
  158. package/dist/core/skill-script-runner.js.map +1 -0
  159. package/dist/core/skill-selector.d.ts +65 -0
  160. package/dist/core/skill-selector.d.ts.map +1 -0
  161. package/dist/core/skill-selector.js +190 -0
  162. package/dist/core/skill-selector.js.map +1 -0
  163. package/dist/core/skill-settings.d.ts +20 -0
  164. package/dist/core/skill-settings.d.ts.map +1 -0
  165. package/dist/core/skill-settings.js +40 -0
  166. package/dist/core/skill-settings.js.map +1 -0
  167. package/dist/core/storage/agent-storage.d.ts +134 -0
  168. package/dist/core/storage/agent-storage.d.ts.map +1 -0
  169. package/dist/core/storage/agent-storage.js +498 -0
  170. package/dist/core/storage/agent-storage.js.map +1 -0
  171. package/dist/core/storage/eventStorage/fileEventStorage.d.ts +100 -0
  172. package/dist/core/storage/eventStorage/fileEventStorage.d.ts.map +1 -0
  173. package/dist/core/storage/eventStorage/fileEventStorage.js +494 -0
  174. package/dist/core/storage/eventStorage/fileEventStorage.js.map +1 -0
  175. package/dist/core/storage/eventStorage/index.d.ts +31 -0
  176. package/dist/core/storage/eventStorage/index.d.ts.map +1 -0
  177. package/dist/core/storage/eventStorage/index.js +31 -0
  178. package/dist/core/storage/eventStorage/index.js.map +1 -0
  179. package/dist/core/storage/eventStorage/memoryEventStorage.d.ts +87 -0
  180. package/dist/core/storage/eventStorage/memoryEventStorage.d.ts.map +1 -0
  181. package/dist/core/storage/eventStorage/memoryEventStorage.js +244 -0
  182. package/dist/core/storage/eventStorage/memoryEventStorage.js.map +1 -0
  183. package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts +45 -0
  184. package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts.map +1 -0
  185. package/dist/core/storage/eventStorage/sqliteEventStorage.js +301 -0
  186. package/dist/core/storage/eventStorage/sqliteEventStorage.js.map +1 -0
  187. package/dist/core/storage/eventStorage/types.d.ts +142 -0
  188. package/dist/core/storage/eventStorage/types.d.ts.map +1 -0
  189. package/dist/core/storage/eventStorage/types.js +43 -0
  190. package/dist/core/storage/eventStorage/types.js.map +1 -0
  191. package/dist/core/storage/eventStorage/validation.d.ts +30 -0
  192. package/dist/core/storage/eventStorage/validation.d.ts.map +1 -0
  193. package/dist/core/storage/eventStorage/validation.js +68 -0
  194. package/dist/core/storage/eventStorage/validation.js.map +1 -0
  195. package/dist/core/storage/legacy-migrations.d.ts +45 -0
  196. package/dist/core/storage/legacy-migrations.d.ts.map +1 -0
  197. package/dist/core/storage/legacy-migrations.js +295 -0
  198. package/dist/core/storage/legacy-migrations.js.map +1 -0
  199. package/dist/core/storage/memory-storage.d.ts +105 -0
  200. package/dist/core/storage/memory-storage.d.ts.map +1 -0
  201. package/dist/core/storage/memory-storage.js +415 -0
  202. package/dist/core/storage/memory-storage.js.map +1 -0
  203. package/dist/core/storage/migration-runner.d.ts +96 -0
  204. package/dist/core/storage/migration-runner.d.ts.map +1 -0
  205. package/dist/core/storage/migration-runner.js +306 -0
  206. package/dist/core/storage/migration-runner.js.map +1 -0
  207. package/dist/core/storage/queue-storage.d.ts +147 -0
  208. package/dist/core/storage/queue-storage.d.ts.map +1 -0
  209. package/dist/core/storage/queue-storage.js +290 -0
  210. package/dist/core/storage/queue-storage.js.map +1 -0
  211. package/dist/core/storage/skill-storage.d.ts +136 -0
  212. package/dist/core/storage/skill-storage.d.ts.map +1 -0
  213. package/dist/core/storage/skill-storage.js +474 -0
  214. package/dist/core/storage/skill-storage.js.map +1 -0
  215. package/dist/core/storage/sqlite-schema.d.ts +95 -0
  216. package/dist/core/storage/sqlite-schema.d.ts.map +1 -0
  217. package/dist/core/storage/sqlite-schema.js +156 -0
  218. package/dist/core/storage/sqlite-schema.js.map +1 -0
  219. package/dist/core/storage/sqlite-storage.d.ts +146 -0
  220. package/dist/core/storage/sqlite-storage.d.ts.map +1 -0
  221. package/dist/core/storage/sqlite-storage.js +709 -0
  222. package/dist/core/storage/sqlite-storage.js.map +1 -0
  223. package/dist/core/storage/storage-factory.d.ts +61 -0
  224. package/dist/core/storage/storage-factory.d.ts.map +1 -0
  225. package/dist/core/storage/storage-factory.js +794 -0
  226. package/dist/core/storage/storage-factory.js.map +1 -0
  227. package/dist/core/storage/validation.d.ts +36 -0
  228. package/dist/core/storage/validation.d.ts.map +1 -0
  229. package/dist/core/storage/validation.js +79 -0
  230. package/dist/core/storage/validation.js.map +1 -0
  231. package/dist/core/storage/world-storage.d.ts +114 -0
  232. package/dist/core/storage/world-storage.d.ts.map +1 -0
  233. package/dist/core/storage/world-storage.js +378 -0
  234. package/dist/core/storage/world-storage.js.map +1 -0
  235. package/dist/core/subscription.d.ts +43 -0
  236. package/dist/core/subscription.d.ts.map +1 -0
  237. package/dist/core/subscription.js +227 -0
  238. package/dist/core/subscription.js.map +1 -0
  239. package/dist/core/tool-utils.d.ts +80 -0
  240. package/dist/core/tool-utils.d.ts.map +1 -0
  241. package/dist/core/tool-utils.js +273 -0
  242. package/dist/core/tool-utils.js.map +1 -0
  243. package/dist/core/types.d.ts +595 -0
  244. package/dist/core/types.d.ts.map +1 -0
  245. package/dist/core/types.js +158 -0
  246. package/dist/core/types.js.map +1 -0
  247. package/dist/core/utils.d.ts +138 -0
  248. package/dist/core/utils.d.ts.map +1 -0
  249. package/dist/core/utils.js +478 -0
  250. package/dist/core/utils.js.map +1 -0
  251. package/dist/core/world-class.d.ts +43 -0
  252. package/dist/core/world-class.d.ts.map +1 -0
  253. package/dist/core/world-class.js +90 -0
  254. package/dist/core/world-class.js.map +1 -0
  255. package/dist/index.d.ts +18 -0
  256. package/dist/public/assets/agent-sprites-DJFgj-zP.png +0 -0
  257. package/dist/public/assets/border-KHK37r8y.svg +83 -0
  258. package/dist/public/assets/index-C9kPXL6G.css +1 -0
  259. package/dist/public/assets/index-DOQEHGWt.js +96 -0
  260. package/dist/public/index.html +21 -0
  261. package/dist/server/api.d.ts +2 -0
  262. package/dist/server/api.js +1124 -0
  263. package/dist/server/index.d.ts +29 -0
  264. package/dist/server/sse-handler.d.ts +62 -0
  265. package/dist/server/sse-handler.js +234 -0
  266. package/package.json +15 -3
  267. package/scripts/launch-electron.js +0 -58
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Skill Script Runner Module - Dedicated executor for skill-bundled scripts
3
+ *
4
+ * Purpose:
5
+ * Provides a sandboxed, path-guarded script executor for files stored under
6
+ * `{world-dir}/skills/{skill-name}/scripts/`. This is the ONLY entry point
7
+ * for skill script execution — the generic `shell-cmd-tool.ts` must NEVER
8
+ * be used for skill scripts.
9
+ *
10
+ * Features:
11
+ * - Strict path guards: resolve + verify target stays within the skill's `scripts/` directory
12
+ * - Runtime sandbox permissions: allowlist of allowed interpreters
13
+ * - Execution controls: configurable timeout, max output size, exit code capture
14
+ * - stdout/stderr capture with size limits
15
+ * - Explicit error handling with structured results
16
+ *
17
+ * Notes:
18
+ * - This module does NOT use `shell-cmd-tool.ts` — it is a separate executor
19
+ * - Scripts are executed via child_process.spawn with explicit interpreter
20
+ * - Path traversal is prevented by resolving + verifying all paths
21
+ * - Only scripts under `{skill-dir}/scripts/` are allowed to execute
22
+ *
23
+ * Recent Changes:
24
+ * - 2026-02-09: Initial implementation for world-level skills system
25
+ */
26
+ import { spawn } from 'child_process';
27
+ import * as path from 'path';
28
+ import { promises as fs } from 'fs';
29
+ import { createCategoryLogger } from './logger.js';
30
+ const logger = createCategoryLogger('skill.runner');
31
+ // ============================================================
32
+ // Constants
33
+ // ============================================================
34
+ /** Default timeout for skill scripts (30 seconds — much shorter than shell-cmd-tool's 10 minutes) */
35
+ const DEFAULT_TIMEOUT_MS = 30_000;
36
+ /** Default maximum output size per stream (64KB) */
37
+ const DEFAULT_MAX_OUTPUT_SIZE = 65_536;
38
+ /** Allowed interpreter commands for skill scripts */
39
+ const ALLOWED_INTERPRETERS = new Set([
40
+ 'node',
41
+ 'npx',
42
+ 'python3',
43
+ 'python',
44
+ 'bash',
45
+ 'sh',
46
+ 'deno',
47
+ 'bun',
48
+ ]);
49
+ /** File extension to interpreter mapping */
50
+ const EXTENSION_INTERPRETER_MAP = {
51
+ '.js': 'node',
52
+ '.mjs': 'node',
53
+ '.ts': 'npx', // Will use `npx tsx` for TypeScript
54
+ '.py': 'python3',
55
+ '.sh': 'bash',
56
+ '.bash': 'bash',
57
+ };
58
+ /** Extra args needed for certain interpreters */
59
+ const INTERPRETER_EXTRA_ARGS = {
60
+ 'npx': ['tsx'], // npx tsx script.ts
61
+ };
62
+ // ============================================================
63
+ // Path Guards
64
+ // ============================================================
65
+ /**
66
+ * Build the scripts directory path for a skill.
67
+ */
68
+ export function getSkillScriptsDir(rootPath, worldId, skillName) {
69
+ return path.join(rootPath, 'worlds', worldId, 'skills', skillName, 'scripts');
70
+ }
71
+ /**
72
+ * Resolve and validate that a script path stays within the skill's scripts/ directory.
73
+ * Throws if the resolved path escapes the boundary.
74
+ */
75
+ export function resolveAndGuardScriptPath(scriptsDir, scriptName) {
76
+ // Reject absolute paths and path traversal patterns before resolving
77
+ if (path.isAbsolute(scriptName)) {
78
+ throw new Error(`Path traversal denied: "${scriptName}" resolves outside scripts directory "${scriptsDir}"`);
79
+ }
80
+ // Check for .. segments that could escape the directory
81
+ const segments = scriptName.split('/');
82
+ if (segments.some(s => s === '..')) {
83
+ throw new Error(`Path traversal denied: "${scriptName}" resolves outside scripts directory "${scriptsDir}"`);
84
+ }
85
+ const resolved = path.resolve(scriptsDir, scriptName);
86
+ const resolvedBase = path.resolve(scriptsDir);
87
+ if (!resolved.startsWith(resolvedBase + '/') && resolved !== resolvedBase) {
88
+ throw new Error(`Path traversal denied: "${scriptName}" resolves outside scripts directory "${scriptsDir}"`);
89
+ }
90
+ return resolved;
91
+ }
92
+ // ============================================================
93
+ // Interpreter Detection
94
+ // ============================================================
95
+ /**
96
+ * Determine the interpreter to use for a script based on its file extension.
97
+ * Returns null if the extension is not in the allowlist.
98
+ */
99
+ export function detectInterpreter(scriptPath) {
100
+ const ext = path.extname(scriptPath).toLowerCase();
101
+ const interpreter = EXTENSION_INTERPRETER_MAP[ext];
102
+ if (!interpreter) {
103
+ return null;
104
+ }
105
+ const extraArgs = INTERPRETER_EXTRA_ARGS[interpreter] || [];
106
+ return { command: interpreter, extraArgs };
107
+ }
108
+ // ============================================================
109
+ // Script Execution
110
+ // ============================================================
111
+ /**
112
+ * Execute a skill script with sandbox/permission guards.
113
+ *
114
+ * This function:
115
+ * 1. Validates the script path stays within the skill's scripts/ directory
116
+ * 2. Verifies the script file exists
117
+ * 3. Detects the appropriate interpreter from file extension
118
+ * 4. Executes with timeout, output size limits, and environment isolation
119
+ * 5. Captures stdout, stderr, exit code, and timing
120
+ *
121
+ * @param rootPath - Root storage path (e.g., ~/agent-world)
122
+ * @param worldId - World identifier
123
+ * @param skillName - Skill name (kebab-case)
124
+ * @param scriptName - Script filename (e.g., "run.sh", "analyze.py")
125
+ * @param options - Execution options (timeout, max output, env, args)
126
+ * @returns Structured execution result
127
+ */
128
+ export async function runSkillScript(rootPath, worldId, skillName, scriptName, options = {}) {
129
+ const timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
130
+ const maxOutputSize = options.maxOutputSize ?? DEFAULT_MAX_OUTPUT_SIZE;
131
+ const scriptArgs = options.args ?? [];
132
+ // Step 1: Resolve and guard the script path
133
+ const scriptsDir = getSkillScriptsDir(rootPath, worldId, skillName);
134
+ const resolvedScriptPath = resolveAndGuardScriptPath(scriptsDir, scriptName);
135
+ logger.debug('Running skill script', {
136
+ worldId,
137
+ skillName,
138
+ scriptName,
139
+ resolvedScriptPath,
140
+ timeout,
141
+ });
142
+ // Step 2: Verify the script file exists
143
+ try {
144
+ await fs.access(resolvedScriptPath);
145
+ }
146
+ catch {
147
+ throw new Error(`Script not found: "${scriptName}" in skill "${skillName}"`);
148
+ }
149
+ // Step 3: Detect interpreter from file extension
150
+ const interpreterInfo = detectInterpreter(resolvedScriptPath);
151
+ if (!interpreterInfo) {
152
+ const ext = path.extname(resolvedScriptPath);
153
+ throw new Error(`Unsupported script type: "${ext}" — allowed: ${Object.keys(EXTENSION_INTERPRETER_MAP).join(', ')}`);
154
+ }
155
+ // Step 4: Validate the interpreter is in the allowlist
156
+ const baseCmd = interpreterInfo.command;
157
+ if (!ALLOWED_INTERPRETERS.has(baseCmd)) {
158
+ throw new Error(`Interpreter "${baseCmd}" is not in the allowlist`);
159
+ }
160
+ // Step 5: Execute with sandbox controls
161
+ return executeScript(interpreterInfo.command, [...interpreterInfo.extraArgs, resolvedScriptPath, ...scriptArgs], scriptsDir, {
162
+ timeout,
163
+ maxOutputSize,
164
+ env: options.env,
165
+ });
166
+ }
167
+ /**
168
+ * Internal: Execute a script via child_process.spawn with output limits and timeout.
169
+ */
170
+ function executeScript(command, args, cwd, options) {
171
+ return new Promise((resolve) => {
172
+ const startTime = Date.now();
173
+ let stdout = '';
174
+ let stderr = '';
175
+ let timedOut = false;
176
+ let stdoutTruncated = false;
177
+ let stderrTruncated = false;
178
+ // Merge environment: inherit process env + optional overrides
179
+ // Explicitly strip sensitive vars for sandbox isolation
180
+ const childEnv = {
181
+ ...process.env,
182
+ ...options.env,
183
+ // Remove potentially sensitive vars
184
+ OPENAI_API_KEY: undefined,
185
+ ANTHROPIC_API_KEY: undefined,
186
+ GOOGLE_API_KEY: undefined,
187
+ };
188
+ const child = spawn(command, args, {
189
+ cwd,
190
+ env: childEnv,
191
+ stdio: ['ignore', 'pipe', 'pipe'],
192
+ shell: false, // No shell — direct execution only
193
+ });
194
+ // Capture stdout with size limit
195
+ child.stdout.on('data', (data) => {
196
+ if (stdoutTruncated)
197
+ return;
198
+ const chunk = data.toString();
199
+ if (stdout.length + chunk.length > options.maxOutputSize) {
200
+ stdout += chunk.slice(0, options.maxOutputSize - stdout.length);
201
+ stdoutTruncated = true;
202
+ }
203
+ else {
204
+ stdout += chunk;
205
+ }
206
+ });
207
+ // Capture stderr with size limit
208
+ child.stderr.on('data', (data) => {
209
+ if (stderrTruncated)
210
+ return;
211
+ const chunk = data.toString();
212
+ if (stderr.length + chunk.length > options.maxOutputSize) {
213
+ stderr += chunk.slice(0, options.maxOutputSize - stderr.length);
214
+ stderrTruncated = true;
215
+ }
216
+ else {
217
+ stderr += chunk;
218
+ }
219
+ });
220
+ // Timeout handling
221
+ const timer = setTimeout(() => {
222
+ timedOut = true;
223
+ child.kill('SIGTERM');
224
+ // Force kill after 5s if SIGTERM doesn't work
225
+ setTimeout(() => {
226
+ if (!child.killed) {
227
+ child.kill('SIGKILL');
228
+ }
229
+ }, 5000);
230
+ }, options.timeout);
231
+ // Process exit
232
+ child.on('close', (exitCode, signal) => {
233
+ clearTimeout(timer);
234
+ const durationMs = Date.now() - startTime;
235
+ logger.debug('Skill script completed', {
236
+ command,
237
+ exitCode,
238
+ signal,
239
+ timedOut,
240
+ durationMs,
241
+ stdoutLen: stdout.length,
242
+ stderrLen: stderr.length,
243
+ });
244
+ resolve({
245
+ exitCode,
246
+ signal: signal ?? null,
247
+ stdout,
248
+ stderr,
249
+ timedOut,
250
+ truncated: stdoutTruncated || stderrTruncated,
251
+ durationMs,
252
+ });
253
+ });
254
+ // Handle spawn errors (e.g., command not found)
255
+ child.on('error', (error) => {
256
+ clearTimeout(timer);
257
+ const durationMs = Date.now() - startTime;
258
+ logger.error('Skill script spawn error', {
259
+ command,
260
+ error: error.message,
261
+ });
262
+ resolve({
263
+ exitCode: null,
264
+ signal: null,
265
+ stdout,
266
+ stderr: error.message,
267
+ timedOut: false,
268
+ truncated: false,
269
+ durationMs,
270
+ });
271
+ });
272
+ });
273
+ }
274
+ //# sourceMappingURL=skill-script-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-script-runner.js","sourceRoot":"","sources":["../../core/skill-script-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Skill Selector Module - Two-Step LLM Skill Selection
3
+ *
4
+ * Purpose:
5
+ * Implements the lightweight LLM call that selects the best matching skill for a
6
+ * user message. This is Step 1 of the two-step skill flow:
7
+ * Step 1: selectSkillForMessage() → skill name or null
8
+ * Step 2: buildSkillAugmentedMessage() → injects SKILL.md content into user message
9
+ *
10
+ * Features:
11
+ * - Lightweight LLM call for skill selection from metadata list
12
+ * - Structured prompt template for consistent selection behavior
13
+ * - Edge case handling: no skills installed, invalid LLM response, LLM errors
14
+ * - World's configured LLM provider/model used for selection call
15
+ * - Fast path: skips selection when no skills are installed
16
+ * - Skill content injection into user message (NOT system prompt)
17
+ *
18
+ * Notes:
19
+ * - Skills are NOT tool calls — they follow the Anthropic Agent Skills Protocol
20
+ * - Selection uses a separate lightweight LLM call before the main response call
21
+ * - Selected skill content is injected into the user message, never the system prompt
22
+ * - This module is application-side; the LLM never accesses the filesystem directly
23
+ *
24
+ * Recent Changes:
25
+ * - 2026-02-09: Initial implementation for world-level skills system
26
+ */
27
+ import type { World, Agent, SkillMetadata, LLMResponse } from './types.js';
28
+ /**
29
+ * Build the structured prompt for skill selection.
30
+ * The LLM receives skill metadata (names + descriptions) and the user's message,
31
+ * then returns the best matching skill name or "none".
32
+ */
33
+ declare function buildSelectionPrompt(skills: SkillMetadata[], userMessage: string): string;
34
+ /**
35
+ * Select the best matching skill for a user message using a lightweight LLM call.
36
+ *
37
+ * This is Step 1 of the two-step skill flow. It loads the skill metadata list,
38
+ * makes a lightweight LLM call to select the best match, and returns the skill name.
39
+ *
40
+ * @param world - World instance (provides LLM config and world ID)
41
+ * @param agent - Agent instance (provides LLM provider/model for selection call)
42
+ * @param userMessage - The user's message to match against skills
43
+ * @returns Selected skill name, or null if no skill matches
44
+ */
45
+ export declare function selectSkillForMessage(world: World, agent: Agent, userMessage: string): Promise<string | null>;
46
+ /**
47
+ * Parse the LLM's selection response to extract a valid skill name.
48
+ * Returns null if the response is "none" or doesn't match any installed skill.
49
+ */
50
+ declare function parseSelectionResponse(response: LLMResponse, skills: SkillMetadata[]): string | null;
51
+ /**
52
+ * Build a user message augmented with skill instructions.
53
+ *
54
+ * This is Step 2 of the two-step skill flow. If a skill was selected,
55
+ * the application reads the SKILL.md content and wraps it around the
56
+ * user's original message.
57
+ *
58
+ * @param worldId - World ID to locate skill files
59
+ * @param skillName - Selected skill name (from Step 1)
60
+ * @param originalMessage - The user's original message content
61
+ * @returns Augmented message content with skill instructions, or original message if skill not found
62
+ */
63
+ export declare function buildSkillAugmentedMessage(worldId: string, skillName: string, originalMessage: string): Promise<string>;
64
+ export { buildSelectionPrompt, parseSelectionResponse };
65
+ //# sourceMappingURL=skill-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-selector.d.ts","sourceRoot":"","sources":["../../core/skill-selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAgB,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAYzF;;;;GAIG;AACH,iBAAS,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAclF;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuDxB;AAED;;;GAGG;AACH,iBAAS,sBAAsB,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,GAAG,IAAI,CA2B7F;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAMD,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,CAAC"}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Skill Selector Module - Two-Step LLM Skill Selection
3
+ *
4
+ * Purpose:
5
+ * Implements the lightweight LLM call that selects the best matching skill for a
6
+ * user message. This is Step 1 of the two-step skill flow:
7
+ * Step 1: selectSkillForMessage() → skill name or null
8
+ * Step 2: buildSkillAugmentedMessage() → injects SKILL.md content into user message
9
+ *
10
+ * Features:
11
+ * - Lightweight LLM call for skill selection from metadata list
12
+ * - Structured prompt template for consistent selection behavior
13
+ * - Edge case handling: no skills installed, invalid LLM response, LLM errors
14
+ * - World's configured LLM provider/model used for selection call
15
+ * - Fast path: skips selection when no skills are installed
16
+ * - Skill content injection into user message (NOT system prompt)
17
+ *
18
+ * Notes:
19
+ * - Skills are NOT tool calls — they follow the Anthropic Agent Skills Protocol
20
+ * - Selection uses a separate lightweight LLM call before the main response call
21
+ * - Selected skill content is injected into the user message, never the system prompt
22
+ * - This module is application-side; the LLM never accesses the filesystem directly
23
+ *
24
+ * Recent Changes:
25
+ * - 2026-02-09: Initial implementation for world-level skills system
26
+ */
27
+ import { listWorldSkillsMetadata, loadSkillFile, parseSkillFile } from './storage/skill-storage.js';
28
+ import { createCategoryLogger } from './logger.js';
29
+ import { getDefaultRootPath } from './storage/storage-factory.js';
30
+ import { generateAgentResponse } from './llm-manager.js';
31
+ const logger = createCategoryLogger('skill.selector');
32
+ // ============================================================
33
+ // Skill Selection Prompt Template
34
+ // ============================================================
35
+ /**
36
+ * Build the structured prompt for skill selection.
37
+ * The LLM receives skill metadata (names + descriptions) and the user's message,
38
+ * then returns the best matching skill name or "none".
39
+ */
40
+ function buildSelectionPrompt(skills, userMessage) {
41
+ const skillList = skills
42
+ .map((s, i) => `${i + 1}. ${s.name}: ${s.description}`)
43
+ .join('\n');
44
+ return `You are a skill selector. Given the user's message and a list of available skills, select the ONE skill that best matches the user's request. If no skill matches well, respond with "none".
45
+
46
+ Available skills:
47
+ ${skillList}
48
+
49
+ User message:
50
+ ${userMessage}
51
+
52
+ Respond with ONLY the skill name (e.g., "code-review") or "none". Do not explain your choice.`;
53
+ }
54
+ // ============================================================
55
+ // Skill Selection
56
+ // ============================================================
57
+ /**
58
+ * Select the best matching skill for a user message using a lightweight LLM call.
59
+ *
60
+ * This is Step 1 of the two-step skill flow. It loads the skill metadata list,
61
+ * makes a lightweight LLM call to select the best match, and returns the skill name.
62
+ *
63
+ * @param world - World instance (provides LLM config and world ID)
64
+ * @param agent - Agent instance (provides LLM provider/model for selection call)
65
+ * @param userMessage - The user's message to match against skills
66
+ * @returns Selected skill name, or null if no skill matches
67
+ */
68
+ export async function selectSkillForMessage(world, agent, userMessage) {
69
+ try {
70
+ // Load skill metadata list
71
+ const rootPath = getDefaultRootPath();
72
+ const skills = await listWorldSkillsMetadata(rootPath, world.id);
73
+ // Fast path: no skills installed, skip selection
74
+ if (skills.length === 0) {
75
+ logger.debug('No skills installed, skipping selection', { worldId: world.id });
76
+ return null;
77
+ }
78
+ logger.debug('Starting skill selection', {
79
+ worldId: world.id,
80
+ agentId: agent.id,
81
+ skillCount: skills.length,
82
+ skillNames: skills.map(s => s.name),
83
+ });
84
+ // Build selection prompt
85
+ const selectionPrompt = buildSelectionPrompt(skills, userMessage);
86
+ // Make lightweight LLM call for selection
87
+ // Create a minimal agent message array for the selection call
88
+ const selectionMessages = [
89
+ {
90
+ role: 'user',
91
+ content: selectionPrompt,
92
+ createdAt: new Date(),
93
+ },
94
+ ];
95
+ // Use the agent's configured provider/model for the selection call
96
+ // skipTools=true to avoid MCP tool overhead for this lightweight call
97
+ const { response } = await generateAgentResponse(world, agent, selectionMessages, undefined, true);
98
+ // Extract skill name from response
99
+ const selectedSkill = parseSelectionResponse(response, skills);
100
+ logger.debug('Skill selection result', {
101
+ worldId: world.id,
102
+ agentId: agent.id,
103
+ selectedSkill,
104
+ });
105
+ return selectedSkill;
106
+ }
107
+ catch (error) {
108
+ logger.error('Skill selection failed, continuing without skill', {
109
+ worldId: world.id,
110
+ agentId: agent.id,
111
+ error: error instanceof Error ? error.message : String(error),
112
+ });
113
+ return null;
114
+ }
115
+ }
116
+ /**
117
+ * Parse the LLM's selection response to extract a valid skill name.
118
+ * Returns null if the response is "none" or doesn't match any installed skill.
119
+ */
120
+ function parseSelectionResponse(response, skills) {
121
+ if (response.type !== 'text' || !response.content) {
122
+ return null;
123
+ }
124
+ const rawAnswer = response.content.trim().toLowerCase();
125
+ // Check for explicit "none" response
126
+ if (rawAnswer === 'none') {
127
+ return null;
128
+ }
129
+ // Match against installed skill names (case-insensitive)
130
+ const validNames = skills.map(s => s.name.toLowerCase());
131
+ if (validNames.includes(rawAnswer)) {
132
+ return skills.find(s => s.name.toLowerCase() === rawAnswer).name;
133
+ }
134
+ // Try to extract a skill name if LLM included extra text
135
+ for (const skill of skills) {
136
+ if (rawAnswer.includes(skill.name.toLowerCase())) {
137
+ return skill.name;
138
+ }
139
+ }
140
+ logger.debug('LLM returned unrecognized skill selection', { rawAnswer });
141
+ return null;
142
+ }
143
+ // ============================================================
144
+ // Skill Content Injection
145
+ // ============================================================
146
+ /**
147
+ * Build a user message augmented with skill instructions.
148
+ *
149
+ * This is Step 2 of the two-step skill flow. If a skill was selected,
150
+ * the application reads the SKILL.md content and wraps it around the
151
+ * user's original message.
152
+ *
153
+ * @param worldId - World ID to locate skill files
154
+ * @param skillName - Selected skill name (from Step 1)
155
+ * @param originalMessage - The user's original message content
156
+ * @returns Augmented message content with skill instructions, or original message if skill not found
157
+ */
158
+ export async function buildSkillAugmentedMessage(worldId, skillName, originalMessage) {
159
+ try {
160
+ const rootPath = getDefaultRootPath();
161
+ const skillContent = await loadSkillFile(rootPath, worldId, skillName);
162
+ // Parse to get just the body content (instructions), skip frontmatter
163
+ const skillData = parseSkillFile(skillContent);
164
+ const augmented = `<skill name="${skillName}">
165
+ ${skillData.content}
166
+ </skill>
167
+
168
+ ${originalMessage}`;
169
+ logger.debug('Augmented user message with skill', {
170
+ worldId,
171
+ skillName,
172
+ originalLength: originalMessage.length,
173
+ augmentedLength: augmented.length,
174
+ });
175
+ return augmented;
176
+ }
177
+ catch (error) {
178
+ logger.error('Failed to load skill content, using original message', {
179
+ worldId,
180
+ skillName,
181
+ error: error instanceof Error ? error.message : String(error),
182
+ });
183
+ return originalMessage;
184
+ }
185
+ }
186
+ // ============================================================
187
+ // Exported for testing
188
+ // ============================================================
189
+ export { buildSelectionPrompt, parseSelectionResponse };
190
+ //# sourceMappingURL=skill-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-selector.js","sourceRoot":"","sources":["../../core/skill-selector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Skill Settings Utilities
3
+ *
4
+ * Purpose:
5
+ * - Provide shared parsing helpers for skill-related environment settings.
6
+ *
7
+ * Key Features:
8
+ * - Parses disabled skill ID lists from either JSON array or CSV strings.
9
+ * - Normalizes values by trimming and removing empty entries.
10
+ * - Returns de-duplicated sets for O(1) membership checks.
11
+ *
12
+ * Implementation Notes:
13
+ * - JSON parsing is attempted first to support persisted array payloads.
14
+ * - Falls back to CSV for backward compatibility with env-style values.
15
+ *
16
+ * Recent Changes:
17
+ * - 2026-02-16: Added shared parser to avoid duplication between prompt and load-skill flows.
18
+ */
19
+ export declare function parseSkillIdListFromEnv(value: string | undefined): Set<string>;
20
+ //# sourceMappingURL=skill-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-settings.d.ts","sourceRoot":"","sources":["../../core/skill-settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,CAyB9E"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Skill Settings Utilities
3
+ *
4
+ * Purpose:
5
+ * - Provide shared parsing helpers for skill-related environment settings.
6
+ *
7
+ * Key Features:
8
+ * - Parses disabled skill ID lists from either JSON array or CSV strings.
9
+ * - Normalizes values by trimming and removing empty entries.
10
+ * - Returns de-duplicated sets for O(1) membership checks.
11
+ *
12
+ * Implementation Notes:
13
+ * - JSON parsing is attempted first to support persisted array payloads.
14
+ * - Falls back to CSV for backward compatibility with env-style values.
15
+ *
16
+ * Recent Changes:
17
+ * - 2026-02-16: Added shared parser to avoid duplication between prompt and load-skill flows.
18
+ */
19
+ export function parseSkillIdListFromEnv(value) {
20
+ const raw = String(value || '').trim();
21
+ if (!raw) {
22
+ return new Set();
23
+ }
24
+ try {
25
+ const parsed = JSON.parse(raw);
26
+ if (Array.isArray(parsed)) {
27
+ return new Set(parsed
28
+ .map((entry) => String(entry || '').trim())
29
+ .filter((entry) => entry.length > 0));
30
+ }
31
+ }
32
+ catch {
33
+ // Fall back to CSV parsing.
34
+ }
35
+ return new Set(raw
36
+ .split(',')
37
+ .map((entry) => entry.trim())
38
+ .filter((entry) => entry.length > 0));
39
+ }
40
+ //# sourceMappingURL=skill-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-settings.js","sourceRoot":"","sources":["../../core/skill-settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,UAAU,uBAAuB,CAAC,KAAyB;IAC/D,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,GAAG,EAAE,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,GAAG,CACZ,MAAM;iBACH,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;iBAC1C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CACvC,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,4BAA4B;IAC9B,CAAC;IAED,OAAO,IAAI,GAAG,CACZ,GAAG;SACA,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CACvC,CAAC;AACJ,CAAC"}