qlogicagent 0.2.1 → 0.4.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 (226) hide show
  1. package/README.md +45 -45
  2. package/package.json +56 -42
  3. package/dist/agent/agent.d.ts +0 -43
  4. package/dist/agent/agent.js +0 -113
  5. package/dist/agent/tool-loop.d.ts +0 -64
  6. package/dist/agent/tool-loop.js +0 -575
  7. package/dist/agent/types.d.ts +0 -175
  8. package/dist/agent/types.js +0 -14
  9. package/dist/cli/main.d.ts +0 -11
  10. package/dist/cli/main.js +0 -23
  11. package/dist/cli/stdio-server.d.ts +0 -45
  12. package/dist/cli/stdio-server.js +0 -463
  13. package/dist/config/config.d.ts +0 -17
  14. package/dist/config/config.js +0 -21
  15. package/dist/contracts/hooks.d.ts +0 -120
  16. package/dist/contracts/hooks.js +0 -7
  17. package/dist/contracts/index.d.ts +0 -10
  18. package/dist/contracts/index.js +0 -10
  19. package/dist/contracts/planner.d.ts +0 -35
  20. package/dist/contracts/planner.js +0 -2
  21. package/dist/contracts/skill-candidate.d.ts +0 -63
  22. package/dist/contracts/skill-candidate.js +0 -195
  23. package/dist/contracts/todo.d.ts +0 -14
  24. package/dist/contracts/todo.js +0 -9
  25. package/dist/index.d.ts +0 -13
  26. package/dist/index.js +0 -15
  27. package/dist/llm/builtin-providers.d.ts +0 -10
  28. package/dist/llm/builtin-providers.js +0 -531
  29. package/dist/llm/index.d.ts +0 -15
  30. package/dist/llm/index.js +0 -14
  31. package/dist/llm/llm-client.d.ts +0 -43
  32. package/dist/llm/llm-client.js +0 -67
  33. package/dist/llm/model-catalog.d.ts +0 -53
  34. package/dist/llm/model-catalog.js +0 -191
  35. package/dist/llm/provider-def.d.ts +0 -59
  36. package/dist/llm/provider-def.js +0 -12
  37. package/dist/llm/provider-registry.d.ts +0 -54
  38. package/dist/llm/provider-registry.js +0 -147
  39. package/dist/llm/transport.d.ts +0 -62
  40. package/dist/llm/transport.js +0 -27
  41. package/dist/llm/transports/anthropic-messages.d.ts +0 -31
  42. package/dist/llm/transports/anthropic-messages.js +0 -293
  43. package/dist/llm/transports/openai-chat.d.ts +0 -36
  44. package/dist/llm/transports/openai-chat.js +0 -165
  45. package/dist/orchestration/agent-registry.d.ts +0 -41
  46. package/dist/orchestration/agent-registry.js +0 -116
  47. package/dist/orchestration/approval-aware-tool-plan.d.ts +0 -32
  48. package/dist/orchestration/approval-aware-tool-plan.js +0 -87
  49. package/dist/orchestration/context-compression.d.ts +0 -220
  50. package/dist/orchestration/context-compression.js +0 -583
  51. package/dist/orchestration/conversation-repair.d.ts +0 -61
  52. package/dist/orchestration/conversation-repair.js +0 -429
  53. package/dist/orchestration/curator-scheduler.d.ts +0 -119
  54. package/dist/orchestration/curator-scheduler.js +0 -135
  55. package/dist/orchestration/embedded-failover-policy.d.ts +0 -110
  56. package/dist/orchestration/embedded-failover-policy.js +0 -168
  57. package/dist/orchestration/error-classification.d.ts +0 -12
  58. package/dist/orchestration/error-classification.js +0 -77
  59. package/dist/orchestration/failover-classification.d.ts +0 -8
  60. package/dist/orchestration/failover-classification.js +0 -381
  61. package/dist/orchestration/failover-error.d.ts +0 -33
  62. package/dist/orchestration/failover-error.js +0 -198
  63. package/dist/orchestration/fork-subagent.d.ts +0 -100
  64. package/dist/orchestration/fork-subagent.js +0 -98
  65. package/dist/orchestration/index.d.ts +0 -120
  66. package/dist/orchestration/index.js +0 -267
  67. package/dist/orchestration/memory-flush-policy.d.ts +0 -57
  68. package/dist/orchestration/memory-flush-policy.js +0 -85
  69. package/dist/orchestration/memory-provider.d.ts +0 -14
  70. package/dist/orchestration/memory-provider.js +0 -2
  71. package/dist/orchestration/parallel-tool-calls.d.ts +0 -41
  72. package/dist/orchestration/parallel-tool-calls.js +0 -59
  73. package/dist/orchestration/prompt-cache-strategy.d.ts +0 -126
  74. package/dist/orchestration/prompt-cache-strategy.js +0 -228
  75. package/dist/orchestration/reactive-compact.d.ts +0 -73
  76. package/dist/orchestration/reactive-compact.js +0 -78
  77. package/dist/orchestration/retry-loop.d.ts +0 -22
  78. package/dist/orchestration/retry-loop.js +0 -24
  79. package/dist/orchestration/skill-candidate.d.ts +0 -52
  80. package/dist/orchestration/skill-candidate.js +0 -141
  81. package/dist/orchestration/skill-consolidation.d.ts +0 -123
  82. package/dist/orchestration/skill-consolidation.js +0 -220
  83. package/dist/orchestration/skill-improvement.d.ts +0 -59
  84. package/dist/orchestration/skill-improvement.js +0 -66
  85. package/dist/orchestration/skill-similarity.d.ts +0 -98
  86. package/dist/orchestration/skill-similarity.js +0 -131
  87. package/dist/orchestration/streaming-tool-executor.d.ts +0 -73
  88. package/dist/orchestration/streaming-tool-executor.js +0 -96
  89. package/dist/orchestration/team-orchestration.d.ts +0 -195
  90. package/dist/orchestration/team-orchestration.js +0 -369
  91. package/dist/orchestration/team-tool-loop-wiring.d.ts +0 -92
  92. package/dist/orchestration/team-tool-loop-wiring.js +0 -147
  93. package/dist/orchestration/tool-choice-policy.d.ts +0 -54
  94. package/dist/orchestration/tool-choice-policy.js +0 -164
  95. package/dist/orchestration/tool-loop-state.d.ts +0 -50
  96. package/dist/orchestration/tool-loop-state.js +0 -133
  97. package/dist/orchestration/tool-schema.d.ts +0 -39
  98. package/dist/orchestration/tool-schema.js +0 -297
  99. package/dist/orchestration/transcript-repair.d.ts +0 -42
  100. package/dist/orchestration/transcript-repair.js +0 -426
  101. package/dist/orchestration/turn-loop-guard.d.ts +0 -86
  102. package/dist/orchestration/turn-loop-guard.js +0 -92
  103. package/dist/orchestration/web-browser-policy.d.ts +0 -17
  104. package/dist/orchestration/web-browser-policy.js +0 -39
  105. package/dist/runtime/context-compression.d.ts +0 -61
  106. package/dist/runtime/context-compression.js +0 -274
  107. package/dist/runtime/hook-registry.d.ts +0 -12
  108. package/dist/runtime/hook-registry.js +0 -53
  109. package/dist/runtime/memory-hooks.d.ts +0 -23
  110. package/dist/runtime/memory-hooks.js +0 -65
  111. package/dist/runtime/tool-eligibility.d.ts +0 -59
  112. package/dist/runtime/tool-eligibility.js +0 -111
  113. package/dist/skills/index.d.ts +0 -108
  114. package/dist/skills/index.js +0 -82
  115. package/dist/skills/memory-extractor.d.ts +0 -64
  116. package/dist/skills/memory-extractor.js +0 -173
  117. package/dist/skills/memory-query-tool.d.ts +0 -43
  118. package/dist/skills/memory-query-tool.js +0 -127
  119. package/dist/skills/memory-store.d.ts +0 -66
  120. package/dist/skills/memory-store.js +0 -228
  121. package/dist/skills/memory-tool.d.ts +0 -67
  122. package/dist/skills/memory-tool.js +0 -192
  123. package/dist/skills/portable-tool.d.ts +0 -71
  124. package/dist/skills/portable-tool.js +0 -14
  125. package/dist/skills/qmemory-adapter.d.ts +0 -52
  126. package/dist/skills/qmemory-adapter.js +0 -165
  127. package/dist/skills/skill-frontmatter.d.ts +0 -19
  128. package/dist/skills/skill-frontmatter.js +0 -344
  129. package/dist/skills/skill-guard.d.ts +0 -23
  130. package/dist/skills/skill-guard.js +0 -229
  131. package/dist/skills/skill-loader.d.ts +0 -16
  132. package/dist/skills/skill-loader.js +0 -303
  133. package/dist/skills/skill-source.d.ts +0 -119
  134. package/dist/skills/skill-source.js +0 -126
  135. package/dist/skills/skill-types.d.ts +0 -199
  136. package/dist/skills/skill-types.js +0 -6
  137. package/dist/skills/think-tool.d.ts +0 -16
  138. package/dist/skills/think-tool.js +0 -59
  139. package/dist/skills/todo-tool.d.ts +0 -63
  140. package/dist/skills/todo-tool.js +0 -114
  141. package/dist/skills/tools/agent-tool.d.ts +0 -91
  142. package/dist/skills/tools/agent-tool.js +0 -142
  143. package/dist/skills/tools/apply-patch-tool.d.ts +0 -29
  144. package/dist/skills/tools/apply-patch-tool.js +0 -184
  145. package/dist/skills/tools/ask-user-tool.d.ts +0 -80
  146. package/dist/skills/tools/ask-user-tool.js +0 -121
  147. package/dist/skills/tools/brief-tool.d.ts +0 -74
  148. package/dist/skills/tools/brief-tool.js +0 -95
  149. package/dist/skills/tools/browser-tool.d.ts +0 -114
  150. package/dist/skills/tools/browser-tool.js +0 -155
  151. package/dist/skills/tools/checkpoint-tool.d.ts +0 -66
  152. package/dist/skills/tools/checkpoint-tool.js +0 -102
  153. package/dist/skills/tools/config-tool.d.ts +0 -63
  154. package/dist/skills/tools/config-tool.js +0 -143
  155. package/dist/skills/tools/cron-tool.d.ts +0 -116
  156. package/dist/skills/tools/cron-tool.js +0 -175
  157. package/dist/skills/tools/edit-tool.d.ts +0 -43
  158. package/dist/skills/tools/edit-tool.js +0 -70
  159. package/dist/skills/tools/exec-tool.d.ts +0 -102
  160. package/dist/skills/tools/exec-tool.js +0 -133
  161. package/dist/skills/tools/image-generate-tool.d.ts +0 -62
  162. package/dist/skills/tools/image-generate-tool.js +0 -67
  163. package/dist/skills/tools/instructions-tool.d.ts +0 -103
  164. package/dist/skills/tools/instructions-tool.js +0 -187
  165. package/dist/skills/tools/lsp-tool.d.ts +0 -153
  166. package/dist/skills/tools/lsp-tool.js +0 -227
  167. package/dist/skills/tools/mcp-client-types.d.ts +0 -269
  168. package/dist/skills/tools/mcp-client-types.js +0 -53
  169. package/dist/skills/tools/mcp-tool.d.ts +0 -249
  170. package/dist/skills/tools/mcp-tool.js +0 -503
  171. package/dist/skills/tools/memory-tool.d.ts +0 -74
  172. package/dist/skills/tools/memory-tool.js +0 -88
  173. package/dist/skills/tools/monitor-tool.d.ts +0 -113
  174. package/dist/skills/tools/monitor-tool.js +0 -131
  175. package/dist/skills/tools/music-generate-tool.d.ts +0 -55
  176. package/dist/skills/tools/music-generate-tool.js +0 -62
  177. package/dist/skills/tools/notify-tool.d.ts +0 -53
  178. package/dist/skills/tools/notify-tool.js +0 -62
  179. package/dist/skills/tools/patch-tool.d.ts +0 -45
  180. package/dist/skills/tools/patch-tool.js +0 -505
  181. package/dist/skills/tools/pdf-tool.d.ts +0 -66
  182. package/dist/skills/tools/pdf-tool.js +0 -88
  183. package/dist/skills/tools/plan-mode-tool.d.ts +0 -59
  184. package/dist/skills/tools/plan-mode-tool.js +0 -122
  185. package/dist/skills/tools/read-tool.d.ts +0 -51
  186. package/dist/skills/tools/read-tool.js +0 -84
  187. package/dist/skills/tools/repl-tool.d.ts +0 -70
  188. package/dist/skills/tools/repl-tool.js +0 -69
  189. package/dist/skills/tools/search-tool.d.ts +0 -112
  190. package/dist/skills/tools/search-tool.js +0 -225
  191. package/dist/skills/tools/send-message-tool.d.ts +0 -51
  192. package/dist/skills/tools/send-message-tool.js +0 -76
  193. package/dist/skills/tools/skill-list-tool.d.ts +0 -33
  194. package/dist/skills/tools/skill-list-tool.js +0 -54
  195. package/dist/skills/tools/skill-manage-tool.d.ts +0 -73
  196. package/dist/skills/tools/skill-manage-tool.js +0 -153
  197. package/dist/skills/tools/skill-view-tool.d.ts +0 -37
  198. package/dist/skills/tools/skill-view-tool.js +0 -72
  199. package/dist/skills/tools/sleep-tool.d.ts +0 -49
  200. package/dist/skills/tools/sleep-tool.js +0 -81
  201. package/dist/skills/tools/structured-output-tool.d.ts +0 -116
  202. package/dist/skills/tools/structured-output-tool.js +0 -176
  203. package/dist/skills/tools/task-tool.d.ts +0 -104
  204. package/dist/skills/tools/task-tool.js +0 -161
  205. package/dist/skills/tools/team-tool.d.ts +0 -89
  206. package/dist/skills/tools/team-tool.js +0 -105
  207. package/dist/skills/tools/tool-search-tool.d.ts +0 -51
  208. package/dist/skills/tools/tool-search-tool.js +0 -110
  209. package/dist/skills/tools/tts-tool.d.ts +0 -38
  210. package/dist/skills/tools/tts-tool.js +0 -45
  211. package/dist/skills/tools/video-edit-tool.d.ts +0 -69
  212. package/dist/skills/tools/video-edit-tool.js +0 -74
  213. package/dist/skills/tools/video-generate-tool.d.ts +0 -62
  214. package/dist/skills/tools/video-generate-tool.js +0 -66
  215. package/dist/skills/tools/video-merge-tool.d.ts +0 -105
  216. package/dist/skills/tools/video-merge-tool.js +0 -92
  217. package/dist/skills/tools/video-upscale-tool.d.ts +0 -45
  218. package/dist/skills/tools/video-upscale-tool.js +0 -52
  219. package/dist/skills/tools/web-fetch-tool.d.ts +0 -78
  220. package/dist/skills/tools/web-fetch-tool.js +0 -92
  221. package/dist/skills/tools/web-search-tool.d.ts +0 -57
  222. package/dist/skills/tools/web-search-tool.js +0 -86
  223. package/dist/skills/tools/worktree-tool.d.ts +0 -69
  224. package/dist/skills/tools/worktree-tool.js +0 -147
  225. package/dist/skills/tools/write-tool.d.ts +0 -45
  226. package/dist/skills/tools/write-tool.js +0 -81
