@soleri/core 9.14.4 → 9.16.7
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/deliver.flow.yaml +11 -0
- package/data/flows/design.flow.yaml +4 -14
- package/data/flows/enhance.flow.yaml +10 -0
- package/data/flows/explore.flow.yaml +16 -0
- package/data/flows/fix.flow.yaml +1 -1
- package/data/flows/review.flow.yaml +13 -4
- package/dist/brain/brain.d.ts +9 -0
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +11 -1
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +24 -0
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/types.d.ts +1 -0
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/capabilities/chain-mapping.d.ts.map +1 -1
- package/dist/capabilities/chain-mapping.js +5 -4
- package/dist/capabilities/chain-mapping.js.map +1 -1
- package/dist/capabilities/registry.d.ts +6 -0
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +3 -2
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/chat/chat-session.d.ts +6 -0
- package/dist/chat/chat-session.d.ts.map +1 -1
- package/dist/chat/chat-session.js +68 -17
- package/dist/chat/chat-session.js.map +1 -1
- package/dist/context/context-engine.js +1 -1
- package/dist/context/context-engine.js.map +1 -1
- package/dist/curator/curator.d.ts +6 -0
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +138 -0
- package/dist/curator/curator.js.map +1 -1
- package/dist/curator/types.d.ts +10 -0
- package/dist/curator/types.d.ts.map +1 -1
- package/dist/engine/bin/soleri-engine.js +0 -0
- package/dist/engine/core-ops.d.ts.map +1 -1
- package/dist/engine/core-ops.js +38 -1
- package/dist/engine/core-ops.js.map +1 -1
- package/dist/flows/epilogue.d.ts +5 -1
- package/dist/flows/epilogue.d.ts.map +1 -1
- package/dist/flows/epilogue.js +11 -3
- package/dist/flows/epilogue.js.map +1 -1
- package/dist/flows/executor.d.ts.map +1 -1
- package/dist/flows/executor.js +13 -5
- package/dist/flows/executor.js.map +1 -1
- package/dist/flows/index.d.ts +1 -2
- package/dist/flows/index.d.ts.map +1 -1
- package/dist/flows/index.js +1 -0
- package/dist/flows/index.js.map +1 -1
- package/dist/flows/plan-builder.d.ts +17 -1
- package/dist/flows/plan-builder.d.ts.map +1 -1
- package/dist/flows/plan-builder.js +67 -6
- package/dist/flows/plan-builder.js.map +1 -1
- package/dist/flows/probes.d.ts +1 -1
- package/dist/flows/probes.d.ts.map +1 -1
- package/dist/flows/probes.js +15 -3
- package/dist/flows/probes.js.map +1 -1
- package/dist/flows/types.d.ts +47 -20
- package/dist/flows/types.d.ts.map +1 -1
- package/dist/flows/types.js +6 -1
- package/dist/flows/types.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts +10 -4
- package/dist/intake/content-classifier.d.ts.map +1 -1
- package/dist/intake/content-classifier.js +19 -5
- package/dist/intake/content-classifier.js.map +1 -1
- package/dist/intake/text-ingester.d.ts +18 -0
- package/dist/intake/text-ingester.d.ts.map +1 -1
- package/dist/intake/text-ingester.js +37 -13
- package/dist/intake/text-ingester.js.map +1 -1
- package/dist/packs/pack-installer.d.ts.map +1 -1
- package/dist/packs/pack-installer.js +28 -2
- package/dist/packs/pack-installer.js.map +1 -1
- package/dist/planning/planner-types.d.ts +2 -0
- package/dist/planning/planner-types.d.ts.map +1 -1
- package/dist/planning/planner.d.ts +4 -0
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +50 -4
- package/dist/planning/planner.js.map +1 -1
- package/dist/playbooks/playbook-executor.d.ts +10 -1
- package/dist/playbooks/playbook-executor.d.ts.map +1 -1
- package/dist/playbooks/playbook-executor.js +8 -2
- package/dist/playbooks/playbook-executor.js.map +1 -1
- package/dist/playbooks/playbook-types.d.ts +8 -0
- package/dist/playbooks/playbook-types.d.ts.map +1 -1
- package/dist/plugins/types.d.ts +2 -2
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
- package/dist/runtime/admin-extra-ops.js +30 -0
- 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 +60 -21
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/admin-setup-ops.d.ts +11 -0
- package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
- package/dist/runtime/admin-setup-ops.js +146 -37
- package/dist/runtime/admin-setup-ops.js.map +1 -1
- package/dist/runtime/capture-ops.d.ts.map +1 -1
- package/dist/runtime/capture-ops.js +38 -12
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
- package/dist/runtime/facades/brain-facade.js +16 -4
- package/dist/runtime/facades/brain-facade.js.map +1 -1
- package/dist/runtime/facades/context-facade.d.ts.map +1 -1
- package/dist/runtime/facades/context-facade.js +9 -3
- package/dist/runtime/facades/context-facade.js.map +1 -1
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
- package/dist/runtime/facades/memory-facade.js +20 -7
- package/dist/runtime/facades/memory-facade.js.map +1 -1
- package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -1
- package/dist/runtime/facades/orchestrate-facade.js +40 -1
- package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
- package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
- package/dist/runtime/facades/plan-facade.js +113 -4
- package/dist/runtime/facades/plan-facade.js.map +1 -1
- package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
- package/dist/runtime/facades/vault-facade.js +24 -3
- package/dist/runtime/facades/vault-facade.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts +21 -0
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +132 -38
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +16 -0
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/schema-helpers.d.ts.map +1 -1
- package/dist/runtime/schema-helpers.js +4 -0
- package/dist/runtime/schema-helpers.js.map +1 -1
- package/dist/runtime/types.d.ts +19 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
- package/dist/runtime/vault-linking-ops.js +16 -3
- package/dist/runtime/vault-linking-ops.js.map +1 -1
- package/dist/scheduler/cron-validator.d.ts +15 -0
- package/dist/scheduler/cron-validator.d.ts.map +1 -0
- package/dist/scheduler/cron-validator.js +93 -0
- package/dist/scheduler/cron-validator.js.map +1 -0
- package/dist/scheduler/platform-linux.d.ts +14 -0
- package/dist/scheduler/platform-linux.d.ts.map +1 -0
- package/dist/scheduler/platform-linux.js +107 -0
- package/dist/scheduler/platform-linux.js.map +1 -0
- package/dist/scheduler/platform-macos.d.ts +15 -0
- package/dist/scheduler/platform-macos.d.ts.map +1 -0
- package/dist/scheduler/platform-macos.js +131 -0
- package/dist/scheduler/platform-macos.js.map +1 -0
- package/dist/scheduler/scheduler-ops.d.ts +14 -0
- package/dist/scheduler/scheduler-ops.d.ts.map +1 -0
- package/dist/scheduler/scheduler-ops.js +77 -0
- package/dist/scheduler/scheduler-ops.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +55 -0
- package/dist/scheduler/scheduler.d.ts.map +1 -0
- package/dist/scheduler/scheduler.js +144 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/dist/scheduler/types.d.ts +48 -0
- package/dist/scheduler/types.d.ts.map +1 -0
- package/dist/scheduler/types.js +6 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/skills/sync-skills.d.ts +11 -0
- package/dist/skills/sync-skills.d.ts.map +1 -1
- package/dist/skills/sync-skills.js +132 -38
- package/dist/skills/sync-skills.js.map +1 -1
- package/dist/skills/validate-skills.d.ts +32 -0
- package/dist/skills/validate-skills.d.ts.map +1 -0
- package/dist/skills/validate-skills.js +396 -0
- package/dist/skills/validate-skills.js.map +1 -0
- package/dist/utils/worktree-reaper.d.ts +38 -0
- package/dist/utils/worktree-reaper.d.ts.map +1 -0
- package/dist/utils/worktree-reaper.js +85 -0
- package/dist/utils/worktree-reaper.js.map +1 -0
- package/dist/vault/default-canonical-tags.d.ts +15 -0
- package/dist/vault/default-canonical-tags.d.ts.map +1 -0
- package/dist/vault/default-canonical-tags.js +65 -0
- package/dist/vault/default-canonical-tags.js.map +1 -0
- package/dist/vault/scope-detector.d.ts.map +1 -1
- package/dist/vault/scope-detector.js +37 -4
- package/dist/vault/scope-detector.js.map +1 -1
- package/dist/vault/tag-normalizer.d.ts +42 -0
- package/dist/vault/tag-normalizer.d.ts.map +1 -0
- package/dist/vault/tag-normalizer.js +157 -0
- package/dist/vault/tag-normalizer.js.map +1 -0
- package/dist/vault/vault-entries.d.ts.map +1 -1
- package/dist/vault/vault-entries.js +3 -1
- package/dist/vault/vault-entries.js.map +1 -1
- package/package.json +5 -1
- package/src/__tests__/embeddings.test.ts +3 -3
- package/src/agency/agency-manager.test.ts +4 -4
- package/src/agency/default-rules.test.ts +0 -13
- package/src/brain/brain-intelligence.test.ts +0 -5
- package/src/brain/brain.ts +25 -1
- package/src/brain/intelligence.ts +25 -0
- package/src/brain/second-brain-features.test.ts +2 -14
- package/src/brain/types.ts +1 -0
- package/src/capabilities/chain-mapping.test.ts +1 -6
- package/src/capabilities/chain-mapping.ts +6 -4
- package/src/capabilities/registry.test.ts +1 -1
- package/src/capabilities/registry.ts +9 -2
- package/src/chat/agent-loop.test.ts +1 -1
- package/src/chat/chat-enhanced.test.ts +0 -8
- package/src/chat/chat-session.ts +75 -17
- package/src/chat/chat-transport.test.ts +31 -1
- package/src/claudemd/compose.test.ts +0 -5
- package/src/context/context-engine.test.ts +0 -1
- package/src/context/context-engine.ts +1 -1
- package/src/control/intent-router.test.ts +2 -2
- package/src/curator/curator.ts +180 -0
- package/src/curator/tag-manager.test.ts +0 -4
- package/src/curator/types.ts +10 -0
- package/src/domain-packs/types.test.ts +0 -5
- package/src/dream/dream.test.ts +0 -7
- package/src/enforcement/registry.test.ts +2 -2
- package/src/engine/core-ops.test.ts +4 -22
- package/src/engine/core-ops.ts +36 -1
- package/src/engine/module-manifest.test.ts +1 -31
- package/src/engine/register-engine.test.ts +3 -33
- package/src/errors/retry.test.ts +3 -1
- package/src/flows/chain-runner.test.ts +0 -6
- package/src/flows/context-router.test.ts +3 -3
- package/src/flows/epilogue.test.ts +40 -2
- package/src/flows/epilogue.ts +11 -2
- package/src/flows/executor.test.ts +48 -2
- package/src/flows/executor.ts +15 -5
- package/src/flows/index.ts +1 -3
- package/src/flows/plan-builder.test.ts +201 -0
- package/src/flows/plan-builder.ts +81 -5
- package/src/flows/probes.ts +17 -3
- package/src/flows/types.ts +31 -2
- package/src/health/health-registry.test.ts +3 -1
- package/src/index.ts +24 -0
- package/src/intake/content-classifier.ts +22 -4
- package/src/intake/dedup-gate.test.ts +2 -6
- package/src/intake/text-ingester.test.ts +3 -4
- package/src/intake/text-ingester.ts +61 -12
- package/src/llm/llm-client.test.ts +1 -1
- package/src/llm/utils.test.ts +1 -1
- package/src/migrations/migration-runner.test.ts +0 -1
- package/src/operator/operator-context-store.test.ts +0 -13
- package/src/operator/operator-profile.test.ts +2 -20
- package/src/packs/pack-installer.ts +28 -2
- package/src/packs/pack-system.test.ts +2 -2
- package/src/persona/defaults.test.ts +19 -19
- package/src/planning/gap-passes.test.ts +0 -46
- package/src/planning/gap-patterns.test.ts +0 -42
- package/src/planning/goal-ancestry.test.ts +3 -1
- package/src/planning/plan-lifecycle.test.ts +15 -7
- package/src/planning/planner-types.ts +2 -0
- package/src/planning/planner.test.ts +86 -90
- package/src/planning/planner.ts +56 -4
- package/src/planning/reconciliation-engine.test.ts +3 -10
- package/src/planning/task-complexity-assessor.test.ts +0 -5
- package/src/planning/task-verifier.test.ts +3 -1
- package/src/playbooks/generic/generic-playbooks.test.ts +0 -28
- package/src/playbooks/index.test.ts +0 -55
- package/src/playbooks/playbook-executor.test.ts +76 -0
- package/src/playbooks/playbook-executor.ts +24 -3
- package/src/playbooks/playbook-types.ts +8 -0
- package/src/plugins/plugin-registry.test.ts +6 -2
- package/src/project/project-registry.test.ts +2 -0
- package/src/queue/async-infrastructure.test.ts +6 -4
- package/src/queue/job-queue.test.ts +13 -7
- package/src/runtime/admin-extra-ops.test.ts +35 -30
- package/src/runtime/admin-extra-ops.ts +30 -0
- package/src/runtime/admin-ops.test.ts +0 -4
- package/src/runtime/admin-ops.ts +63 -21
- package/src/runtime/admin-setup-ops.test.ts +229 -13
- package/src/runtime/admin-setup-ops.ts +145 -36
- package/src/runtime/archive-ops.test.ts +0 -28
- package/src/runtime/branching-ops.test.ts +0 -17
- package/src/runtime/capture-ops.test.ts +41 -16
- package/src/runtime/capture-ops.ts +78 -46
- package/src/runtime/chain-ops.test.ts +0 -21
- package/src/runtime/facades/admin-facade.test.ts +0 -34
- package/src/runtime/facades/agency-facade.test.ts +0 -39
- package/src/runtime/facades/archive-facade.test.ts +0 -43
- package/src/runtime/facades/brain-facade.test.ts +8 -99
- package/src/runtime/facades/brain-facade.ts +29 -12
- package/src/runtime/facades/branching-facade.test.ts +30 -17
- package/src/runtime/facades/chat-facade.test.ts +0 -91
- package/src/runtime/facades/chat-service-ops.test.ts +0 -24
- package/src/runtime/facades/chat-session-ops.test.ts +0 -12
- package/src/runtime/facades/chat-transport-ops.test.ts +0 -23
- package/src/runtime/facades/context-facade.test.ts +0 -17
- package/src/runtime/facades/context-facade.ts +11 -4
- package/src/runtime/facades/control-facade.test.ts +0 -30
- package/src/runtime/facades/curator-facade.test.ts +0 -33
- package/src/runtime/facades/intake-facade.test.ts +0 -33
- package/src/runtime/facades/links-facade.test.ts +0 -37
- package/src/runtime/facades/loop-facade.test.ts +0 -26
- package/src/runtime/facades/memory-facade.test.ts +0 -18
- package/src/runtime/facades/memory-facade.ts +27 -11
- package/src/runtime/facades/operator-facade.test.ts +0 -31
- package/src/runtime/facades/orchestrate-facade.test.ts +0 -21
- package/src/runtime/facades/orchestrate-facade.ts +39 -1
- package/src/runtime/facades/plan-facade.test.ts +7 -32
- package/src/runtime/facades/plan-facade.ts +137 -4
- package/src/runtime/facades/review-facade.test.ts +1 -49
- package/src/runtime/facades/sync-facade.test.ts +24 -41
- package/src/runtime/facades/tier-facade.test.ts +30 -22
- package/src/runtime/facades/vault-facade.test.ts +0 -41
- package/src/runtime/facades/vault-facade.ts +26 -3
- package/src/runtime/grading-ops.test.ts +0 -27
- package/src/runtime/intake-ops.test.ts +0 -19
- package/src/runtime/loop-ops.test.ts +0 -48
- package/src/runtime/memory-cross-project-ops.test.ts +0 -14
- package/src/runtime/memory-extra-ops.test.ts +4 -8
- package/src/runtime/orchestrate-ops.test.ts +238 -19
- package/src/runtime/orchestrate-ops.ts +166 -41
- package/src/runtime/pack-ops.test.ts +0 -26
- package/src/runtime/planning-extra-ops.test.ts +2 -14
- package/src/runtime/playbook-ops-execution.test.ts +9 -20
- package/src/runtime/playbook-ops.test.ts +4 -67
- package/src/runtime/review-ops.test.ts +0 -15
- package/src/runtime/runtime.ts +18 -0
- package/src/runtime/schema-helpers.ts +4 -0
- package/src/runtime/sync-ops.test.ts +0 -18
- package/src/runtime/tier-ops.test.ts +0 -21
- package/src/runtime/types.ts +19 -0
- package/src/runtime/vault-extra-ops.test.ts +0 -12
- package/src/runtime/vault-linking-ops.test.ts +0 -4
- package/src/runtime/vault-linking-ops.ts +26 -8
- package/src/runtime/vault-sharing-ops.test.ts +0 -9
- package/src/scheduler/cron-validator.ts +101 -0
- package/src/scheduler/platform-linux.ts +122 -0
- package/src/scheduler/platform-macos.ts +150 -0
- package/src/scheduler/scheduler-ops.ts +77 -0
- package/src/scheduler/scheduler.test.ts +247 -0
- package/src/scheduler/scheduler.ts +174 -0
- package/src/scheduler/types.ts +52 -0
- package/src/skills/__tests__/sync-skills.test.ts +6 -17
- package/src/skills/global-claude-md.test.ts +113 -0
- package/src/skills/sync-skills.ts +143 -35
- package/src/skills/validate-skills.test.ts +206 -0
- package/src/skills/validate-skills.ts +470 -0
- package/src/telemetry/telemetry.test.ts +1 -0
- package/src/transport/http-server.test.ts +3 -0
- package/src/transport/session-manager.test.ts +3 -1
- package/src/transport/token-auth.test.ts +6 -9
- package/src/transport/ws-server.test.ts +10 -2
- package/src/utils/worktree-reaper.ts +113 -0
- package/src/vault/__tests__/vault-characterization.test.ts +0 -108
- package/src/vault/default-canonical-tags.ts +64 -0
- package/src/vault/linking.test.ts +0 -2
- package/src/vault/playbook.test.ts +4 -1
- package/src/vault/scope-detector.test.ts +3 -1
- package/src/vault/scope-detector.ts +42 -4
- package/src/vault/tag-normalizer.test.ts +214 -0
- package/src/vault/tag-normalizer.ts +188 -0
- package/src/vault/vault-connect.test.ts +1 -1
- package/src/vault/vault-entries.ts +3 -1
- package/src/vault/vault.test.ts +23 -8
- package/dist/embeddings/index.d.ts +0 -5
- package/dist/embeddings/index.d.ts.map +0 -1
- package/dist/embeddings/index.js +0 -3
- package/dist/embeddings/index.js.map +0 -1
package/src/flows/epilogue.ts
CHANGED
|
@@ -22,18 +22,27 @@ export async function runEpilogue(
|
|
|
22
22
|
probes: ProbeResults,
|
|
23
23
|
projectPath: string,
|
|
24
24
|
summary: string,
|
|
25
|
+
planContext?: { intent?: string; objective?: string; domain?: string },
|
|
25
26
|
): Promise<{ captured: boolean; sessionId?: string }> {
|
|
26
27
|
let captured = false;
|
|
27
28
|
let sessionId: string | undefined;
|
|
28
29
|
|
|
29
30
|
// Capture knowledge to vault
|
|
30
31
|
if (probes.vault) {
|
|
32
|
+
const intent = planContext?.intent?.toUpperCase() ?? 'FLOW';
|
|
33
|
+
const objective = planContext?.objective ?? summary;
|
|
34
|
+
const title = `${intent} execution — ${objective}`.slice(0, 120);
|
|
35
|
+
const tags = [
|
|
36
|
+
'auto-captured',
|
|
37
|
+
intent.toLowerCase(),
|
|
38
|
+
...(planContext?.domain ? [planContext.domain] : []),
|
|
39
|
+
];
|
|
31
40
|
try {
|
|
32
41
|
await dispatch('capture_knowledge', {
|
|
33
|
-
title
|
|
42
|
+
title,
|
|
34
43
|
content: summary,
|
|
35
44
|
type: 'workflow',
|
|
36
|
-
tags
|
|
45
|
+
tags,
|
|
37
46
|
projectPath,
|
|
38
47
|
});
|
|
39
48
|
captured = true;
|
|
@@ -230,9 +230,9 @@ describe('FlowExecutor', () => {
|
|
|
230
230
|
expect(result.planId).toBe('test-plan');
|
|
231
231
|
expect(result.totalSteps).toBe(2);
|
|
232
232
|
expect(result.stepsCompleted).toBe(2);
|
|
233
|
-
expect(result.durationMs).
|
|
233
|
+
expect(typeof result.durationMs).toBe('number');
|
|
234
234
|
expect(result.stepResults).toHaveLength(2);
|
|
235
|
-
expect(result.stepResults[0].durationMs).
|
|
235
|
+
expect(typeof result.stepResults[0].durationMs).toBe('number');
|
|
236
236
|
});
|
|
237
237
|
|
|
238
238
|
it('returns failed status when a step has a STOP gate that fails', async () => {
|
|
@@ -260,4 +260,50 @@ describe('FlowExecutor', () => {
|
|
|
260
260
|
expect(result.stepResults[0].gateResult?.message).toBe('Blocked');
|
|
261
261
|
});
|
|
262
262
|
});
|
|
263
|
+
|
|
264
|
+
describe('step context (output → input flow)', () => {
|
|
265
|
+
it('passes prior step outputs as context to subsequent steps', async () => {
|
|
266
|
+
const received: Array<Record<string, unknown>> = [];
|
|
267
|
+
|
|
268
|
+
const dispatch = vi.fn(async (tool: string, params: Record<string, unknown>) => {
|
|
269
|
+
received.push({ tool, context: params.context });
|
|
270
|
+
return {
|
|
271
|
+
tool,
|
|
272
|
+
status: 'ok',
|
|
273
|
+
data: { 'vault-patterns': ['pattern-A', 'pattern-B'] },
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const executor = new FlowExecutor(dispatch);
|
|
278
|
+
const plan = makePlan([
|
|
279
|
+
step('search-vault', ['vault.search'], { output: ['vault-patterns'] }),
|
|
280
|
+
step('brainstorm', ['brain.recommend']),
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
await executor.execute(plan);
|
|
284
|
+
|
|
285
|
+
// Step 1 receives empty context
|
|
286
|
+
expect(received[0].context).toEqual({});
|
|
287
|
+
|
|
288
|
+
// Step 2 receives vault-patterns from step 1
|
|
289
|
+
expect(received[1].context).toEqual({ 'vault-patterns': ['pattern-A', 'pattern-B'] });
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('steps with no output declaration do not pollute context', async () => {
|
|
293
|
+
const received: Array<Record<string, unknown>> = [];
|
|
294
|
+
|
|
295
|
+
const dispatch = vi.fn(async (tool: string, params: Record<string, unknown>) => {
|
|
296
|
+
received.push({ tool, context: params.context });
|
|
297
|
+
return { tool, status: 'ok', data: { 'some-key': 'value' } };
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const executor = new FlowExecutor(dispatch);
|
|
301
|
+
const plan = makePlan([step('s1', ['tool-a']), step('s2', ['tool-b'])]);
|
|
302
|
+
|
|
303
|
+
await executor.execute(plan);
|
|
304
|
+
|
|
305
|
+
// No output declared on s1 — context stays empty for s2
|
|
306
|
+
expect(received[1].context).toEqual({});
|
|
307
|
+
});
|
|
308
|
+
});
|
|
263
309
|
});
|
package/src/flows/executor.ts
CHANGED
|
@@ -104,6 +104,9 @@ export class FlowExecutor {
|
|
|
104
104
|
let branchIterations = 0;
|
|
105
105
|
let currentIndex = 0;
|
|
106
106
|
|
|
107
|
+
// Accumulated outputs from completed steps — passed as context to subsequent dispatches
|
|
108
|
+
const stepContext: Record<string, unknown> = {};
|
|
109
|
+
|
|
107
110
|
// Set up persistence if configured
|
|
108
111
|
let runDir: string | undefined;
|
|
109
112
|
let manifest: PlanRunManifest | undefined;
|
|
@@ -118,12 +121,13 @@ export class FlowExecutor {
|
|
|
118
121
|
step.status = 'running';
|
|
119
122
|
|
|
120
123
|
const toolResults: StepResult['toolResults'] = {};
|
|
124
|
+
const dispatchParams = { stepId: step.id, planId: plan.planId, context: { ...stepContext } };
|
|
121
125
|
|
|
122
126
|
try {
|
|
123
127
|
if (step.parallel && step.tools.length > 1) {
|
|
124
128
|
// Execute tools in parallel
|
|
125
129
|
const results = await Promise.allSettled(
|
|
126
|
-
step.tools.map((tool) => this.dispatch(tool,
|
|
130
|
+
step.tools.map((tool) => this.dispatch(tool, dispatchParams)),
|
|
127
131
|
);
|
|
128
132
|
for (let i = 0; i < step.tools.length; i++) {
|
|
129
133
|
const toolName = step.tools[i];
|
|
@@ -144,10 +148,7 @@ export class FlowExecutor {
|
|
|
144
148
|
// Execute tools sequentially
|
|
145
149
|
for (const toolName of step.tools) {
|
|
146
150
|
try {
|
|
147
|
-
toolResults[toolName] = await this.dispatch(toolName,
|
|
148
|
-
stepId: step.id,
|
|
149
|
-
planId: plan.planId,
|
|
150
|
-
});
|
|
151
|
+
toolResults[toolName] = await this.dispatch(toolName, dispatchParams);
|
|
151
152
|
} catch (_err) {
|
|
152
153
|
toolResults[toolName] = {
|
|
153
154
|
tool: toolName,
|
|
@@ -180,6 +181,15 @@ export class FlowExecutor {
|
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
|
|
184
|
+
// Accumulate declared step outputs into stepContext for subsequent steps
|
|
185
|
+
if (step.output) {
|
|
186
|
+
for (const outputKey of step.output) {
|
|
187
|
+
if (outputKey in flatData) {
|
|
188
|
+
stepContext[outputKey] = flatData[outputKey];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
183
193
|
const verdict = evaluateGate(step.gate, flatData);
|
|
184
194
|
|
|
185
195
|
const stepResult: StepResult = {
|
package/src/flows/index.ts
CHANGED
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
export type {
|
|
7
7
|
Flow,
|
|
8
8
|
FlowStep,
|
|
9
|
-
Gate,
|
|
10
|
-
GateAction,
|
|
11
9
|
ProbeName,
|
|
12
10
|
ProbeResults,
|
|
13
11
|
PlanStep,
|
|
@@ -38,7 +36,7 @@ export {
|
|
|
38
36
|
|
|
39
37
|
// Context router
|
|
40
38
|
export { detectContext, applyContextOverrides, getFlowOverrides } from './context-router.js';
|
|
41
|
-
|
|
39
|
+
// ContextOverride is intentionally unexported — internal use only
|
|
42
40
|
|
|
43
41
|
// Gate evaluator
|
|
44
42
|
export { evaluateGate, evaluateCondition, extractScore, resolvePath } from './gate-evaluator.js';
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan-builder — colocated contract tests.
|
|
3
|
+
*
|
|
4
|
+
* Contract:
|
|
5
|
+
* - buildPlan() returns blocked:true with zero steps when a blocking capability's probe fails
|
|
6
|
+
* - buildPlan() skips (not blocks) steps whose optional probes are unavailable
|
|
7
|
+
* - buildPlan() builds a normal plan when all blocking capabilities are available
|
|
8
|
+
* - capabilityToProbe() maps known capability ID prefixes to probe names
|
|
9
|
+
* - capabilityToProbe() returns undefined for unmapped capabilities (no spurious blocking)
|
|
10
|
+
* - buildPlan() attaches vault constraints as recommendations (not gate steps)
|
|
11
|
+
* - buildPlan() marks mandatory entries and anti-patterns as mandatory:true in recommendations
|
|
12
|
+
* - buildPlan() includes recommendations in blocked plans
|
|
13
|
+
* - buildPlan() does not inject vault-gate-* steps
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
17
|
+
import { buildPlan, capabilityToProbe, type VaultConstraint } from './plan-builder.js';
|
|
18
|
+
import type { AgentRuntime } from '../runtime/types.js';
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Helpers
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
function makeRuntime(vaultAvailable: boolean, brainAvailable = false): AgentRuntime {
|
|
25
|
+
return {
|
|
26
|
+
vault: {
|
|
27
|
+
stats: vi.fn(() =>
|
|
28
|
+
vaultAvailable
|
|
29
|
+
? { totalEntries: 10 }
|
|
30
|
+
: (() => {
|
|
31
|
+
throw new Error('vault down');
|
|
32
|
+
})(),
|
|
33
|
+
),
|
|
34
|
+
},
|
|
35
|
+
brain: {
|
|
36
|
+
getVocabularySize: vi.fn(() => (brainAvailable ? 5 : 0)),
|
|
37
|
+
},
|
|
38
|
+
projectRegistry: {
|
|
39
|
+
list: vi.fn(() => []),
|
|
40
|
+
},
|
|
41
|
+
} as unknown as AgentRuntime;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// capabilityToProbe unit tests
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
describe('capabilityToProbe', () => {
|
|
49
|
+
it('maps vault.* capabilities to the vault probe', () => {
|
|
50
|
+
expect(capabilityToProbe('vault.search')).toBe('vault');
|
|
51
|
+
expect(capabilityToProbe('vault.load')).toBe('vault');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('maps brain.* capabilities to the brain probe', () => {
|
|
55
|
+
expect(capabilityToProbe('brain.recommend')).toBe('brain');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('returns undefined for capabilities with no probe mapping — unknown cap does not block', () => {
|
|
59
|
+
// An unmapped capability must never trigger a blocking halt.
|
|
60
|
+
// If this returned a valid probe name, unrelated capabilities would silently block flows.
|
|
61
|
+
expect(capabilityToProbe('auth.validate')).toBeUndefined();
|
|
62
|
+
expect(capabilityToProbe('unknown.op')).toBeUndefined();
|
|
63
|
+
expect(capabilityToProbe('')).toBeUndefined();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// buildPlan blocking behaviour
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
describe('buildPlan — blocking capability enforcement', () => {
|
|
72
|
+
it('returns blocked:true with zero steps when vault is down and vault.search is blocking', async () => {
|
|
73
|
+
// vault.search is in the blocking list of all 8 flows.
|
|
74
|
+
// When vault probe fails, the plan must halt — not silently skip steps.
|
|
75
|
+
const runtime = makeRuntime(false);
|
|
76
|
+
const plan = await buildPlan('BUILD', 'myagent', '/tmp/proj', runtime);
|
|
77
|
+
|
|
78
|
+
expect(plan.blocked).toBe(true);
|
|
79
|
+
expect(plan.steps).toHaveLength(0);
|
|
80
|
+
expect(plan.warnings[0]).toMatch(/Blocked/);
|
|
81
|
+
expect(plan.warnings[0]).toMatch(/vault\.search/);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('builds a normal plan when vault is available', async () => {
|
|
85
|
+
// Blocking check must pass through when the probe is healthy.
|
|
86
|
+
// If blocking fired regardless of probe state, no plan would ever build.
|
|
87
|
+
const runtime = makeRuntime(true);
|
|
88
|
+
const plan = await buildPlan('BUILD', 'myagent', '/tmp/proj', runtime);
|
|
89
|
+
|
|
90
|
+
expect(plan.blocked).toBeUndefined();
|
|
91
|
+
expect(plan.steps.length).toBeGreaterThanOrEqual(1);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('skips (not blocks) steps whose required probe is missing but not in blocking list', async () => {
|
|
95
|
+
// brain is not in the blocking list — its absence should skip brain-dependent
|
|
96
|
+
// steps with a warning, not halt the entire plan.
|
|
97
|
+
const runtime = makeRuntime(true, false); // vault up, brain down
|
|
98
|
+
const plan = await buildPlan('BUILD', 'myagent', '/tmp/proj', runtime);
|
|
99
|
+
|
|
100
|
+
expect(plan.blocked).toBeUndefined();
|
|
101
|
+
// Plan continues; brain-dependent steps are skipped or warnings added
|
|
102
|
+
const hasBrainWarning =
|
|
103
|
+
plan.warnings.some((w) => /brain/i.test(w)) ||
|
|
104
|
+
plan.skipped.some((s) => /brain/i.test(s.reason));
|
|
105
|
+
// Either skipped or warned — what matters is the plan is not blocked
|
|
106
|
+
expect(plan.steps.length).toBeGreaterThanOrEqual(0);
|
|
107
|
+
expect(plan.blocked).toBeUndefined();
|
|
108
|
+
// suppress unused-var lint
|
|
109
|
+
void hasBrainWarning;
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// buildPlan vault recommendations
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
describe('buildPlan — vault recommendations', () => {
|
|
118
|
+
it('attaches mandatory constraint as recommendation with mandatory:true', async () => {
|
|
119
|
+
// Critical vault entries must be surfaced as mandatory recommendations so the
|
|
120
|
+
// executor can enforce them. They must NOT become gate steps (evaluateCondition
|
|
121
|
+
// cannot parse free-text narrative — gates would always fire STOP).
|
|
122
|
+
const runtime = makeRuntime(true);
|
|
123
|
+
const constraint: VaultConstraint = {
|
|
124
|
+
entryId: 'crit-1',
|
|
125
|
+
title: 'No skipping tests',
|
|
126
|
+
context: 'Tests must not be skipped under time pressure.',
|
|
127
|
+
mandatory: true,
|
|
128
|
+
entryType: 'pattern',
|
|
129
|
+
};
|
|
130
|
+
const plan = await buildPlan('BUILD', 'myagent', '/tmp/proj', runtime, undefined, [constraint]);
|
|
131
|
+
|
|
132
|
+
const rec = plan.recommendations?.find((r) => r.entryId === 'crit-1');
|
|
133
|
+
expect(rec).toBeDefined();
|
|
134
|
+
expect(rec?.title).toBe('No skipping tests');
|
|
135
|
+
expect(rec?.context).toBe('Tests must not be skipped under time pressure.');
|
|
136
|
+
expect(rec?.mandatory).toBe(true);
|
|
137
|
+
expect(rec?.strength).toBe(100);
|
|
138
|
+
expect(rec?.source).toBe('vault');
|
|
139
|
+
// No gate step injected
|
|
140
|
+
expect(plan.steps.filter((s) => s.id.startsWith('vault-gate-'))).toHaveLength(0);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('marks anti-pattern entry as mandatory:true even when mandatory flag is false', async () => {
|
|
144
|
+
// anti-pattern entries are always treated as mandatory regardless of severity flag.
|
|
145
|
+
const runtime = makeRuntime(true);
|
|
146
|
+
const constraint: VaultConstraint = {
|
|
147
|
+
entryId: 'ap-1',
|
|
148
|
+
title: 'Avoid God Objects',
|
|
149
|
+
context: 'Classes must not exceed 500 lines.',
|
|
150
|
+
mandatory: false,
|
|
151
|
+
entryType: 'anti-pattern',
|
|
152
|
+
};
|
|
153
|
+
const plan = await buildPlan('BUILD', 'myagent', '/tmp/proj', runtime, undefined, [constraint]);
|
|
154
|
+
|
|
155
|
+
const rec = plan.recommendations?.find((r) => r.entryId === 'ap-1');
|
|
156
|
+
expect(rec).toBeDefined();
|
|
157
|
+
expect(rec?.mandatory).toBe(true);
|
|
158
|
+
expect(plan.steps.filter((s) => s.id.startsWith('vault-gate-'))).toHaveLength(0);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('does not attach recommendations when no constraints are passed', async () => {
|
|
162
|
+
// Backward compatibility: callers that omit vaultConstraints get an unchanged plan.
|
|
163
|
+
const runtime = makeRuntime(true);
|
|
164
|
+
const plan = await buildPlan('BUILD', 'myagent', '/tmp/proj', runtime);
|
|
165
|
+
expect(plan.recommendations).toBeUndefined();
|
|
166
|
+
expect(plan.steps.filter((s) => s.id.startsWith('vault-gate-'))).toHaveLength(0);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('attaches non-mandatory pattern as recommendation with mandatory:false', async () => {
|
|
170
|
+
// Warning and suggestion vault entries are surfaced as non-mandatory recommendations.
|
|
171
|
+
const runtime = makeRuntime(true);
|
|
172
|
+
const constraint: VaultConstraint = {
|
|
173
|
+
entryId: 'sug-1',
|
|
174
|
+
title: 'Consider using named exports',
|
|
175
|
+
mandatory: false,
|
|
176
|
+
entryType: 'pattern',
|
|
177
|
+
};
|
|
178
|
+
const plan = await buildPlan('BUILD', 'myagent', '/tmp/proj', runtime, undefined, [constraint]);
|
|
179
|
+
const rec = plan.recommendations?.find((r) => r.entryId === 'sug-1');
|
|
180
|
+
expect(rec).toBeDefined();
|
|
181
|
+
expect(rec?.mandatory).toBe(false);
|
|
182
|
+
expect(rec?.strength).toBe(80);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('includes recommendations in blocked plans', async () => {
|
|
186
|
+
// Blocked plans must still carry vault constraints so callers can surface them.
|
|
187
|
+
const runtime = makeRuntime(false); // vault down → blocked
|
|
188
|
+
const constraint: VaultConstraint = {
|
|
189
|
+
entryId: 'crit-2',
|
|
190
|
+
title: 'No direct DB writes outside repositories',
|
|
191
|
+
mandatory: true,
|
|
192
|
+
entryType: 'anti-pattern',
|
|
193
|
+
};
|
|
194
|
+
const plan = await buildPlan('BUILD', 'myagent', '/tmp/proj', runtime, undefined, [constraint]);
|
|
195
|
+
|
|
196
|
+
expect(plan.blocked).toBe(true);
|
|
197
|
+
const rec = plan.recommendations?.find((r) => r.entryId === 'crit-2');
|
|
198
|
+
expect(rec).toBeDefined();
|
|
199
|
+
expect(rec?.mandatory).toBe(true);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
OrchestrationPlan,
|
|
12
12
|
ProbeResults,
|
|
13
13
|
ProbeName,
|
|
14
|
+
VaultRecommendation,
|
|
14
15
|
} from './types.js';
|
|
15
16
|
import { loadFlowById } from './loader.js';
|
|
16
17
|
import { runProbes } from './probes.js';
|
|
@@ -56,6 +57,7 @@ export function chainToRequires(chain: string): ProbeName | undefined {
|
|
|
56
57
|
if (lower.startsWith('component') || lower.startsWith('token') || lower.startsWith('design'))
|
|
57
58
|
return 'designSystem';
|
|
58
59
|
if (lower.startsWith('session')) return 'sessionStore';
|
|
60
|
+
if (lower.startsWith('test')) return 'test';
|
|
59
61
|
// recommend-* and get-stack-* have no hard requirements
|
|
60
62
|
if (lower.startsWith('recommend') || lower.startsWith('get-stack')) return undefined;
|
|
61
63
|
return undefined;
|
|
@@ -119,6 +121,7 @@ export function flowStepsToPlanSteps(
|
|
|
119
121
|
tools,
|
|
120
122
|
parallel: step.parallel ?? false,
|
|
121
123
|
requires,
|
|
124
|
+
output: step.output,
|
|
122
125
|
status: 'pending',
|
|
123
126
|
};
|
|
124
127
|
|
|
@@ -155,6 +158,19 @@ export function flowStepsToPlanSteps(
|
|
|
155
158
|
});
|
|
156
159
|
}
|
|
157
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Map a capability ID (e.g. "vault.search") to the probe name that covers it.
|
|
163
|
+
* Returns undefined for capability IDs that have no corresponding probe.
|
|
164
|
+
*/
|
|
165
|
+
export function capabilityToProbe(capId: string): ProbeName | undefined {
|
|
166
|
+
if (capId.startsWith('vault.') || capId === 'vault') return 'vault';
|
|
167
|
+
if (capId.startsWith('brain.') || capId === 'brain') return 'brain';
|
|
168
|
+
if (capId.startsWith('design.') || capId.startsWith('component.') || capId.startsWith('token.'))
|
|
169
|
+
return 'designSystem';
|
|
170
|
+
if (capId.startsWith('session.')) return 'sessionStore';
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
158
174
|
/**
|
|
159
175
|
* Remove steps whose required capabilities are not available.
|
|
160
176
|
*/
|
|
@@ -181,6 +197,18 @@ export function pruneSteps(
|
|
|
181
197
|
return { kept, skipped };
|
|
182
198
|
}
|
|
183
199
|
|
|
200
|
+
/**
|
|
201
|
+
* A vault entry that should influence plan structure.
|
|
202
|
+
* critical severity OR anti-pattern type entries are surfaced as mandatory recommendations.
|
|
203
|
+
*/
|
|
204
|
+
export interface VaultConstraint {
|
|
205
|
+
entryId: string;
|
|
206
|
+
title: string;
|
|
207
|
+
context?: string;
|
|
208
|
+
mandatory: boolean;
|
|
209
|
+
entryType?: 'pattern' | 'anti-pattern' | 'rule' | 'playbook';
|
|
210
|
+
}
|
|
211
|
+
|
|
184
212
|
/**
|
|
185
213
|
* Build a full orchestration plan from intent, agent config, and runtime.
|
|
186
214
|
*/
|
|
@@ -190,6 +218,7 @@ export async function buildPlan(
|
|
|
190
218
|
projectPath: string,
|
|
191
219
|
runtime: AgentRuntime,
|
|
192
220
|
prompt?: string,
|
|
221
|
+
vaultConstraints: VaultConstraint[] = [],
|
|
193
222
|
): Promise<OrchestrationPlan> {
|
|
194
223
|
const normalizedIntent = intent.toUpperCase();
|
|
195
224
|
const flowId = INTENT_TO_FLOW[normalizedIntent] ?? 'BUILD-flow';
|
|
@@ -197,17 +226,63 @@ export async function buildPlan(
|
|
|
197
226
|
|
|
198
227
|
const probes = await runProbes(runtime, projectPath);
|
|
199
228
|
|
|
229
|
+
// Map vault constraints to recommendations — surfaced to executor as knowledge context.
|
|
230
|
+
// Anti-pattern entries are always mandatory regardless of the mandatory flag.
|
|
231
|
+
const recommendations: VaultRecommendation[] = vaultConstraints.map((c) => ({
|
|
232
|
+
entryId: c.entryId,
|
|
233
|
+
title: c.title,
|
|
234
|
+
...(c.context ? { context: c.context } : {}),
|
|
235
|
+
mandatory: c.mandatory || c.entryType === 'anti-pattern',
|
|
236
|
+
entryType: c.entryType,
|
|
237
|
+
source: 'vault' as const,
|
|
238
|
+
strength: c.mandatory ? 100 : 80,
|
|
239
|
+
}));
|
|
240
|
+
|
|
241
|
+
// Detect context entities from prompt before any early returns — blocked plans
|
|
242
|
+
// should still carry entity context so callers can surface useful information.
|
|
243
|
+
const entities = { components: [] as string[], actions: [] as string[] };
|
|
244
|
+
const contexts = prompt ? detectContext(prompt, entities) : [];
|
|
245
|
+
|
|
200
246
|
let steps: PlanStep[] = [];
|
|
201
247
|
let skipped: SkippedStep[] = [];
|
|
202
248
|
const warnings: string[] = [];
|
|
203
249
|
|
|
204
250
|
if (flow) {
|
|
251
|
+
// Check blocking capabilities before pruning optional steps.
|
|
252
|
+
// If any blocking capability maps to an unavailable probe, the plan cannot run.
|
|
253
|
+
const blockingCaps = flow['on-missing-capability']?.blocking ?? [];
|
|
254
|
+
const missingBlockers = blockingCaps.filter((capId) => {
|
|
255
|
+
const probe = capabilityToProbe(capId);
|
|
256
|
+
return probe !== undefined && !probes[probe];
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (missingBlockers.length > 0) {
|
|
260
|
+
return {
|
|
261
|
+
planId: randomUUID(),
|
|
262
|
+
intent: normalizedIntent,
|
|
263
|
+
flowId,
|
|
264
|
+
steps: [],
|
|
265
|
+
skipped: [],
|
|
266
|
+
epilogue: [],
|
|
267
|
+
warnings: [
|
|
268
|
+
`Blocked: required capabilities unavailable — ${missingBlockers.join(', ')}. Resolve these before running this flow.`,
|
|
269
|
+
],
|
|
270
|
+
summary: prompt ?? `${normalizedIntent} plan blocked`,
|
|
271
|
+
estimatedTools: 0,
|
|
272
|
+
blocked: true,
|
|
273
|
+
...(recommendations.length > 0 ? { recommendations } : {}),
|
|
274
|
+
context: {
|
|
275
|
+
intent: normalizedIntent,
|
|
276
|
+
probes,
|
|
277
|
+
entities,
|
|
278
|
+
projectPath,
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
205
283
|
let allSteps = flowStepsToPlanSteps(flow, agentId);
|
|
206
284
|
|
|
207
|
-
//
|
|
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) : [];
|
|
285
|
+
// Apply context-sensitive chain overrides (inject, skip, substitute) before pruning.
|
|
211
286
|
if (contexts.length > 0) {
|
|
212
287
|
allSteps = applyContextOverrides(allSteps, contexts, flowId, agentId);
|
|
213
288
|
}
|
|
@@ -240,10 +315,11 @@ export async function buildPlan(
|
|
|
240
315
|
warnings,
|
|
241
316
|
summary: prompt ?? `${normalizedIntent} plan with ${steps.length} step(s)`,
|
|
242
317
|
estimatedTools: steps.reduce((acc, s) => acc + s.tools.length, 0),
|
|
318
|
+
...(recommendations.length > 0 ? { recommendations } : {}),
|
|
243
319
|
context: {
|
|
244
320
|
intent: normalizedIntent,
|
|
245
321
|
probes,
|
|
246
|
-
entities
|
|
322
|
+
entities,
|
|
247
323
|
projectPath,
|
|
248
324
|
},
|
|
249
325
|
};
|
package/src/flows/probes.ts
CHANGED
|
@@ -9,19 +9,20 @@ import type { AgentRuntime } from '../runtime/types.js';
|
|
|
9
9
|
import type { ProbeResults } from './types.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Run all
|
|
12
|
+
* Run all capability probes in parallel and return results.
|
|
13
13
|
*/
|
|
14
14
|
export async function runProbes(runtime: AgentRuntime, projectPath: string): Promise<ProbeResults> {
|
|
15
|
-
const [vault, brain, designSystem, sessionStore, projectRules, active] = await Promise.all([
|
|
15
|
+
const [vault, brain, designSystem, sessionStore, projectRules, active, test] = await Promise.all([
|
|
16
16
|
probeVault(runtime),
|
|
17
17
|
probeBrain(runtime),
|
|
18
18
|
probeDesignSystem(runtime),
|
|
19
19
|
probeSessionStore(),
|
|
20
20
|
probeProjectRules(projectPath),
|
|
21
21
|
probeActive(),
|
|
22
|
+
probeTestRunner(projectPath),
|
|
22
23
|
]);
|
|
23
24
|
|
|
24
|
-
return { vault, brain, designSystem, sessionStore, projectRules, active };
|
|
25
|
+
return { vault, brain, designSystem, sessionStore, projectRules, active, test };
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
async function probeVault(runtime: AgentRuntime): Promise<boolean> {
|
|
@@ -68,3 +69,16 @@ async function probeActive(): Promise<boolean> {
|
|
|
68
69
|
// Always true when the engine is running
|
|
69
70
|
return true;
|
|
70
71
|
}
|
|
72
|
+
|
|
73
|
+
async function probeTestRunner(projectPath: string): Promise<boolean> {
|
|
74
|
+
try {
|
|
75
|
+
return (
|
|
76
|
+
existsSync(join(projectPath, 'vitest.config.ts')) ||
|
|
77
|
+
existsSync(join(projectPath, 'vitest.config.js')) ||
|
|
78
|
+
existsSync(join(projectPath, 'jest.config.ts')) ||
|
|
79
|
+
existsSync(join(projectPath, 'jest.config.js'))
|
|
80
|
+
);
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/flows/types.ts
CHANGED
|
@@ -75,6 +75,11 @@ export const flowSchema = z.object({
|
|
|
75
75
|
'min-confidence': z.enum(['HIGH', 'MEDIUM', 'LOW']).optional(),
|
|
76
76
|
}),
|
|
77
77
|
steps: z.array(flowStepSchema),
|
|
78
|
+
/**
|
|
79
|
+
* Scoring weights declared per step — parsed but not yet computed by the executor.
|
|
80
|
+
* Weighted-sum formula is not implemented; gate thresholds in steps are the active enforcement.
|
|
81
|
+
* @see https://github.com/adrozdenko/soleri/issues/632
|
|
82
|
+
*/
|
|
78
83
|
scoring: z
|
|
79
84
|
.object({
|
|
80
85
|
weights: z.record(z.number()),
|
|
@@ -100,7 +105,7 @@ export const flowSchema = z.object({
|
|
|
100
105
|
})
|
|
101
106
|
.optional(),
|
|
102
107
|
/** Strategy when a step's capability requirement is not satisfied */
|
|
103
|
-
|
|
108
|
+
'on-missing-capability': z
|
|
104
109
|
.object({
|
|
105
110
|
default: z.enum(['skip-with-warning', 'fail', 'ask-user']).default('skip-with-warning'),
|
|
106
111
|
blocking: z.array(z.string()).optional().default([]),
|
|
@@ -130,7 +135,8 @@ export type ProbeName =
|
|
|
130
135
|
| 'designSystem'
|
|
131
136
|
| 'sessionStore'
|
|
132
137
|
| 'projectRules'
|
|
133
|
-
| 'active'
|
|
138
|
+
| 'active'
|
|
139
|
+
| 'test';
|
|
134
140
|
|
|
135
141
|
export interface ProbeResults {
|
|
136
142
|
vault: boolean;
|
|
@@ -139,6 +145,7 @@ export interface ProbeResults {
|
|
|
139
145
|
sessionStore: boolean;
|
|
140
146
|
projectRules: boolean;
|
|
141
147
|
active: boolean;
|
|
148
|
+
test: boolean;
|
|
142
149
|
}
|
|
143
150
|
|
|
144
151
|
// ---------------------------------------------------------------------------
|
|
@@ -158,6 +165,8 @@ export interface PlanStep {
|
|
|
158
165
|
min?: number;
|
|
159
166
|
onFail?: { action: string; goto?: string; message?: string };
|
|
160
167
|
};
|
|
168
|
+
/** Output keys this step produces — merged into stepContext for subsequent steps. */
|
|
169
|
+
output?: string[];
|
|
161
170
|
status:
|
|
162
171
|
| 'pending'
|
|
163
172
|
| 'running'
|
|
@@ -175,6 +184,22 @@ export interface SkippedStep {
|
|
|
175
184
|
reason: string;
|
|
176
185
|
}
|
|
177
186
|
|
|
187
|
+
/**
|
|
188
|
+
* A vault knowledge entry surfaced as a planning constraint.
|
|
189
|
+
* Replaces gate-step injection — constraints are carried as metadata
|
|
190
|
+
* so the executor can apply judgment rather than mechanical evaluation.
|
|
191
|
+
*/
|
|
192
|
+
export interface VaultRecommendation {
|
|
193
|
+
entryId: string;
|
|
194
|
+
title: string;
|
|
195
|
+
context?: string;
|
|
196
|
+
example?: string;
|
|
197
|
+
mandatory: boolean;
|
|
198
|
+
entryType?: 'pattern' | 'anti-pattern' | 'rule' | 'playbook';
|
|
199
|
+
source: 'vault';
|
|
200
|
+
strength: number;
|
|
201
|
+
}
|
|
202
|
+
|
|
178
203
|
export interface ToolDeviation {
|
|
179
204
|
stepId: string;
|
|
180
205
|
expectedTools: string[];
|
|
@@ -198,6 +223,10 @@ export interface OrchestrationPlan {
|
|
|
198
223
|
workflowPrompt?: string;
|
|
199
224
|
/** Name of the matched workflow */
|
|
200
225
|
workflowName?: string;
|
|
226
|
+
/** True when a blocking capability is unavailable — plan cannot run */
|
|
227
|
+
blocked?: boolean;
|
|
228
|
+
/** Vault knowledge constraints relevant to this plan — executor reads these as context */
|
|
229
|
+
recommendations?: VaultRecommendation[];
|
|
201
230
|
}
|
|
202
231
|
|
|
203
232
|
export interface OrchestrationContext {
|
|
@@ -21,6 +21,7 @@ describe('HealthRegistry', () => {
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
it('tracks transitions: healthy -> degraded -> healthy', () => {
|
|
24
|
+
const before = Date.now();
|
|
24
25
|
const reg = new HealthRegistry();
|
|
25
26
|
reg.register('svc');
|
|
26
27
|
reg.update('svc', 'degraded', 'timeout');
|
|
@@ -31,7 +32,8 @@ describe('HealthRegistry', () => {
|
|
|
31
32
|
reg.update('svc', 'healthy');
|
|
32
33
|
expect(reg.get('svc')!.failureCount).toBe(0);
|
|
33
34
|
expect(reg.get('svc')!.lastError).toBeNull();
|
|
34
|
-
expect(reg.get('svc')!.lastHealthyAt).
|
|
35
|
+
expect(reg.get('svc')!.lastHealthyAt).toBeGreaterThanOrEqual(before);
|
|
36
|
+
expect(reg.get('svc')!.lastHealthyAt).toBeLessThanOrEqual(Date.now());
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
it('auto-registers on update if not previously registered', () => {
|