@soleri/core 2.11.0 → 7.0.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/data/flows/build.flow.yaml +128 -0
- package/data/flows/deliver.flow.yaml +110 -0
- package/data/flows/design.flow.yaml +108 -0
- package/data/flows/enhance.flow.yaml +90 -0
- package/data/flows/explore.flow.yaml +84 -0
- package/data/flows/fix.flow.yaml +90 -0
- package/data/flows/plan.flow.yaml +87 -0
- package/data/flows/review.flow.yaml +90 -0
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +10 -0
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +16 -2
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/capabilities/chain-mapping.d.ts +21 -0
- package/dist/capabilities/chain-mapping.d.ts.map +1 -0
- package/dist/capabilities/chain-mapping.js +86 -0
- package/dist/capabilities/chain-mapping.js.map +1 -0
- package/dist/capabilities/index.d.ts +10 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +8 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/registry.d.ts +95 -0
- package/dist/capabilities/registry.d.ts.map +1 -0
- package/dist/capabilities/registry.js +227 -0
- package/dist/capabilities/registry.js.map +1 -0
- package/dist/capabilities/types.d.ts +106 -0
- package/dist/capabilities/types.d.ts.map +1 -0
- package/dist/capabilities/types.js +12 -0
- package/dist/capabilities/types.js.map +1 -0
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +58 -2
- package/dist/control/intent-router.js.map +1 -1
- package/dist/domain-packs/index.d.ts +8 -0
- package/dist/domain-packs/index.d.ts.map +1 -0
- package/dist/domain-packs/index.js +8 -0
- package/dist/domain-packs/index.js.map +1 -0
- package/dist/domain-packs/inject-rules.d.ts +24 -0
- package/dist/domain-packs/inject-rules.d.ts.map +1 -0
- package/dist/domain-packs/inject-rules.js +65 -0
- package/dist/domain-packs/inject-rules.js.map +1 -0
- package/dist/domain-packs/knowledge-installer.d.ts +27 -0
- package/dist/domain-packs/knowledge-installer.d.ts.map +1 -0
- package/dist/domain-packs/knowledge-installer.js +89 -0
- package/dist/domain-packs/knowledge-installer.js.map +1 -0
- package/dist/domain-packs/loader.d.ts +28 -0
- package/dist/domain-packs/loader.d.ts.map +1 -0
- package/dist/domain-packs/loader.js +105 -0
- package/dist/domain-packs/loader.js.map +1 -0
- package/dist/domain-packs/pack-runtime.d.ts +80 -0
- package/dist/domain-packs/pack-runtime.d.ts.map +1 -0
- package/dist/domain-packs/pack-runtime.js +36 -0
- package/dist/domain-packs/pack-runtime.js.map +1 -0
- package/dist/domain-packs/skills-installer.d.ts +21 -0
- package/dist/domain-packs/skills-installer.d.ts.map +1 -0
- package/dist/domain-packs/skills-installer.js +38 -0
- package/dist/domain-packs/skills-installer.js.map +1 -0
- package/dist/domain-packs/token-resolver.d.ts +37 -0
- package/dist/domain-packs/token-resolver.d.ts.map +1 -0
- package/dist/domain-packs/token-resolver.js +109 -0
- package/dist/domain-packs/token-resolver.js.map +1 -0
- package/dist/domain-packs/types.d.ts +91 -0
- package/dist/domain-packs/types.d.ts.map +1 -0
- package/dist/domain-packs/types.js +122 -0
- package/dist/domain-packs/types.js.map +1 -0
- package/dist/engine/bin/soleri-engine.d.ts +12 -0
- package/dist/engine/bin/soleri-engine.d.ts.map +1 -0
- package/dist/engine/bin/soleri-engine.js +183 -0
- package/dist/engine/bin/soleri-engine.js.map +1 -0
- package/dist/engine/core-ops.d.ts +27 -0
- package/dist/engine/core-ops.d.ts.map +1 -0
- package/dist/engine/core-ops.js +159 -0
- package/dist/engine/core-ops.js.map +1 -0
- package/dist/engine/index.d.ts +19 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +17 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/register-engine.d.ts +54 -0
- package/dist/engine/register-engine.d.ts.map +1 -0
- package/dist/engine/register-engine.js +270 -0
- package/dist/engine/register-engine.js.map +1 -0
- package/dist/engine/test-helpers.d.ts +30 -0
- package/dist/engine/test-helpers.d.ts.map +1 -0
- package/dist/engine/test-helpers.js +59 -0
- package/dist/engine/test-helpers.js.map +1 -0
- package/dist/flows/context-router.d.ts +39 -0
- package/dist/flows/context-router.d.ts.map +1 -0
- package/dist/flows/context-router.js +206 -0
- package/dist/flows/context-router.js.map +1 -0
- package/dist/flows/dispatch-registry.d.ts +24 -0
- package/dist/flows/dispatch-registry.d.ts.map +1 -0
- package/dist/flows/dispatch-registry.js +70 -0
- package/dist/flows/dispatch-registry.js.map +1 -0
- package/dist/flows/epilogue.d.ts +24 -0
- package/dist/flows/epilogue.d.ts.map +1 -0
- package/dist/flows/epilogue.js +52 -0
- package/dist/flows/epilogue.js.map +1 -0
- package/dist/flows/executor.d.ts +25 -0
- package/dist/flows/executor.d.ts.map +1 -0
- package/dist/flows/executor.js +153 -0
- package/dist/flows/executor.js.map +1 -0
- package/dist/flows/gate-evaluator.d.ts +26 -0
- package/dist/flows/gate-evaluator.d.ts.map +1 -0
- package/dist/flows/gate-evaluator.js +162 -0
- package/dist/flows/gate-evaluator.js.map +1 -0
- package/dist/flows/index.d.ts +14 -0
- package/dist/flows/index.d.ts.map +1 -0
- package/dist/flows/index.js +20 -0
- package/dist/flows/index.js.map +1 -0
- package/dist/flows/loader.d.ts +17 -0
- package/dist/flows/loader.d.ts.map +1 -0
- package/dist/flows/loader.js +61 -0
- package/dist/flows/loader.js.map +1 -0
- package/dist/flows/plan-builder.d.ts +40 -0
- package/dist/flows/plan-builder.d.ts.map +1 -0
- package/dist/flows/plan-builder.js +213 -0
- package/dist/flows/plan-builder.js.map +1 -0
- package/dist/flows/probes.d.ts +11 -0
- package/dist/flows/probes.d.ts.map +1 -0
- package/dist/flows/probes.js +62 -0
- package/dist/flows/probes.js.map +1 -0
- package/dist/flows/types.d.ts +950 -0
- package/dist/flows/types.d.ts.map +1 -0
- package/dist/flows/types.js +105 -0
- package/dist/flows/types.js.map +1 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/intelligence/loader.d.ts +19 -0
- package/dist/intelligence/loader.d.ts.map +1 -1
- package/dist/intelligence/loader.js +86 -5
- package/dist/intelligence/loader.js.map +1 -1
- package/dist/intelligence/types.d.ts +1 -0
- package/dist/intelligence/types.d.ts.map +1 -1
- package/dist/packs/types.d.ts +58 -19
- package/dist/packs/types.d.ts.map +1 -1
- package/dist/packs/types.js +14 -0
- package/dist/packs/types.js.map +1 -1
- package/dist/playbooks/generic/onboarding.d.ts +9 -0
- package/dist/playbooks/generic/onboarding.d.ts.map +1 -0
- package/dist/playbooks/generic/onboarding.js +74 -0
- package/dist/playbooks/generic/onboarding.js.map +1 -0
- package/dist/playbooks/playbook-registry.d.ts.map +1 -1
- package/dist/playbooks/playbook-registry.js +2 -0
- package/dist/playbooks/playbook-registry.js.map +1 -1
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
- package/dist/runtime/admin-extra-ops.js +15 -9
- package/dist/runtime/admin-extra-ops.js.map +1 -1
- package/dist/runtime/admin-ops.js +4 -4
- 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 +33 -1
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/domain-ops.d.ts +21 -5
- package/dist/runtime/domain-ops.d.ts.map +1 -1
- package/dist/runtime/domain-ops.js +85 -8
- package/dist/runtime/domain-ops.js.map +1 -1
- package/dist/runtime/facades/cognee-facade.d.ts.map +1 -1
- package/dist/runtime/facades/cognee-facade.js +3 -1
- package/dist/runtime/facades/cognee-facade.js.map +1 -1
- package/dist/runtime/facades/index.d.ts.map +1 -1
- package/dist/runtime/facades/index.js +10 -6
- package/dist/runtime/facades/index.js.map +1 -1
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
- package/dist/runtime/facades/vault-facade.js +2 -0
- package/dist/runtime/facades/vault-facade.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts +8 -7
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +227 -58
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +23 -17
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +6 -2
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-linking-ops.d.ts +13 -0
- package/dist/runtime/vault-linking-ops.d.ts.map +1 -0
- package/dist/runtime/vault-linking-ops.js +367 -0
- package/dist/runtime/vault-linking-ops.js.map +1 -0
- package/dist/vault/linking.d.ts +46 -0
- package/dist/vault/linking.d.ts.map +1 -0
- package/dist/vault/linking.js +275 -0
- package/dist/vault/linking.js.map +1 -0
- package/dist/vault/vault-types.d.ts +37 -0
- package/dist/vault/vault-types.d.ts.map +1 -1
- package/dist/vault/vault.d.ts +12 -0
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +85 -6
- package/dist/vault/vault.js.map +1 -1
- package/package.json +4 -1
- package/src/__tests__/admin-extra-ops.test.ts +1 -1
- package/src/__tests__/admin-ops.test.ts +2 -1
- package/src/__tests__/cognee-client-gaps.test.ts +470 -0
- package/src/__tests__/cognee-hybrid-search.test.ts +478 -0
- package/src/__tests__/cognee-sync-manager-deep.test.ts +630 -0
- package/src/__tests__/cognee-sync-manager.test.ts +1 -0
- package/src/__tests__/core-ops.test.ts +9 -61
- package/src/__tests__/domain-packs.test.ts +421 -0
- package/src/__tests__/flows.test.ts +604 -0
- package/src/__tests__/playbook-registry.test.ts +2 -2
- package/src/__tests__/playbook-seeder.test.ts +8 -8
- package/src/__tests__/playbook.test.ts +5 -5
- package/src/__tests__/token-resolver.test.ts +79 -0
- package/src/brain/brain.ts +12 -0
- package/src/brain/intelligence.ts +21 -2
- package/src/capabilities/chain-mapping.ts +93 -0
- package/src/capabilities/index.ts +21 -0
- package/src/capabilities/registry.ts +290 -0
- package/src/capabilities/types.ts +143 -0
- package/src/control/intent-router.ts +46 -2
- package/src/domain-packs/index.ts +27 -0
- package/src/domain-packs/inject-rules.ts +74 -0
- package/src/domain-packs/knowledge-installer.ts +116 -0
- package/src/domain-packs/loader.ts +124 -0
- package/src/domain-packs/pack-runtime.ts +99 -0
- package/src/domain-packs/skills-installer.ts +56 -0
- package/src/domain-packs/token-resolver.ts +126 -0
- package/src/domain-packs/types.ts +229 -0
- package/src/engine/__tests__/register-engine.test.ts +104 -0
- package/src/engine/bin/soleri-engine.ts +217 -0
- package/src/engine/core-ops.ts +178 -0
- package/src/engine/index.ts +19 -0
- package/src/engine/register-engine.ts +385 -0
- package/src/engine/test-helpers.ts +83 -0
- package/src/flows/context-router.ts +257 -0
- package/src/flows/dispatch-registry.ts +80 -0
- package/src/flows/epilogue.ts +65 -0
- package/src/flows/executor.ts +182 -0
- package/src/flows/gate-evaluator.ts +171 -0
- package/src/flows/index.ts +52 -0
- package/src/flows/loader.ts +63 -0
- package/src/flows/plan-builder.ts +250 -0
- package/src/flows/probes.ts +70 -0
- package/src/flows/types.ts +217 -0
- package/src/index.ts +68 -1
- package/src/intelligence/loader.ts +96 -5
- package/src/intelligence/types.ts +1 -0
- package/src/packs/types.ts +19 -0
- package/src/playbooks/generic/onboarding.ts +79 -0
- package/src/playbooks/playbook-registry.ts +2 -0
- package/src/runtime/admin-extra-ops.ts +14 -8
- package/src/runtime/admin-ops.ts +4 -4
- package/src/runtime/capture-ops.ts +40 -1
- package/src/runtime/domain-ops.ts +92 -7
- package/src/runtime/facades/cognee-facade.ts +3 -1
- package/src/runtime/facades/index.ts +12 -6
- package/src/runtime/facades/vault-facade.ts +2 -0
- package/src/runtime/orchestrate-ops.ts +271 -62
- package/src/runtime/runtime.ts +27 -18
- package/src/runtime/types.ts +6 -2
- package/src/runtime/vault-linking-ops.ts +454 -0
- package/src/vault/linking.ts +333 -0
- package/src/vault/vault-types.ts +46 -0
- package/src/vault/vault.ts +94 -7
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate evaluator — checks step gates against tool results to decide
|
|
3
|
+
* whether execution should CONTINUE, STOP, or BRANCH.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PlanStep, GateVerdict } from './types.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Evaluate a plan step's gate against collected tool results.
|
|
10
|
+
* If no gate is defined, returns CONTINUE (passed).
|
|
11
|
+
*/
|
|
12
|
+
export function evaluateGate(
|
|
13
|
+
gate: PlanStep['gate'],
|
|
14
|
+
toolResults: Record<string, unknown>,
|
|
15
|
+
): GateVerdict {
|
|
16
|
+
if (!gate) {
|
|
17
|
+
return { passed: true, action: 'CONTINUE' };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
switch (gate.type) {
|
|
21
|
+
case 'GATE': {
|
|
22
|
+
const passed = gate.condition ? evaluateCondition(gate.condition, toolResults) : true;
|
|
23
|
+
if (passed) return { passed: true, action: 'CONTINUE' };
|
|
24
|
+
return {
|
|
25
|
+
passed: false,
|
|
26
|
+
action: (gate.onFail?.action as GateVerdict['action']) ?? 'STOP',
|
|
27
|
+
goto: gate.onFail?.goto,
|
|
28
|
+
message: gate.onFail?.message,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
case 'SCORE': {
|
|
33
|
+
const score = extractScore(toolResults);
|
|
34
|
+
const minScore = gate.min ?? 0;
|
|
35
|
+
const passed = score >= minScore;
|
|
36
|
+
if (passed) return { passed: true, action: 'CONTINUE', score };
|
|
37
|
+
return {
|
|
38
|
+
passed: false,
|
|
39
|
+
action: (gate.onFail?.action as GateVerdict['action']) ?? 'STOP',
|
|
40
|
+
goto: gate.onFail?.goto,
|
|
41
|
+
message: gate.onFail?.message ?? `Score ${score} below minimum ${minScore}`,
|
|
42
|
+
score,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
case 'CHECKPOINT': {
|
|
47
|
+
const passed = gate.condition ? evaluateCondition(gate.condition, toolResults) : true;
|
|
48
|
+
if (passed) return { passed: true, action: 'CONTINUE' };
|
|
49
|
+
return {
|
|
50
|
+
passed: false,
|
|
51
|
+
action: (gate.onFail?.action as GateVerdict['action']) ?? 'CONTINUE',
|
|
52
|
+
goto: gate.onFail?.goto,
|
|
53
|
+
message: gate.onFail?.message,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
case 'BRANCH': {
|
|
58
|
+
// BRANCH gates always trigger branching
|
|
59
|
+
return {
|
|
60
|
+
passed: true,
|
|
61
|
+
action: 'BRANCH',
|
|
62
|
+
goto: gate.onFail?.goto,
|
|
63
|
+
message: gate.onFail?.message,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
default:
|
|
68
|
+
return { passed: true, action: 'CONTINUE' };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Evaluate a simple condition string: "lhs op rhs".
|
|
74
|
+
* Supported operators: ==, !=, >=, <=, >, <
|
|
75
|
+
* lhs is resolved as a dotted path from data, rhs is a literal.
|
|
76
|
+
*/
|
|
77
|
+
export function evaluateCondition(condition: string, data: Record<string, unknown>): boolean {
|
|
78
|
+
const operators = ['>=', '<=', '!=', '==', '>', '<'] as const;
|
|
79
|
+
for (const op of operators) {
|
|
80
|
+
const idx = condition.indexOf(op);
|
|
81
|
+
if (idx === -1) continue;
|
|
82
|
+
|
|
83
|
+
const lhsPath = condition.slice(0, idx).trim();
|
|
84
|
+
const rhsRaw = condition.slice(idx + op.length).trim();
|
|
85
|
+
const lhsValue = resolvePath(data, lhsPath);
|
|
86
|
+
const rhsValue = parseConditionValue(rhsRaw);
|
|
87
|
+
|
|
88
|
+
const lNum = typeof lhsValue === 'number' ? lhsValue : Number(lhsValue);
|
|
89
|
+
const rNum = typeof rhsValue === 'number' ? rhsValue : Number(rhsValue);
|
|
90
|
+
const useNumeric = !Number.isNaN(lNum) && !Number.isNaN(rNum);
|
|
91
|
+
|
|
92
|
+
switch (op) {
|
|
93
|
+
case '==':
|
|
94
|
+
return useNumeric ? lNum === rNum : String(lhsValue) === String(rhsValue);
|
|
95
|
+
case '!=':
|
|
96
|
+
return useNumeric ? lNum !== rNum : String(lhsValue) !== String(rhsValue);
|
|
97
|
+
case '>=':
|
|
98
|
+
return useNumeric ? lNum >= rNum : false;
|
|
99
|
+
case '<=':
|
|
100
|
+
return useNumeric ? lNum <= rNum : false;
|
|
101
|
+
case '>':
|
|
102
|
+
return useNumeric ? lNum > rNum : false;
|
|
103
|
+
case '<':
|
|
104
|
+
return useNumeric ? lNum < rNum : false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// No operator found — check if value is truthy
|
|
109
|
+
const val = resolvePath(data, condition.trim());
|
|
110
|
+
return !!val;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract a numeric score from tool results.
|
|
115
|
+
* Looks for common score field names.
|
|
116
|
+
*/
|
|
117
|
+
export function extractScore(data: Record<string, unknown>): number {
|
|
118
|
+
// Direct score fields
|
|
119
|
+
for (const key of ['score', 'validationScore', 'total']) {
|
|
120
|
+
if (typeof data[key] === 'number') return data[key] as number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Search within nested result objects
|
|
124
|
+
for (const val of Object.values(data)) {
|
|
125
|
+
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
|
126
|
+
const nested = val as Record<string, unknown>;
|
|
127
|
+
for (const key of ['score', 'validationScore', 'total']) {
|
|
128
|
+
if (typeof nested[key] === 'number') return nested[key] as number;
|
|
129
|
+
}
|
|
130
|
+
// One more level: data property
|
|
131
|
+
if (nested.data && typeof nested.data === 'object') {
|
|
132
|
+
const deep = nested.data as Record<string, unknown>;
|
|
133
|
+
for (const key of ['score', 'validationScore', 'total']) {
|
|
134
|
+
if (typeof deep[key] === 'number') return deep[key] as number;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Resolve a dotted path like "result.data.score" against an object.
|
|
145
|
+
*/
|
|
146
|
+
export function resolvePath(obj: Record<string, unknown>, path: string): unknown {
|
|
147
|
+
const parts = path.split('.');
|
|
148
|
+
let current: unknown = obj;
|
|
149
|
+
for (const part of parts) {
|
|
150
|
+
if (current === null || current === undefined || typeof current !== 'object') return undefined;
|
|
151
|
+
current = (current as Record<string, unknown>)[part];
|
|
152
|
+
}
|
|
153
|
+
return current;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// Internal
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
function parseConditionValue(raw: string): string | number | boolean {
|
|
161
|
+
if (raw === 'true') return true;
|
|
162
|
+
if (raw === 'false') return false;
|
|
163
|
+
if (raw === 'null') return 0;
|
|
164
|
+
// Strip quotes
|
|
165
|
+
if ((raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith("'") && raw.endsWith("'"))) {
|
|
166
|
+
return raw.slice(1, -1);
|
|
167
|
+
}
|
|
168
|
+
const num = Number(raw);
|
|
169
|
+
if (!Number.isNaN(num)) return num;
|
|
170
|
+
return raw;
|
|
171
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow engine — YAML-driven workflow orchestration.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Types
|
|
6
|
+
export type {
|
|
7
|
+
Flow,
|
|
8
|
+
FlowStep,
|
|
9
|
+
Gate,
|
|
10
|
+
GateAction,
|
|
11
|
+
ProbeName,
|
|
12
|
+
ProbeResults,
|
|
13
|
+
PlanStep,
|
|
14
|
+
SkippedStep,
|
|
15
|
+
OrchestrationPlan,
|
|
16
|
+
OrchestrationContext,
|
|
17
|
+
StepResult,
|
|
18
|
+
ExecutionResult,
|
|
19
|
+
GateVerdict,
|
|
20
|
+
} from './types.js';
|
|
21
|
+
|
|
22
|
+
// Loader
|
|
23
|
+
export { loadFlowById, loadAllFlows, parseSimpleYaml } from './loader.js';
|
|
24
|
+
|
|
25
|
+
// Probes
|
|
26
|
+
export { runProbes } from './probes.js';
|
|
27
|
+
|
|
28
|
+
// Plan builder
|
|
29
|
+
export {
|
|
30
|
+
INTENT_TO_FLOW,
|
|
31
|
+
chainToToolName,
|
|
32
|
+
chainToRequires,
|
|
33
|
+
flowStepsToPlanSteps,
|
|
34
|
+
pruneSteps,
|
|
35
|
+
buildPlan,
|
|
36
|
+
} from './plan-builder.js';
|
|
37
|
+
|
|
38
|
+
// Context router
|
|
39
|
+
export { detectContext, applyContextOverrides, getFlowOverrides } from './context-router.js';
|
|
40
|
+
export type { ContextOverride } from './context-router.js';
|
|
41
|
+
|
|
42
|
+
// Gate evaluator
|
|
43
|
+
export { evaluateGate, evaluateCondition, extractScore, resolvePath } from './gate-evaluator.js';
|
|
44
|
+
|
|
45
|
+
// Executor
|
|
46
|
+
export { FlowExecutor } from './executor.js';
|
|
47
|
+
|
|
48
|
+
// Dispatch registry
|
|
49
|
+
export { createDispatcher } from './dispatch-registry.js';
|
|
50
|
+
|
|
51
|
+
// Epilogue
|
|
52
|
+
export { runEpilogue } from './epilogue.js';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow loader — reads and validates YAML flow files from a directory.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
6
|
+
import { join, dirname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { parse as parseYaml } from 'yaml';
|
|
9
|
+
import { flowSchema, type Flow } from './types.js';
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const DEFAULT_FLOWS_DIR = join(__dirname, '..', '..', 'data', 'flows');
|
|
13
|
+
|
|
14
|
+
/** Re-export for backward compat (tests import this). */
|
|
15
|
+
export const parseSimpleYaml = parseYaml;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Load a single flow by its `id` field from *.flow.yaml files in a directory.
|
|
19
|
+
* Returns `null` if not found or validation fails.
|
|
20
|
+
*/
|
|
21
|
+
export function loadFlowById(flowId: string, flowsDir?: string): Flow | null {
|
|
22
|
+
const dir = flowsDir ?? DEFAULT_FLOWS_DIR;
|
|
23
|
+
if (!existsSync(dir)) return null;
|
|
24
|
+
|
|
25
|
+
const files = readdirSync(dir).filter((f) => f.endsWith('.flow.yaml'));
|
|
26
|
+
for (const file of files) {
|
|
27
|
+
try {
|
|
28
|
+
const content = readFileSync(join(dir, file), 'utf-8');
|
|
29
|
+
const raw = parseYaml(content);
|
|
30
|
+
const parsed = flowSchema.safeParse(raw);
|
|
31
|
+
if (parsed.success && parsed.data.id === flowId) {
|
|
32
|
+
return parsed.data;
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// skip invalid files
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Load all valid flows from *.flow.yaml files in a directory.
|
|
43
|
+
*/
|
|
44
|
+
export function loadAllFlows(flowsDir?: string): Flow[] {
|
|
45
|
+
const dir = flowsDir ?? DEFAULT_FLOWS_DIR;
|
|
46
|
+
if (!existsSync(dir)) return [];
|
|
47
|
+
|
|
48
|
+
const files = readdirSync(dir).filter((f) => f.endsWith('.flow.yaml'));
|
|
49
|
+
const flows: Flow[] = [];
|
|
50
|
+
for (const file of files) {
|
|
51
|
+
try {
|
|
52
|
+
const content = readFileSync(join(dir, file), 'utf-8');
|
|
53
|
+
const raw = parseYaml(content);
|
|
54
|
+
const parsed = flowSchema.safeParse(raw);
|
|
55
|
+
if (parsed.success) {
|
|
56
|
+
flows.push(parsed.data);
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
// skip invalid files
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return flows;
|
|
63
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan builder — converts intent + flow + probes into an OrchestrationPlan.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import type { AgentRuntime } from '../runtime/types.js';
|
|
7
|
+
import type {
|
|
8
|
+
Flow,
|
|
9
|
+
PlanStep,
|
|
10
|
+
SkippedStep,
|
|
11
|
+
OrchestrationPlan,
|
|
12
|
+
ProbeResults,
|
|
13
|
+
ProbeName,
|
|
14
|
+
} from './types.js';
|
|
15
|
+
import { loadFlowById } from './loader.js';
|
|
16
|
+
import { runProbes } from './probes.js';
|
|
17
|
+
import { detectContext, applyContextOverrides } from './context-router.js';
|
|
18
|
+
import { chainToCapability } from '../capabilities/index.js';
|
|
19
|
+
import type { CapabilityRegistry } from '../capabilities/index.js';
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Intent → Flow mapping
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
export const INTENT_TO_FLOW: Record<string, string> = {
|
|
26
|
+
BUILD: 'BUILD-flow',
|
|
27
|
+
CREATE: 'BUILD-flow',
|
|
28
|
+
FIX: 'FIX-flow',
|
|
29
|
+
REVIEW: 'REVIEW-flow',
|
|
30
|
+
PLAN: 'PLAN-flow',
|
|
31
|
+
DESIGN: 'DESIGN-flow',
|
|
32
|
+
ENHANCE: 'ENHANCE-flow',
|
|
33
|
+
IMPROVE: 'ENHANCE-flow',
|
|
34
|
+
EXPLORE: 'EXPLORE-flow',
|
|
35
|
+
DELIVER: 'DELIVER-flow',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Helpers
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Convert a chain name (e.g. "vault-search") to a tool name (e.g. "myagent_vault_search").
|
|
44
|
+
*/
|
|
45
|
+
export function chainToToolName(chain: string, agentId: string): string {
|
|
46
|
+
return `${agentId}_${chain.replace(/-/g, '_')}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Infer which probe capability a chain requires, or undefined if none.
|
|
51
|
+
*/
|
|
52
|
+
export function chainToRequires(chain: string): ProbeName | undefined {
|
|
53
|
+
const lower = chain.toLowerCase();
|
|
54
|
+
if (lower.startsWith('vault') || lower.startsWith('memory')) return 'vault';
|
|
55
|
+
if (lower.startsWith('brain')) return 'brain';
|
|
56
|
+
if (lower.startsWith('component') || lower.startsWith('token') || lower.startsWith('design'))
|
|
57
|
+
return 'designSystem';
|
|
58
|
+
if (lower.startsWith('session')) return 'sessionStore';
|
|
59
|
+
// recommend-* and get-stack-* have no hard requirements
|
|
60
|
+
if (lower.startsWith('recommend') || lower.startsWith('get-stack')) return undefined;
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Convert flow steps into plan steps.
|
|
66
|
+
*
|
|
67
|
+
* Resolution order for capability IDs:
|
|
68
|
+
* 1. If the step has `needs:` (v2), use those capability IDs directly
|
|
69
|
+
* 2. If the step only has `chains:` (v1), map via chainToCapability()
|
|
70
|
+
* 3. chainToToolName() is still used for tool dispatch (fallback path)
|
|
71
|
+
*
|
|
72
|
+
* When an optional `registry` is provided, each resolved capability is
|
|
73
|
+
* validated. Unavailable capabilities are recorded in the step's
|
|
74
|
+
* `unavailableCapabilities` list (informational — pruning is separate).
|
|
75
|
+
*/
|
|
76
|
+
export function flowStepsToPlanSteps(
|
|
77
|
+
flow: Flow,
|
|
78
|
+
agentId: string,
|
|
79
|
+
registry?: CapabilityRegistry,
|
|
80
|
+
): PlanStep[] {
|
|
81
|
+
return flow.steps.map((step) => {
|
|
82
|
+
// Tool names for dispatch fallback (always computed from chains)
|
|
83
|
+
const tools = (step.chains ?? []).map((c) => chainToToolName(c, agentId));
|
|
84
|
+
|
|
85
|
+
// Resolve capability IDs: prefer needs (v2), fall back to chains (v1)
|
|
86
|
+
const capabilityIds: string[] = [];
|
|
87
|
+
if (step.needs && step.needs.length > 0) {
|
|
88
|
+
capabilityIds.push(...step.needs);
|
|
89
|
+
} else if (step.chains) {
|
|
90
|
+
for (const chain of step.chains) {
|
|
91
|
+
const capId = chainToCapability(chain);
|
|
92
|
+
if (capId && !capabilityIds.includes(capId)) {
|
|
93
|
+
capabilityIds.push(capId);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Probe-level requires (existing behavior, derived from chains)
|
|
99
|
+
const requires: ProbeName[] = [];
|
|
100
|
+
for (const chain of step.chains ?? []) {
|
|
101
|
+
const req = chainToRequires(chain);
|
|
102
|
+
if (req && !requires.includes(req)) requires.push(req);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Validate capabilities against registry if provided
|
|
106
|
+
const unavailableCapabilities: string[] = [];
|
|
107
|
+
if (registry) {
|
|
108
|
+
for (const capId of capabilityIds) {
|
|
109
|
+
const resolved = registry.resolve(capId);
|
|
110
|
+
if (!resolved.available) {
|
|
111
|
+
unavailableCapabilities.push(capId);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const planStep: PlanStep = {
|
|
117
|
+
id: step.id,
|
|
118
|
+
name: step.name ?? step.id,
|
|
119
|
+
tools,
|
|
120
|
+
parallel: step.parallel ?? false,
|
|
121
|
+
requires,
|
|
122
|
+
status: 'pending',
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Attach capability metadata (non-breaking additions)
|
|
126
|
+
if (capabilityIds.length > 0) {
|
|
127
|
+
(planStep as PlanStep & { capabilities?: string[] }).capabilities = capabilityIds;
|
|
128
|
+
}
|
|
129
|
+
if (unavailableCapabilities.length > 0) {
|
|
130
|
+
(planStep as PlanStep & { unavailableCapabilities?: string[] }).unavailableCapabilities =
|
|
131
|
+
unavailableCapabilities;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (step.gate) {
|
|
135
|
+
const gate = step.gate;
|
|
136
|
+
planStep.gate = {
|
|
137
|
+
type: gate.type,
|
|
138
|
+
};
|
|
139
|
+
if ('condition' in gate && gate.condition) {
|
|
140
|
+
planStep.gate.condition = gate.condition;
|
|
141
|
+
}
|
|
142
|
+
if ('min' in gate && gate.min !== undefined) {
|
|
143
|
+
planStep.gate.min = gate.min;
|
|
144
|
+
}
|
|
145
|
+
if (gate['on-false']) {
|
|
146
|
+
planStep.gate.onFail = {
|
|
147
|
+
action: gate['on-false'].action,
|
|
148
|
+
goto: gate['on-false'].goto,
|
|
149
|
+
message: gate['on-false'].message,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return planStep;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Remove steps whose required capabilities are not available.
|
|
160
|
+
*/
|
|
161
|
+
export function pruneSteps(
|
|
162
|
+
steps: PlanStep[],
|
|
163
|
+
probes: ProbeResults,
|
|
164
|
+
): { kept: PlanStep[]; skipped: SkippedStep[] } {
|
|
165
|
+
const kept: PlanStep[] = [];
|
|
166
|
+
const skipped: SkippedStep[] = [];
|
|
167
|
+
|
|
168
|
+
for (const step of steps) {
|
|
169
|
+
const missingProbes = step.requires.filter((r) => !probes[r]);
|
|
170
|
+
if (missingProbes.length > 0) {
|
|
171
|
+
skipped.push({
|
|
172
|
+
id: step.id,
|
|
173
|
+
name: step.name,
|
|
174
|
+
reason: `Missing capabilities: ${missingProbes.join(', ')}`,
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
kept.push(step);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return { kept, skipped };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Build a full orchestration plan from intent, agent config, and runtime.
|
|
186
|
+
*/
|
|
187
|
+
export async function buildPlan(
|
|
188
|
+
intent: string,
|
|
189
|
+
agentId: string,
|
|
190
|
+
projectPath: string,
|
|
191
|
+
runtime: AgentRuntime,
|
|
192
|
+
prompt?: string,
|
|
193
|
+
): Promise<OrchestrationPlan> {
|
|
194
|
+
const normalizedIntent = intent.toUpperCase();
|
|
195
|
+
const flowId = INTENT_TO_FLOW[normalizedIntent] ?? 'BUILD-flow';
|
|
196
|
+
const flow = loadFlowById(flowId);
|
|
197
|
+
|
|
198
|
+
const probes = await runProbes(runtime, projectPath);
|
|
199
|
+
|
|
200
|
+
let steps: PlanStep[] = [];
|
|
201
|
+
let skipped: SkippedStep[] = [];
|
|
202
|
+
const warnings: string[] = [];
|
|
203
|
+
|
|
204
|
+
if (flow) {
|
|
205
|
+
let allSteps = flowStepsToPlanSteps(flow, agentId);
|
|
206
|
+
|
|
207
|
+
// Context-sensitive chain routing: detect what's being built/fixed/reviewed
|
|
208
|
+
// and apply chain overrides (inject, skip, substitute) before pruning.
|
|
209
|
+
const entities = { components: [] as string[], actions: [] as string[] };
|
|
210
|
+
const contexts = prompt ? detectContext(prompt, entities) : [];
|
|
211
|
+
if (contexts.length > 0) {
|
|
212
|
+
allSteps = applyContextOverrides(allSteps, contexts, flowId, agentId);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const pruneResult = pruneSteps(allSteps, probes);
|
|
216
|
+
steps = pruneResult.kept;
|
|
217
|
+
skipped = pruneResult.skipped;
|
|
218
|
+
|
|
219
|
+
if (pruneResult.skipped.length > 0) {
|
|
220
|
+
warnings.push(`${pruneResult.skipped.length} step(s) skipped due to missing capabilities.`);
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
warnings.push(`Flow "${flowId}" not found — plan will have no steps.`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!probes.vault) warnings.push('Vault unavailable — knowledge capture will be skipped.');
|
|
227
|
+
if (!probes.brain) warnings.push('Brain has no vocabulary — recommendations may be limited.');
|
|
228
|
+
|
|
229
|
+
const epilogue: string[] = [];
|
|
230
|
+
if (probes.vault) epilogue.push('capture_knowledge');
|
|
231
|
+
if (probes.sessionStore) epilogue.push('session_capture');
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
planId: randomUUID(),
|
|
235
|
+
intent: normalizedIntent,
|
|
236
|
+
flowId,
|
|
237
|
+
steps,
|
|
238
|
+
skipped,
|
|
239
|
+
epilogue,
|
|
240
|
+
warnings,
|
|
241
|
+
summary: prompt ?? `${normalizedIntent} plan with ${steps.length} step(s)`,
|
|
242
|
+
estimatedTools: steps.reduce((acc, s) => acc + s.tools.length, 0),
|
|
243
|
+
context: {
|
|
244
|
+
intent: normalizedIntent,
|
|
245
|
+
probes,
|
|
246
|
+
entities: { components: [], actions: [] },
|
|
247
|
+
projectPath,
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability probes — detect what subsystems are available at runtime.
|
|
3
|
+
* All probes are resilient: they catch errors and return false on failure.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import type { AgentRuntime } from '../runtime/types.js';
|
|
9
|
+
import type { ProbeResults } from './types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Run all 6 capability probes in parallel and return results.
|
|
13
|
+
*/
|
|
14
|
+
export async function runProbes(runtime: AgentRuntime, projectPath: string): Promise<ProbeResults> {
|
|
15
|
+
const [vault, brain, designSystem, sessionStore, projectRules, active] = await Promise.all([
|
|
16
|
+
probeVault(runtime),
|
|
17
|
+
probeBrain(runtime),
|
|
18
|
+
probeDesignSystem(runtime),
|
|
19
|
+
probeSessionStore(),
|
|
20
|
+
probeProjectRules(projectPath),
|
|
21
|
+
probeActive(),
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
return { vault, brain, designSystem, sessionStore, projectRules, active };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function probeVault(runtime: AgentRuntime): Promise<boolean> {
|
|
28
|
+
try {
|
|
29
|
+
const stats = runtime.vault.stats();
|
|
30
|
+
return stats.totalEntries >= 0;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function probeBrain(runtime: AgentRuntime): Promise<boolean> {
|
|
37
|
+
try {
|
|
38
|
+
return runtime.brain.getVocabularySize() > 0;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function probeDesignSystem(runtime: AgentRuntime): Promise<boolean> {
|
|
45
|
+
try {
|
|
46
|
+
return runtime.projectRegistry.list().length > 0;
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function probeSessionStore(): Promise<boolean> {
|
|
53
|
+
// Session store is always available in Soleri runtime
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function probeProjectRules(projectPath: string): Promise<boolean> {
|
|
58
|
+
try {
|
|
59
|
+
return (
|
|
60
|
+
existsSync(join(projectPath, 'docs', 'vault')) || existsSync(join(projectPath, '.soleri'))
|
|
61
|
+
);
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function probeActive(): Promise<boolean> {
|
|
68
|
+
// Always true when the engine is running
|
|
69
|
+
return true;
|
|
70
|
+
}
|