@@ -1,229 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Skill security scanner — portable, zero external dependencies.
3
- // Mirrors openclaw/src/security/skill-scanner.ts rules but uses DI deps
4
- // instead of Node.js fs/path directly.
5
- // ---------------------------------------------------------------------------
6
- const LINE_RULES = [
7
- {
8
- ruleId: "dangerous-exec",
9
- severity: "critical",
10
- message: "Shell command execution detected (child_process)",
11
- pattern: /\b(exec|execSync|spawn|spawnSync|execFile|execFileSync)\s*\(/,
12
- requiresContext: /child_process/,
13
- },
14
- {
15
- ruleId: "dynamic-code-execution",
16
- severity: "critical",
17
- message: "Dynamic code execution detected",
18
- pattern: /\beval\s*\(|new\s+Function\s*\(/,
19
- },
20
- {
21
- ruleId: "crypto-mining",
22
- severity: "critical",
23
- message: "Possible crypto-mining reference detected",
24
- pattern: /stratum\+tcp|stratum\+ssl|coinhive|cryptonight|xmrig/i,
25
- },
26
- {
27
- ruleId: "suspicious-network",
28
- severity: "warn",
29
- message: "WebSocket connection to non-standard port",
30
- pattern: /new\s+WebSocket\s*\(\s*["']wss?:\/\/[^"']*:(\d+)/,
31
- },
32
- ];
33
- const STANDARD_PORTS = new Set([80, 443, 8080, 8443, 3000]);
34
- const SOURCE_RULES = [
35
- {
36
- ruleId: "potential-exfiltration",
37
- severity: "warn",
38
- message: "File read combined with network send — possible data exfiltration",
39
- pattern: /readFileSync|readFile/,
40
- requiresContext: /\bfetch\b|\bpost\b|http\.request/i,
41
- },
42
- {
43
- ruleId: "obfuscated-code",
44
- severity: "warn",
45
- message: "Hex-encoded string sequence detected (possible obfuscation)",
46
- pattern: /(\\x[0-9a-fA-F]{2}){6,}/,
47
- },
48
- {
49
- ruleId: "obfuscated-code",
50
- severity: "warn",
51
- message: "Large base64 payload with decode call detected (possible obfuscation)",
52
- pattern: /(?:atob|Buffer\.from)\s*\(\s*["'][A-Za-z0-9+/=]{200,}["']/,
53
- },
54
- {
55
- ruleId: "env-harvesting",
56
- severity: "critical",
57
- message: "Environment variable access combined with network send — possible credential harvesting",
58
- pattern: /process\.env/,
59
- requiresContext: /\bfetch\b|\bpost\b|http\.request/i,
60
- },
61
- ];
62
- // ---------------------------------------------------------------------------
63
- // Scannable extensions
64
- // ---------------------------------------------------------------------------
65
- const SCANNABLE_EXTENSIONS = new Set([
66
- ".js", ".ts", ".mjs", ".cjs", ".mts", ".cts", ".jsx", ".tsx",
67
- ]);
68
- const DEFAULT_MAX_SCAN_FILES = 500;
69
- const DEFAULT_MAX_FILE_BYTES = 1024 * 1024;
70
- // ---------------------------------------------------------------------------
71
- // Core scanner (operates on source text — no FS dependency)
72
- // ---------------------------------------------------------------------------
73
- function truncateEvidence(evidence, maxLen = 120) {
74
- return evidence.length <= maxLen ? evidence : `${evidence.slice(0, maxLen)}…`;
75
- }
76
- /**
77
- * Scan a source string for security findings.
78
- * This is a pure function — no file I/O.
79
- */
80
- export function scanSource(source, filePath) {
81
- const findings = [];
82
- const lines = source.split("\n");
83
- const matchedLineRules = new Set();
84
- // Line rules
85
- for (const rule of LINE_RULES) {
86
- if (matchedLineRules.has(rule.ruleId))
87
- continue;
88
- if (rule.requiresContext && !rule.requiresContext.test(source))
89
- continue;
90
- for (let i = 0; i < lines.length; i++) {
91
- const line = lines[i];
92
- const match = rule.pattern.exec(line);
93
- if (!match)
94
- continue;
95
- // Special: suspicious-network checks port number
96
- if (rule.ruleId === "suspicious-network") {
97
- const port = parseInt(match[1], 10);
98
- if (STANDARD_PORTS.has(port))
99
- continue;
100
- }
101
- findings.push({
102
- ruleId: rule.ruleId,
103
- severity: rule.severity,
104
- file: filePath,
105
- line: i + 1,
106
- message: rule.message,
107
- evidence: truncateEvidence(line.trim()),
108
- });
109
- matchedLineRules.add(rule.ruleId);
110
- break; // one finding per line-rule per file
111
- }
112
- }
113
- // Source rules
114
- const matchedSourceRules = new Set();
115
- for (const rule of SOURCE_RULES) {
116
- const ruleKey = `${rule.ruleId}::${rule.message}`;
117
- if (matchedSourceRules.has(ruleKey))
118
- continue;
119
- if (!rule.pattern.test(source))
120
- continue;
121
- if (rule.requiresContext && !rule.requiresContext.test(source))
122
- continue;
123
- let matchLine = 0;
124
- let matchEvidence = "";
125
- for (let i = 0; i < lines.length; i++) {
126
- if (rule.pattern.test(lines[i])) {
127
- matchLine = i + 1;
128
- matchEvidence = lines[i].trim();
129
- break;
130
- }
131
- }
132
- if (matchLine === 0) {
133
- matchLine = 1;
134
- matchEvidence = source.slice(0, 120);
135
- }
136
- findings.push({
137
- ruleId: rule.ruleId,
138
- severity: rule.severity,
139
- file: filePath,
140
- line: matchLine,
141
- message: rule.message,
142
- evidence: truncateEvidence(matchEvidence),
143
- });
144
- matchedSourceRules.add(ruleKey);
145
- }
146
- return findings;
147
- }
148
- async function walkDirWithLimit(deps, dirPath, maxFiles) {
149
- const files = [];
150
- const stack = [dirPath];
151
- while (stack.length > 0 && files.length < maxFiles) {
152
- const currentDir = stack.pop();
153
- if (!currentDir)
154
- break;
155
- let entries;
156
- try {
157
- entries = deps.fs.readdirSync(currentDir, { withFileTypes: true });
158
- }
159
- catch {
160
- continue;
161
- }
162
- for (const entry of entries) {
163
- if (files.length >= maxFiles)
164
- break;
165
- if (entry.name.startsWith(".") || entry.name === "node_modules")
166
- continue;
167
- const fullPath = deps.path.join(currentDir, entry.name);
168
- if (entry.isDirectory()) {
169
- stack.push(fullPath);
170
- }
171
- else if (SCANNABLE_EXTENSIONS.has(deps.path.extname(entry.name).toLowerCase())) {
172
- files.push(fullPath);
173
- }
174
- }
175
- }
176
- return files;
177
- }
178
- /**
179
- * Scan an entire skill directory for security issues.
180
- * Returns a summary with per-file findings.
181
- */
182
- export async function scanSkillDirectory(deps, skillDir, opts) {
183
- const maxFiles = Math.max(1, opts?.maxFiles ?? DEFAULT_MAX_SCAN_FILES);
184
- const maxFileBytes = Math.max(1, opts?.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES);
185
- const files = opts?.includeFiles && opts.includeFiles.length > 0
186
- ? opts.includeFiles
187
- : await walkDirWithLimit(deps, skillDir, maxFiles);
188
- const allFindings = [];
189
- let scannedFiles = 0;
190
- for (const filePath of files) {
191
- try {
192
- const stat = deps.fs.statSync(filePath);
193
- if (stat.size > maxFileBytes)
194
- continue;
195
- }
196
- catch {
197
- continue;
198
- }
199
- let source;
200
- try {
201
- source = await deps.fs.readFile(filePath, "utf-8");
202
- }
203
- catch {
204
- continue;
205
- }
206
- scannedFiles += 1;
207
- const findings = scanSource(source, filePath);
208
- allFindings.push(...findings);
209
- }
210
- return {
211
- scannedFiles,
212
- critical: allFindings.filter((f) => f.severity === "critical").length,
213
- warn: allFindings.filter((f) => f.severity === "warn").length,
214
- info: allFindings.filter((f) => f.severity === "info").length,
215
- findings: allFindings,
216
- };
217
- }
218
- /**
219
- * Quick check: does this source have any critical findings?
220
- */
221
- export function hasCriticalFindings(source, filePath) {
222
- return scanSource(source, filePath).some((f) => f.severity === "critical");
223
- }
224
- /**
225
- * Check if a file extension is scannable by the guard.
226
- */
227
- export function isScannable(filePath, pathDeps) {
228
- return SCANNABLE_EXTENSIONS.has(pathDeps.extname(filePath).toLowerCase());
229
- }
@@ -1,16 +0,0 @@
1
- import type { SkillEntry, SkillLoadLimits, SkillLoaderDeps, SkillSnapshot } from "./skill-types.js";
2
- /**
3
- * Discover all skills from configured search paths, applying precedence merge
4
- * and size/count limits. Returns fully parsed `SkillEntry` objects.
5
- */
6
- export declare function loadSkillEntries(deps: SkillLoaderDeps): SkillEntry[];
7
- /**
8
- * Filter skill entries by a name-based filter list.
9
- * If filter is undefined or empty, returns all entries.
10
- */
11
- export declare function filterSkillEntries(entries: SkillEntry[], skillFilter?: string[]): SkillEntry[];
12
- /**
13
- * Build a system-prompt-embeddable skill snapshot.
14
- * Applies count and character limits from the provided config.
15
- */
16
- export declare function buildSkillSnapshot(entries: SkillEntry[], limits?: SkillLoadLimits, skillFilter?: string[]): SkillSnapshot;
@@ -1,303 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Skill discovery & loading — portable, DI-based.
3
- // Mirrors openclaw/src/agents/skills/workspace.ts logic but decoupled from
4
- // Node.js fs/path via SkillLoaderDeps.
5
- // ---------------------------------------------------------------------------
6
- import { parseFrontmatter, resolveSkillMetadata, resolveSkillInvocationPolicy } from "./skill-frontmatter.js";
7
- // ---------------------------------------------------------------------------
8
- // Default limits (match openclaw upstream)
9
- // ---------------------------------------------------------------------------
10
- const DEFAULT_LIMITS = {
11
- maxCandidatesPerRoot: 300,
12
- maxSkillsLoadedPerSource: 200,
13
- maxSkillsInPrompt: 150,
14
- maxSkillsPromptChars: 30_000,
15
- maxSkillFileBytes: 256_000,
16
- };
17
- function resolveLimits(input) {
18
- return {
19
- maxCandidatesPerRoot: input?.maxCandidatesPerRoot ?? DEFAULT_LIMITS.maxCandidatesPerRoot,
20
- maxSkillsLoadedPerSource: input?.maxSkillsLoadedPerSource ?? DEFAULT_LIMITS.maxSkillsLoadedPerSource,
21
- maxSkillsInPrompt: input?.maxSkillsInPrompt ?? DEFAULT_LIMITS.maxSkillsInPrompt,
22
- maxSkillsPromptChars: input?.maxSkillsPromptChars ?? DEFAULT_LIMITS.maxSkillsPromptChars,
23
- maxSkillFileBytes: input?.maxSkillFileBytes ?? DEFAULT_LIMITS.maxSkillFileBytes,
24
- };
25
- }
26
- // ---------------------------------------------------------------------------
27
- // Path containment guard
28
- // ---------------------------------------------------------------------------
29
- function isPathInside(parentRealPath, childRealPath, sep) {
30
- const normalizedParent = parentRealPath.endsWith(sep) ? parentRealPath : parentRealPath + sep;
31
- return childRealPath === parentRealPath || childRealPath.startsWith(normalizedParent);
32
- }
33
- function resolveContainedPath(deps, rootRealPath, candidatePath) {
34
- try {
35
- const real = deps.fs.realpathSync(candidatePath);
36
- return isPathInside(rootRealPath, real, deps.path.sep) ? real : null;
37
- }
38
- catch {
39
- return null;
40
- }
41
- }
42
- // ---------------------------------------------------------------------------
43
- // Directory listing helpers
44
- // ---------------------------------------------------------------------------
45
- function listChildDirectories(deps, dir) {
46
- try {
47
- const entries = deps.fs.readdirSync(dir, { withFileTypes: true });
48
- const dirs = [];
49
- for (const entry of entries) {
50
- if (entry.name.startsWith(".") || entry.name === "node_modules")
51
- continue;
52
- if (entry.isDirectory()) {
53
- dirs.push(entry.name);
54
- continue;
55
- }
56
- if (entry.isSymbolicLink()) {
57
- try {
58
- const fullPath = deps.path.join(dir, entry.name);
59
- if (deps.fs.statSync(fullPath).isDirectory()) {
60
- dirs.push(entry.name);
61
- }
62
- }
63
- catch {
64
- // broken symlink — skip
65
- }
66
- }
67
- }
68
- return dirs;
69
- }
70
- catch {
71
- return [];
72
- }
73
- }
74
- /**
75
- * Detect nested `skills/` subfolder. If `dir/skills/<x>/SKILL.md` exists,
76
- * treat `dir/skills/` as the real root.
77
- */
78
- function resolveNestedSkillsRoot(deps, dir, maxEntriesToScan) {
79
- const nested = deps.path.join(dir, "skills");
80
- try {
81
- if (!deps.fs.existsSync(nested) || !deps.fs.statSync(nested).isDirectory()) {
82
- return dir;
83
- }
84
- }
85
- catch {
86
- return dir;
87
- }
88
- const nestedDirs = listChildDirectories(deps, nested);
89
- const toScan = nestedDirs.slice(0, Math.min(nestedDirs.length, maxEntriesToScan));
90
- for (const name of toScan) {
91
- const skillMd = deps.path.join(nested, name, "SKILL.md");
92
- if (deps.fs.existsSync(skillMd)) {
93
- return nested;
94
- }
95
- }
96
- return dir;
97
- }
98
- // ---------------------------------------------------------------------------
99
- // Single-source skill loading
100
- // ---------------------------------------------------------------------------
101
- function loadSkillsFromSourceDir(deps, dir, source, limits) {
102
- const rootDir = deps.path.resolve(dir);
103
- let rootRealPath;
104
- try {
105
- rootRealPath = deps.fs.realpathSync(rootDir);
106
- }
107
- catch {
108
- return [];
109
- }
110
- const baseDir = resolveNestedSkillsRoot(deps, rootDir, limits.maxCandidatesPerRoot);
111
- const baseDirRealPath = resolveContainedPath(deps, rootRealPath, baseDir);
112
- if (!baseDirRealPath)
113
- return [];
114
- // Case 1: root itself is a single skill
115
- const rootSkillMd = deps.path.join(baseDir, "SKILL.md");
116
- if (deps.fs.existsSync(rootSkillMd)) {
117
- if (!resolveContainedPath(deps, baseDirRealPath, rootSkillMd))
118
- return [];
119
- try {
120
- const size = deps.fs.statSync(rootSkillMd).size;
121
- if (size > limits.maxSkillFileBytes)
122
- return [];
123
- }
124
- catch {
125
- return [];
126
- }
127
- return [buildWorkspaceSkill(deps, rootSkillMd, baseDir, source)].filter((s) => s !== null);
128
- }
129
- // Case 2: enumerate child directories for `<child>/SKILL.md`
130
- const childDirs = listChildDirectories(deps, baseDir);
131
- const maxCandidates = Math.max(0, limits.maxSkillsLoadedPerSource);
132
- const limitedChildren = childDirs.slice().sort().slice(0, maxCandidates);
133
- const skills = [];
134
- for (const name of limitedChildren) {
135
- const skillDir = deps.path.join(baseDir, name);
136
- if (!resolveContainedPath(deps, baseDirRealPath, skillDir))
137
- continue;
138
- const skillMd = deps.path.join(skillDir, "SKILL.md");
139
- if (!deps.fs.existsSync(skillMd))
140
- continue;
141
- if (!resolveContainedPath(deps, baseDirRealPath, skillMd))
142
- continue;
143
- try {
144
- const size = deps.fs.statSync(skillMd).size;
145
- if (size > limits.maxSkillFileBytes)
146
- continue;
147
- }
148
- catch {
149
- continue;
150
- }
151
- const skill = buildWorkspaceSkill(deps, skillMd, skillDir, source);
152
- if (skill)
153
- skills.push(skill);
154
- if (skills.length >= limits.maxSkillsLoadedPerSource)
155
- break;
156
- }
157
- return skills;
158
- }
159
- function buildWorkspaceSkill(deps, filePath, baseDir, source) {
160
- try {
161
- const content = deps.fs.readFileSync(filePath, "utf-8");
162
- const fm = parseFrontmatter(content);
163
- const name = fm["name"] ?? deps.path.resolve(baseDir).split(deps.path.sep).pop() ?? "unknown";
164
- return {
165
- name,
166
- description: fm["description"],
167
- source,
168
- filePath: deps.path.resolve(filePath),
169
- baseDir: deps.path.resolve(baseDir),
170
- primaryEnv: fm["primaryEnv"] ?? fm["primary-env"],
171
- requiredEnv: fm["required-env"]?.split(",").map((s) => s.trim()).filter(Boolean),
172
- };
173
- }
174
- catch {
175
- return null;
176
- }
177
- }
178
- // ---------------------------------------------------------------------------
179
- // Public API
180
- // ---------------------------------------------------------------------------
181
- /**
182
- * Discover all skills from configured search paths, applying precedence merge
183
- * and size/count limits. Returns fully parsed `SkillEntry` objects.
184
- */
185
- export function loadSkillEntries(deps) {
186
- const limits = resolveLimits(deps.limits);
187
- const { searchPaths } = deps;
188
- const sources = [];
189
- // Order: extra < bundled < managed < personalAgents < projectAgents < workspace
190
- if (searchPaths.extra) {
191
- for (const dir of searchPaths.extra) {
192
- sources.push({ dir, source: "extra" });
193
- }
194
- }
195
- if (searchPaths.bundled) {
196
- sources.push({ dir: searchPaths.bundled, source: "bundled" });
197
- }
198
- if (searchPaths.managed) {
199
- sources.push({ dir: searchPaths.managed, source: "managed" });
200
- }
201
- if (searchPaths.personalAgents) {
202
- sources.push({ dir: searchPaths.personalAgents, source: "agents-personal" });
203
- }
204
- if (searchPaths.projectAgents) {
205
- sources.push({ dir: searchPaths.projectAgents, source: "agents-project" });
206
- }
207
- if (searchPaths.workspace) {
208
- sources.push({ dir: searchPaths.workspace, source: "workspace" });
209
- }
210
- // Merge with later sources overriding earlier ones (by name)
211
- const merged = new Map();
212
- for (const src of sources) {
213
- const skills = loadSkillsFromSourceDir(deps, src.dir, src.source, limits);
214
- for (const skill of skills) {
215
- merged.set(skill.name, skill);
216
- }
217
- }
218
- // Build entries with parsed metadata
219
- const entries = [];
220
- for (const skill of merged.values()) {
221
- let frontmatter = parseFrontmatter("");
222
- try {
223
- const raw = deps.fs.readFileSync(skill.filePath, "utf-8");
224
- frontmatter = parseFrontmatter(raw);
225
- }
226
- catch {
227
- // ignore malformed
228
- }
229
- entries.push({
230
- skill,
231
- frontmatter,
232
- metadata: resolveSkillMetadata(frontmatter),
233
- invocation: resolveSkillInvocationPolicy(frontmatter),
234
- });
235
- }
236
- return entries;
237
- }
238
- /**
239
- * Filter skill entries by a name-based filter list.
240
- * If filter is undefined or empty, returns all entries.
241
- */
242
- export function filterSkillEntries(entries, skillFilter) {
243
- if (!skillFilter || skillFilter.length === 0)
244
- return entries;
245
- const normalized = new Set(skillFilter.map((s) => s.trim().toLowerCase()).filter(Boolean));
246
- if (normalized.size === 0)
247
- return [];
248
- return entries.filter((e) => normalized.has(e.skill.name.toLowerCase()));
249
- }
250
- /**
251
- * Build a system-prompt-embeddable skill snapshot.
252
- * Applies count and character limits from the provided config.
253
- */
254
- export function buildSkillSnapshot(entries, limits, skillFilter) {
255
- const resolved = resolveLimits(limits);
256
- const filtered = filterSkillEntries(entries, skillFilter);
257
- // Truncate by count first
258
- let promptSkills = filtered.slice(0, Math.max(0, resolved.maxSkillsInPrompt));
259
- // Format function for prompt
260
- const formatForPrompt = (skills) => {
261
- if (skills.length === 0)
262
- return "";
263
- const lines = [];
264
- for (const entry of skills) {
265
- const s = entry.skill;
266
- const meta = entry.metadata;
267
- const desc = s.description ? ` — ${s.description}` : "";
268
- const env = s.primaryEnv ? ` [${s.primaryEnv}]` : "";
269
- const emoji = meta?.emoji ? `${meta.emoji} ` : "";
270
- lines.push(`- ${emoji}**${s.name}**${env}${desc}`);
271
- // Compact path with ~ prefix
272
- lines.push(` File: ${s.filePath}`);
273
- }
274
- return lines.join("\n");
275
- };
276
- // Truncate by character budget via binary search
277
- let prompt = formatForPrompt(promptSkills);
278
- if (prompt.length > resolved.maxSkillsPromptChars) {
279
- let lo = 0;
280
- let hi = promptSkills.length;
281
- while (lo < hi) {
282
- const mid = Math.ceil((lo + hi) / 2);
283
- const candidate = formatForPrompt(promptSkills.slice(0, mid));
284
- if (candidate.length <= resolved.maxSkillsPromptChars) {
285
- lo = mid;
286
- }
287
- else {
288
- hi = mid - 1;
289
- }
290
- }
291
- promptSkills = promptSkills.slice(0, lo);
292
- prompt = formatForPrompt(promptSkills);
293
- }
294
- return {
295
- prompt,
296
- skills: promptSkills.map((e) => ({
297
- name: e.skill.name,
298
- primaryEnv: e.skill.primaryEnv,
299
- requiredEnv: e.skill.requiredEnv,
300
- })),
301
- skillFilter,
302
- };
303
- }
@@ -1,119 +0,0 @@
1
- import type { SkillScanSummary } from "./skill-types.js";
2
- /**
3
- * Trust level for a skill based on its origin.
4
- * Higher trust = fewer restrictions.
5
- */
6
- export type TrustLevel = "builtin" | "verified" | "community" | "unknown";
7
- /**
8
- * Result of a skill security scan decision.
9
- */
10
- export type ScanVerdict = "pass" | "quarantine" | "reject";
11
- /**
12
- * A resolved skill source descriptor.
13
- */
14
- export interface SkillSourceDescriptor {
15
- /** Source type. */
16
- kind: "bundled" | "github" | "npm" | "local" | "tap";
17
- /** Human-readable identifier, e.g. "github:user/repo" or "npm:@scope/pkg". */
18
- identifier: string;
19
- /** Trust level derived from source type + verification status. */
20
- trust: TrustLevel;
21
- /** Version constraint (semver or tag). */
22
- version?: string;
23
- }
24
- /**
25
- * Result of fetching a skill from a remote source.
26
- */
27
- export interface SkillFetchResult {
28
- /** Local directory where fetched content was placed. */
29
- quarantineDir: string;
30
- /** Manifest metadata if found. */
31
- name?: string;
32
- version?: string;
33
- }
34
- /**
35
- * Dependencies for the skill fetch step.
36
- * Consumers inject actual HTTP/git/npm implementations.
37
- */
38
- export interface SkillFetchDeps {
39
- /**
40
- * Download / clone / extract a skill from its source into a quarantine dir.
41
- * Must NOT place content in the final install location.
42
- */
43
- fetch(source: SkillSourceDescriptor, quarantineDir: string): Promise<SkillFetchResult>;
44
- }
45
- /**
46
- * Dependencies for the skill storage step.
47
- * Manages the local skill directory and registry.
48
- */
49
- export interface SkillStorageDeps {
50
- /** Root directory for installed skills (e.g. ~/.xiaozhiclaw/skills/). */
51
- skillsDir: string;
52
- /** Registry subdirectory (e.g. ~/.xiaozhiclaw/skills/.registry/). */
53
- registryDir: string;
54
- /** Move content from quarantine to final location. */
55
- moveToInstallDir(quarantineDir: string, skillName: string): Promise<string>;
56
- /** Remove an installed skill directory. */
57
- removeSkill(skillName: string): Promise<void>;
58
- /** Read the registry lock file. */
59
- readLockfile(): Promise<SkillLockfile>;
60
- /** Write the registry lock file. */
61
- writeLockfile(lockfile: SkillLockfile): Promise<void>;
62
- }
63
- /**
64
- * Single entry in the skill lockfile.
65
- */
66
- export interface SkillLockEntry {
67
- name: string;
68
- source: SkillSourceDescriptor;
69
- installedAt: string;
70
- scanVerdict: ScanVerdict;
71
- scanSummary?: SkillScanSummary;
72
- /** Integrity hash of the SKILL.md content (sha256). */
73
- integrity?: string;
74
- }
75
- /**
76
- * Complete lockfile for all installed skills.
77
- */
78
- export interface SkillLockfile {
79
- version: 1;
80
- entries: Record<string, SkillLockEntry>;
81
- }
82
- /**
83
- * Result of a skill install operation.
84
- */
85
- export interface SkillInstallResult {
86
- success: boolean;
87
- name: string;
88
- installDir?: string;
89
- verdict: ScanVerdict;
90
- scanSummary?: SkillScanSummary;
91
- error?: string;
92
- }
93
- /**
94
- * Orchestrate the full install pipeline:
95
- * Fetch → Quarantine → Scan → Validate → Move → Lock
96
- *
97
- * This function is pure orchestration — actual I/O is delegated to deps.
98
- */
99
- export declare function installSkill(params: {
100
- source: SkillSourceDescriptor;
101
- fetchDeps: SkillFetchDeps;
102
- storageDeps: SkillStorageDeps;
103
- scanSkillDir: (dir: string) => Promise<SkillScanSummary>;
104
- quarantineDir: string;
105
- }): Promise<SkillInstallResult>;
106
- /**
107
- * Uninstall a skill: remove from disk + update lockfile.
108
- */
109
- export declare function uninstallSkill(params: {
110
- skillName: string;
111
- storageDeps: SkillStorageDeps;
112
- }): Promise<{
113
- success: boolean;
114
- error?: string;
115
- }>;
116
- /**
117
- * Create an empty lockfile.
118
- */
119
- export declare function createEmptyLockfile(): SkillLockfile;