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.
- package/README.md +17 -7
- package/dist/cli/commands.d.ts +109 -0
- package/dist/cli/commands.js +2024 -0
- package/dist/cli/display.d.ts +124 -0
- package/dist/cli/display.js +381 -0
- package/dist/cli/hitl.d.ts +33 -0
- package/dist/cli/hitl.js +81 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/stream.d.ts +41 -0
- package/dist/cli/stream.js +222 -0
- package/dist/core/activity-tracker.d.ts +16 -0
- package/dist/core/activity-tracker.d.ts.map +1 -0
- package/dist/core/activity-tracker.js +91 -0
- package/dist/core/activity-tracker.js.map +1 -0
- package/dist/core/ai-commands.d.ts +16 -0
- package/dist/core/ai-commands.d.ts.map +1 -0
- package/dist/core/ai-commands.js +24 -0
- package/dist/core/ai-commands.js.map +1 -0
- package/dist/core/ai-sdk-patch.d.ts +24 -0
- package/dist/core/ai-sdk-patch.d.ts.map +1 -0
- package/dist/core/ai-sdk-patch.js +169 -0
- package/dist/core/ai-sdk-patch.js.map +1 -0
- package/dist/core/anthropic-direct.d.ts +52 -0
- package/dist/core/anthropic-direct.d.ts.map +1 -0
- package/dist/core/anthropic-direct.js +301 -0
- package/dist/core/anthropic-direct.js.map +1 -0
- package/dist/core/approval-cache.d.ts +104 -0
- package/dist/core/approval-cache.d.ts.map +1 -0
- package/dist/core/approval-cache.js +150 -0
- package/dist/core/approval-cache.js.map +1 -0
- package/dist/core/chat-constants.d.ts +20 -0
- package/dist/core/chat-constants.d.ts.map +1 -0
- package/dist/core/chat-constants.js +22 -0
- package/dist/core/chat-constants.js.map +1 -0
- package/dist/core/create-agent-tool.d.ts +66 -0
- package/dist/core/create-agent-tool.d.ts.map +1 -0
- package/dist/core/create-agent-tool.js +212 -0
- package/dist/core/create-agent-tool.js.map +1 -0
- package/dist/core/events/approval-checker.d.ts +61 -0
- package/dist/core/events/approval-checker.d.ts.map +1 -0
- package/dist/core/events/approval-checker.js +226 -0
- package/dist/core/events/approval-checker.js.map +1 -0
- package/dist/core/events/index.d.ts +25 -0
- package/dist/core/events/index.d.ts.map +1 -0
- package/dist/core/events/index.js +30 -0
- package/dist/core/events/index.js.map +1 -0
- package/dist/core/events/memory-manager.d.ts +73 -0
- package/dist/core/events/memory-manager.d.ts.map +1 -0
- package/dist/core/events/memory-manager.js +1218 -0
- package/dist/core/events/memory-manager.js.map +1 -0
- package/dist/core/events/mention-logic.d.ts +39 -0
- package/dist/core/events/mention-logic.d.ts.map +1 -0
- package/dist/core/events/mention-logic.js +163 -0
- package/dist/core/events/mention-logic.js.map +1 -0
- package/dist/core/events/orchestrator.d.ts +69 -0
- package/dist/core/events/orchestrator.d.ts.map +1 -0
- package/dist/core/events/orchestrator.js +883 -0
- package/dist/core/events/orchestrator.js.map +1 -0
- package/dist/core/events/persistence.d.ts +41 -0
- package/dist/core/events/persistence.d.ts.map +1 -0
- package/dist/core/events/persistence.js +296 -0
- package/dist/core/events/persistence.js.map +1 -0
- package/dist/core/events/publishers.d.ts +81 -0
- package/dist/core/events/publishers.d.ts.map +1 -0
- package/dist/core/events/publishers.js +272 -0
- package/dist/core/events/publishers.js.map +1 -0
- package/dist/core/events/subscribers.d.ts +45 -0
- package/dist/core/events/subscribers.d.ts.map +1 -0
- package/dist/core/events/subscribers.js +288 -0
- package/dist/core/events/subscribers.js.map +1 -0
- package/dist/core/events/tool-bridge-logging.d.ts +28 -0
- package/dist/core/events/tool-bridge-logging.d.ts.map +1 -0
- package/dist/core/events/tool-bridge-logging.js +94 -0
- package/dist/core/events/tool-bridge-logging.js.map +1 -0
- package/dist/core/events-metadata.d.ts +72 -0
- package/dist/core/events-metadata.d.ts.map +1 -0
- package/dist/core/events-metadata.js +167 -0
- package/dist/core/events-metadata.js.map +1 -0
- package/dist/core/events.d.ts +186 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +1248 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/export.d.ts +106 -0
- package/dist/core/export.d.ts.map +1 -0
- package/dist/core/export.js +705 -0
- package/dist/core/export.js.map +1 -0
- package/dist/core/file-tools.d.ts +114 -0
- package/dist/core/file-tools.d.ts.map +1 -0
- package/dist/core/file-tools.js +370 -0
- package/dist/core/file-tools.js.map +1 -0
- package/dist/core/google-direct.d.ts +58 -0
- package/dist/core/google-direct.d.ts.map +1 -0
- package/dist/core/google-direct.js +298 -0
- package/dist/core/google-direct.js.map +1 -0
- package/dist/core/hitl.d.ts +54 -0
- package/dist/core/hitl.d.ts.map +1 -0
- package/dist/core/hitl.js +153 -0
- package/dist/core/hitl.js.map +1 -0
- package/dist/core/index.d.ts +59 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +70 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/llm-config.d.ts +128 -0
- package/dist/core/llm-config.d.ts.map +1 -0
- package/dist/core/llm-config.js +164 -0
- package/dist/core/llm-config.js.map +1 -0
- package/dist/core/llm-manager.d.ts +163 -0
- package/dist/core/llm-manager.d.ts.map +1 -0
- package/dist/core/llm-manager.js +669 -0
- package/dist/core/llm-manager.js.map +1 -0
- package/dist/core/load-skill-tool.d.ts +55 -0
- package/dist/core/load-skill-tool.d.ts.map +1 -0
- package/dist/core/load-skill-tool.js +468 -0
- package/dist/core/load-skill-tool.js.map +1 -0
- package/dist/core/logger.d.ts +88 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +358 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/managers.d.ts +131 -0
- package/dist/core/managers.d.ts.map +1 -0
- package/dist/core/managers.js +1223 -0
- package/dist/core/managers.js.map +1 -0
- package/dist/core/mcp-server-registry.d.ts +304 -0
- package/dist/core/mcp-server-registry.d.ts.map +1 -0
- package/dist/core/mcp-server-registry.js +1769 -0
- package/dist/core/mcp-server-registry.js.map +1 -0
- package/dist/core/mcp-tools.d.ts +56 -0
- package/dist/core/mcp-tools.d.ts.map +1 -0
- package/dist/core/mcp-tools.js +186 -0
- package/dist/core/mcp-tools.js.map +1 -0
- package/dist/core/message-prep.d.ts +81 -0
- package/dist/core/message-prep.d.ts.map +1 -0
- package/dist/core/message-prep.js +223 -0
- package/dist/core/message-prep.js.map +1 -0
- package/dist/core/message-processing-control.d.ts +54 -0
- package/dist/core/message-processing-control.d.ts.map +1 -0
- package/dist/core/message-processing-control.js +139 -0
- package/dist/core/message-processing-control.js.map +1 -0
- package/dist/core/openai-direct.d.ts +80 -0
- package/dist/core/openai-direct.d.ts.map +1 -0
- package/dist/core/openai-direct.js +374 -0
- package/dist/core/openai-direct.js.map +1 -0
- package/dist/core/shell-cmd-tool.d.ts +235 -0
- package/dist/core/shell-cmd-tool.d.ts.map +1 -0
- package/dist/core/shell-cmd-tool.js +1157 -0
- package/dist/core/shell-cmd-tool.js.map +1 -0
- package/dist/core/shell-process-registry.d.ts +88 -0
- package/dist/core/shell-process-registry.d.ts.map +1 -0
- package/dist/core/shell-process-registry.js +309 -0
- package/dist/core/shell-process-registry.js.map +1 -0
- package/dist/core/skill-registry.d.ts +75 -0
- package/dist/core/skill-registry.d.ts.map +1 -0
- package/dist/core/skill-registry.js +369 -0
- package/dist/core/skill-registry.js.map +1 -0
- package/dist/core/skill-script-runner.d.ts +89 -0
- package/dist/core/skill-script-runner.d.ts.map +1 -0
- package/dist/core/skill-script-runner.js +274 -0
- package/dist/core/skill-script-runner.js.map +1 -0
- package/dist/core/skill-selector.d.ts +65 -0
- package/dist/core/skill-selector.d.ts.map +1 -0
- package/dist/core/skill-selector.js +190 -0
- package/dist/core/skill-selector.js.map +1 -0
- package/dist/core/skill-settings.d.ts +20 -0
- package/dist/core/skill-settings.d.ts.map +1 -0
- package/dist/core/skill-settings.js +40 -0
- package/dist/core/skill-settings.js.map +1 -0
- package/dist/core/storage/agent-storage.d.ts +134 -0
- package/dist/core/storage/agent-storage.d.ts.map +1 -0
- package/dist/core/storage/agent-storage.js +498 -0
- package/dist/core/storage/agent-storage.js.map +1 -0
- package/dist/core/storage/eventStorage/fileEventStorage.d.ts +100 -0
- package/dist/core/storage/eventStorage/fileEventStorage.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/fileEventStorage.js +494 -0
- package/dist/core/storage/eventStorage/fileEventStorage.js.map +1 -0
- package/dist/core/storage/eventStorage/index.d.ts +31 -0
- package/dist/core/storage/eventStorage/index.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/index.js +31 -0
- package/dist/core/storage/eventStorage/index.js.map +1 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.d.ts +87 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.js +244 -0
- package/dist/core/storage/eventStorage/memoryEventStorage.js.map +1 -0
- package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts +45 -0
- package/dist/core/storage/eventStorage/sqliteEventStorage.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/sqliteEventStorage.js +301 -0
- package/dist/core/storage/eventStorage/sqliteEventStorage.js.map +1 -0
- package/dist/core/storage/eventStorage/types.d.ts +142 -0
- package/dist/core/storage/eventStorage/types.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/types.js +43 -0
- package/dist/core/storage/eventStorage/types.js.map +1 -0
- package/dist/core/storage/eventStorage/validation.d.ts +30 -0
- package/dist/core/storage/eventStorage/validation.d.ts.map +1 -0
- package/dist/core/storage/eventStorage/validation.js +68 -0
- package/dist/core/storage/eventStorage/validation.js.map +1 -0
- package/dist/core/storage/legacy-migrations.d.ts +45 -0
- package/dist/core/storage/legacy-migrations.d.ts.map +1 -0
- package/dist/core/storage/legacy-migrations.js +295 -0
- package/dist/core/storage/legacy-migrations.js.map +1 -0
- package/dist/core/storage/memory-storage.d.ts +105 -0
- package/dist/core/storage/memory-storage.d.ts.map +1 -0
- package/dist/core/storage/memory-storage.js +415 -0
- package/dist/core/storage/memory-storage.js.map +1 -0
- package/dist/core/storage/migration-runner.d.ts +96 -0
- package/dist/core/storage/migration-runner.d.ts.map +1 -0
- package/dist/core/storage/migration-runner.js +306 -0
- package/dist/core/storage/migration-runner.js.map +1 -0
- package/dist/core/storage/queue-storage.d.ts +147 -0
- package/dist/core/storage/queue-storage.d.ts.map +1 -0
- package/dist/core/storage/queue-storage.js +290 -0
- package/dist/core/storage/queue-storage.js.map +1 -0
- package/dist/core/storage/skill-storage.d.ts +136 -0
- package/dist/core/storage/skill-storage.d.ts.map +1 -0
- package/dist/core/storage/skill-storage.js +474 -0
- package/dist/core/storage/skill-storage.js.map +1 -0
- package/dist/core/storage/sqlite-schema.d.ts +95 -0
- package/dist/core/storage/sqlite-schema.d.ts.map +1 -0
- package/dist/core/storage/sqlite-schema.js +156 -0
- package/dist/core/storage/sqlite-schema.js.map +1 -0
- package/dist/core/storage/sqlite-storage.d.ts +146 -0
- package/dist/core/storage/sqlite-storage.d.ts.map +1 -0
- package/dist/core/storage/sqlite-storage.js +709 -0
- package/dist/core/storage/sqlite-storage.js.map +1 -0
- package/dist/core/storage/storage-factory.d.ts +61 -0
- package/dist/core/storage/storage-factory.d.ts.map +1 -0
- package/dist/core/storage/storage-factory.js +794 -0
- package/dist/core/storage/storage-factory.js.map +1 -0
- package/dist/core/storage/validation.d.ts +36 -0
- package/dist/core/storage/validation.d.ts.map +1 -0
- package/dist/core/storage/validation.js +79 -0
- package/dist/core/storage/validation.js.map +1 -0
- package/dist/core/storage/world-storage.d.ts +114 -0
- package/dist/core/storage/world-storage.d.ts.map +1 -0
- package/dist/core/storage/world-storage.js +378 -0
- package/dist/core/storage/world-storage.js.map +1 -0
- package/dist/core/subscription.d.ts +43 -0
- package/dist/core/subscription.d.ts.map +1 -0
- package/dist/core/subscription.js +227 -0
- package/dist/core/subscription.js.map +1 -0
- package/dist/core/tool-utils.d.ts +80 -0
- package/dist/core/tool-utils.d.ts.map +1 -0
- package/dist/core/tool-utils.js +273 -0
- package/dist/core/tool-utils.js.map +1 -0
- package/dist/core/types.d.ts +595 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +158 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/utils.d.ts +138 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +478 -0
- package/dist/core/utils.js.map +1 -0
- package/dist/core/world-class.d.ts +43 -0
- package/dist/core/world-class.d.ts.map +1 -0
- package/dist/core/world-class.js +90 -0
- package/dist/core/world-class.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/public/assets/agent-sprites-DJFgj-zP.png +0 -0
- package/dist/public/assets/border-KHK37r8y.svg +83 -0
- package/dist/public/assets/index-C9kPXL6G.css +1 -0
- package/dist/public/assets/index-DOQEHGWt.js +96 -0
- package/dist/public/index.html +21 -0
- package/dist/server/api.d.ts +2 -0
- package/dist/server/api.js +1124 -0
- package/dist/server/index.d.ts +29 -0
- package/dist/server/sse-handler.d.ts +62 -0
- package/dist/server/sse-handler.js +234 -0
- package/package.json +15 -3
- 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"}
|