@soleri/core 2.4.0 → 2.5.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 (300) hide show
  1. package/dist/brain/brain.d.ts +7 -0
  2. package/dist/brain/brain.d.ts.map +1 -1
  3. package/dist/brain/brain.js +56 -9
  4. package/dist/brain/brain.js.map +1 -1
  5. package/dist/brain/types.d.ts +2 -2
  6. package/dist/brain/types.d.ts.map +1 -1
  7. package/dist/cognee/client.d.ts +3 -0
  8. package/dist/cognee/client.d.ts.map +1 -1
  9. package/dist/cognee/client.js +17 -0
  10. package/dist/cognee/client.js.map +1 -1
  11. package/dist/cognee/sync-manager.d.ts +94 -0
  12. package/dist/cognee/sync-manager.d.ts.map +1 -0
  13. package/dist/cognee/sync-manager.js +293 -0
  14. package/dist/cognee/sync-manager.js.map +1 -0
  15. package/dist/curator/curator.d.ts +8 -1
  16. package/dist/curator/curator.d.ts.map +1 -1
  17. package/dist/curator/curator.js +64 -1
  18. package/dist/curator/curator.js.map +1 -1
  19. package/dist/errors/classify.d.ts +13 -0
  20. package/dist/errors/classify.d.ts.map +1 -0
  21. package/dist/errors/classify.js +97 -0
  22. package/dist/errors/classify.js.map +1 -0
  23. package/dist/errors/index.d.ts +6 -0
  24. package/dist/errors/index.d.ts.map +1 -0
  25. package/dist/errors/index.js +4 -0
  26. package/dist/errors/index.js.map +1 -0
  27. package/dist/errors/retry.d.ts +40 -0
  28. package/dist/errors/retry.d.ts.map +1 -0
  29. package/dist/errors/retry.js +97 -0
  30. package/dist/errors/retry.js.map +1 -0
  31. package/dist/errors/types.d.ts +48 -0
  32. package/dist/errors/types.d.ts.map +1 -0
  33. package/dist/errors/types.js +59 -0
  34. package/dist/errors/types.js.map +1 -0
  35. package/dist/index.d.ts +25 -5
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +21 -3
  38. package/dist/index.js.map +1 -1
  39. package/dist/intake/content-classifier.d.ts +14 -0
  40. package/dist/intake/content-classifier.d.ts.map +1 -0
  41. package/dist/intake/content-classifier.js +125 -0
  42. package/dist/intake/content-classifier.js.map +1 -0
  43. package/dist/intake/dedup-gate.d.ts +17 -0
  44. package/dist/intake/dedup-gate.d.ts.map +1 -0
  45. package/dist/intake/dedup-gate.js +66 -0
  46. package/dist/intake/dedup-gate.js.map +1 -0
  47. package/dist/intake/intake-pipeline.d.ts +63 -0
  48. package/dist/intake/intake-pipeline.d.ts.map +1 -0
  49. package/dist/intake/intake-pipeline.js +373 -0
  50. package/dist/intake/intake-pipeline.js.map +1 -0
  51. package/dist/intake/types.d.ts +65 -0
  52. package/dist/intake/types.d.ts.map +1 -0
  53. package/dist/intake/types.js +3 -0
  54. package/dist/intake/types.js.map +1 -0
  55. package/dist/intelligence/loader.js +1 -1
  56. package/dist/intelligence/loader.js.map +1 -1
  57. package/dist/intelligence/types.d.ts +3 -1
  58. package/dist/intelligence/types.d.ts.map +1 -1
  59. package/dist/loop/loop-manager.d.ts +58 -7
  60. package/dist/loop/loop-manager.d.ts.map +1 -1
  61. package/dist/loop/loop-manager.js +280 -6
  62. package/dist/loop/loop-manager.js.map +1 -1
  63. package/dist/loop/types.d.ts +69 -1
  64. package/dist/loop/types.d.ts.map +1 -1
  65. package/dist/loop/types.js +4 -1
  66. package/dist/loop/types.js.map +1 -1
  67. package/dist/persistence/index.d.ts +3 -0
  68. package/dist/persistence/index.d.ts.map +1 -0
  69. package/dist/persistence/index.js +2 -0
  70. package/dist/persistence/index.js.map +1 -0
  71. package/dist/persistence/sqlite-provider.d.ts +25 -0
  72. package/dist/persistence/sqlite-provider.d.ts.map +1 -0
  73. package/dist/persistence/sqlite-provider.js +59 -0
  74. package/dist/persistence/sqlite-provider.js.map +1 -0
  75. package/dist/persistence/types.d.ts +36 -0
  76. package/dist/persistence/types.d.ts.map +1 -0
  77. package/dist/persistence/types.js +8 -0
  78. package/dist/persistence/types.js.map +1 -0
  79. package/dist/planning/gap-analysis.d.ts +47 -4
  80. package/dist/planning/gap-analysis.d.ts.map +1 -1
  81. package/dist/planning/gap-analysis.js +190 -13
  82. package/dist/planning/gap-analysis.js.map +1 -1
  83. package/dist/planning/gap-types.d.ts +1 -1
  84. package/dist/planning/gap-types.d.ts.map +1 -1
  85. package/dist/planning/gap-types.js.map +1 -1
  86. package/dist/planning/planner.d.ts +277 -9
  87. package/dist/planning/planner.d.ts.map +1 -1
  88. package/dist/planning/planner.js +611 -46
  89. package/dist/planning/planner.js.map +1 -1
  90. package/dist/playbooks/generic/brainstorming.d.ts +9 -0
  91. package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
  92. package/dist/playbooks/generic/brainstorming.js +105 -0
  93. package/dist/playbooks/generic/brainstorming.js.map +1 -0
  94. package/dist/playbooks/generic/code-review.d.ts +11 -0
  95. package/dist/playbooks/generic/code-review.d.ts.map +1 -0
  96. package/dist/playbooks/generic/code-review.js +176 -0
  97. package/dist/playbooks/generic/code-review.js.map +1 -0
  98. package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
  99. package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
  100. package/dist/playbooks/generic/subagent-execution.js +68 -0
  101. package/dist/playbooks/generic/subagent-execution.js.map +1 -0
  102. package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
  103. package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
  104. package/dist/playbooks/generic/systematic-debugging.js +87 -0
  105. package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
  106. package/dist/playbooks/generic/tdd.d.ts +9 -0
  107. package/dist/playbooks/generic/tdd.d.ts.map +1 -0
  108. package/dist/playbooks/generic/tdd.js +70 -0
  109. package/dist/playbooks/generic/tdd.js.map +1 -0
  110. package/dist/playbooks/generic/verification.d.ts +9 -0
  111. package/dist/playbooks/generic/verification.d.ts.map +1 -0
  112. package/dist/playbooks/generic/verification.js +74 -0
  113. package/dist/playbooks/generic/verification.js.map +1 -0
  114. package/dist/playbooks/index.d.ts +4 -0
  115. package/dist/playbooks/index.d.ts.map +1 -0
  116. package/dist/playbooks/index.js +5 -0
  117. package/dist/playbooks/index.js.map +1 -0
  118. package/dist/playbooks/playbook-registry.d.ts +42 -0
  119. package/dist/playbooks/playbook-registry.d.ts.map +1 -0
  120. package/dist/playbooks/playbook-registry.js +227 -0
  121. package/dist/playbooks/playbook-registry.js.map +1 -0
  122. package/dist/playbooks/playbook-seeder.d.ts +47 -0
  123. package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
  124. package/dist/playbooks/playbook-seeder.js +104 -0
  125. package/dist/playbooks/playbook-seeder.js.map +1 -0
  126. package/dist/playbooks/playbook-types.d.ts +132 -0
  127. package/dist/playbooks/playbook-types.d.ts.map +1 -0
  128. package/dist/playbooks/playbook-types.js +12 -0
  129. package/dist/playbooks/playbook-types.js.map +1 -0
  130. package/dist/project/project-registry.d.ts.map +1 -1
  131. package/dist/project/project-registry.js +9 -11
  132. package/dist/project/project-registry.js.map +1 -1
  133. package/dist/prompts/index.d.ts +4 -0
  134. package/dist/prompts/index.d.ts.map +1 -0
  135. package/dist/prompts/index.js +3 -0
  136. package/dist/prompts/index.js.map +1 -0
  137. package/dist/prompts/parser.d.ts +17 -0
  138. package/dist/prompts/parser.d.ts.map +1 -0
  139. package/dist/prompts/parser.js +47 -0
  140. package/dist/prompts/parser.js.map +1 -0
  141. package/dist/prompts/template-manager.d.ts +25 -0
  142. package/dist/prompts/template-manager.d.ts.map +1 -0
  143. package/dist/prompts/template-manager.js +71 -0
  144. package/dist/prompts/template-manager.js.map +1 -0
  145. package/dist/prompts/types.d.ts +26 -0
  146. package/dist/prompts/types.d.ts.map +1 -0
  147. package/dist/prompts/types.js +5 -0
  148. package/dist/prompts/types.js.map +1 -0
  149. package/dist/runtime/admin-extra-ops.d.ts +5 -3
  150. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  151. package/dist/runtime/admin-extra-ops.js +322 -11
  152. package/dist/runtime/admin-extra-ops.js.map +1 -1
  153. package/dist/runtime/admin-ops.d.ts.map +1 -1
  154. package/dist/runtime/admin-ops.js +10 -3
  155. package/dist/runtime/admin-ops.js.map +1 -1
  156. package/dist/runtime/capture-ops.d.ts.map +1 -1
  157. package/dist/runtime/capture-ops.js +20 -2
  158. package/dist/runtime/capture-ops.js.map +1 -1
  159. package/dist/runtime/cognee-sync-ops.d.ts +12 -0
  160. package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
  161. package/dist/runtime/cognee-sync-ops.js +55 -0
  162. package/dist/runtime/cognee-sync-ops.js.map +1 -0
  163. package/dist/runtime/core-ops.d.ts +8 -6
  164. package/dist/runtime/core-ops.d.ts.map +1 -1
  165. package/dist/runtime/core-ops.js +226 -9
  166. package/dist/runtime/core-ops.js.map +1 -1
  167. package/dist/runtime/curator-extra-ops.d.ts +2 -2
  168. package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
  169. package/dist/runtime/curator-extra-ops.js +15 -3
  170. package/dist/runtime/curator-extra-ops.js.map +1 -1
  171. package/dist/runtime/domain-ops.js +2 -2
  172. package/dist/runtime/domain-ops.js.map +1 -1
  173. package/dist/runtime/grading-ops.d.ts.map +1 -1
  174. package/dist/runtime/grading-ops.js.map +1 -1
  175. package/dist/runtime/intake-ops.d.ts +14 -0
  176. package/dist/runtime/intake-ops.d.ts.map +1 -0
  177. package/dist/runtime/intake-ops.js +110 -0
  178. package/dist/runtime/intake-ops.js.map +1 -0
  179. package/dist/runtime/loop-ops.d.ts +5 -4
  180. package/dist/runtime/loop-ops.d.ts.map +1 -1
  181. package/dist/runtime/loop-ops.js +84 -12
  182. package/dist/runtime/loop-ops.js.map +1 -1
  183. package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -1
  184. package/dist/runtime/memory-cross-project-ops.js.map +1 -1
  185. package/dist/runtime/memory-extra-ops.js +5 -5
  186. package/dist/runtime/memory-extra-ops.js.map +1 -1
  187. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  188. package/dist/runtime/orchestrate-ops.js +8 -2
  189. package/dist/runtime/orchestrate-ops.js.map +1 -1
  190. package/dist/runtime/planning-extra-ops.d.ts +13 -5
  191. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  192. package/dist/runtime/planning-extra-ops.js +381 -18
  193. package/dist/runtime/planning-extra-ops.js.map +1 -1
  194. package/dist/runtime/playbook-ops.d.ts +14 -0
  195. package/dist/runtime/playbook-ops.d.ts.map +1 -0
  196. package/dist/runtime/playbook-ops.js +141 -0
  197. package/dist/runtime/playbook-ops.js.map +1 -0
  198. package/dist/runtime/project-ops.d.ts.map +1 -1
  199. package/dist/runtime/project-ops.js +7 -2
  200. package/dist/runtime/project-ops.js.map +1 -1
  201. package/dist/runtime/runtime.d.ts.map +1 -1
  202. package/dist/runtime/runtime.js +27 -8
  203. package/dist/runtime/runtime.js.map +1 -1
  204. package/dist/runtime/types.d.ts +8 -0
  205. package/dist/runtime/types.d.ts.map +1 -1
  206. package/dist/runtime/vault-extra-ops.d.ts +3 -2
  207. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  208. package/dist/runtime/vault-extra-ops.js +345 -4
  209. package/dist/runtime/vault-extra-ops.js.map +1 -1
  210. package/dist/vault/playbook.d.ts +34 -0
  211. package/dist/vault/playbook.d.ts.map +1 -0
  212. package/dist/vault/playbook.js +60 -0
  213. package/dist/vault/playbook.js.map +1 -0
  214. package/dist/vault/vault.d.ts +31 -32
  215. package/dist/vault/vault.d.ts.map +1 -1
  216. package/dist/vault/vault.js +201 -181
  217. package/dist/vault/vault.js.map +1 -1
  218. package/package.json +7 -3
  219. package/src/__tests__/admin-extra-ops.test.ts +62 -15
  220. package/src/__tests__/admin-ops.test.ts +2 -2
  221. package/src/__tests__/brain.test.ts +3 -3
  222. package/src/__tests__/cognee-integration.test.ts +80 -0
  223. package/src/__tests__/cognee-sync-manager.test.ts +103 -0
  224. package/src/__tests__/core-ops.test.ts +30 -4
  225. package/src/__tests__/curator-extra-ops.test.ts +24 -2
  226. package/src/__tests__/errors.test.ts +388 -0
  227. package/src/__tests__/grading-ops.test.ts +28 -7
  228. package/src/__tests__/intake-pipeline.test.ts +162 -0
  229. package/src/__tests__/loop-ops.test.ts +74 -3
  230. package/src/__tests__/memory-cross-project-ops.test.ts +3 -1
  231. package/src/__tests__/orchestrate-ops.test.ts +8 -3
  232. package/src/__tests__/persistence.test.ts +225 -0
  233. package/src/__tests__/planner.test.ts +99 -21
  234. package/src/__tests__/planning-extra-ops.test.ts +168 -10
  235. package/src/__tests__/playbook-registry.test.ts +326 -0
  236. package/src/__tests__/playbook-seeder.test.ts +163 -0
  237. package/src/__tests__/playbook.test.ts +389 -0
  238. package/src/__tests__/project-ops.test.ts +18 -4
  239. package/src/__tests__/template-manager.test.ts +222 -0
  240. package/src/__tests__/vault-extra-ops.test.ts +82 -7
  241. package/src/brain/brain.ts +71 -9
  242. package/src/brain/types.ts +2 -2
  243. package/src/cognee/client.ts +18 -0
  244. package/src/cognee/sync-manager.ts +389 -0
  245. package/src/curator/curator.ts +88 -7
  246. package/src/errors/classify.ts +102 -0
  247. package/src/errors/index.ts +5 -0
  248. package/src/errors/retry.ts +132 -0
  249. package/src/errors/types.ts +81 -0
  250. package/src/index.ts +114 -3
  251. package/src/intake/content-classifier.ts +146 -0
  252. package/src/intake/dedup-gate.ts +92 -0
  253. package/src/intake/intake-pipeline.ts +503 -0
  254. package/src/intake/types.ts +69 -0
  255. package/src/intelligence/loader.ts +1 -1
  256. package/src/intelligence/types.ts +3 -1
  257. package/src/loop/loop-manager.ts +325 -7
  258. package/src/loop/types.ts +72 -1
  259. package/src/persistence/index.ts +7 -0
  260. package/src/persistence/sqlite-provider.ts +62 -0
  261. package/src/persistence/types.ts +44 -0
  262. package/src/planning/gap-analysis.ts +286 -17
  263. package/src/planning/gap-types.ts +4 -1
  264. package/src/planning/planner.ts +828 -55
  265. package/src/playbooks/generic/brainstorming.ts +110 -0
  266. package/src/playbooks/generic/code-review.ts +181 -0
  267. package/src/playbooks/generic/subagent-execution.ts +74 -0
  268. package/src/playbooks/generic/systematic-debugging.ts +92 -0
  269. package/src/playbooks/generic/tdd.ts +75 -0
  270. package/src/playbooks/generic/verification.ts +79 -0
  271. package/src/playbooks/index.ts +27 -0
  272. package/src/playbooks/playbook-registry.ts +284 -0
  273. package/src/playbooks/playbook-seeder.ts +119 -0
  274. package/src/playbooks/playbook-types.ts +162 -0
  275. package/src/project/project-registry.ts +29 -17
  276. package/src/prompts/index.ts +3 -0
  277. package/src/prompts/parser.ts +59 -0
  278. package/src/prompts/template-manager.ts +77 -0
  279. package/src/prompts/types.ts +28 -0
  280. package/src/runtime/admin-extra-ops.ts +358 -13
  281. package/src/runtime/admin-ops.ts +17 -6
  282. package/src/runtime/capture-ops.ts +25 -6
  283. package/src/runtime/cognee-sync-ops.ts +63 -0
  284. package/src/runtime/core-ops.ts +258 -8
  285. package/src/runtime/curator-extra-ops.ts +17 -3
  286. package/src/runtime/domain-ops.ts +2 -2
  287. package/src/runtime/grading-ops.ts +11 -2
  288. package/src/runtime/intake-ops.ts +126 -0
  289. package/src/runtime/loop-ops.ts +96 -13
  290. package/src/runtime/memory-cross-project-ops.ts +1 -2
  291. package/src/runtime/memory-extra-ops.ts +5 -5
  292. package/src/runtime/orchestrate-ops.ts +8 -2
  293. package/src/runtime/planning-extra-ops.ts +414 -23
  294. package/src/runtime/playbook-ops.ts +169 -0
  295. package/src/runtime/project-ops.ts +9 -3
  296. package/src/runtime/runtime.ts +35 -9
  297. package/src/runtime/types.ts +8 -0
  298. package/src/runtime/vault-extra-ops.ts +385 -4
  299. package/src/vault/playbook.ts +87 -0
  300. package/src/vault/vault.ts +301 -235
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Template parser — extract variables and resolve includes.
3
+ */
4
+
5
+ import type { TemplateVariable } from './types.js';
6
+
7
+ const VAR_PATTERN = /\{\{(\w+)(?::([^}]*))?\}\}/g;
8
+ const INCLUDE_PATTERN = /@include\(([^)]+)\)/g;
9
+ const MAX_INCLUDE_DEPTH = 10;
10
+
11
+ /**
12
+ * Extract unique variables from template content.
13
+ * Supports {{name}} (required) and {{name:default}} (optional with default).
14
+ */
15
+ export function parseVariables(content: string): TemplateVariable[] {
16
+ const seen = new Map<string, TemplateVariable>();
17
+
18
+ for (const match of content.matchAll(VAR_PATTERN)) {
19
+ const name = match[1];
20
+ const defaultValue = match[2];
21
+ if (!seen.has(name)) {
22
+ seen.set(name, {
23
+ name,
24
+ required: defaultValue === undefined,
25
+ defaultValue,
26
+ });
27
+ }
28
+ }
29
+
30
+ return Array.from(seen.values());
31
+ }
32
+
33
+ /**
34
+ * Resolve @include(partial-name) directives.
35
+ *
36
+ * The loader function receives the partial name and returns its content.
37
+ * Cycle detection prevents infinite recursion.
38
+ */
39
+ export function resolveIncludes(
40
+ content: string,
41
+ loader: (name: string) => string,
42
+ _stack: Set<string> = new Set(),
43
+ _depth: number = 0,
44
+ ): string {
45
+ if (_depth > MAX_INCLUDE_DEPTH) {
46
+ throw new Error(`Include depth exceeded ${MAX_INCLUDE_DEPTH}. Possible circular include.`);
47
+ }
48
+
49
+ return content.replace(INCLUDE_PATTERN, (_match, partialName: string) => {
50
+ const trimmed = partialName.trim();
51
+ if (_stack.has(trimmed)) {
52
+ throw new Error(`Circular include detected: ${trimmed} (stack: ${[..._stack].join(' → ')})`);
53
+ }
54
+ const newStack = new Set(_stack);
55
+ newStack.add(trimmed);
56
+ const partialContent = loader(trimmed);
57
+ return resolveIncludes(partialContent, loader, newStack, _depth + 1);
58
+ });
59
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Template manager — load, render, and list .prompt templates.
3
+ */
4
+
5
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
6
+ import { join, basename } from 'node:path';
7
+ import type { PromptTemplate, RenderOptions } from './types.js';
8
+ import { parseVariables, resolveIncludes } from './parser.js';
9
+
10
+ const VAR_REGEX = /\{\{(\w+)(?::([^}]*))?\}\}/g;
11
+
12
+ export class TemplateManager {
13
+ private templates = new Map<string, PromptTemplate>();
14
+ private templatesDir: string;
15
+
16
+ constructor(templatesDir: string) {
17
+ this.templatesDir = templatesDir;
18
+ }
19
+
20
+ /** Load all .prompt files from templatesDir. */
21
+ load(): void {
22
+ if (!existsSync(this.templatesDir)) return;
23
+
24
+ const files = readdirSync(this.templatesDir).filter((f) => f.endsWith('.prompt'));
25
+ for (const file of files) {
26
+ const fullPath = join(this.templatesDir, file);
27
+ const content = readFileSync(fullPath, 'utf-8');
28
+ const name = basename(file, '.prompt');
29
+ this.templates.set(name, {
30
+ name,
31
+ content,
32
+ variables: parseVariables(content),
33
+ path: fullPath,
34
+ });
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Render a template by name with variable substitution.
40
+ *
41
+ * @param name - Template name (without .prompt extension)
42
+ * @param vars - Variable values to substitute
43
+ * @param options - Render options (strict mode)
44
+ * @throws If template not found, or strict mode and required variable missing
45
+ */
46
+ render(name: string, vars: Record<string, string> = {}, options?: RenderOptions): string {
47
+ const template = this.templates.get(name);
48
+ if (!template) throw new Error(`Template not found: ${name}`);
49
+
50
+ const strict = options?.strict ?? true;
51
+
52
+ // Resolve @include() directives
53
+ const resolved = resolveIncludes(template.content, (partialName) => {
54
+ const partial = this.templates.get(partialName);
55
+ if (!partial) throw new Error(`Include not found: ${partialName} (in template: ${name})`);
56
+ return partial.content;
57
+ });
58
+
59
+ // Replace {{var}} and {{var:default}}
60
+ return resolved.replace(VAR_REGEX, (_match, varName: string, defaultValue?: string) => {
61
+ if (vars[varName] !== undefined) return vars[varName];
62
+ if (defaultValue !== undefined) return defaultValue;
63
+ if (strict) throw new Error(`Missing required variable: ${varName} (in template: ${name})`);
64
+ return _match; // Leave placeholder as-is in non-strict mode
65
+ });
66
+ }
67
+
68
+ /** List all loaded template names. */
69
+ listTemplates(): string[] {
70
+ return Array.from(this.templates.keys());
71
+ }
72
+
73
+ /** Get a template by name (raw, unrendered). */
74
+ getTemplate(name: string): PromptTemplate | null {
75
+ return this.templates.get(name) ?? null;
76
+ }
77
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Prompt template types.
3
+ */
4
+
5
+ export interface TemplateVariable {
6
+ /** Variable name, e.g. 'agentName' */
7
+ name: string;
8
+ /** Whether the variable must be provided at render time. */
9
+ required: boolean;
10
+ /** Default value used when variable is not provided (makes required=false). */
11
+ defaultValue?: string;
12
+ }
13
+
14
+ export interface PromptTemplate {
15
+ /** Template name (filename without .prompt extension). */
16
+ name: string;
17
+ /** Raw template content before variable substitution. */
18
+ content: string;
19
+ /** Variables extracted from the template. */
20
+ variables: TemplateVariable[];
21
+ /** Absolute file path. */
22
+ path: string;
23
+ }
24
+
25
+ export interface RenderOptions {
26
+ /** If true (default), throw on missing required variables. */
27
+ strict?: boolean;
28
+ }
@@ -1,18 +1,39 @@
1
1
  /**
2
- * Extended admin operations — 10 ops for production readiness.
2
+ * Extended admin operations — 23 ops for production readiness.
3
3
  *
4
4
  * Groups: telemetry (3), permissions (1), vault analytics (1),
5
- * search insights (1), module status (1), env (1), gc (1), export config (1).
5
+ * search insights (1), module status (1), env (1), gc (1), export config (1),
6
+ * key pool (4), profiles (5), plugins (2), instruction validation (1),
7
+ * hot reload (1).
6
8
  */
7
9
 
8
10
  import { z } from 'zod';
11
+ import { readFileSync, existsSync } from 'node:fs';
9
12
  import type { OpDefinition } from '../facades/types.js';
10
13
  import type { AgentRuntime } from './types.js';
11
14
 
12
15
  type PermissionLevel = 'strict' | 'moderate' | 'permissive';
13
16
 
17
+ interface ApiToken {
18
+ name: string;
19
+ role: string;
20
+ createdAt: number;
21
+ }
22
+ interface AccountProfile {
23
+ name: string;
24
+ provider: string;
25
+ active: boolean;
26
+ addedAt: number;
27
+ }
28
+ interface PluginInfo {
29
+ id: string;
30
+ name: string;
31
+ status: 'active' | 'inactive' | 'error';
32
+ opsCount: number;
33
+ }
34
+
14
35
  /**
15
- * Create 10 extended admin operations for production observability.
36
+ * Create 23 extended admin operations for production observability.
16
37
  */
17
38
  export function createAdminExtraOps(runtime: AgentRuntime): OpDefinition[] {
18
39
  const { vault, brain, cognee, telemetry } = runtime;
@@ -102,9 +123,9 @@ export function createAdminExtraOps(runtime: AgentRuntime): OpDefinition[] {
102
123
  '90d+': 0,
103
124
  };
104
125
 
105
- const rows = db
106
- .prepare('SELECT created_at FROM entries')
107
- .all() as Array<{ created_at: number }>;
126
+ const rows = db.prepare('SELECT created_at FROM entries').all() as Array<{
127
+ created_at: number;
128
+ }>;
108
129
 
109
130
  for (const row of rows) {
110
131
  const ageSeconds = now - row.created_at;
@@ -115,9 +136,7 @@ export function createAdminExtraOps(runtime: AgentRuntime): OpDefinition[] {
115
136
  }
116
137
 
117
138
  // Average tags per entry
118
- const tagRows = db
119
- .prepare('SELECT tags FROM entries')
120
- .all() as Array<{ tags: string }>;
139
+ const tagRows = db.prepare('SELECT tags FROM entries').all() as Array<{ tags: string }>;
121
140
 
122
141
  let totalTags = 0;
123
142
  let noTags = 0;
@@ -135,7 +154,9 @@ export function createAdminExtraOps(runtime: AgentRuntime): OpDefinition[] {
135
154
 
136
155
  // Entries without descriptions
137
156
  const noDescResult = db
138
- .prepare("SELECT COUNT(*) as count FROM entries WHERE description IS NULL OR description = ''")
157
+ .prepare(
158
+ "SELECT COUNT(*) as count FROM entries WHERE description IS NULL OR description = ''",
159
+ )
139
160
  .get() as { count: number } | undefined;
140
161
  noDescription = noDescResult?.count ?? 0;
141
162
 
@@ -146,12 +167,16 @@ export function createAdminExtraOps(runtime: AgentRuntime): OpDefinition[] {
146
167
  byDomain: Object.fromEntries(byDomain.map((r) => [r.domain, r.count])),
147
168
  byType: Object.fromEntries(byType.map((r) => [r.type, r.count])),
148
169
  byAge: ageBuckets,
149
- avgTagsPerEntry: totalEntries > 0 ? Math.round((totalTags / totalEntries) * 10) / 10 : 0,
170
+ avgTagsPerEntry:
171
+ totalEntries > 0 ? Math.round((totalTags / totalEntries) * 10) / 10 : 0,
150
172
  entriesWithoutTags: noTags,
151
173
  entriesWithoutDescription: noDescription,
152
174
  };
153
175
  } catch (err) {
154
- return { error: 'Failed to compute vault analytics', detail: err instanceof Error ? err.message : String(err) };
176
+ return {
177
+ error: 'Failed to compute vault analytics',
178
+ detail: err instanceof Error ? err.message : String(err),
179
+ };
155
180
  }
156
181
  },
157
182
  },
@@ -185,7 +210,7 @@ export function createAdminExtraOps(runtime: AgentRuntime): OpDefinition[] {
185
210
  topMissedQueries: missedQueries,
186
211
  byAction: feedbackStats.byAction,
187
212
  };
188
- } catch (err) {
213
+ } catch {
189
214
  return {
190
215
  totalFeedback: 0,
191
216
  missRate: 0,
@@ -303,5 +328,325 @@ export function createAdminExtraOps(runtime: AgentRuntime): OpDefinition[] {
303
328
  };
304
329
  },
305
330
  },
331
+
332
+ // ─── Key Pool (#157) ──────────────────────────────────────────
333
+ {
334
+ name: 'admin_key_pool_status',
335
+ description:
336
+ 'LLM key pool status — pool size, active key index, per-key circuit breaker state (open/closed/half-open).',
337
+ auth: 'read',
338
+ handler: async () => {
339
+ const available = runtime.llmClient.isAvailable();
340
+ return {
341
+ openai: {
342
+ available: available.openai,
343
+ },
344
+ anthropic: {
345
+ available: available.anthropic,
346
+ },
347
+ };
348
+ },
349
+ },
350
+ {
351
+ name: 'admin_create_token',
352
+ description: 'Create a named API token with role-based access.',
353
+ auth: 'admin',
354
+ schema: z.object({
355
+ name: z.string().describe('Token name (unique identifier)'),
356
+ role: z.enum(['read', 'write', 'admin']).describe('Access role'),
357
+ }),
358
+ handler: async (params) => {
359
+ const token: ApiToken = {
360
+ name: params.name as string,
361
+ role: params.role as string,
362
+ createdAt: Date.now(),
363
+ };
364
+ // Store in vault metadata
365
+ vault.add({
366
+ id: `api-token-${token.name}`,
367
+ type: 'rule',
368
+ domain: 'admin',
369
+ title: `API Token: ${token.name}`,
370
+ severity: 'suggestion',
371
+ description: `API token with ${token.role} access`,
372
+ tags: ['api-token', token.role],
373
+ });
374
+ return { created: true, name: token.name, role: token.role };
375
+ },
376
+ },
377
+ {
378
+ name: 'admin_revoke_token',
379
+ description: 'Revoke (delete) a named API token.',
380
+ auth: 'admin',
381
+ schema: z.object({
382
+ name: z.string().describe('Token name to revoke'),
383
+ }),
384
+ handler: async (params) => {
385
+ const removed = vault.remove(`api-token-${params.name}`);
386
+ return { revoked: removed, name: params.name };
387
+ },
388
+ },
389
+ {
390
+ name: 'admin_list_tokens',
391
+ description: 'List all API tokens (names and roles only, no secrets).',
392
+ auth: 'read',
393
+ handler: async () => {
394
+ const entries = vault.list({ domain: 'admin' });
395
+ const tokens = entries
396
+ .filter((e) => e.id.startsWith('api-token-'))
397
+ .map((e) => ({
398
+ name: e.id.replace('api-token-', ''),
399
+ role: e.tags?.find((t) => ['read', 'write', 'admin'].includes(t)) ?? 'unknown',
400
+ }));
401
+ return { tokens, count: tokens.length };
402
+ },
403
+ },
404
+
405
+ // ─── Account Profiles (#158) ─────────────────────────────────
406
+ {
407
+ name: 'admin_add_account',
408
+ description: 'Add an API account profile. Keys are stored in vault, never exposed.',
409
+ auth: 'admin',
410
+ schema: z.object({
411
+ name: z.string().describe('Profile name'),
412
+ provider: z.enum(['openai', 'anthropic']).describe('API provider'),
413
+ }),
414
+ handler: async (params) => {
415
+ const profile: AccountProfile = {
416
+ name: params.name as string,
417
+ provider: params.provider as string,
418
+ active: false,
419
+ addedAt: Date.now(),
420
+ };
421
+ vault.add({
422
+ id: `account-profile-${profile.name}`,
423
+ type: 'rule',
424
+ domain: 'admin',
425
+ title: `Account: ${profile.name} (${profile.provider})`,
426
+ severity: 'suggestion',
427
+ description: `API account profile for ${profile.provider}`,
428
+ tags: ['account-profile', profile.provider],
429
+ });
430
+ return { added: true, name: profile.name, provider: profile.provider };
431
+ },
432
+ },
433
+ {
434
+ name: 'admin_remove_account',
435
+ description: 'Remove an API account profile.',
436
+ auth: 'admin',
437
+ schema: z.object({
438
+ name: z.string().describe('Profile name to remove'),
439
+ }),
440
+ handler: async (params) => {
441
+ const removed = vault.remove(`account-profile-${params.name}`);
442
+ return { removed, name: params.name };
443
+ },
444
+ },
445
+ {
446
+ name: 'admin_rotate_account',
447
+ description: 'Rotate to a different API account profile.',
448
+ auth: 'admin',
449
+ schema: z.object({
450
+ name: z.string().describe('Profile name to activate'),
451
+ }),
452
+ handler: async (params) => {
453
+ const entry = vault.get(`account-profile-${params.name}`);
454
+ if (!entry) return { error: `Account profile not found: ${params.name}` };
455
+ return {
456
+ rotated: true,
457
+ name: params.name,
458
+ note: 'Profile activated (key rotation requires restart)',
459
+ };
460
+ },
461
+ },
462
+ {
463
+ name: 'admin_list_accounts',
464
+ description: 'List all account profiles (names and providers only, no keys).',
465
+ auth: 'read',
466
+ handler: async () => {
467
+ const entries = vault.list({ domain: 'admin' });
468
+ const accounts = entries
469
+ .filter((e) => e.id.startsWith('account-profile-'))
470
+ .map((e) => ({
471
+ name: e.id.replace('account-profile-', ''),
472
+ provider: e.tags?.find((t) => ['openai', 'anthropic'].includes(t)) ?? 'unknown',
473
+ }));
474
+ return { accounts, count: accounts.length };
475
+ },
476
+ },
477
+ {
478
+ name: 'admin_account_status',
479
+ description: 'Get current active account profile status.',
480
+ auth: 'read',
481
+ handler: async () => {
482
+ const available = runtime.llmClient.isAvailable();
483
+ return {
484
+ openai: { available: available.openai },
485
+ anthropic: { available: available.anthropic },
486
+ };
487
+ },
488
+ },
489
+
490
+ // ─── Plugins (#159) ──────────────────────────────────────────
491
+ {
492
+ name: 'admin_list_plugins',
493
+ description: 'List all registered domain plugins and their status.',
494
+ auth: 'read',
495
+ handler: async () => {
496
+ // Plugins are domain facades — discover via vault domains
497
+ const domains = vault.getDomains();
498
+ const plugins: PluginInfo[] = domains
499
+ .filter((d) => d.domain !== 'admin' && d.domain !== 'planning')
500
+ .map((d) => ({
501
+ id: d.domain,
502
+ name: d.domain,
503
+ status: 'active' as const,
504
+ opsCount: 5, // standard domain ops: get_patterns, search, get_entry, capture, remove
505
+ }));
506
+ return { plugins, count: plugins.length };
507
+ },
508
+ },
509
+ {
510
+ name: 'admin_plugin_status',
511
+ description: 'Get detailed status of a specific plugin (domain facade).',
512
+ auth: 'read',
513
+ schema: z.object({
514
+ pluginId: z.string().describe('Plugin/domain ID'),
515
+ }),
516
+ handler: async (params) => {
517
+ const domainId = params.pluginId as string;
518
+ const domainEntries = vault.list({ domain: domainId });
519
+ if (domainEntries.length === 0) {
520
+ return { error: `Plugin not found or empty: ${domainId}` };
521
+ }
522
+ return {
523
+ id: domainId,
524
+ status: 'active',
525
+ entryCount: domainEntries.length,
526
+ opsCount: 5,
527
+ ops: ['get_patterns', 'search', 'get_entry', 'capture', 'remove'],
528
+ };
529
+ },
530
+ },
531
+
532
+ // ─── Hot Reload (#63) ──────────────────────────────────────
533
+ {
534
+ name: 'admin_hot_reload',
535
+ description:
536
+ 'Hot-reload runtime caches — rebuilds brain vocabulary, vault FTS index, and prompt templates. Use after bulk vault changes.',
537
+ auth: 'write',
538
+ handler: async () => {
539
+ const reloaded: string[] = [];
540
+ let brainTerms = 0;
541
+ let templateCount = 0;
542
+
543
+ try {
544
+ brain.rebuildVocabulary();
545
+ brainTerms = brain.getStats().vocabularySize;
546
+ reloaded.push('brain');
547
+ } catch {
548
+ // Graceful degradation
549
+ }
550
+
551
+ try {
552
+ vault.rebuildFtsIndex();
553
+ reloaded.push('vault_fts');
554
+ } catch {
555
+ // Graceful degradation
556
+ }
557
+
558
+ try {
559
+ runtime.templateManager.load();
560
+ templateCount = runtime.templateManager.listTemplates().length;
561
+ reloaded.push('templates');
562
+ } catch {
563
+ // Graceful degradation
564
+ }
565
+
566
+ return { reloaded, brainTerms, templateCount };
567
+ },
568
+ },
569
+
570
+ // ─── Instruction Validation (#160) ───────────────────────────
571
+ {
572
+ name: 'admin_validate_instructions',
573
+ description:
574
+ 'Validate instruction files (CLAUDE.md, SKILL.md) for governance and quality — checks structure, required fields, formatting.',
575
+ auth: 'read',
576
+ schema: z.object({
577
+ filePath: z.string().describe('Path to the instruction file to validate'),
578
+ }),
579
+ handler: async (params) => {
580
+ try {
581
+ const filePath = params.filePath as string;
582
+ if (!existsSync(filePath)) {
583
+ return { valid: false, errors: [{ line: 0, issue: 'File not found' }] };
584
+ }
585
+
586
+ const content = readFileSync(filePath, 'utf-8');
587
+ const errors: Array<{ line: number; issue: string }> = [];
588
+ const warnings: Array<{ line: number; issue: string }> = [];
589
+
590
+ // Check for YAML frontmatter (SKILL.md files)
591
+ if (filePath.endsWith('SKILL.md') || filePath.includes('/skills/')) {
592
+ if (!content.startsWith('---')) {
593
+ errors.push({ line: 1, issue: 'SKILL.md must start with YAML frontmatter (---)' });
594
+ } else {
595
+ const fmEnd = content.indexOf('---', 3);
596
+ if (fmEnd === -1) {
597
+ errors.push({
598
+ line: 1,
599
+ issue: 'YAML frontmatter not closed (missing closing ---)',
600
+ });
601
+ } else {
602
+ const fm = new Set(content.slice(3, fmEnd));
603
+ if (!fm.has('name:'))
604
+ errors.push({ line: 1, issue: 'Missing required field: name' });
605
+ if (!fm.has('description:'))
606
+ errors.push({ line: 1, issue: 'Missing required field: description' });
607
+ }
608
+ }
609
+ }
610
+
611
+ // General checks for any instruction file
612
+ const lines = content.split('\n');
613
+ for (let i = 0; i < lines.length; i++) {
614
+ const line = lines[i];
615
+ // Check for extremely long lines
616
+ if (line.length > 500) {
617
+ warnings.push({ line: i + 1, issue: `Line too long (${line.length} chars)` });
618
+ }
619
+ }
620
+
621
+ // Check for conflicting instructions
622
+ if (content.includes('ALWAYS') && content.includes('NEVER')) {
623
+ const alwaysLines = lines.filter((l) => l.includes('ALWAYS'));
624
+ const neverLines = lines.filter((l) => l.includes('NEVER'));
625
+ if (alwaysLines.length > 5 && neverLines.length > 5) {
626
+ warnings.push({
627
+ line: 0,
628
+ issue: 'Many ALWAYS/NEVER directives — check for contradictions',
629
+ });
630
+ }
631
+ }
632
+
633
+ // Check for empty content
634
+ if (content.trim().length < 10) {
635
+ errors.push({ line: 1, issue: 'File is essentially empty' });
636
+ }
637
+
638
+ return {
639
+ valid: errors.length === 0,
640
+ filePath,
641
+ errors,
642
+ warnings,
643
+ lineCount: lines.length,
644
+ charCount: content.length,
645
+ };
646
+ } catch (err) {
647
+ return { error: (err as Error).message };
648
+ }
649
+ },
650
+ },
306
651
  ];
307
652
  }
