@soleri/core 9.11.0 → 9.13.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 (234) hide show
  1. package/dist/adapters/types.d.ts +2 -0
  2. package/dist/adapters/types.d.ts.map +1 -1
  3. package/dist/brain/brain.d.ts +5 -1
  4. package/dist/brain/brain.d.ts.map +1 -1
  5. package/dist/brain/brain.js +97 -10
  6. package/dist/brain/brain.js.map +1 -1
  7. package/dist/dream/cron-manager.d.ts +10 -0
  8. package/dist/dream/cron-manager.d.ts.map +1 -0
  9. package/dist/dream/cron-manager.js +122 -0
  10. package/dist/dream/cron-manager.js.map +1 -0
  11. package/dist/dream/dream-engine.d.ts +34 -0
  12. package/dist/dream/dream-engine.d.ts.map +1 -0
  13. package/dist/dream/dream-engine.js +88 -0
  14. package/dist/dream/dream-engine.js.map +1 -0
  15. package/dist/dream/dream-ops.d.ts +8 -0
  16. package/dist/dream/dream-ops.d.ts.map +1 -0
  17. package/dist/dream/dream-ops.js +49 -0
  18. package/dist/dream/dream-ops.js.map +1 -0
  19. package/dist/dream/index.d.ts +7 -0
  20. package/dist/dream/index.d.ts.map +1 -0
  21. package/dist/dream/index.js +5 -0
  22. package/dist/dream/index.js.map +1 -0
  23. package/dist/dream/schema.d.ts +3 -0
  24. package/dist/dream/schema.d.ts.map +1 -0
  25. package/dist/dream/schema.js +16 -0
  26. package/dist/dream/schema.js.map +1 -0
  27. package/dist/embeddings/index.d.ts +5 -0
  28. package/dist/embeddings/index.d.ts.map +1 -0
  29. package/dist/embeddings/index.js +3 -0
  30. package/dist/embeddings/index.js.map +1 -0
  31. package/dist/embeddings/openai-provider.d.ts +31 -0
  32. package/dist/embeddings/openai-provider.d.ts.map +1 -0
  33. package/dist/embeddings/openai-provider.js +120 -0
  34. package/dist/embeddings/openai-provider.js.map +1 -0
  35. package/dist/embeddings/pipeline.d.ts +36 -0
  36. package/dist/embeddings/pipeline.d.ts.map +1 -0
  37. package/dist/embeddings/pipeline.js +78 -0
  38. package/dist/embeddings/pipeline.js.map +1 -0
  39. package/dist/embeddings/types.d.ts +62 -0
  40. package/dist/embeddings/types.d.ts.map +1 -0
  41. package/dist/embeddings/types.js +3 -0
  42. package/dist/embeddings/types.js.map +1 -0
  43. package/dist/engine/bin/soleri-engine.js +4 -1
  44. package/dist/engine/bin/soleri-engine.js.map +1 -1
  45. package/dist/engine/module-manifest.d.ts.map +1 -1
  46. package/dist/engine/module-manifest.js +20 -0
  47. package/dist/engine/module-manifest.js.map +1 -1
  48. package/dist/engine/register-engine.d.ts.map +1 -1
  49. package/dist/engine/register-engine.js +12 -0
  50. package/dist/engine/register-engine.js.map +1 -1
  51. package/dist/flows/chain-types.d.ts +8 -8
  52. package/dist/flows/dispatch-registry.d.ts +15 -1
  53. package/dist/flows/dispatch-registry.d.ts.map +1 -1
  54. package/dist/flows/dispatch-registry.js +28 -1
  55. package/dist/flows/dispatch-registry.js.map +1 -1
  56. package/dist/flows/executor.d.ts +20 -2
  57. package/dist/flows/executor.d.ts.map +1 -1
  58. package/dist/flows/executor.js +79 -1
  59. package/dist/flows/executor.js.map +1 -1
  60. package/dist/flows/index.d.ts +2 -1
  61. package/dist/flows/index.d.ts.map +1 -1
  62. package/dist/flows/index.js.map +1 -1
  63. package/dist/flows/types.d.ts +43 -21
  64. package/dist/flows/types.d.ts.map +1 -1
  65. package/dist/index.d.ts +6 -1
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +4 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/persona/defaults.d.ts +8 -0
  70. package/dist/persona/defaults.d.ts.map +1 -1
  71. package/dist/persona/defaults.js +49 -0
  72. package/dist/persona/defaults.js.map +1 -1
  73. package/dist/plugins/types.d.ts +31 -31
  74. package/dist/runtime/admin-ops.d.ts.map +1 -1
  75. package/dist/runtime/admin-ops.js +15 -0
  76. package/dist/runtime/admin-ops.js.map +1 -1
  77. package/dist/runtime/admin-setup-ops.js +2 -2
  78. package/dist/runtime/admin-setup-ops.js.map +1 -1
  79. package/dist/runtime/embedding-ops.d.ts +12 -0
  80. package/dist/runtime/embedding-ops.d.ts.map +1 -0
  81. package/dist/runtime/embedding-ops.js +96 -0
  82. package/dist/runtime/embedding-ops.js.map +1 -0
  83. package/dist/runtime/facades/embedding-facade.d.ts +7 -0
  84. package/dist/runtime/facades/embedding-facade.d.ts.map +1 -0
  85. package/dist/runtime/facades/embedding-facade.js +8 -0
  86. package/dist/runtime/facades/embedding-facade.js.map +1 -0
  87. package/dist/runtime/facades/index.d.ts.map +1 -1
  88. package/dist/runtime/facades/index.js +12 -0
  89. package/dist/runtime/facades/index.js.map +1 -1
  90. package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -1
  91. package/dist/runtime/facades/orchestrate-facade.js +120 -0
  92. package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
  93. package/dist/runtime/feature-flags.d.ts.map +1 -1
  94. package/dist/runtime/feature-flags.js +4 -0
  95. package/dist/runtime/feature-flags.js.map +1 -1
  96. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  97. package/dist/runtime/orchestrate-ops.js +140 -9
  98. package/dist/runtime/orchestrate-ops.js.map +1 -1
  99. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  100. package/dist/runtime/planning-extra-ops.js +51 -0
  101. package/dist/runtime/planning-extra-ops.js.map +1 -1
  102. package/dist/runtime/preflight.d.ts +32 -0
  103. package/dist/runtime/preflight.d.ts.map +1 -0
  104. package/dist/runtime/preflight.js +29 -0
  105. package/dist/runtime/preflight.js.map +1 -0
  106. package/dist/runtime/runtime.d.ts.map +1 -1
  107. package/dist/runtime/runtime.js +33 -2
  108. package/dist/runtime/runtime.js.map +1 -1
  109. package/dist/runtime/types.d.ts +27 -0
  110. package/dist/runtime/types.d.ts.map +1 -1
  111. package/dist/skills/step-tracker.d.ts +39 -0
  112. package/dist/skills/step-tracker.d.ts.map +1 -0
  113. package/dist/skills/step-tracker.js +105 -0
  114. package/dist/skills/step-tracker.js.map +1 -0
  115. package/dist/skills/sync-skills.d.ts +3 -2
  116. package/dist/skills/sync-skills.d.ts.map +1 -1
  117. package/dist/skills/sync-skills.js +42 -8
  118. package/dist/skills/sync-skills.js.map +1 -1
  119. package/dist/subagent/dispatcher.d.ts +4 -3
  120. package/dist/subagent/dispatcher.d.ts.map +1 -1
  121. package/dist/subagent/dispatcher.js +57 -35
  122. package/dist/subagent/dispatcher.js.map +1 -1
  123. package/dist/subagent/index.d.ts +1 -0
  124. package/dist/subagent/index.d.ts.map +1 -1
  125. package/dist/subagent/index.js.map +1 -1
  126. package/dist/subagent/orphan-reaper.d.ts +51 -4
  127. package/dist/subagent/orphan-reaper.d.ts.map +1 -1
  128. package/dist/subagent/orphan-reaper.js +103 -3
  129. package/dist/subagent/orphan-reaper.js.map +1 -1
  130. package/dist/subagent/types.d.ts +7 -0
  131. package/dist/subagent/types.d.ts.map +1 -1
  132. package/dist/subagent/workspace-resolver.d.ts +2 -0
  133. package/dist/subagent/workspace-resolver.d.ts.map +1 -1
  134. package/dist/subagent/workspace-resolver.js +3 -1
  135. package/dist/subagent/workspace-resolver.js.map +1 -1
  136. package/dist/vault/vault-entries.d.ts +18 -0
  137. package/dist/vault/vault-entries.d.ts.map +1 -1
  138. package/dist/vault/vault-entries.js +73 -0
  139. package/dist/vault/vault-entries.js.map +1 -1
  140. package/dist/vault/vault-manager.d.ts.map +1 -1
  141. package/dist/vault/vault-manager.js +1 -0
  142. package/dist/vault/vault-manager.js.map +1 -1
  143. package/dist/vault/vault-schema.d.ts.map +1 -1
  144. package/dist/vault/vault-schema.js +14 -0
  145. package/dist/vault/vault-schema.js.map +1 -1
  146. package/dist/vault/vault.d.ts +1 -0
  147. package/dist/vault/vault.d.ts.map +1 -1
  148. package/dist/vault/vault.js.map +1 -1
  149. package/package.json +3 -5
  150. package/src/__tests__/cron-manager.test.ts +132 -0
  151. package/src/__tests__/deviation-detection.test.ts +234 -0
  152. package/src/__tests__/embeddings.test.ts +536 -0
  153. package/src/__tests__/preflight.test.ts +97 -0
  154. package/src/__tests__/step-persistence.test.ts +324 -0
  155. package/src/__tests__/step-tracker.test.ts +260 -0
  156. package/src/__tests__/subagent/dispatcher.test.ts +122 -4
  157. package/src/__tests__/subagent/orphan-reaper.test.ts +148 -12
  158. package/src/__tests__/subagent/process-lifecycle.test.ts +422 -0
  159. package/src/__tests__/subagent/workspace-resolver.test.ts +6 -1
  160. package/src/adapters/types.ts +2 -0
  161. package/src/brain/brain.ts +117 -9
  162. package/src/dream/cron-manager.ts +137 -0
  163. package/src/dream/dream-engine.ts +119 -0
  164. package/src/dream/dream-ops.ts +56 -0
  165. package/src/dream/dream.test.ts +182 -0
  166. package/src/dream/index.ts +6 -0
  167. package/src/dream/schema.ts +17 -0
  168. package/src/embeddings/openai-provider.ts +158 -0
  169. package/src/embeddings/pipeline.ts +126 -0
  170. package/src/embeddings/types.ts +67 -0
  171. package/src/engine/bin/soleri-engine.ts +4 -1
  172. package/src/engine/module-manifest.test.ts +4 -4
  173. package/src/engine/module-manifest.ts +20 -0
  174. package/src/engine/register-engine.ts +12 -0
  175. package/src/flows/dispatch-registry.ts +44 -1
  176. package/src/flows/executor.ts +93 -2
  177. package/src/flows/index.ts +2 -0
  178. package/src/flows/types.ts +39 -1
  179. package/src/index.ts +12 -0
  180. package/src/persona/defaults.test.ts +39 -1
  181. package/src/persona/defaults.ts +65 -0
  182. package/src/planning/goal-ancestry.test.ts +3 -5
  183. package/src/planning/planner.test.ts +2 -3
  184. package/src/runtime/admin-ops.test.ts +2 -2
  185. package/src/runtime/admin-ops.ts +17 -0
  186. package/src/runtime/admin-setup-ops.ts +2 -2
  187. package/src/runtime/embedding-ops.ts +116 -0
  188. package/src/runtime/facades/admin-facade.test.ts +31 -0
  189. package/src/runtime/facades/embedding-facade.ts +11 -0
  190. package/src/runtime/facades/index.ts +12 -0
  191. package/src/runtime/facades/orchestrate-facade.test.ts +16 -0
  192. package/src/runtime/facades/orchestrate-facade.ts +146 -0
  193. package/src/runtime/feature-flags.ts +4 -0
  194. package/src/runtime/orchestrate-ops.test.ts +131 -0
  195. package/src/runtime/orchestrate-ops.ts +158 -10
  196. package/src/runtime/planning-extra-ops.ts +77 -0
  197. package/src/runtime/preflight.ts +53 -0
  198. package/src/runtime/runtime.ts +41 -2
  199. package/src/runtime/types.ts +20 -0
  200. package/src/skills/__tests__/sync-skills.test.ts +132 -0
  201. package/src/skills/step-tracker.ts +162 -0
  202. package/src/skills/sync-skills.ts +54 -9
  203. package/src/subagent/dispatcher.ts +62 -39
  204. package/src/subagent/index.ts +1 -0
  205. package/src/subagent/orphan-reaper.test.ts +135 -0
  206. package/src/subagent/orphan-reaper.ts +130 -7
  207. package/src/subagent/types.ts +10 -0
  208. package/src/subagent/workspace-resolver.ts +3 -1
  209. package/src/vault/vault-entries.ts +112 -0
  210. package/src/vault/vault-manager.ts +1 -0
  211. package/src/vault/vault-scaling.test.ts +3 -2
  212. package/src/vault/vault-schema.ts +15 -0
  213. package/src/vault/vault.ts +1 -0
  214. package/vitest.config.ts +2 -1
  215. package/dist/brain/strength-scorer.d.ts +0 -31
  216. package/dist/brain/strength-scorer.d.ts.map +0 -1
  217. package/dist/brain/strength-scorer.js +0 -264
  218. package/dist/brain/strength-scorer.js.map +0 -1
  219. package/dist/engine/index.d.ts +0 -21
  220. package/dist/engine/index.d.ts.map +0 -1
  221. package/dist/engine/index.js +0 -18
  222. package/dist/engine/index.js.map +0 -1
  223. package/dist/hooks/index.d.ts +0 -2
  224. package/dist/hooks/index.d.ts.map +0 -1
  225. package/dist/hooks/index.js +0 -2
  226. package/dist/hooks/index.js.map +0 -1
  227. package/dist/persona/index.d.ts +0 -5
  228. package/dist/persona/index.d.ts.map +0 -1
  229. package/dist/persona/index.js +0 -4
  230. package/dist/persona/index.js.map +0 -1
  231. package/dist/vault/vault-interfaces.d.ts +0 -153
  232. package/dist/vault/vault-interfaces.d.ts.map +0 -1
  233. package/dist/vault/vault-interfaces.js +0 -2
  234. package/dist/vault/vault-interfaces.js.map +0 -1
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Embedding pipeline — batch and incremental embedding of vault entries.
3
+ */
4
+ import type { EmbeddingProvider } from './types.js';
5
+ import type { PersistenceProvider } from '../persistence/types.js';
6
+ import { storeVector, getVector, getEntriesWithoutVectors } from '../vault/vault-entries.js';
7
+
8
+ export interface BatchEmbedOptions {
9
+ /** Number of entries per API call (default 100). */
10
+ batchSize?: number;
11
+ /** Called after each batch with (completed, total). */
12
+ onProgress?: (completed: number, total: number) => void;
13
+ }
14
+
15
+ export interface BatchEmbedResult {
16
+ embedded: number;
17
+ skipped: number;
18
+ failed: number;
19
+ tokensUsed: number;
20
+ }
21
+
22
+ export class EmbeddingPipeline {
23
+ constructor(
24
+ private provider: EmbeddingProvider,
25
+ private persistence: PersistenceProvider,
26
+ ) {}
27
+
28
+ /**
29
+ * Batch embed all entries missing vectors for the current model.
30
+ * Processes in chunks; skips entries that already have vectors.
31
+ * On batch failure, logs and continues with the next batch.
32
+ */
33
+ async batchEmbed(options?: BatchEmbedOptions): Promise<BatchEmbedResult> {
34
+ const batchSize = options?.batchSize ?? 100;
35
+ const onProgress = options?.onProgress;
36
+
37
+ const missingIds = getEntriesWithoutVectors(this.persistence, this.provider.model);
38
+ if (missingIds.length === 0) {
39
+ return { embedded: 0, skipped: 0, failed: 0, tokensUsed: 0 };
40
+ }
41
+
42
+ let embedded = 0;
43
+ let failed = 0;
44
+ let tokensUsed = 0;
45
+
46
+ for (let i = 0; i < missingIds.length; i += batchSize) {
47
+ const batchIds = missingIds.slice(i, i + batchSize);
48
+
49
+ // Load entries for this batch
50
+ const placeholders = batchIds.map(() => '?').join(',');
51
+ const rows = this.persistence.all<{
52
+ id: string;
53
+ title: string;
54
+ description: string;
55
+ context: string;
56
+ }>(
57
+ `SELECT id, title, description, context FROM entries WHERE id IN (${placeholders})`,
58
+ batchIds,
59
+ );
60
+
61
+ if (rows.length === 0) continue;
62
+
63
+ const texts = rows.map((r) => this.getEmbeddableText(r));
64
+
65
+ try {
66
+ const result = await this.provider.embed(texts);
67
+ for (let j = 0; j < rows.length; j++) {
68
+ storeVector(
69
+ this.persistence,
70
+ rows[j].id,
71
+ result.vectors[j],
72
+ this.provider.model,
73
+ this.provider.dimensions,
74
+ );
75
+ embedded++;
76
+ }
77
+ tokensUsed += result.tokensUsed;
78
+ } catch (err) {
79
+ // Log and continue — don't abort entire pipeline for one bad batch
80
+ console.error(`[EmbeddingPipeline] batch failed (offset ${i}):`, err);
81
+ failed += batchIds.length;
82
+ }
83
+
84
+ onProgress?.(embedded + failed, missingIds.length);
85
+ }
86
+
87
+ return {
88
+ embedded,
89
+ skipped: 0,
90
+ failed,
91
+ tokensUsed,
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Embed a single entry and store its vector.
97
+ * Returns true if embedded, false if skipped (vector already exists).
98
+ */
99
+ async embedEntry(entryId: string, text: string): Promise<boolean> {
100
+ const existing = getVector(this.persistence, entryId);
101
+ if (existing && existing.model === this.provider.model) return false;
102
+
103
+ const result = await this.provider.embed([text]);
104
+ storeVector(
105
+ this.persistence,
106
+ entryId,
107
+ result.vectors[0],
108
+ this.provider.model,
109
+ this.provider.dimensions,
110
+ );
111
+ return true;
112
+ }
113
+
114
+ /** Build the text to embed for a vault entry. */
115
+ private getEmbeddableText(entry: {
116
+ title?: string;
117
+ description?: string;
118
+ context?: string;
119
+ }): string {
120
+ const parts: string[] = [];
121
+ if (entry.title) parts.push(entry.title);
122
+ if (entry.description) parts.push(entry.description);
123
+ if (entry.context) parts.push(entry.context);
124
+ return parts.join('\n');
125
+ }
126
+ }
@@ -0,0 +1,67 @@
1
+ // ─── Embedding Types ─────────────────────────────────────────────
2
+
3
+ /** Result of an embedding call — vectors plus metadata. */
4
+ export interface EmbeddingResult {
5
+ /** One vector per input text, each vector is number[] of length = dimensions. */
6
+ vectors: number[][];
7
+ /** Total tokens consumed by this call. */
8
+ tokensUsed: number;
9
+ /** Model that produced these embeddings. */
10
+ model: string;
11
+ }
12
+
13
+ /** Provider-agnostic interface for generating dense vector embeddings. */
14
+ export interface EmbeddingProvider {
15
+ /** Provider name (e.g. 'openai', 'ollama'). */
16
+ readonly providerName: string;
17
+ /** Model identifier (e.g. 'text-embedding-3-small'). */
18
+ readonly model: string;
19
+ /** Vector dimensions produced by this model. */
20
+ readonly dimensions: number;
21
+ /** Embed one or more texts, returning one vector per text. */
22
+ embed(texts: string[]): Promise<EmbeddingResult>;
23
+ }
24
+
25
+ /** Configuration for initializing an embedding provider. */
26
+ export interface EmbeddingConfig {
27
+ /** Which provider to use: 'openai' | 'ollama' | string. */
28
+ provider: string;
29
+ /** Model to use for embeddings. */
30
+ model: string;
31
+ /** Optional API key override (otherwise uses KeyPool). */
32
+ apiKey?: string;
33
+ /** Optional base URL override (for self-hosted or proxy). */
34
+ baseUrl?: string;
35
+ /** Max texts per batch API call (provider-specific default if omitted). */
36
+ batchSize?: number;
37
+ }
38
+
39
+ /** A persisted embedding vector tied to a vault entry. */
40
+ export interface StoredVector {
41
+ /** Vault entry ID this vector belongs to. */
42
+ entryId: string;
43
+ /** The embedding vector. */
44
+ vector: number[];
45
+ /** Model that produced this vector. */
46
+ model: string;
47
+ /** Number of dimensions in the vector. */
48
+ dimensions: number;
49
+ /** Unix timestamp (ms) when this vector was created. */
50
+ createdAt: number;
51
+ }
52
+
53
+ /** Aggregate statistics for the embedding subsystem. */
54
+ export interface EmbeddingStats {
55
+ /** Active provider name. */
56
+ provider: string;
57
+ /** Active model name. */
58
+ model: string;
59
+ /** Vector dimensions for the active model. */
60
+ dimensions: number;
61
+ /** Number of entries with embeddings. */
62
+ totalEmbedded: number;
63
+ /** Number of entries missing embeddings. */
64
+ totalMissing: number;
65
+ /** Cumulative tokens consumed across all embedding calls. */
66
+ totalTokensUsed: number;
67
+ }
@@ -163,7 +163,7 @@ async function main(): Promise<void> {
163
163
  .join(', ')})`,
164
164
  );
165
165
 
166
- // 6b. Auto-sync skills to ~/.claude/commands/
166
+ // 6b. Auto-sync skills to ~/.claude/skills/
167
167
  const skillsDir = join(agentDir, 'skills');
168
168
  if (existsSync(skillsDir)) {
169
169
  const syncResult = syncSkillsToClaudeCode([skillsDir], config.name as string);
@@ -173,6 +173,9 @@ async function main(): Promise<void> {
173
173
  `${tag} Skills synced: ${syncResult.installed.length} new, ${syncResult.updated.length} updated`,
174
174
  );
175
175
  }
176
+ if (syncResult.removed.length) {
177
+ console.error(`${tag} Removed ${syncResult.removed.length} orphaned skill(s)`);
178
+ }
176
179
  }
177
180
 
178
181
  // 7. Load domain packs
@@ -32,8 +32,8 @@ describe('ENGINE_MODULE_MANIFEST', () => {
32
32
  expect(suffixes).toContain('intake');
33
33
  });
34
34
 
35
- it('has exactly 20 modules', () => {
36
- expect(ENGINE_MODULE_MANIFEST).toHaveLength(20);
35
+ it('has exactly 22 modules', () => {
36
+ expect(ENGINE_MODULE_MANIFEST).toHaveLength(22);
37
37
  });
38
38
 
39
39
  it('has no duplicate suffixes', () => {
@@ -168,7 +168,7 @@ describe('manifest order stability', () => {
168
168
  expect(ENGINE_MODULE_MANIFEST[0].suffix).toBe('vault');
169
169
  });
170
170
 
171
- it('tier is the last module', () => {
172
- expect(ENGINE_MODULE_MANIFEST[ENGINE_MODULE_MANIFEST.length - 1].suffix).toBe('tier');
171
+ it('dream is the last module', () => {
172
+ expect(ENGINE_MODULE_MANIFEST[ENGINE_MODULE_MANIFEST.length - 1].suffix).toBe('dream');
173
173
  });
174
174
  });
@@ -231,6 +231,16 @@ export const ENGINE_MODULE_MANIFEST: ModuleManifestEntry[] = [
231
231
  'merge branch': 'vault_merge_branch',
232
232
  },
233
233
  },
234
+ {
235
+ suffix: 'embedding',
236
+ description: 'Embedding management — status, batch rebuild, single-entry embedding.',
237
+ keyOps: ['embed_status', 'embed_rebuild', 'embed_entry'],
238
+ intentSignals: {
239
+ 'embedding status': 'embed_status',
240
+ 'rebuild embeddings': 'embed_rebuild',
241
+ 'embed entry': 'embed_entry',
242
+ },
243
+ },
234
244
  {
235
245
  suffix: 'tier',
236
246
  description: 'Multi-vault tiers — connect, disconnect, search across sources.',
@@ -241,6 +251,16 @@ export const ENGINE_MODULE_MANIFEST: ModuleManifestEntry[] = [
241
251
  'list sources': 'vault_list_sources',
242
252
  },
243
253
  },
254
+ {
255
+ suffix: 'dream',
256
+ description: 'Dream — automatic memory consolidation, vault cleanup, and maintenance.',
257
+ keyOps: ['dream_status', 'dream_trigger', 'dream_history'],
258
+ intentSignals: {
259
+ 'dream status': 'dream_status',
260
+ 'run dream': 'dream_trigger',
261
+ 'dream history': 'dream_history',
262
+ },
263
+ },
244
264
  ];
245
265
 
246
266
  /** Core facade ops (always present, not in ENGINE_MODULES) */
@@ -43,6 +43,8 @@ import { createIntakeFacadeOps } from '../runtime/facades/intake-facade.js';
43
43
  import { createLinksFacadeOps } from '../runtime/facades/links-facade.js';
44
44
  import { createBranchingFacadeOps } from '../runtime/facades/branching-facade.js';
45
45
  import { createTierFacadeOps } from '../runtime/facades/tier-facade.js';
46
+ import { createEmbeddingFacadeOps } from '../runtime/facades/embedding-facade.js';
47
+ import { createDreamOps } from '../dream/dream-ops.js';
46
48
  import { createDomainFacade } from '../runtime/domain-ops.js';
47
49
 
48
50
  // ─── Types ────────────────────────────────────────────────────────────
@@ -185,11 +187,21 @@ export const ENGINE_MODULES: ModuleDef[] = [
185
187
  description: 'Vault branching — create, list, merge, delete branches.',
186
188
  createOps: createBranchingFacadeOps,
187
189
  },
190
+ {
191
+ suffix: 'embedding',
192
+ description: 'Embedding management — status, batch rebuild, single-entry embedding.',
193
+ createOps: createEmbeddingFacadeOps,
194
+ },
188
195
  {
189
196
  suffix: 'tier',
190
197
  description: 'Multi-vault tiers — connect, disconnect, search across sources.',
191
198
  createOps: createTierFacadeOps,
192
199
  },
200
+ {
201
+ suffix: 'dream',
202
+ description: 'Dream — automatic memory consolidation, vault cleanup, and maintenance.',
203
+ createOps: createDreamOps,
204
+ },
193
205
  ];
194
206
 
195
207
  // ─── Core Registration ────────────────────────────────────────────────
@@ -10,14 +10,51 @@ import type { FacadeConfig } from '../facades/types.js';
10
10
  type DispatchResult = { tool: string; status: string; data?: unknown; error?: string };
11
11
  type DispatchFn = (toolName: string, params: Record<string, unknown>) => Promise<DispatchResult>;
12
12
 
13
+ export interface ActivePlanRef {
14
+ steps: Array<{ id: string; allowedTools?: string[]; status: string }>;
15
+ deviations?: Array<{
16
+ stepId: string;
17
+ expectedTools: string[];
18
+ actualTool: string;
19
+ timestamp: string;
20
+ }>;
21
+ }
22
+
23
+ /**
24
+ * Check whether the tool call deviates from the current running step's allowedTools.
25
+ * If it does, record the deviation on the plan (warn-only, does not block execution).
26
+ */
27
+ function checkDeviation(activePlan: ActivePlanRef, toolName: string, opName: string): void {
28
+ const currentStep = activePlan.steps.find((s) => s.status === 'running');
29
+ if (currentStep?.allowedTools && currentStep.allowedTools.length > 0) {
30
+ if (
31
+ !currentStep.allowedTools.includes(toolName) &&
32
+ !currentStep.allowedTools.includes(opName)
33
+ ) {
34
+ if (!activePlan.deviations) activePlan.deviations = [];
35
+ activePlan.deviations.push({
36
+ stepId: currentStep.id,
37
+ expectedTools: currentStep.allowedTools,
38
+ actualTool: toolName,
39
+ timestamp: new Date().toISOString(),
40
+ });
41
+ }
42
+ }
43
+ }
44
+
13
45
  /**
14
46
  * Create a dispatcher function that routes tool calls to the correct facade op.
15
47
  *
16
48
  * @param agentId - Agent identifier prefix (e.g. "myagent")
17
49
  * @param facades - Array of registered facade configs
50
+ * @param activePlan - Optional active plan reference for deviation tracking
18
51
  * @returns A dispatch function that takes (toolName, params) and calls the matching handler
19
52
  */
20
- export function createDispatcher(agentId: string, facades: FacadeConfig[]): DispatchFn {
53
+ export function createDispatcher(
54
+ agentId: string,
55
+ facades: FacadeConfig[],
56
+ activePlan?: ActivePlanRef,
57
+ ): DispatchFn {
21
58
  // Build a lookup map: facadeName → { opName → handler }
22
59
  const facadeMap = new Map<string, FacadeConfig>();
23
60
  for (const facade of facades) {
@@ -45,6 +82,9 @@ export function createDispatcher(agentId: string, facades: FacadeConfig[]): Disp
45
82
  const op = facade.ops.find((o) => o.name === opName);
46
83
  if (!op) continue;
47
84
 
85
+ // Check for plan deviation (warn-only)
86
+ if (activePlan) checkDeviation(activePlan, toolName, opName);
87
+
48
88
  try {
49
89
  const result = await op.handler(params);
50
90
  return { tool: toolName, status: 'ok', data: result };
@@ -62,6 +102,9 @@ export function createDispatcher(agentId: string, facades: FacadeConfig[]): Disp
62
102
  if (facade && params.op && typeof params.op === 'string') {
63
103
  const op = facade.ops.find((o) => o.name === params.op);
64
104
  if (op) {
105
+ // Check for plan deviation (warn-only)
106
+ if (activePlan) checkDeviation(activePlan, toolName, params.op as string);
107
+
65
108
  try {
66
109
  const result = await op.handler(params);
67
110
  return { tool: toolName, status: 'ok', data: result };
@@ -3,7 +3,9 @@
3
3
  * evaluating gates and handling branching.
4
4
  */
5
5
 
6
- import type { OrchestrationPlan, ExecutionResult, StepResult } from './types.js';
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import type { OrchestrationPlan, ExecutionResult, StepResult, PlanRunManifest } from './types.js';
7
9
  import { evaluateGate } from './gate-evaluator.js';
8
10
 
9
11
  /** Maximum iterations for BRANCH loops to prevent infinite cycles. */
@@ -14,14 +16,81 @@ type DispatchFn = (
14
16
  params: Record<string, unknown>,
15
17
  ) => Promise<{ tool: string; status: string; data?: unknown; error?: string }>;
16
18
 
19
+ // ---------------------------------------------------------------------------
20
+ // Step persistence helpers
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Resolve the persistence directory for a plan run.
25
+ * Returns `{persistDir}/.soleri/plan-runs/{planId}/`.
26
+ */
27
+ export function getPlanRunDir(persistDir: string, planId: string): string {
28
+ return path.join(persistDir, '.soleri', 'plan-runs', planId);
29
+ }
30
+
31
+ /**
32
+ * Load or create a PlanRunManifest from disk.
33
+ */
34
+ export function loadManifest(runDir: string, planId: string): PlanRunManifest {
35
+ const manifestPath = path.join(runDir, 'manifest.json');
36
+ if (fs.existsSync(manifestPath)) {
37
+ try {
38
+ return JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as PlanRunManifest;
39
+ } catch {
40
+ // Malformed manifest — return fresh one
41
+ }
42
+ }
43
+ const now = new Date().toISOString();
44
+ return { planId, steps: {}, lastRun: now, createdAt: now };
45
+ }
46
+
47
+ /**
48
+ * Write a PlanRunManifest to disk.
49
+ */
50
+ export function saveManifest(runDir: string, manifest: PlanRunManifest): void {
51
+ fs.mkdirSync(runDir, { recursive: true });
52
+ fs.writeFileSync(path.join(runDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
53
+ }
54
+
55
+ /**
56
+ * Persist a single step's output to disk and update the manifest.
57
+ */
58
+ export function persistStepOutput(
59
+ runDir: string,
60
+ manifest: PlanRunManifest,
61
+ stepIndex: number,
62
+ stepId: string,
63
+ output: unknown,
64
+ ): void {
65
+ fs.mkdirSync(runDir, { recursive: true });
66
+ const safeStepId = stepId.replace(/[/\\:*?"<>|.]/g, '_');
67
+ const fileName = `step-${stepIndex}-${safeStepId}.json`;
68
+ fs.writeFileSync(path.join(runDir, fileName), JSON.stringify(output, null, 2));
69
+
70
+ const existing = manifest.steps[stepId];
71
+ const now = new Date().toISOString();
72
+
73
+ manifest.steps[stepId] = {
74
+ status: 'completed',
75
+ output,
76
+ timestamp: now,
77
+ rerunCount: existing ? existing.rerunCount + 1 : 0,
78
+ rerunReason: existing?.rerunReason,
79
+ };
80
+ manifest.lastRun = now;
81
+ saveManifest(runDir, manifest);
82
+ }
83
+
17
84
  /**
18
85
  * Executes an orchestration plan sequentially (with parallel inner steps).
19
86
  */
20
87
  export class FlowExecutor {
21
88
  private dispatch: DispatchFn;
89
+ private persistDir: string | undefined;
22
90
 
23
- constructor(dispatch: DispatchFn) {
91
+ constructor(dispatch: DispatchFn, persistDir?: string) {
24
92
  this.dispatch = dispatch;
93
+ this.persistDir = persistDir;
25
94
  }
26
95
 
27
96
  /**
@@ -35,6 +104,14 @@ export class FlowExecutor {
35
104
  let branchIterations = 0;
36
105
  let currentIndex = 0;
37
106
 
107
+ // Set up persistence if configured
108
+ let runDir: string | undefined;
109
+ let manifest: PlanRunManifest | undefined;
110
+ if (this.persistDir) {
111
+ runDir = getPlanRunDir(this.persistDir, plan.planId);
112
+ manifest = loadManifest(runDir, plan.planId);
113
+ }
114
+
38
115
  while (currentIndex < plan.steps.length) {
39
116
  const step = plan.steps[currentIndex];
40
117
  const stepStart = Date.now();
@@ -121,6 +198,20 @@ export class FlowExecutor {
121
198
 
122
199
  stepResults.push(stepResult);
123
200
 
201
+ // Persist step output to disk if configured
202
+ if (runDir && manifest) {
203
+ try {
204
+ persistStepOutput(runDir, manifest, currentIndex, step.id, {
205
+ toolResults,
206
+ gateResult: stepResult.gateResult,
207
+ status: stepResult.status,
208
+ durationMs: stepResult.durationMs,
209
+ });
210
+ } catch {
211
+ // Persistence is best-effort — never blocks execution
212
+ }
213
+ }
214
+
124
215
  // Handle gate action
125
216
  switch (verdict.action) {
126
217
  case 'STOP':
@@ -17,6 +17,7 @@ export type {
17
17
  StepResult,
18
18
  ExecutionResult,
19
19
  GateVerdict,
20
+ ToolDeviation,
20
21
  } from './types.js';
21
22
 
22
23
  // Loader
@@ -47,6 +48,7 @@ export { FlowExecutor } from './executor.js';
47
48
 
48
49
  // Dispatch registry
49
50
  export { createDispatcher } from './dispatch-registry.js';
51
+ export type { ActivePlanRef } from './dispatch-registry.js';
50
52
 
51
53
  // Epilogue
52
54
  export { runEpilogue } from './epilogue.js';
@@ -151,13 +151,22 @@ export interface PlanStep {
151
151
  tools: string[];
152
152
  parallel: boolean;
153
153
  requires: ProbeName[];
154
+ allowedTools?: string[];
154
155
  gate?: {
155
156
  type: string;
156
157
  condition?: string;
157
158
  min?: number;
158
159
  onFail?: { action: string; goto?: string; message?: string };
159
160
  };
160
- status: 'pending' | 'running' | 'passed' | 'failed' | 'skipped' | 'gate-paused';
161
+ status:
162
+ | 'pending'
163
+ | 'running'
164
+ | 'passed'
165
+ | 'failed'
166
+ | 'skipped'
167
+ | 'gate-paused'
168
+ | 'stale'
169
+ | 'rerun';
161
170
  }
162
171
 
163
172
  export interface SkippedStep {
@@ -166,6 +175,13 @@ export interface SkippedStep {
166
175
  reason: string;
167
176
  }
168
177
 
178
+ export interface ToolDeviation {
179
+ stepId: string;
180
+ expectedTools: string[];
181
+ actualTool: string;
182
+ timestamp: string;
183
+ }
184
+
169
185
  export interface OrchestrationPlan {
170
186
  planId: string;
171
187
  intent: string;
@@ -177,6 +193,7 @@ export interface OrchestrationPlan {
177
193
  summary: string;
178
194
  estimatedTools: number;
179
195
  context: OrchestrationContext;
196
+ deviations?: ToolDeviation[];
180
197
  }
181
198
 
182
199
  export interface OrchestrationContext {
@@ -186,6 +203,27 @@ export interface OrchestrationContext {
186
203
  projectPath: string;
187
204
  }
188
205
 
206
+ // ---------------------------------------------------------------------------
207
+ // Step persistence (incremental correction protocol)
208
+ // ---------------------------------------------------------------------------
209
+
210
+ export type StepPersistenceStatus = 'completed' | 'stale' | 'invalidated' | 'rerun';
211
+
212
+ export interface StepState {
213
+ status: StepPersistenceStatus;
214
+ output: unknown;
215
+ timestamp: string;
216
+ rerunCount: number;
217
+ rerunReason?: string;
218
+ }
219
+
220
+ export interface PlanRunManifest {
221
+ planId: string;
222
+ steps: Record<string, StepState>;
223
+ lastRun: string;
224
+ createdAt: string;
225
+ }
226
+
189
227
  // ---------------------------------------------------------------------------
190
228
  // Execution
191
229
  // ---------------------------------------------------------------------------
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export { TaskCheckout } from './subagent/task-checkout.js';
17
17
  export { WorkspaceResolver } from './subagent/workspace-resolver.js';
18
18
  export { ConcurrencyManager } from './subagent/concurrency-manager.js';
19
19
  export { OrphanReaper } from './subagent/orphan-reaper.js';
20
+ export type { ReapResult } from './subagent/orphan-reaper.js';
20
21
  export { aggregate as aggregateResults } from './subagent/result-aggregator.js';
21
22
  export type {
22
23
  SubagentTask,
@@ -160,6 +161,16 @@ export type {
160
161
  CuratorStatus,
161
162
  } from './curator/types.js';
162
163
 
164
+ // ─── Dream ──────────────────────────────────────────────────────────
165
+ export { DreamEngine, ensureDreamSchema, createDreamOps } from './dream/index.js';
166
+ export {
167
+ getSchedule as getDreamSchedule,
168
+ schedule as scheduleDream,
169
+ unschedule as unscheduleDream,
170
+ } from './dream/index.js';
171
+ export type { DreamReport, DreamStatus } from './dream/dream-engine.js';
172
+ export type { CronSchedule } from './dream/cron-manager.js';
173
+
163
174
  // ─── Governance ─────────────────────────────────────────────────────
164
175
  export { Governance } from './governance/governance.js';
165
176
  export type {
@@ -590,6 +601,7 @@ export type {
590
601
  } from './persona/types.js';
591
602
  export {
592
603
  ITALIAN_CRAFTSPERSON,
604
+ NEUTRAL_PERSONA,
593
605
  PERSONA_TEMPLATES,
594
606
  createDefaultPersona,
595
607
  } from './persona/defaults.js';
@@ -1,5 +1,10 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { ITALIAN_CRAFTSPERSON, PERSONA_TEMPLATES, createDefaultPersona } from './defaults.js';
2
+ import {
3
+ ITALIAN_CRAFTSPERSON,
4
+ NEUTRAL_PERSONA,
5
+ PERSONA_TEMPLATES,
6
+ createDefaultPersona,
7
+ } from './defaults.js';
3
8
 
4
9
  describe('ITALIAN_CRAFTSPERSON', () => {
5
10
  it('uses italian-craftsperson template id', () => {
@@ -25,11 +30,44 @@ describe('ITALIAN_CRAFTSPERSON', () => {
25
30
  });
26
31
  });
27
32
 
33
+ describe('NEUTRAL_PERSONA', () => {
34
+ it('uses neutral-custom template id', () => {
35
+ expect(NEUTRAL_PERSONA.template).toBe('neutral-custom');
36
+ });
37
+
38
+ it('has non-empty voice and inspiration', () => {
39
+ expect(NEUTRAL_PERSONA.voice.length).toBeGreaterThan(0);
40
+ expect(NEUTRAL_PERSONA.inspiration.length).toBeGreaterThan(0);
41
+ });
42
+
43
+ it('has no cultural flavor', () => {
44
+ expect(NEUTRAL_PERSONA.culture).toBe('');
45
+ });
46
+
47
+ it('has all arrays populated with minimum counts', () => {
48
+ expect(NEUTRAL_PERSONA.traits.length).toBeGreaterThanOrEqual(3);
49
+ expect(NEUTRAL_PERSONA.quirks.length).toBeGreaterThanOrEqual(3);
50
+ expect(NEUTRAL_PERSONA.opinions.length).toBeGreaterThanOrEqual(3);
51
+ expect(NEUTRAL_PERSONA.metaphors.length).toBeGreaterThanOrEqual(3);
52
+ expect(NEUTRAL_PERSONA.greetings.length).toBeGreaterThanOrEqual(2);
53
+ expect(NEUTRAL_PERSONA.signoffs.length).toBeGreaterThanOrEqual(2);
54
+ });
55
+
56
+ it('has non-empty language and name rules', () => {
57
+ expect(NEUTRAL_PERSONA.languageRule.length).toBeGreaterThan(0);
58
+ expect(NEUTRAL_PERSONA.nameRule.length).toBeGreaterThan(0);
59
+ });
60
+ });
61
+
28
62
  describe('PERSONA_TEMPLATES', () => {
29
63
  it('contains italian-craftsperson entry', () => {
30
64
  expect(PERSONA_TEMPLATES['italian-craftsperson']).toBe(ITALIAN_CRAFTSPERSON);
31
65
  });
32
66
 
67
+ it('contains neutral-custom entry', () => {
68
+ expect(PERSONA_TEMPLATES['neutral-custom']).toBe(NEUTRAL_PERSONA);
69
+ });
70
+
33
71
  it('does not include name or history in template entries', () => {
34
72
  for (const tmpl of Object.values(PERSONA_TEMPLATES)) {
35
73
  expect(tmpl).not.toHaveProperty('name');