@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.
- package/dist/adapters/types.d.ts +2 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/brain/brain.d.ts +5 -1
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +97 -10
- package/dist/brain/brain.js.map +1 -1
- package/dist/dream/cron-manager.d.ts +10 -0
- package/dist/dream/cron-manager.d.ts.map +1 -0
- package/dist/dream/cron-manager.js +122 -0
- package/dist/dream/cron-manager.js.map +1 -0
- package/dist/dream/dream-engine.d.ts +34 -0
- package/dist/dream/dream-engine.d.ts.map +1 -0
- package/dist/dream/dream-engine.js +88 -0
- package/dist/dream/dream-engine.js.map +1 -0
- package/dist/dream/dream-ops.d.ts +8 -0
- package/dist/dream/dream-ops.d.ts.map +1 -0
- package/dist/dream/dream-ops.js +49 -0
- package/dist/dream/dream-ops.js.map +1 -0
- package/dist/dream/index.d.ts +7 -0
- package/dist/dream/index.d.ts.map +1 -0
- package/dist/dream/index.js +5 -0
- package/dist/dream/index.js.map +1 -0
- package/dist/dream/schema.d.ts +3 -0
- package/dist/dream/schema.d.ts.map +1 -0
- package/dist/dream/schema.js +16 -0
- package/dist/dream/schema.js.map +1 -0
- package/dist/embeddings/index.d.ts +5 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +3 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/openai-provider.d.ts +31 -0
- package/dist/embeddings/openai-provider.d.ts.map +1 -0
- package/dist/embeddings/openai-provider.js +120 -0
- package/dist/embeddings/openai-provider.js.map +1 -0
- package/dist/embeddings/pipeline.d.ts +36 -0
- package/dist/embeddings/pipeline.d.ts.map +1 -0
- package/dist/embeddings/pipeline.js +78 -0
- package/dist/embeddings/pipeline.js.map +1 -0
- package/dist/embeddings/types.d.ts +62 -0
- package/dist/embeddings/types.d.ts.map +1 -0
- package/dist/embeddings/types.js +3 -0
- package/dist/embeddings/types.js.map +1 -0
- package/dist/engine/bin/soleri-engine.js +4 -1
- package/dist/engine/bin/soleri-engine.js.map +1 -1
- package/dist/engine/module-manifest.d.ts.map +1 -1
- package/dist/engine/module-manifest.js +20 -0
- package/dist/engine/module-manifest.js.map +1 -1
- package/dist/engine/register-engine.d.ts.map +1 -1
- package/dist/engine/register-engine.js +12 -0
- package/dist/engine/register-engine.js.map +1 -1
- package/dist/flows/chain-types.d.ts +8 -8
- package/dist/flows/dispatch-registry.d.ts +15 -1
- package/dist/flows/dispatch-registry.d.ts.map +1 -1
- package/dist/flows/dispatch-registry.js +28 -1
- package/dist/flows/dispatch-registry.js.map +1 -1
- package/dist/flows/executor.d.ts +20 -2
- package/dist/flows/executor.d.ts.map +1 -1
- package/dist/flows/executor.js +79 -1
- package/dist/flows/executor.js.map +1 -1
- package/dist/flows/index.d.ts +2 -1
- package/dist/flows/index.d.ts.map +1 -1
- package/dist/flows/index.js.map +1 -1
- package/dist/flows/types.d.ts +43 -21
- package/dist/flows/types.d.ts.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/persona/defaults.d.ts +8 -0
- package/dist/persona/defaults.d.ts.map +1 -1
- package/dist/persona/defaults.js +49 -0
- package/dist/persona/defaults.js.map +1 -1
- package/dist/plugins/types.d.ts +31 -31
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +15 -0
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/admin-setup-ops.js +2 -2
- package/dist/runtime/admin-setup-ops.js.map +1 -1
- package/dist/runtime/embedding-ops.d.ts +12 -0
- package/dist/runtime/embedding-ops.d.ts.map +1 -0
- package/dist/runtime/embedding-ops.js +96 -0
- package/dist/runtime/embedding-ops.js.map +1 -0
- package/dist/runtime/facades/embedding-facade.d.ts +7 -0
- package/dist/runtime/facades/embedding-facade.d.ts.map +1 -0
- package/dist/runtime/facades/embedding-facade.js +8 -0
- package/dist/runtime/facades/embedding-facade.js.map +1 -0
- package/dist/runtime/facades/index.d.ts.map +1 -1
- package/dist/runtime/facades/index.js +12 -0
- package/dist/runtime/facades/index.js.map +1 -1
- package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -1
- package/dist/runtime/facades/orchestrate-facade.js +120 -0
- package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
- package/dist/runtime/feature-flags.d.ts.map +1 -1
- package/dist/runtime/feature-flags.js +4 -0
- package/dist/runtime/feature-flags.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +140 -9
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +51 -0
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/preflight.d.ts +32 -0
- package/dist/runtime/preflight.d.ts.map +1 -0
- package/dist/runtime/preflight.js +29 -0
- package/dist/runtime/preflight.js.map +1 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +33 -2
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +27 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/skills/step-tracker.d.ts +39 -0
- package/dist/skills/step-tracker.d.ts.map +1 -0
- package/dist/skills/step-tracker.js +105 -0
- package/dist/skills/step-tracker.js.map +1 -0
- package/dist/skills/sync-skills.d.ts +3 -2
- package/dist/skills/sync-skills.d.ts.map +1 -1
- package/dist/skills/sync-skills.js +42 -8
- package/dist/skills/sync-skills.js.map +1 -1
- package/dist/subagent/dispatcher.d.ts +4 -3
- package/dist/subagent/dispatcher.d.ts.map +1 -1
- package/dist/subagent/dispatcher.js +57 -35
- package/dist/subagent/dispatcher.js.map +1 -1
- package/dist/subagent/index.d.ts +1 -0
- package/dist/subagent/index.d.ts.map +1 -1
- package/dist/subagent/index.js.map +1 -1
- package/dist/subagent/orphan-reaper.d.ts +51 -4
- package/dist/subagent/orphan-reaper.d.ts.map +1 -1
- package/dist/subagent/orphan-reaper.js +103 -3
- package/dist/subagent/orphan-reaper.js.map +1 -1
- package/dist/subagent/types.d.ts +7 -0
- package/dist/subagent/types.d.ts.map +1 -1
- package/dist/subagent/workspace-resolver.d.ts +2 -0
- package/dist/subagent/workspace-resolver.d.ts.map +1 -1
- package/dist/subagent/workspace-resolver.js +3 -1
- package/dist/subagent/workspace-resolver.js.map +1 -1
- package/dist/vault/vault-entries.d.ts +18 -0
- package/dist/vault/vault-entries.d.ts.map +1 -1
- package/dist/vault/vault-entries.js +73 -0
- package/dist/vault/vault-entries.js.map +1 -1
- package/dist/vault/vault-manager.d.ts.map +1 -1
- package/dist/vault/vault-manager.js +1 -0
- package/dist/vault/vault-manager.js.map +1 -1
- package/dist/vault/vault-schema.d.ts.map +1 -1
- package/dist/vault/vault-schema.js +14 -0
- package/dist/vault/vault-schema.js.map +1 -1
- package/dist/vault/vault.d.ts +1 -0
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js.map +1 -1
- package/package.json +3 -5
- package/src/__tests__/cron-manager.test.ts +132 -0
- package/src/__tests__/deviation-detection.test.ts +234 -0
- package/src/__tests__/embeddings.test.ts +536 -0
- package/src/__tests__/preflight.test.ts +97 -0
- package/src/__tests__/step-persistence.test.ts +324 -0
- package/src/__tests__/step-tracker.test.ts +260 -0
- package/src/__tests__/subagent/dispatcher.test.ts +122 -4
- package/src/__tests__/subagent/orphan-reaper.test.ts +148 -12
- package/src/__tests__/subagent/process-lifecycle.test.ts +422 -0
- package/src/__tests__/subagent/workspace-resolver.test.ts +6 -1
- package/src/adapters/types.ts +2 -0
- package/src/brain/brain.ts +117 -9
- package/src/dream/cron-manager.ts +137 -0
- package/src/dream/dream-engine.ts +119 -0
- package/src/dream/dream-ops.ts +56 -0
- package/src/dream/dream.test.ts +182 -0
- package/src/dream/index.ts +6 -0
- package/src/dream/schema.ts +17 -0
- package/src/embeddings/openai-provider.ts +158 -0
- package/src/embeddings/pipeline.ts +126 -0
- package/src/embeddings/types.ts +67 -0
- package/src/engine/bin/soleri-engine.ts +4 -1
- package/src/engine/module-manifest.test.ts +4 -4
- package/src/engine/module-manifest.ts +20 -0
- package/src/engine/register-engine.ts +12 -0
- package/src/flows/dispatch-registry.ts +44 -1
- package/src/flows/executor.ts +93 -2
- package/src/flows/index.ts +2 -0
- package/src/flows/types.ts +39 -1
- package/src/index.ts +12 -0
- package/src/persona/defaults.test.ts +39 -1
- package/src/persona/defaults.ts +65 -0
- package/src/planning/goal-ancestry.test.ts +3 -5
- package/src/planning/planner.test.ts +2 -3
- package/src/runtime/admin-ops.test.ts +2 -2
- package/src/runtime/admin-ops.ts +17 -0
- package/src/runtime/admin-setup-ops.ts +2 -2
- package/src/runtime/embedding-ops.ts +116 -0
- package/src/runtime/facades/admin-facade.test.ts +31 -0
- package/src/runtime/facades/embedding-facade.ts +11 -0
- package/src/runtime/facades/index.ts +12 -0
- package/src/runtime/facades/orchestrate-facade.test.ts +16 -0
- package/src/runtime/facades/orchestrate-facade.ts +146 -0
- package/src/runtime/feature-flags.ts +4 -0
- package/src/runtime/orchestrate-ops.test.ts +131 -0
- package/src/runtime/orchestrate-ops.ts +158 -10
- package/src/runtime/planning-extra-ops.ts +77 -0
- package/src/runtime/preflight.ts +53 -0
- package/src/runtime/runtime.ts +41 -2
- package/src/runtime/types.ts +20 -0
- package/src/skills/__tests__/sync-skills.test.ts +132 -0
- package/src/skills/step-tracker.ts +162 -0
- package/src/skills/sync-skills.ts +54 -9
- package/src/subagent/dispatcher.ts +62 -39
- package/src/subagent/index.ts +1 -0
- package/src/subagent/orphan-reaper.test.ts +135 -0
- package/src/subagent/orphan-reaper.ts +130 -7
- package/src/subagent/types.ts +10 -0
- package/src/subagent/workspace-resolver.ts +3 -1
- package/src/vault/vault-entries.ts +112 -0
- package/src/vault/vault-manager.ts +1 -0
- package/src/vault/vault-scaling.test.ts +3 -2
- package/src/vault/vault-schema.ts +15 -0
- package/src/vault/vault.ts +1 -0
- package/vitest.config.ts +2 -1
- package/dist/brain/strength-scorer.d.ts +0 -31
- package/dist/brain/strength-scorer.d.ts.map +0 -1
- package/dist/brain/strength-scorer.js +0 -264
- package/dist/brain/strength-scorer.js.map +0 -1
- package/dist/engine/index.d.ts +0 -21
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -18
- package/dist/engine/index.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -2
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js +0 -2
- package/dist/hooks/index.js.map +0 -1
- package/dist/persona/index.d.ts +0 -5
- package/dist/persona/index.d.ts.map +0 -1
- package/dist/persona/index.js +0 -4
- package/dist/persona/index.js.map +0 -1
- package/dist/vault/vault-interfaces.d.ts +0 -153
- package/dist/vault/vault-interfaces.d.ts.map +0 -1
- package/dist/vault/vault-interfaces.js +0 -2
- 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/
|
|
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
|
|
36
|
-
expect(ENGINE_MODULE_MANIFEST).toHaveLength(
|
|
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('
|
|
172
|
-
expect(ENGINE_MODULE_MANIFEST[ENGINE_MODULE_MANIFEST.length - 1].suffix).toBe('
|
|
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(
|
|
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 };
|
package/src/flows/executor.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* evaluating gates and handling branching.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
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':
|
package/src/flows/index.ts
CHANGED
|
@@ -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';
|
package/src/flows/types.ts
CHANGED
|
@@ -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:
|
|
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 {
|
|
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');
|