@@ -41,7 +41,7 @@ function getCoreVersion(): string {
41
41
  * Groups: health (1), introspection (4), diagnostics (2), mutation (1).
42
42
  */
43
43
  export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
44
- const { vault, brain, brainIntelligence, cognee, llmClient, keyPool, curator } = runtime;
44
+ const { vault, brain, brainIntelligence, cognee, llmClient, curator } = runtime;
45
45
 
46
46
  return [
47
47
  // ─── Health ──────────────────────────────────────────────────────
@@ -61,7 +61,10 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
61
61
  vault: { entries: vaultStats.totalEntries, domains: Object.keys(vaultStats.byDomain) },
62
62
  cognee: { available: cogneeStatus?.available ?? false },
63
63
  llm: llmAvailable,
64
- brain: { vocabularySize: brainStats.vocabularySize, feedbackCount: brainStats.feedbackCount },
64
+ brain: {
65
+ vocabularySize: brainStats.vocabularySize,
66
+ feedbackCount: brainStats.feedbackCount,
67
+ },
65
68
  curator: { initialized: curatorStatus.initialized },
66
69
  };
67
70
  },
@@ -75,7 +78,9 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
75
78
  handler: async (params) => {
76
79
  // The caller can pass in the full ops list via `_allOps` (injected by
77
80
  // the facade builder). If not provided, we return only admin ops.
78
- const allOps = params._allOps as Array<{ name: string; description: string; auth: string }> | undefined;
81
+ const allOps = params._allOps as
82
+ | Array<{ name: string; description: string; auth: string }>
83
+ | undefined;
79
84
  if (allOps) {
80
85
  return {
81
86
  count: allOps.length,
@@ -119,7 +124,8 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
119
124
  },
120
125
  {
121
126
  name: 'admin_vault_size',
122
- description: 'Get vault database file size on disk (bytes). Returns null for in-memory vaults.',
127
+ description:
128
+ 'Get vault database file size on disk (bytes). Returns null for in-memory vaults.',
123
129
  auth: 'read',
124
130
  handler: async () => {
125
131
  const dbPath = runtime.config.vaultPath;
@@ -168,7 +174,8 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
168
174
  // ─── Mutation ────────────────────────────────────────────────────
169
175
  {
170
176
  name: 'admin_reset_cache',
171
- description: 'Clear all caches — brain vocabulary and cognee health cache. Forces fresh data on next access.',
177
+ description:
178
+ 'Clear all caches — brain vocabulary and cognee health cache. Forces fresh data on next access.',
172
179
  auth: 'write',
173
180
  handler: async () => {
174
181
  // Rebuild brain vocabulary (clears old TF-IDF state, rebuilds from vault)
@@ -247,7 +254,11 @@ export function createAdminOps(runtime: AgentRuntime): OpDefinition[] {
247
254
  try {
248
255
  const cogneeStatus = cognee.getStatus();
249
256
  if (cogneeStatus?.available) {
250
- checks.push({ name: 'cognee', status: 'ok', detail: `Connected to ${cogneeStatus.url}` });
257
+ checks.push({
258
+ name: 'cognee',
259
+ status: 'ok',
260
+ detail: `Connected to ${cogneeStatus.url}`,
261
+ });
251
262
  } else {
252
263
  checks.push({
253
264
  name: 'cognee',