@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.
- package/dist/brain/brain.d.ts +7 -0
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +56 -9
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/types.d.ts +2 -2
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/cognee/client.d.ts +3 -0
- package/dist/cognee/client.d.ts.map +1 -1
- package/dist/cognee/client.js +17 -0
- package/dist/cognee/client.js.map +1 -1
- package/dist/cognee/sync-manager.d.ts +94 -0
- package/dist/cognee/sync-manager.d.ts.map +1 -0
- package/dist/cognee/sync-manager.js +293 -0
- package/dist/cognee/sync-manager.js.map +1 -0
- package/dist/curator/curator.d.ts +8 -1
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +64 -1
- package/dist/curator/curator.js.map +1 -1
- package/dist/errors/classify.d.ts +13 -0
- package/dist/errors/classify.d.ts.map +1 -0
- package/dist/errors/classify.js +97 -0
- package/dist/errors/classify.js.map +1 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/retry.d.ts +40 -0
- package/dist/errors/retry.d.ts.map +1 -0
- package/dist/errors/retry.js +97 -0
- package/dist/errors/retry.js.map +1 -0
- package/dist/errors/types.d.ts +48 -0
- package/dist/errors/types.d.ts.map +1 -0
- package/dist/errors/types.js +59 -0
- package/dist/errors/types.js.map +1 -0
- package/dist/index.d.ts +25 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -3
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts +14 -0
- package/dist/intake/content-classifier.d.ts.map +1 -0
- package/dist/intake/content-classifier.js +125 -0
- package/dist/intake/content-classifier.js.map +1 -0
- package/dist/intake/dedup-gate.d.ts +17 -0
- package/dist/intake/dedup-gate.d.ts.map +1 -0
- package/dist/intake/dedup-gate.js +66 -0
- package/dist/intake/dedup-gate.js.map +1 -0
- package/dist/intake/intake-pipeline.d.ts +63 -0
- package/dist/intake/intake-pipeline.d.ts.map +1 -0
- package/dist/intake/intake-pipeline.js +373 -0
- package/dist/intake/intake-pipeline.js.map +1 -0
- package/dist/intake/types.d.ts +65 -0
- package/dist/intake/types.d.ts.map +1 -0
- package/dist/intake/types.js +3 -0
- package/dist/intake/types.js.map +1 -0
- package/dist/intelligence/loader.js +1 -1
- package/dist/intelligence/loader.js.map +1 -1
- package/dist/intelligence/types.d.ts +3 -1
- package/dist/intelligence/types.d.ts.map +1 -1
- package/dist/loop/loop-manager.d.ts +58 -7
- package/dist/loop/loop-manager.d.ts.map +1 -1
- package/dist/loop/loop-manager.js +280 -6
- package/dist/loop/loop-manager.js.map +1 -1
- package/dist/loop/types.d.ts +69 -1
- package/dist/loop/types.d.ts.map +1 -1
- package/dist/loop/types.js +4 -1
- package/dist/loop/types.js.map +1 -1
- package/dist/persistence/index.d.ts +3 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +2 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/sqlite-provider.d.ts +25 -0
- package/dist/persistence/sqlite-provider.d.ts.map +1 -0
- package/dist/persistence/sqlite-provider.js +59 -0
- package/dist/persistence/sqlite-provider.js.map +1 -0
- package/dist/persistence/types.d.ts +36 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +8 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/planning/gap-analysis.d.ts +47 -4
- package/dist/planning/gap-analysis.d.ts.map +1 -1
- package/dist/planning/gap-analysis.js +190 -13
- package/dist/planning/gap-analysis.js.map +1 -1
- package/dist/planning/gap-types.d.ts +1 -1
- package/dist/planning/gap-types.d.ts.map +1 -1
- package/dist/planning/gap-types.js.map +1 -1
- package/dist/planning/planner.d.ts +277 -9
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +611 -46
- package/dist/planning/planner.js.map +1 -1
- package/dist/playbooks/generic/brainstorming.d.ts +9 -0
- package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
- package/dist/playbooks/generic/brainstorming.js +105 -0
- package/dist/playbooks/generic/brainstorming.js.map +1 -0
- package/dist/playbooks/generic/code-review.d.ts +11 -0
- package/dist/playbooks/generic/code-review.d.ts.map +1 -0
- package/dist/playbooks/generic/code-review.js +176 -0
- package/dist/playbooks/generic/code-review.js.map +1 -0
- package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
- package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
- package/dist/playbooks/generic/subagent-execution.js +68 -0
- package/dist/playbooks/generic/subagent-execution.js.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.js +87 -0
- package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
- package/dist/playbooks/generic/tdd.d.ts +9 -0
- package/dist/playbooks/generic/tdd.d.ts.map +1 -0
- package/dist/playbooks/generic/tdd.js +70 -0
- package/dist/playbooks/generic/tdd.js.map +1 -0
- package/dist/playbooks/generic/verification.d.ts +9 -0
- package/dist/playbooks/generic/verification.d.ts.map +1 -0
- package/dist/playbooks/generic/verification.js +74 -0
- package/dist/playbooks/generic/verification.js.map +1 -0
- package/dist/playbooks/index.d.ts +4 -0
- package/dist/playbooks/index.d.ts.map +1 -0
- package/dist/playbooks/index.js +5 -0
- package/dist/playbooks/index.js.map +1 -0
- package/dist/playbooks/playbook-registry.d.ts +42 -0
- package/dist/playbooks/playbook-registry.d.ts.map +1 -0
- package/dist/playbooks/playbook-registry.js +227 -0
- package/dist/playbooks/playbook-registry.js.map +1 -0
- package/dist/playbooks/playbook-seeder.d.ts +47 -0
- package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
- package/dist/playbooks/playbook-seeder.js +104 -0
- package/dist/playbooks/playbook-seeder.js.map +1 -0
- package/dist/playbooks/playbook-types.d.ts +132 -0
- package/dist/playbooks/playbook-types.d.ts.map +1 -0
- package/dist/playbooks/playbook-types.js +12 -0
- package/dist/playbooks/playbook-types.js.map +1 -0
- package/dist/project/project-registry.d.ts.map +1 -1
- package/dist/project/project-registry.js +9 -11
- package/dist/project/project-registry.js.map +1 -1
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +3 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/parser.d.ts +17 -0
- package/dist/prompts/parser.d.ts.map +1 -0
- package/dist/prompts/parser.js +47 -0
- package/dist/prompts/parser.js.map +1 -0
- package/dist/prompts/template-manager.d.ts +25 -0
- package/dist/prompts/template-manager.d.ts.map +1 -0
- package/dist/prompts/template-manager.js +71 -0
- package/dist/prompts/template-manager.js.map +1 -0
- package/dist/prompts/types.d.ts +26 -0
- package/dist/prompts/types.d.ts.map +1 -0
- package/dist/prompts/types.js +5 -0
- package/dist/prompts/types.js.map +1 -0
- package/dist/runtime/admin-extra-ops.d.ts +5 -3
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
- package/dist/runtime/admin-extra-ops.js +322 -11
- package/dist/runtime/admin-extra-ops.js.map +1 -1
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +10 -3
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/capture-ops.d.ts.map +1 -1
- package/dist/runtime/capture-ops.js +20 -2
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/cognee-sync-ops.d.ts +12 -0
- package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
- package/dist/runtime/cognee-sync-ops.js +55 -0
- package/dist/runtime/cognee-sync-ops.js.map +1 -0
- package/dist/runtime/core-ops.d.ts +8 -6
- package/dist/runtime/core-ops.d.ts.map +1 -1
- package/dist/runtime/core-ops.js +226 -9
- package/dist/runtime/core-ops.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts +2 -2
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
- package/dist/runtime/curator-extra-ops.js +15 -3
- package/dist/runtime/curator-extra-ops.js.map +1 -1
- package/dist/runtime/domain-ops.js +2 -2
- package/dist/runtime/domain-ops.js.map +1 -1
- package/dist/runtime/grading-ops.d.ts.map +1 -1
- package/dist/runtime/grading-ops.js.map +1 -1
- package/dist/runtime/intake-ops.d.ts +14 -0
- package/dist/runtime/intake-ops.d.ts.map +1 -0
- package/dist/runtime/intake-ops.js +110 -0
- package/dist/runtime/intake-ops.js.map +1 -0
- package/dist/runtime/loop-ops.d.ts +5 -4
- package/dist/runtime/loop-ops.d.ts.map +1 -1
- package/dist/runtime/loop-ops.js +84 -12
- package/dist/runtime/loop-ops.js.map +1 -1
- package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -1
- package/dist/runtime/memory-cross-project-ops.js.map +1 -1
- package/dist/runtime/memory-extra-ops.js +5 -5
- package/dist/runtime/memory-extra-ops.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +8 -2
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/planning-extra-ops.d.ts +13 -5
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +381 -18
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/playbook-ops.d.ts +14 -0
- package/dist/runtime/playbook-ops.d.ts.map +1 -0
- package/dist/runtime/playbook-ops.js +141 -0
- package/dist/runtime/playbook-ops.js.map +1 -0
- package/dist/runtime/project-ops.d.ts.map +1 -1
- package/dist/runtime/project-ops.js +7 -2
- package/dist/runtime/project-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +27 -8
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +8 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +3 -2
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.js +345 -4
- package/dist/runtime/vault-extra-ops.js.map +1 -1
- package/dist/vault/playbook.d.ts +34 -0
- package/dist/vault/playbook.d.ts.map +1 -0
- package/dist/vault/playbook.js +60 -0
- package/dist/vault/playbook.js.map +1 -0
- package/dist/vault/vault.d.ts +31 -32
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +201 -181
- package/dist/vault/vault.js.map +1 -1
- package/package.json +7 -3
- package/src/__tests__/admin-extra-ops.test.ts +62 -15
- package/src/__tests__/admin-ops.test.ts +2 -2
- package/src/__tests__/brain.test.ts +3 -3
- package/src/__tests__/cognee-integration.test.ts +80 -0
- package/src/__tests__/cognee-sync-manager.test.ts +103 -0
- package/src/__tests__/core-ops.test.ts +30 -4
- package/src/__tests__/curator-extra-ops.test.ts +24 -2
- package/src/__tests__/errors.test.ts +388 -0
- package/src/__tests__/grading-ops.test.ts +28 -7
- package/src/__tests__/intake-pipeline.test.ts +162 -0
- package/src/__tests__/loop-ops.test.ts +74 -3
- package/src/__tests__/memory-cross-project-ops.test.ts +3 -1
- package/src/__tests__/orchestrate-ops.test.ts +8 -3
- package/src/__tests__/persistence.test.ts +225 -0
- package/src/__tests__/planner.test.ts +99 -21
- package/src/__tests__/planning-extra-ops.test.ts +168 -10
- package/src/__tests__/playbook-registry.test.ts +326 -0
- package/src/__tests__/playbook-seeder.test.ts +163 -0
- package/src/__tests__/playbook.test.ts +389 -0
- package/src/__tests__/project-ops.test.ts +18 -4
- package/src/__tests__/template-manager.test.ts +222 -0
- package/src/__tests__/vault-extra-ops.test.ts +82 -7
- package/src/brain/brain.ts +71 -9
- package/src/brain/types.ts +2 -2
- package/src/cognee/client.ts +18 -0
- package/src/cognee/sync-manager.ts +389 -0
- package/src/curator/curator.ts +88 -7
- package/src/errors/classify.ts +102 -0
- package/src/errors/index.ts +5 -0
- package/src/errors/retry.ts +132 -0
- package/src/errors/types.ts +81 -0
- package/src/index.ts +114 -3
- package/src/intake/content-classifier.ts +146 -0
- package/src/intake/dedup-gate.ts +92 -0
- package/src/intake/intake-pipeline.ts +503 -0
- package/src/intake/types.ts +69 -0
- package/src/intelligence/loader.ts +1 -1
- package/src/intelligence/types.ts +3 -1
- package/src/loop/loop-manager.ts +325 -7
- package/src/loop/types.ts +72 -1
- package/src/persistence/index.ts +7 -0
- package/src/persistence/sqlite-provider.ts +62 -0
- package/src/persistence/types.ts +44 -0
- package/src/planning/gap-analysis.ts +286 -17
- package/src/planning/gap-types.ts +4 -1
- package/src/planning/planner.ts +828 -55
- package/src/playbooks/generic/brainstorming.ts +110 -0
- package/src/playbooks/generic/code-review.ts +181 -0
- package/src/playbooks/generic/subagent-execution.ts +74 -0
- package/src/playbooks/generic/systematic-debugging.ts +92 -0
- package/src/playbooks/generic/tdd.ts +75 -0
- package/src/playbooks/generic/verification.ts +79 -0
- package/src/playbooks/index.ts +27 -0
- package/src/playbooks/playbook-registry.ts +284 -0
- package/src/playbooks/playbook-seeder.ts +119 -0
- package/src/playbooks/playbook-types.ts +162 -0
- package/src/project/project-registry.ts +29 -17
- package/src/prompts/index.ts +3 -0
- package/src/prompts/parser.ts +59 -0
- package/src/prompts/template-manager.ts +77 -0
- package/src/prompts/types.ts +28 -0
- package/src/runtime/admin-extra-ops.ts +358 -13
- package/src/runtime/admin-ops.ts +17 -6
- package/src/runtime/capture-ops.ts +25 -6
- package/src/runtime/cognee-sync-ops.ts +63 -0
- package/src/runtime/core-ops.ts +258 -8
- package/src/runtime/curator-extra-ops.ts +17 -3
- package/src/runtime/domain-ops.ts +2 -2
- package/src/runtime/grading-ops.ts +11 -2
- package/src/runtime/intake-ops.ts +126 -0
- package/src/runtime/loop-ops.ts +96 -13
- package/src/runtime/memory-cross-project-ops.ts +1 -2
- package/src/runtime/memory-extra-ops.ts +5 -5
- package/src/runtime/orchestrate-ops.ts +8 -2
- package/src/runtime/planning-extra-ops.ts +414 -23
- package/src/runtime/playbook-ops.ts +169 -0
- package/src/runtime/project-ops.ts +9 -3
- package/src/runtime/runtime.ts +35 -9
- package/src/runtime/types.ts +8 -0
- package/src/runtime/vault-extra-ops.ts +385 -4
- package/src/vault/playbook.ts +87 -0
- package/src/vault/vault.ts +301 -235
|
@@ -91,7 +91,9 @@ export function createProjectOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
91
91
|
auth: 'write',
|
|
92
92
|
schema: z.object({
|
|
93
93
|
projectId: z.string().describe('Project ID to add the rule to'),
|
|
94
|
-
category: z
|
|
94
|
+
category: z
|
|
95
|
+
.enum(['behavior', 'preference', 'restriction', 'convention'])
|
|
96
|
+
.describe('Rule category'),
|
|
95
97
|
text: z.string().describe('Rule text'),
|
|
96
98
|
priority: z.number().default(0).describe('Priority (higher = more important)'),
|
|
97
99
|
}),
|
|
@@ -143,7 +145,10 @@ export function createProjectOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
143
145
|
schema: z.object({
|
|
144
146
|
sourceId: z.string().describe('Source project ID'),
|
|
145
147
|
targetId: z.string().describe('Target project ID'),
|
|
146
|
-
linkType: z
|
|
148
|
+
linkType: z
|
|
149
|
+
.enum(['related', 'parent', 'child', 'fork'])
|
|
150
|
+
.optional()
|
|
151
|
+
.describe('Specific link type to remove'),
|
|
147
152
|
}),
|
|
148
153
|
handler: async (params) => {
|
|
149
154
|
const count = projectRegistry.unlink(
|
|
@@ -168,7 +173,8 @@ export function createProjectOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
168
173
|
},
|
|
169
174
|
{
|
|
170
175
|
name: 'project_linked_projects',
|
|
171
|
-
description:
|
|
176
|
+
description:
|
|
177
|
+
'Get linked projects with full details — project info, link type, and direction.',
|
|
172
178
|
auth: 'read',
|
|
173
179
|
schema: z.object({
|
|
174
180
|
projectId: z.string().describe('Project ID'),
|
package/src/runtime/runtime.ts
CHANGED
|
@@ -22,8 +22,12 @@ import { IntentRouter } from '../control/intent-router.js';
|
|
|
22
22
|
import { KeyPool, loadKeyPoolConfig } from '../llm/key-pool.js';
|
|
23
23
|
import { loadIntelligenceData } from '../intelligence/loader.js';
|
|
24
24
|
import { LLMClient } from '../llm/llm-client.js';
|
|
25
|
+
import { CogneeSyncManager } from '../cognee/sync-manager.js';
|
|
26
|
+
import { IntakePipeline } from '../intake/intake-pipeline.js';
|
|
25
27
|
import { Telemetry } from '../telemetry/telemetry.js';
|
|
26
28
|
import { ProjectRegistry } from '../project/project-registry.js';
|
|
29
|
+
import { TemplateManager } from '../prompts/template-manager.js';
|
|
30
|
+
import { existsSync } from 'node:fs';
|
|
27
31
|
import { createLogger } from '../logging/logger.js';
|
|
28
32
|
import type { AgentRuntimeConfig, AgentRuntime } from './types.js';
|
|
29
33
|
|
|
@@ -58,25 +62,25 @@ export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
|
|
|
58
62
|
// Planner — multi-step task tracking
|
|
59
63
|
const planner = new Planner(plansPath);
|
|
60
64
|
|
|
65
|
+
// Cognee — vector search client (graceful degradation if Cognee is down)
|
|
66
|
+
const cogneePartial: Partial<import('../cognee/types.js').CogneeConfig> = { dataset: agentId };
|
|
67
|
+
if (process.env.COGNEE_BASE_URL) cogneePartial.baseUrl = process.env.COGNEE_BASE_URL;
|
|
68
|
+
if (process.env.COGNEE_API_TOKEN) cogneePartial.apiToken = process.env.COGNEE_API_TOKEN;
|
|
69
|
+
if (process.env.COGNEE_DATASET) cogneePartial.dataset = process.env.COGNEE_DATASET;
|
|
70
|
+
const cognee = new CogneeClient(cogneePartial);
|
|
71
|
+
|
|
61
72
|
// Brain — intelligence layer (TF-IDF scoring, auto-tagging, dedup)
|
|
62
|
-
const brain = new Brain(vault);
|
|
73
|
+
const brain = new Brain(vault, cognee);
|
|
63
74
|
|
|
64
75
|
// Brain Intelligence — pattern strengths, session knowledge, intelligence pipeline
|
|
65
76
|
const brainIntelligence = new BrainIntelligence(vault, brain);
|
|
66
77
|
|
|
67
78
|
// Curator — vault self-maintenance (dedup, contradictions, grooming, health)
|
|
68
|
-
const curator = new Curator(vault);
|
|
79
|
+
const curator = new Curator(vault, cognee);
|
|
69
80
|
|
|
70
81
|
// Governance — policy engine + proposal tracker for gated knowledge capture
|
|
71
82
|
const governance = new Governance(vault);
|
|
72
83
|
|
|
73
|
-
// Cognee — vector search client (graceful degradation if Cognee is down)
|
|
74
|
-
const cogneePartial: Partial<import('../cognee/types.js').CogneeConfig> = { dataset: agentId };
|
|
75
|
-
if (process.env.COGNEE_BASE_URL) cogneePartial.baseUrl = process.env.COGNEE_BASE_URL;
|
|
76
|
-
if (process.env.COGNEE_API_TOKEN) cogneePartial.apiToken = process.env.COGNEE_API_TOKEN;
|
|
77
|
-
if (process.env.COGNEE_DATASET) cogneePartial.dataset = process.env.COGNEE_DATASET;
|
|
78
|
-
const cognee = new CogneeClient(cogneePartial);
|
|
79
|
-
|
|
80
84
|
// Loop Manager — iterative validation loop tracking (in-memory, session-scoped)
|
|
81
85
|
const loop = new LoopManager();
|
|
82
86
|
|
|
@@ -92,12 +96,30 @@ export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
|
|
|
92
96
|
// Project Registry — multi-project tracking with rules and links
|
|
93
97
|
const projectRegistry = new ProjectRegistry(vault.getDb());
|
|
94
98
|
|
|
99
|
+
// Template Manager — prompt templates with variable substitution
|
|
100
|
+
const templatesDir = config.templatesDir ?? join(agentHome, 'templates');
|
|
101
|
+
const templateManager = new TemplateManager(templatesDir);
|
|
102
|
+
if (existsSync(templatesDir)) {
|
|
103
|
+
templateManager.load();
|
|
104
|
+
}
|
|
105
|
+
|
|
95
106
|
// LLM key pools and client
|
|
96
107
|
const keyPoolFiles = loadKeyPoolConfig(agentId);
|
|
97
108
|
const openaiKeyPool = new KeyPool(keyPoolFiles.openai);
|
|
98
109
|
const anthropicKeyPool = new KeyPool(keyPoolFiles.anthropic);
|
|
99
110
|
const llmClient = new LLMClient(openaiKeyPool, anthropicKeyPool, agentId);
|
|
100
111
|
|
|
112
|
+
// Cognee Sync Manager — queue-based dirty tracking with offline resilience
|
|
113
|
+
const syncManager = new CogneeSyncManager(
|
|
114
|
+
vault.getProvider(),
|
|
115
|
+
cognee,
|
|
116
|
+
cogneePartial.dataset ?? agentId,
|
|
117
|
+
);
|
|
118
|
+
vault.setSyncManager(syncManager);
|
|
119
|
+
|
|
120
|
+
// Intake Pipeline — PDF/book ingestion with LLM classification
|
|
121
|
+
const intakePipeline = new IntakePipeline(vault.getProvider(), vault, llmClient);
|
|
122
|
+
|
|
101
123
|
return {
|
|
102
124
|
config,
|
|
103
125
|
logger,
|
|
@@ -115,8 +137,12 @@ export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
|
|
|
115
137
|
llmClient,
|
|
116
138
|
telemetry,
|
|
117
139
|
projectRegistry,
|
|
140
|
+
templateManager,
|
|
141
|
+
syncManager,
|
|
142
|
+
intakePipeline,
|
|
118
143
|
createdAt: Date.now(),
|
|
119
144
|
close: () => {
|
|
145
|
+
syncManager.close();
|
|
120
146
|
cognee.resetPendingCognify();
|
|
121
147
|
vault.close();
|
|
122
148
|
},
|
package/src/runtime/types.ts
CHANGED
|
@@ -12,6 +12,9 @@ import type { IntentRouter } from '../control/intent-router.js';
|
|
|
12
12
|
import type { LoopManager } from '../loop/loop-manager.js';
|
|
13
13
|
import type { Telemetry } from '../telemetry/telemetry.js';
|
|
14
14
|
import type { ProjectRegistry } from '../project/project-registry.js';
|
|
15
|
+
import type { TemplateManager } from '../prompts/template-manager.js';
|
|
16
|
+
import type { CogneeSyncManager } from '../cognee/sync-manager.js';
|
|
17
|
+
import type { IntakePipeline } from '../intake/intake-pipeline.js';
|
|
15
18
|
import type { Logger } from '../logging/logger.js';
|
|
16
19
|
import type { LogLevel } from '../logging/types.js';
|
|
17
20
|
|
|
@@ -28,6 +31,8 @@ export interface AgentRuntimeConfig {
|
|
|
28
31
|
plansPath?: string;
|
|
29
32
|
/** Intelligence data directory to seed vault from. Optional. */
|
|
30
33
|
dataDir?: string;
|
|
34
|
+
/** Path to prompt templates directory. Default: ~/.{agentId}/templates */
|
|
35
|
+
templatesDir?: string;
|
|
31
36
|
/** Minimum log level. Default: 'info' (or SOLERI_LOG_LEVEL env var). */
|
|
32
37
|
logLevel?: LogLevel;
|
|
33
38
|
}
|
|
@@ -53,6 +58,9 @@ export interface AgentRuntime {
|
|
|
53
58
|
llmClient: LLMClient;
|
|
54
59
|
telemetry: Telemetry;
|
|
55
60
|
projectRegistry: ProjectRegistry;
|
|
61
|
+
templateManager: TemplateManager;
|
|
62
|
+
syncManager: CogneeSyncManager;
|
|
63
|
+
intakePipeline: IntakePipeline;
|
|
56
64
|
/** Timestamp (ms since epoch) when this runtime was created. */
|
|
57
65
|
createdAt: number;
|
|
58
66
|
/** Close the vault database connection. Call on shutdown. */
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Extra vault operations —
|
|
2
|
+
* Extra vault operations — 20 ops that extend the 4 base vault ops in core-ops.ts.
|
|
3
3
|
*
|
|
4
|
-
* Groups: single-entry CRUD (3), bulk (2), discovery (3), import/export (3),
|
|
4
|
+
* Groups: single-entry CRUD (3), bulk (2), discovery (3), import/export (3),
|
|
5
|
+
* analytics (1), seed canonical (1), knowledge lifecycle (4), temporal (3).
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
import { z } from 'zod';
|
|
9
|
+
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
10
|
+
import { join, basename } from 'node:path';
|
|
8
11
|
import type { OpDefinition } from '../facades/types.js';
|
|
9
12
|
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
10
13
|
import type { AgentRuntime } from './types.js';
|
|
11
14
|
|
|
12
15
|
const entrySchema = z.object({
|
|
13
16
|
id: z.string(),
|
|
14
|
-
type: z.enum(['pattern', 'anti-pattern', 'rule']),
|
|
17
|
+
type: z.enum(['pattern', 'anti-pattern', 'rule', 'playbook']),
|
|
15
18
|
domain: z.string(),
|
|
16
19
|
title: z.string(),
|
|
17
20
|
severity: z.enum(['critical', 'warning', 'suggestion']),
|
|
@@ -56,7 +59,7 @@ export function createVaultExtraOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
56
59
|
tags: z.array(z.string()).optional(),
|
|
57
60
|
appliesTo: z.array(z.string()).optional(),
|
|
58
61
|
severity: z.enum(['critical', 'warning', 'suggestion']).optional(),
|
|
59
|
-
type: z.enum(['pattern', 'anti-pattern', 'rule']).optional(),
|
|
62
|
+
type: z.enum(['pattern', 'anti-pattern', 'rule', 'playbook']).optional(),
|
|
60
63
|
domain: z.string().optional(),
|
|
61
64
|
}),
|
|
62
65
|
handler: async (params) => {
|
|
@@ -221,5 +224,383 @@ export function createVaultExtraOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
221
224
|
return vault.getAgeReport();
|
|
222
225
|
},
|
|
223
226
|
},
|
|
227
|
+
|
|
228
|
+
// ─── Seed Canonical (#153) ───────────────────────────────────
|
|
229
|
+
{
|
|
230
|
+
name: 'vault_seed_canonical',
|
|
231
|
+
description:
|
|
232
|
+
'Seed vault knowledge from structured markdown files with YAML frontmatter. ' +
|
|
233
|
+
'Reads .md files from a directory, parses them into IntelligenceEntry objects, and upserts. Idempotent.',
|
|
234
|
+
auth: 'write',
|
|
235
|
+
schema: z.object({
|
|
236
|
+
directory: z
|
|
237
|
+
.string()
|
|
238
|
+
.describe('Path to directory containing .md files with YAML frontmatter'),
|
|
239
|
+
domain: z
|
|
240
|
+
.string()
|
|
241
|
+
.optional()
|
|
242
|
+
.describe('Override domain for all entries (default: from frontmatter or filename)'),
|
|
243
|
+
}),
|
|
244
|
+
handler: async (params) => {
|
|
245
|
+
try {
|
|
246
|
+
const dir = params.directory as string;
|
|
247
|
+
const domainOverride = params.domain as string | undefined;
|
|
248
|
+
|
|
249
|
+
if (!existsSync(dir)) {
|
|
250
|
+
return { error: `Directory not found: ${dir}` };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const files = readdirSync(dir).filter((f) => f.endsWith('.md'));
|
|
254
|
+
const entries: IntelligenceEntry[] = [];
|
|
255
|
+
const errors: Array<{ file: string; error: string }> = [];
|
|
256
|
+
|
|
257
|
+
for (const file of files) {
|
|
258
|
+
try {
|
|
259
|
+
const content = readFileSync(join(dir, file), 'utf-8');
|
|
260
|
+
const entry = parseMarkdownEntry(content, file, domainOverride);
|
|
261
|
+
if (entry) entries.push(entry);
|
|
262
|
+
} catch (err) {
|
|
263
|
+
errors.push({ file, error: (err as Error).message });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const seeded = entries.length > 0 ? vault.seed(entries) : 0;
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
seeded,
|
|
271
|
+
filesProcessed: files.length,
|
|
272
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
273
|
+
total: vault.stats().totalEntries,
|
|
274
|
+
};
|
|
275
|
+
} catch (err) {
|
|
276
|
+
return { error: (err as Error).message };
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
// ─── Knowledge Audit (#155) ──────────────────────────────────
|
|
282
|
+
{
|
|
283
|
+
name: 'knowledge_audit',
|
|
284
|
+
description:
|
|
285
|
+
'Audit vault quality — coverage gaps, stale entries, tag health, and recommendations.',
|
|
286
|
+
auth: 'read',
|
|
287
|
+
handler: async () => {
|
|
288
|
+
try {
|
|
289
|
+
const stats = vault.stats();
|
|
290
|
+
const tags = vault.getTags();
|
|
291
|
+
const domains = vault.getDomains();
|
|
292
|
+
const ageReport = vault.getAgeReport();
|
|
293
|
+
|
|
294
|
+
// Check coverage
|
|
295
|
+
const entriesWithoutTags = tags.length === 0 ? stats.totalEntries : 0;
|
|
296
|
+
const singletonTags = tags.filter((t) => t.count === 1).length;
|
|
297
|
+
|
|
298
|
+
// Staleness: entries older than 90 days
|
|
299
|
+
const staleCount = ageReport.buckets.find((b) => b.label === 'older')?.count ?? 0;
|
|
300
|
+
|
|
301
|
+
const recommendations: string[] = [];
|
|
302
|
+
if (stats.totalEntries < 10)
|
|
303
|
+
recommendations.push('Vault has few entries — capture more knowledge');
|
|
304
|
+
if (singletonTags > tags.length * 0.5)
|
|
305
|
+
recommendations.push('Many singleton tags — consolidate tagging');
|
|
306
|
+
if (staleCount > stats.totalEntries * 0.3)
|
|
307
|
+
recommendations.push('>30% entries are stale — review and update');
|
|
308
|
+
if (domains.length === 1)
|
|
309
|
+
recommendations.push('Only one domain — consider categorizing by domain');
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
totalEntries: stats.totalEntries,
|
|
313
|
+
domainCount: domains.length,
|
|
314
|
+
tagCount: tags.length,
|
|
315
|
+
singletonTags,
|
|
316
|
+
staleEntries: staleCount,
|
|
317
|
+
entriesWithoutTags,
|
|
318
|
+
recommendations,
|
|
319
|
+
};
|
|
320
|
+
} catch (err) {
|
|
321
|
+
return { error: (err as Error).message };
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
// ─── Knowledge Health (#155) ─────────────────────────────────
|
|
327
|
+
{
|
|
328
|
+
name: 'knowledge_health',
|
|
329
|
+
description:
|
|
330
|
+
'Knowledge base health metrics — entry counts, freshness, staleness, contradiction signals.',
|
|
331
|
+
auth: 'read',
|
|
332
|
+
handler: async () => {
|
|
333
|
+
try {
|
|
334
|
+
const stats = vault.stats();
|
|
335
|
+
const ageReport = vault.getAgeReport();
|
|
336
|
+
const domains = vault.getDomains();
|
|
337
|
+
const tags = vault.getTags();
|
|
338
|
+
|
|
339
|
+
// Detect potential contradictions: entries with same tags but different types (pattern vs anti-pattern)
|
|
340
|
+
const db = vault.getDb();
|
|
341
|
+
const contradictionSignals = db
|
|
342
|
+
.prepare(
|
|
343
|
+
`SELECT t.value as tag, COUNT(DISTINCT e.type) as type_count
|
|
344
|
+
FROM entries e, json_each(e.tags) t
|
|
345
|
+
GROUP BY t.value HAVING type_count > 1 LIMIT 10`,
|
|
346
|
+
)
|
|
347
|
+
.all() as Array<{ tag: string; type_count: number }>;
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
totalEntries: stats.totalEntries,
|
|
351
|
+
freshEntries:
|
|
352
|
+
(ageReport.buckets.find((b) => b.label === 'today')?.count ?? 0) +
|
|
353
|
+
(ageReport.buckets.find((b) => b.label === 'this_week')?.count ?? 0),
|
|
354
|
+
staleEntries: ageReport.buckets.find((b) => b.label === 'older')?.count ?? 0,
|
|
355
|
+
domainCount: domains.length,
|
|
356
|
+
tagCount: tags.length,
|
|
357
|
+
contradictionSignals: contradictionSignals.length,
|
|
358
|
+
contradictionTags: contradictionSignals.map((c) => c.tag),
|
|
359
|
+
oldestTimestamp: ageReport.oldestTimestamp,
|
|
360
|
+
newestTimestamp: ageReport.newestTimestamp,
|
|
361
|
+
};
|
|
362
|
+
} catch (err) {
|
|
363
|
+
return { error: (err as Error).message };
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
// ─── Merge Patterns (#155) ───────────────────────────────────
|
|
369
|
+
{
|
|
370
|
+
name: 'knowledge_merge',
|
|
371
|
+
description:
|
|
372
|
+
'Merge two similar patterns into one — keeps the best metadata from both, removes the duplicate.',
|
|
373
|
+
auth: 'write',
|
|
374
|
+
schema: z.object({
|
|
375
|
+
keepId: z.string().describe('ID of the entry to keep (will receive merged data)'),
|
|
376
|
+
removeId: z.string().describe('ID of the duplicate entry to remove after merge'),
|
|
377
|
+
}),
|
|
378
|
+
handler: async (params) => {
|
|
379
|
+
try {
|
|
380
|
+
const keep = vault.get(params.keepId as string);
|
|
381
|
+
const remove = vault.get(params.removeId as string);
|
|
382
|
+
if (!keep) return { error: `Entry not found: ${params.keepId}` };
|
|
383
|
+
if (!remove) return { error: `Entry not found: ${params.removeId}` };
|
|
384
|
+
|
|
385
|
+
// Merge tags (deduplicated union)
|
|
386
|
+
const mergedTags = [...new Set([...(keep.tags ?? []), ...(remove.tags ?? [])])];
|
|
387
|
+
|
|
388
|
+
// Merge fields — prefer non-empty from either side
|
|
389
|
+
const updates: Partial<IntelligenceEntry> = {
|
|
390
|
+
tags: mergedTags,
|
|
391
|
+
description: keep.description || remove.description,
|
|
392
|
+
context: keep.context || remove.context,
|
|
393
|
+
example: keep.example || remove.example,
|
|
394
|
+
counterExample: keep.counterExample || remove.counterExample,
|
|
395
|
+
why: keep.why || remove.why,
|
|
396
|
+
appliesTo: keep.appliesTo?.length ? keep.appliesTo : remove.appliesTo,
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
vault.update(keep.id, updates);
|
|
400
|
+
vault.remove(remove.id);
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
merged: true,
|
|
404
|
+
keptId: keep.id,
|
|
405
|
+
removedId: remove.id,
|
|
406
|
+
mergedTags,
|
|
407
|
+
};
|
|
408
|
+
} catch (err) {
|
|
409
|
+
return { error: (err as Error).message };
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
// ─── Knowledge Reorganize (#155) ─────────────────────────────
|
|
415
|
+
{
|
|
416
|
+
name: 'knowledge_reorganize',
|
|
417
|
+
description:
|
|
418
|
+
'Re-categorize vault entries — reassign domains, clean up tags, deduplicate. Dry-run by default.',
|
|
419
|
+
auth: 'write',
|
|
420
|
+
schema: z.object({
|
|
421
|
+
dryRun: z
|
|
422
|
+
.boolean()
|
|
423
|
+
.optional()
|
|
424
|
+
.describe('If true, only report what would change (default true)'),
|
|
425
|
+
retagRules: z
|
|
426
|
+
.array(
|
|
427
|
+
z.object({
|
|
428
|
+
from: z.string().describe('Tag to rename/remove'),
|
|
429
|
+
to: z.string().optional().describe('New tag (omit to remove the tag)'),
|
|
430
|
+
}),
|
|
431
|
+
)
|
|
432
|
+
.optional()
|
|
433
|
+
.describe('Tag rename/removal rules'),
|
|
434
|
+
domainRules: z
|
|
435
|
+
.array(
|
|
436
|
+
z.object({
|
|
437
|
+
from: z.string().describe('Old domain name'),
|
|
438
|
+
to: z.string().describe('New domain name'),
|
|
439
|
+
}),
|
|
440
|
+
)
|
|
441
|
+
.optional()
|
|
442
|
+
.describe('Domain rename rules'),
|
|
443
|
+
}),
|
|
444
|
+
handler: async (params) => {
|
|
445
|
+
try {
|
|
446
|
+
const dryRun = (params.dryRun as boolean | undefined) ?? true;
|
|
447
|
+
const retagRules = (params.retagRules as Array<{ from: string; to?: string }>) ?? [];
|
|
448
|
+
const domainRules = (params.domainRules as Array<{ from: string; to: string }>) ?? [];
|
|
449
|
+
const changes: Array<{ id: string; field: string; from: string; to: string }> = [];
|
|
450
|
+
|
|
451
|
+
const allEntries = vault.list({});
|
|
452
|
+
|
|
453
|
+
for (const entry of allEntries) {
|
|
454
|
+
// Apply domain rules
|
|
455
|
+
for (const rule of domainRules) {
|
|
456
|
+
if (entry.domain === rule.from) {
|
|
457
|
+
changes.push({ id: entry.id, field: 'domain', from: rule.from, to: rule.to });
|
|
458
|
+
if (!dryRun) vault.update(entry.id, { domain: rule.to });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Apply retag rules
|
|
463
|
+
if (entry.tags) {
|
|
464
|
+
let tagsChanged = false;
|
|
465
|
+
const newTags = [...entry.tags];
|
|
466
|
+
for (const rule of retagRules) {
|
|
467
|
+
const idx = newTags.indexOf(rule.from);
|
|
468
|
+
if (idx !== -1) {
|
|
469
|
+
if (rule.to) {
|
|
470
|
+
changes.push({ id: entry.id, field: 'tag', from: rule.from, to: rule.to });
|
|
471
|
+
newTags[idx] = rule.to;
|
|
472
|
+
} else {
|
|
473
|
+
changes.push({ id: entry.id, field: 'tag', from: rule.from, to: '(removed)' });
|
|
474
|
+
newTags.splice(idx, 1);
|
|
475
|
+
}
|
|
476
|
+
tagsChanged = true;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (tagsChanged && !dryRun) {
|
|
480
|
+
vault.update(entry.id, { tags: [...new Set(newTags)] });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
dryRun,
|
|
487
|
+
changesFound: changes.length,
|
|
488
|
+
changes: changes.slice(0, 100), // cap output
|
|
489
|
+
entriesScanned: allEntries.length,
|
|
490
|
+
};
|
|
491
|
+
} catch (err) {
|
|
492
|
+
return { error: (err as Error).message };
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
// ─── Temporal (#89) ──────────────────────────────────────────────
|
|
498
|
+
{
|
|
499
|
+
name: 'vault_set_temporal',
|
|
500
|
+
description:
|
|
501
|
+
'Set valid_from and/or valid_until timestamps on a vault entry for bi-temporal validity windows.',
|
|
502
|
+
auth: 'write',
|
|
503
|
+
schema: z.object({
|
|
504
|
+
id: z.string().describe('Entry ID'),
|
|
505
|
+
validFrom: z.number().optional().describe('Unix epoch — when entry becomes active'),
|
|
506
|
+
validUntil: z.number().optional().describe('Unix epoch — when entry expires'),
|
|
507
|
+
}),
|
|
508
|
+
handler: async (params) => {
|
|
509
|
+
const updated = vault.setTemporal(
|
|
510
|
+
params.id as string,
|
|
511
|
+
params.validFrom as number | undefined,
|
|
512
|
+
params.validUntil as number | undefined,
|
|
513
|
+
);
|
|
514
|
+
if (!updated) return { error: 'Entry not found or no fields to update' };
|
|
515
|
+
const entry = vault.get(params.id as string);
|
|
516
|
+
return {
|
|
517
|
+
updated: true,
|
|
518
|
+
id: params.id,
|
|
519
|
+
validFrom: entry?.validFrom ?? null,
|
|
520
|
+
validUntil: entry?.validUntil ?? null,
|
|
521
|
+
};
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: 'vault_find_expiring',
|
|
526
|
+
description:
|
|
527
|
+
'Find vault entries expiring within a given number of days. Useful for proactive knowledge maintenance.',
|
|
528
|
+
auth: 'read',
|
|
529
|
+
schema: z.object({
|
|
530
|
+
withinDays: z.number().describe('Number of days to look ahead'),
|
|
531
|
+
}),
|
|
532
|
+
handler: async (params) => {
|
|
533
|
+
const entries = vault.findExpiring(params.withinDays as number);
|
|
534
|
+
return { entries, count: entries.length };
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
name: 'vault_find_expired',
|
|
539
|
+
description: 'List expired vault entries (valid_until in the past). Useful for cleanup.',
|
|
540
|
+
auth: 'read',
|
|
541
|
+
schema: z.object({
|
|
542
|
+
limit: z.number().optional().describe('Max results (default 50)'),
|
|
543
|
+
}),
|
|
544
|
+
handler: async (params) => {
|
|
545
|
+
const entries = vault.findExpired((params.limit as number | undefined) ?? 50);
|
|
546
|
+
return { entries, count: entries.length };
|
|
547
|
+
},
|
|
548
|
+
},
|
|
224
549
|
];
|
|
225
550
|
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Parse a markdown file with YAML frontmatter into an IntelligenceEntry.
|
|
554
|
+
* Expected frontmatter fields: id, type, domain, severity, title, tags.
|
|
555
|
+
*/
|
|
556
|
+
function parseMarkdownEntry(
|
|
557
|
+
content: string,
|
|
558
|
+
filename: string,
|
|
559
|
+
domainOverride?: string,
|
|
560
|
+
): IntelligenceEntry | null {
|
|
561
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
562
|
+
if (!match) return null;
|
|
563
|
+
|
|
564
|
+
const frontmatter = match[1];
|
|
565
|
+
const body = match[2].trim();
|
|
566
|
+
|
|
567
|
+
// Simple YAML parser for flat key-value pairs
|
|
568
|
+
const meta: Record<string, string | string[]> = {};
|
|
569
|
+
for (const line of frontmatter.split('\n')) {
|
|
570
|
+
const kvMatch = line.match(/^(\w+):\s*(.+)$/);
|
|
571
|
+
if (kvMatch) {
|
|
572
|
+
const key = kvMatch[1];
|
|
573
|
+
let value = kvMatch[2].trim();
|
|
574
|
+
// Handle quoted strings
|
|
575
|
+
if (
|
|
576
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
577
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
578
|
+
) {
|
|
579
|
+
value = value.slice(1, -1);
|
|
580
|
+
}
|
|
581
|
+
// Handle arrays: [a, b, c]
|
|
582
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
583
|
+
meta[key] = value
|
|
584
|
+
.slice(1, -1)
|
|
585
|
+
.split(',')
|
|
586
|
+
.map((s) => s.trim().replace(/^["']|["']$/g, ''));
|
|
587
|
+
} else {
|
|
588
|
+
meta[key] = value;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const id = (meta.id as string) || basename(filename, '.md');
|
|
594
|
+
const tags = Array.isArray(meta.tags) ? meta.tags : meta.tags ? [meta.tags as string] : [];
|
|
595
|
+
|
|
596
|
+
return {
|
|
597
|
+
id,
|
|
598
|
+
type: (meta.type as IntelligenceEntry['type']) || 'pattern',
|
|
599
|
+
domain: domainOverride || (meta.domain as string) || 'general',
|
|
600
|
+
title: (meta.title as string) || basename(filename, '.md'),
|
|
601
|
+
severity: (meta.severity as IntelligenceEntry['severity']) || 'suggestion',
|
|
602
|
+
description: body || (meta.description as string) || '',
|
|
603
|
+
context: meta.context as string | undefined,
|
|
604
|
+
tags,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
2
|
+
|
|
3
|
+
export interface PlaybookStep {
|
|
4
|
+
order: number;
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
validation?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Playbook {
|
|
11
|
+
id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
domain: string;
|
|
14
|
+
description: string;
|
|
15
|
+
steps: PlaybookStep[];
|
|
16
|
+
tags: string[];
|
|
17
|
+
createdAt: number;
|
|
18
|
+
updatedAt: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface PlaybookValidationResult {
|
|
22
|
+
valid: boolean;
|
|
23
|
+
errors: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Validate a playbook's structure.
|
|
28
|
+
* Checks: title non-empty, steps.length > 0, each step has order/title/description,
|
|
29
|
+
* orders are sequential starting from 1.
|
|
30
|
+
*/
|
|
31
|
+
export function validatePlaybook(playbook: Playbook): PlaybookValidationResult {
|
|
32
|
+
const errors: string[] = [];
|
|
33
|
+
|
|
34
|
+
if (!playbook.title || playbook.title.trim() === '') {
|
|
35
|
+
errors.push('Playbook title must not be empty');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!playbook.steps || playbook.steps.length === 0) {
|
|
39
|
+
errors.push('Playbook must have at least one step');
|
|
40
|
+
} else {
|
|
41
|
+
for (let i = 0; i < playbook.steps.length; i++) {
|
|
42
|
+
const step = playbook.steps[i];
|
|
43
|
+
const expectedOrder = i + 1;
|
|
44
|
+
|
|
45
|
+
if (step.order !== expectedOrder) {
|
|
46
|
+
errors.push(`Step ${i + 1} has order ${step.order}, expected ${expectedOrder}`);
|
|
47
|
+
}
|
|
48
|
+
if (!step.title || step.title.trim() === '') {
|
|
49
|
+
errors.push(`Step ${expectedOrder} title must not be empty`);
|
|
50
|
+
}
|
|
51
|
+
if (!step.description || step.description.trim() === '') {
|
|
52
|
+
errors.push(`Step ${expectedOrder} description must not be empty`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { valid: errors.length === 0, errors };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parse a Playbook from a vault IntelligenceEntry.
|
|
62
|
+
* Returns null if the entry is not a playbook type or if the context is not valid
|
|
63
|
+
* JSON with a steps array.
|
|
64
|
+
*/
|
|
65
|
+
export function parsePlaybookFromEntry(entry: IntelligenceEntry): Playbook | null {
|
|
66
|
+
if (entry.type !== 'playbook') return null;
|
|
67
|
+
|
|
68
|
+
let steps: PlaybookStep[];
|
|
69
|
+
try {
|
|
70
|
+
const parsed = JSON.parse(entry.context ?? '');
|
|
71
|
+
if (!Array.isArray(parsed?.steps)) return null;
|
|
72
|
+
steps = parsed.steps;
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
id: entry.id,
|
|
79
|
+
title: entry.title,
|
|
80
|
+
domain: entry.domain,
|
|
81
|
+
description: entry.description,
|
|
82
|
+
steps,
|
|
83
|
+
tags: entry.tags,
|
|
84
|
+
createdAt: 0, // not available from entry directly
|
|
85
|
+
updatedAt: 0,
|
|
86
|
+
};
|
|
87
|
+
}
|