@soleri/core 2.12.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/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 +35 -0
- 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 +64 -6
- 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 +217 -61
- 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/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 +38 -0
- 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 +71 -5
- 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 +261 -65
- 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,604 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow engine tests — loader, probes, plan builder, gate evaluator, executor.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { join, dirname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { createAgentRuntime } from '../runtime/runtime.js';
|
|
9
|
+
import type { AgentRuntime } from '../runtime/types.js';
|
|
10
|
+
import { loadFlowById, loadAllFlows } from '../flows/loader.js';
|
|
11
|
+
import { runProbes } from '../flows/probes.js';
|
|
12
|
+
import {
|
|
13
|
+
INTENT_TO_FLOW,
|
|
14
|
+
chainToToolName,
|
|
15
|
+
chainToRequires,
|
|
16
|
+
pruneSteps,
|
|
17
|
+
} from '../flows/plan-builder.js';
|
|
18
|
+
import { evaluateCondition, extractScore, resolvePath } from '../flows/gate-evaluator.js';
|
|
19
|
+
import { FlowExecutor } from '../flows/executor.js';
|
|
20
|
+
|
|
21
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const FLOWS_DIR = join(__dirname, '..', '..', 'data', 'flows');
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Flow Loader
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
describe('Flow Loader', () => {
|
|
29
|
+
it('should load all 8 flow files', () => {
|
|
30
|
+
const flows = loadAllFlows(FLOWS_DIR);
|
|
31
|
+
expect(flows.length).toBe(8);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should load BUILD-flow by ID', () => {
|
|
35
|
+
const flow = loadFlowById('BUILD-flow', FLOWS_DIR);
|
|
36
|
+
expect(flow).not.toBeNull();
|
|
37
|
+
expect(flow!.id).toBe('BUILD-flow');
|
|
38
|
+
expect(flow!.steps.length).toBeGreaterThan(0);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should load FIX-flow by ID', () => {
|
|
42
|
+
const flow = loadFlowById('FIX-flow', FLOWS_DIR);
|
|
43
|
+
expect(flow).not.toBeNull();
|
|
44
|
+
expect(flow!.triggers.modes).toContain('FIX');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should return null for unknown flow ID', () => {
|
|
48
|
+
const flow = loadFlowById('NONEXISTENT-flow', FLOWS_DIR);
|
|
49
|
+
expect(flow).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('each flow should have valid structure', () => {
|
|
53
|
+
const flows = loadAllFlows(FLOWS_DIR);
|
|
54
|
+
for (const flow of flows) {
|
|
55
|
+
expect(flow.id).toBeTruthy();
|
|
56
|
+
expect(flow.triggers.modes.length).toBeGreaterThan(0);
|
|
57
|
+
expect(flow.steps.length).toBeGreaterThan(0);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Intent Mapping
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
describe('Intent to Flow mapping', () => {
|
|
67
|
+
it('should map BUILD to BUILD-flow', () => {
|
|
68
|
+
expect(INTENT_TO_FLOW.BUILD).toBe('BUILD-flow');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should map FIX to FIX-flow', () => {
|
|
72
|
+
expect(INTENT_TO_FLOW.FIX).toBe('FIX-flow');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should map DELIVER to DELIVER-flow', () => {
|
|
76
|
+
expect(INTENT_TO_FLOW.DELIVER).toBe('DELIVER-flow');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should have all 8 flows mapped', () => {
|
|
80
|
+
const flowIds = new Set(Object.values(INTENT_TO_FLOW));
|
|
81
|
+
expect(flowIds.size).toBeGreaterThanOrEqual(8);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Chain to Tool Name
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
describe('chainToToolName', () => {
|
|
90
|
+
it('should prefix with agentId and convert hyphens', () => {
|
|
91
|
+
expect(chainToToolName('vault-search', 'myagent')).toBe('myagent_vault_search');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should handle single-word chains', () => {
|
|
95
|
+
expect(chainToToolName('validate', 'test')).toBe('test_validate');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('chainToRequires', () => {
|
|
100
|
+
it('should detect vault requirement', () => {
|
|
101
|
+
expect(chainToRequires('vault-search')).toBe('vault');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should detect brain requirement', () => {
|
|
105
|
+
expect(chainToRequires('brain-recommend')).toBe('brain');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should detect designSystem requirement', () => {
|
|
109
|
+
expect(chainToRequires('component-search')).toBe('designSystem');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should return undefined for recommendation chains', () => {
|
|
113
|
+
expect(chainToRequires('recommend-style')).toBeUndefined();
|
|
114
|
+
expect(chainToRequires('get-stack-guidelines')).toBeUndefined();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Step Pruning
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
describe('pruneSteps', () => {
|
|
123
|
+
it('should keep steps with no requirements', () => {
|
|
124
|
+
const steps = [
|
|
125
|
+
{
|
|
126
|
+
id: 's1',
|
|
127
|
+
name: 'Step 1',
|
|
128
|
+
tools: ['t1'],
|
|
129
|
+
parallel: false,
|
|
130
|
+
requires: [],
|
|
131
|
+
status: 'pending' as const,
|
|
132
|
+
},
|
|
133
|
+
];
|
|
134
|
+
const probes = {
|
|
135
|
+
vault: false,
|
|
136
|
+
brain: false,
|
|
137
|
+
designSystem: false,
|
|
138
|
+
sessionStore: false,
|
|
139
|
+
projectRules: false,
|
|
140
|
+
active: true,
|
|
141
|
+
};
|
|
142
|
+
const { kept, skipped } = pruneSteps(steps, probes);
|
|
143
|
+
expect(kept.length).toBe(1);
|
|
144
|
+
expect(skipped.length).toBe(0);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should skip steps with unmet requirements', () => {
|
|
148
|
+
const steps = [
|
|
149
|
+
{
|
|
150
|
+
id: 's1',
|
|
151
|
+
name: 'Vault Step',
|
|
152
|
+
tools: ['t1'],
|
|
153
|
+
parallel: false,
|
|
154
|
+
requires: ['vault' as const],
|
|
155
|
+
status: 'pending' as const,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 's2',
|
|
159
|
+
name: 'No Req',
|
|
160
|
+
tools: ['t2'],
|
|
161
|
+
parallel: false,
|
|
162
|
+
requires: [],
|
|
163
|
+
status: 'pending' as const,
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
const probes = {
|
|
167
|
+
vault: false,
|
|
168
|
+
brain: false,
|
|
169
|
+
designSystem: false,
|
|
170
|
+
sessionStore: false,
|
|
171
|
+
projectRules: false,
|
|
172
|
+
active: true,
|
|
173
|
+
};
|
|
174
|
+
const { kept, skipped } = pruneSteps(steps, probes);
|
|
175
|
+
expect(kept.length).toBe(1);
|
|
176
|
+
expect(kept[0].id).toBe('s2');
|
|
177
|
+
expect(skipped.length).toBe(1);
|
|
178
|
+
expect(skipped[0].reason).toContain('vault');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should keep steps when requirements are met', () => {
|
|
182
|
+
const steps = [
|
|
183
|
+
{
|
|
184
|
+
id: 's1',
|
|
185
|
+
name: 'Vault Step',
|
|
186
|
+
tools: ['t1'],
|
|
187
|
+
parallel: false,
|
|
188
|
+
requires: ['vault' as const],
|
|
189
|
+
status: 'pending' as const,
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
const probes = {
|
|
193
|
+
vault: true,
|
|
194
|
+
brain: false,
|
|
195
|
+
designSystem: false,
|
|
196
|
+
sessionStore: false,
|
|
197
|
+
projectRules: false,
|
|
198
|
+
active: true,
|
|
199
|
+
};
|
|
200
|
+
const { kept } = pruneSteps(steps, probes);
|
|
201
|
+
expect(kept.length).toBe(1);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// Gate Evaluator
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
describe('evaluateCondition', () => {
|
|
210
|
+
it('should evaluate equality', () => {
|
|
211
|
+
expect(evaluateCondition('count == 0', { count: 0 })).toBe(true);
|
|
212
|
+
expect(evaluateCondition('count == 0', { count: 5 })).toBe(false);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should evaluate inequality', () => {
|
|
216
|
+
expect(evaluateCondition('count != 0', { count: 5 })).toBe(true);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should evaluate greater than', () => {
|
|
220
|
+
expect(evaluateCondition('score >= 80', { score: 90 })).toBe(true);
|
|
221
|
+
expect(evaluateCondition('score >= 80', { score: 70 })).toBe(false);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should evaluate boolean', () => {
|
|
225
|
+
expect(evaluateCondition('pass == true', { pass: true })).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('should evaluate truthy path', () => {
|
|
229
|
+
expect(evaluateCondition('result', { result: 'something' })).toBe(true);
|
|
230
|
+
expect(evaluateCondition('result', { result: '' })).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe('extractScore', () => {
|
|
235
|
+
it('should extract score from data.score', () => {
|
|
236
|
+
expect(extractScore({ score: 85 })).toBe(85);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should extract from nested data', () => {
|
|
240
|
+
expect(extractScore({ validationScore: 92 })).toBe(92);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('should return 0 if no score found', () => {
|
|
244
|
+
expect(extractScore({ unrelated: 'data' })).toBe(0);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe('resolvePath', () => {
|
|
249
|
+
it('should resolve simple path', () => {
|
|
250
|
+
expect(resolvePath({ count: 5 }, 'count')).toBe(5);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should resolve dotted path', () => {
|
|
254
|
+
expect(resolvePath({ a: { b: 42 } }, 'a.b')).toBe(42);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should return undefined for missing path', () => {
|
|
258
|
+
expect(resolvePath({ a: 1 }, 'b')).toBeUndefined();
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
// Executor
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
describe('FlowExecutor', () => {
|
|
267
|
+
it('should execute a simple plan', async () => {
|
|
268
|
+
const dispatch = async (tool: string, _params: Record<string, unknown>) => ({
|
|
269
|
+
tool,
|
|
270
|
+
status: 'ok',
|
|
271
|
+
data: { result: true },
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const executor = new FlowExecutor(dispatch);
|
|
275
|
+
const plan = {
|
|
276
|
+
planId: 'test-plan',
|
|
277
|
+
intent: 'BUILD',
|
|
278
|
+
flowId: 'BUILD-flow',
|
|
279
|
+
steps: [
|
|
280
|
+
{
|
|
281
|
+
id: 's1',
|
|
282
|
+
name: 'Step 1',
|
|
283
|
+
tools: ['tool1'],
|
|
284
|
+
parallel: false,
|
|
285
|
+
requires: [],
|
|
286
|
+
status: 'pending' as const,
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: 's2',
|
|
290
|
+
name: 'Step 2',
|
|
291
|
+
tools: ['tool2'],
|
|
292
|
+
parallel: false,
|
|
293
|
+
requires: [],
|
|
294
|
+
status: 'pending' as const,
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
skipped: [],
|
|
298
|
+
epilogue: [],
|
|
299
|
+
warnings: [],
|
|
300
|
+
summary: 'Test plan',
|
|
301
|
+
estimatedTools: 2,
|
|
302
|
+
context: {
|
|
303
|
+
intent: 'BUILD',
|
|
304
|
+
probes: {
|
|
305
|
+
vault: true,
|
|
306
|
+
brain: false,
|
|
307
|
+
designSystem: false,
|
|
308
|
+
sessionStore: true,
|
|
309
|
+
projectRules: false,
|
|
310
|
+
active: true,
|
|
311
|
+
},
|
|
312
|
+
entities: { components: [], actions: [] },
|
|
313
|
+
projectPath: '/test',
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const result = await executor.execute(plan);
|
|
318
|
+
expect(result.status).toBe('completed');
|
|
319
|
+
expect(result.stepsCompleted).toBe(2);
|
|
320
|
+
expect(result.toolsCalled).toContain('tool1');
|
|
321
|
+
expect(result.toolsCalled).toContain('tool2');
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('should handle STOP gate', async () => {
|
|
325
|
+
const dispatch = async (tool: string) => ({
|
|
326
|
+
tool,
|
|
327
|
+
status: 'ok',
|
|
328
|
+
data: { pass: false },
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
const executor = new FlowExecutor(dispatch);
|
|
332
|
+
const plan = {
|
|
333
|
+
planId: 'test-stop',
|
|
334
|
+
intent: 'DELIVER',
|
|
335
|
+
flowId: 'DELIVER-flow',
|
|
336
|
+
steps: [
|
|
337
|
+
{
|
|
338
|
+
id: 's1',
|
|
339
|
+
name: 'Gate Step',
|
|
340
|
+
tools: ['check'],
|
|
341
|
+
parallel: false,
|
|
342
|
+
requires: [],
|
|
343
|
+
gate: {
|
|
344
|
+
type: 'GATE',
|
|
345
|
+
condition: 'pass == true',
|
|
346
|
+
onFail: { action: 'STOP', message: 'Failed' },
|
|
347
|
+
},
|
|
348
|
+
status: 'pending' as const,
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
id: 's2',
|
|
352
|
+
name: 'After Gate',
|
|
353
|
+
tools: ['next'],
|
|
354
|
+
parallel: false,
|
|
355
|
+
requires: [],
|
|
356
|
+
status: 'pending' as const,
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
skipped: [],
|
|
360
|
+
epilogue: [],
|
|
361
|
+
warnings: [],
|
|
362
|
+
summary: 'Test stop',
|
|
363
|
+
estimatedTools: 2,
|
|
364
|
+
context: {
|
|
365
|
+
intent: 'DELIVER',
|
|
366
|
+
probes: {
|
|
367
|
+
vault: true,
|
|
368
|
+
brain: false,
|
|
369
|
+
designSystem: false,
|
|
370
|
+
sessionStore: true,
|
|
371
|
+
projectRules: false,
|
|
372
|
+
active: true,
|
|
373
|
+
},
|
|
374
|
+
entities: { components: [], actions: [] },
|
|
375
|
+
projectPath: '/test',
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const result = await executor.execute(plan);
|
|
380
|
+
expect(result.stepsCompleted).toBeLessThan(2);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should execute parallel steps', async () => {
|
|
384
|
+
const callOrder: string[] = [];
|
|
385
|
+
const dispatch = async (tool: string) => {
|
|
386
|
+
callOrder.push(tool);
|
|
387
|
+
return { tool, status: 'ok', data: {} };
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const executor = new FlowExecutor(dispatch);
|
|
391
|
+
const plan = {
|
|
392
|
+
planId: 'test-parallel',
|
|
393
|
+
intent: 'BUILD',
|
|
394
|
+
flowId: 'BUILD-flow',
|
|
395
|
+
steps: [
|
|
396
|
+
{
|
|
397
|
+
id: 's1',
|
|
398
|
+
name: 'Parallel',
|
|
399
|
+
tools: ['a', 'b', 'c'],
|
|
400
|
+
parallel: true,
|
|
401
|
+
requires: [],
|
|
402
|
+
status: 'pending' as const,
|
|
403
|
+
},
|
|
404
|
+
],
|
|
405
|
+
skipped: [],
|
|
406
|
+
epilogue: [],
|
|
407
|
+
warnings: [],
|
|
408
|
+
summary: 'Test parallel',
|
|
409
|
+
estimatedTools: 3,
|
|
410
|
+
context: {
|
|
411
|
+
intent: 'BUILD',
|
|
412
|
+
probes: {
|
|
413
|
+
vault: true,
|
|
414
|
+
brain: false,
|
|
415
|
+
designSystem: false,
|
|
416
|
+
sessionStore: true,
|
|
417
|
+
projectRules: false,
|
|
418
|
+
active: true,
|
|
419
|
+
},
|
|
420
|
+
entities: { components: [], actions: [] },
|
|
421
|
+
projectPath: '/test',
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const result = await executor.execute(plan);
|
|
426
|
+
expect(result.toolsCalled.length).toBe(3);
|
|
427
|
+
expect(result.toolsCalled).toContain('a');
|
|
428
|
+
expect(result.toolsCalled).toContain('b');
|
|
429
|
+
expect(result.toolsCalled).toContain('c');
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// ---------------------------------------------------------------------------
|
|
434
|
+
// Context Router
|
|
435
|
+
// ---------------------------------------------------------------------------
|
|
436
|
+
|
|
437
|
+
import { detectContext, applyContextOverrides } from '../flows/context-router.js';
|
|
438
|
+
import { flowStepsToPlanSteps } from '../flows/plan-builder.js';
|
|
439
|
+
|
|
440
|
+
describe('detectContext', () => {
|
|
441
|
+
const emptyEntities = { components: [], actions: [] };
|
|
442
|
+
|
|
443
|
+
it('should find "small-component" context for button prompts', () => {
|
|
444
|
+
const contexts = detectContext('Build a submit button', emptyEntities);
|
|
445
|
+
expect(contexts).toContain('small-component');
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('should find "large-component" context for dashboard prompts', () => {
|
|
449
|
+
const contexts = detectContext('Create a dashboard layout', emptyEntities);
|
|
450
|
+
expect(contexts).toContain('large-component');
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should find "form-component" context for input prompts', () => {
|
|
454
|
+
const contexts = detectContext('Build a select dropdown input', emptyEntities);
|
|
455
|
+
expect(contexts).toContain('form-component');
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('should find "container-component" context for modal prompts', () => {
|
|
459
|
+
const contexts = detectContext('Build a confirmation dialog', emptyEntities);
|
|
460
|
+
expect(contexts).toContain('container-component');
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('should find "design-fix" context for styling fix prompts', () => {
|
|
464
|
+
const contexts = detectContext('Fix the color tokens in the header', emptyEntities);
|
|
465
|
+
expect(contexts).toContain('design-fix');
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('should find "a11y-fix" context for accessibility fix prompts', () => {
|
|
469
|
+
const contexts = detectContext('Fix accessibility issues with ARIA labels', emptyEntities);
|
|
470
|
+
expect(contexts).toContain('a11y-fix');
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('should find "pr-review" context for pull request prompts', () => {
|
|
474
|
+
const contexts = detectContext('Review this PR diff', emptyEntities);
|
|
475
|
+
expect(contexts).toContain('pr-review');
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it('should find "architecture-review" context for architecture prompts', () => {
|
|
479
|
+
const contexts = detectContext('Review the import structure', emptyEntities);
|
|
480
|
+
expect(contexts).toContain('architecture-review');
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should return empty array for generic prompts', () => {
|
|
484
|
+
const contexts = detectContext('Do something useful', emptyEntities);
|
|
485
|
+
expect(contexts).toHaveLength(0);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('should detect multiple contexts when prompt matches several', () => {
|
|
489
|
+
const contexts = detectContext(
|
|
490
|
+
'Build a form with input fields and a submit button',
|
|
491
|
+
emptyEntities,
|
|
492
|
+
);
|
|
493
|
+
expect(contexts).toContain('small-component');
|
|
494
|
+
expect(contexts).toContain('form-component');
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('should also match entity content', () => {
|
|
498
|
+
const contexts = detectContext('Build this', { components: ['Button'], actions: [] });
|
|
499
|
+
expect(contexts).toContain('small-component');
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
describe('applyContextOverrides', () => {
|
|
504
|
+
const agentId = 'test';
|
|
505
|
+
|
|
506
|
+
function loadBuildSteps(): PlanStep[] {
|
|
507
|
+
const flow = loadFlowById('BUILD-flow', FLOWS_DIR);
|
|
508
|
+
return flowStepsToPlanSteps(flow!, agentId);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function loadFixSteps(): PlanStep[] {
|
|
512
|
+
const flow = loadFlowById('FIX-flow', FLOWS_DIR);
|
|
513
|
+
return flowStepsToPlanSteps(flow!, agentId);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
it('should skip get-architecture for small-component context', () => {
|
|
517
|
+
const steps = loadBuildSteps();
|
|
518
|
+
const result = applyContextOverrides(steps, ['small-component'], 'BUILD-flow', agentId);
|
|
519
|
+
const ids = result.map((s) => s.id);
|
|
520
|
+
expect(ids).not.toContain('get-architecture');
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('should inject button-semantics-check before validate for small-component', () => {
|
|
524
|
+
const steps = loadBuildSteps();
|
|
525
|
+
const result = applyContextOverrides(steps, ['small-component'], 'BUILD-flow', agentId);
|
|
526
|
+
const ids = result.map((s) => s.id);
|
|
527
|
+
expect(ids).toContain('ctx-before-validate');
|
|
528
|
+
const injected = result.find((s) => s.id === 'ctx-before-validate');
|
|
529
|
+
expect(injected!.tools).toContain('test_button_semantics_check');
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it('should inject responsive-patterns before validate for large-component', () => {
|
|
533
|
+
const steps = loadBuildSteps();
|
|
534
|
+
const result = applyContextOverrides(steps, ['large-component'], 'BUILD-flow', agentId);
|
|
535
|
+
const ids = result.map((s) => s.id);
|
|
536
|
+
const beforeIdx = ids.indexOf('ctx-before-validate');
|
|
537
|
+
const validateIdx = ids.indexOf('validate');
|
|
538
|
+
expect(beforeIdx).toBeGreaterThan(-1);
|
|
539
|
+
expect(beforeIdx).toBeLessThan(validateIdx);
|
|
540
|
+
const injected = result.find((s) => s.id === 'ctx-before-validate');
|
|
541
|
+
expect(injected!.tools).toContain('test_responsive_patterns');
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('should inject performance-check after validate for large-component', () => {
|
|
545
|
+
const steps = loadBuildSteps();
|
|
546
|
+
const result = applyContextOverrides(steps, ['large-component'], 'BUILD-flow', agentId);
|
|
547
|
+
const ids = result.map((s) => s.id);
|
|
548
|
+
const afterIdx = ids.indexOf('ctx-after-validate');
|
|
549
|
+
const validateIdx = ids.indexOf('validate');
|
|
550
|
+
expect(afterIdx).toBeGreaterThan(validateIdx);
|
|
551
|
+
const injected = result.find((s) => s.id === 'ctx-after-validate');
|
|
552
|
+
expect(injected!.tools).toContain('test_performance_check');
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it('should inject contrast-check and token-validation for design-fix context', () => {
|
|
556
|
+
const steps = loadFixSteps();
|
|
557
|
+
const result = applyContextOverrides(steps, ['design-fix'], 'FIX-flow', agentId);
|
|
558
|
+
const injected = result.find((s) => s.id === 'ctx-before-validate');
|
|
559
|
+
expect(injected).toBeDefined();
|
|
560
|
+
expect(injected!.tools).toContain('test_contrast_check');
|
|
561
|
+
expect(injected!.tools).toContain('test_token_validation');
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it('should return steps unchanged for unknown flow', () => {
|
|
565
|
+
const steps = loadBuildSteps();
|
|
566
|
+
const result = applyContextOverrides(steps, ['small-component'], 'UNKNOWN-flow', agentId);
|
|
567
|
+
expect(result).toEqual(steps);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('should return steps unchanged for empty contexts', () => {
|
|
571
|
+
const steps = loadBuildSteps();
|
|
572
|
+
const result = applyContextOverrides(steps, [], 'BUILD-flow', agentId);
|
|
573
|
+
expect(result).toEqual(steps);
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
// ---------------------------------------------------------------------------
|
|
578
|
+
// Context Probes
|
|
579
|
+
// ---------------------------------------------------------------------------
|
|
580
|
+
|
|
581
|
+
describe('Context Probes', () => {
|
|
582
|
+
let runtime: AgentRuntime;
|
|
583
|
+
|
|
584
|
+
beforeEach(() => {
|
|
585
|
+
runtime = createAgentRuntime({ agentId: 'test-probes', vaultPath: ':memory:' });
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
afterEach(() => {
|
|
589
|
+
runtime.close();
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it('should return probe results', async () => {
|
|
593
|
+
const probes = await runProbes(runtime, '/tmp/nonexistent');
|
|
594
|
+
expect(typeof probes.vault).toBe('boolean');
|
|
595
|
+
expect(typeof probes.brain).toBe('boolean');
|
|
596
|
+
expect(typeof probes.active).toBe('boolean');
|
|
597
|
+
expect(probes.active).toBe(true);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('should detect vault as available', async () => {
|
|
601
|
+
const probes = await runProbes(runtime, '/tmp/nonexistent');
|
|
602
|
+
expect(probes.vault).toBe(true); // :memory: vault is connected
|
|
603
|
+
});
|
|
604
|
+
});
|
|
@@ -308,9 +308,9 @@ describe('mergePlaybooks', () => {
|
|
|
308
308
|
});
|
|
309
309
|
|
|
310
310
|
describe('getAllBuiltinPlaybooks', () => {
|
|
311
|
-
it('should return
|
|
311
|
+
it('should return all built-in playbooks', () => {
|
|
312
312
|
const all = getAllBuiltinPlaybooks();
|
|
313
|
-
expect(all).
|
|
313
|
+
expect(all.length).toBeGreaterThanOrEqual(6);
|
|
314
314
|
});
|
|
315
315
|
|
|
316
316
|
it('should all be generic tier', () => {
|
|
@@ -101,32 +101,32 @@ describe('seedDefaultPlaybooks', () => {
|
|
|
101
101
|
vault.close();
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
-
it('should seed
|
|
104
|
+
it('should seed built-in playbooks into empty vault', () => {
|
|
105
105
|
const result = seedDefaultPlaybooks(vault);
|
|
106
|
-
expect(result.seeded).
|
|
106
|
+
expect(result.seeded).toBeGreaterThanOrEqual(6);
|
|
107
107
|
expect(result.skipped).toBe(0);
|
|
108
108
|
expect(result.errors).toBe(0);
|
|
109
|
-
expect(result.details).
|
|
109
|
+
expect(result.details.length).toBe(result.seeded);
|
|
110
110
|
expect(result.details.every((d) => d.action === 'seeded')).toBe(true);
|
|
111
111
|
|
|
112
112
|
// Verify they're in the vault
|
|
113
113
|
const entries = vault.list({ type: 'playbook' });
|
|
114
|
-
expect(entries).
|
|
114
|
+
expect(entries.length).toBe(result.seeded);
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
it('should be idempotent — second call skips all', () => {
|
|
118
|
-
seedDefaultPlaybooks(vault);
|
|
118
|
+
const first = seedDefaultPlaybooks(vault);
|
|
119
119
|
const result = seedDefaultPlaybooks(vault);
|
|
120
120
|
|
|
121
121
|
expect(result.seeded).toBe(0);
|
|
122
|
-
expect(result.skipped).toBe(
|
|
122
|
+
expect(result.skipped).toBe(first.seeded);
|
|
123
123
|
expect(result.errors).toBe(0);
|
|
124
124
|
expect(result.details.every((d) => d.action === 'skipped')).toBe(true);
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
it('should not overwrite user modifications', () => {
|
|
128
128
|
// Seed first
|
|
129
|
-
seedDefaultPlaybooks(vault);
|
|
129
|
+
const first = seedDefaultPlaybooks(vault);
|
|
130
130
|
|
|
131
131
|
// Simulate user modifying a playbook by removing and re-adding with different content
|
|
132
132
|
const builtins = getAllBuiltinPlaybooks();
|
|
@@ -143,7 +143,7 @@ describe('seedDefaultPlaybooks', () => {
|
|
|
143
143
|
|
|
144
144
|
// Re-seed should skip this one
|
|
145
145
|
const result = seedDefaultPlaybooks(vault);
|
|
146
|
-
expect(result.skipped).toBe(
|
|
146
|
+
expect(result.skipped).toBe(first.seeded); // all exist, including user-modified one
|
|
147
147
|
|
|
148
148
|
// Verify user's version is preserved
|
|
149
149
|
const entry = vault.get(builtins[0].id);
|