@soleri/core 2.1.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brain/brain.d.ts +10 -1
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +116 -13
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts +36 -1
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +119 -14
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/types.d.ts +34 -2
- package/dist/brain/types.d.ts.map +1 -1
- package/dist/cognee/client.d.ts +3 -0
- package/dist/cognee/client.d.ts.map +1 -1
- package/dist/cognee/client.js +17 -0
- package/dist/cognee/client.js.map +1 -1
- package/dist/cognee/sync-manager.d.ts +94 -0
- package/dist/cognee/sync-manager.d.ts.map +1 -0
- package/dist/cognee/sync-manager.js +293 -0
- package/dist/cognee/sync-manager.js.map +1 -0
- package/dist/control/identity-manager.d.ts +22 -0
- package/dist/control/identity-manager.d.ts.map +1 -0
- package/dist/control/identity-manager.js +233 -0
- package/dist/control/identity-manager.js.map +1 -0
- package/dist/control/intent-router.d.ts +32 -0
- package/dist/control/intent-router.d.ts.map +1 -0
- package/dist/control/intent-router.js +242 -0
- package/dist/control/intent-router.js.map +1 -0
- package/dist/control/types.d.ts +68 -0
- package/dist/control/types.d.ts.map +1 -0
- package/dist/control/types.js +9 -0
- package/dist/control/types.js.map +1 -0
- package/dist/curator/curator.d.ts +37 -1
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +199 -1
- package/dist/curator/curator.js.map +1 -1
- package/dist/errors/classify.d.ts +13 -0
- package/dist/errors/classify.d.ts.map +1 -0
- package/dist/errors/classify.js +97 -0
- package/dist/errors/classify.js.map +1 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/retry.d.ts +40 -0
- package/dist/errors/retry.d.ts.map +1 -0
- package/dist/errors/retry.js +97 -0
- package/dist/errors/retry.js.map +1 -0
- package/dist/errors/types.d.ts +48 -0
- package/dist/errors/types.d.ts.map +1 -0
- package/dist/errors/types.js +59 -0
- package/dist/errors/types.js.map +1 -0
- package/dist/facades/types.d.ts +1 -1
- package/dist/governance/governance.d.ts +42 -0
- package/dist/governance/governance.d.ts.map +1 -0
- package/dist/governance/governance.js +488 -0
- package/dist/governance/governance.js.map +1 -0
- package/dist/governance/index.d.ts +3 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/governance/types.d.ts +102 -0
- package/dist/governance/types.d.ts.map +1 -0
- package/dist/governance/types.js +3 -0
- package/dist/governance/types.js.map +1 -0
- package/dist/index.d.ts +52 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -1
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts +14 -0
- package/dist/intake/content-classifier.d.ts.map +1 -0
- package/dist/intake/content-classifier.js +125 -0
- package/dist/intake/content-classifier.js.map +1 -0
- package/dist/intake/dedup-gate.d.ts +17 -0
- package/dist/intake/dedup-gate.d.ts.map +1 -0
- package/dist/intake/dedup-gate.js +66 -0
- package/dist/intake/dedup-gate.js.map +1 -0
- package/dist/intake/intake-pipeline.d.ts +63 -0
- package/dist/intake/intake-pipeline.d.ts.map +1 -0
- package/dist/intake/intake-pipeline.js +373 -0
- package/dist/intake/intake-pipeline.js.map +1 -0
- package/dist/intake/types.d.ts +65 -0
- package/dist/intake/types.d.ts.map +1 -0
- package/dist/intake/types.js +3 -0
- package/dist/intake/types.js.map +1 -0
- package/dist/intelligence/loader.js +1 -1
- package/dist/intelligence/loader.js.map +1 -1
- package/dist/intelligence/types.d.ts +3 -1
- package/dist/intelligence/types.d.ts.map +1 -1
- package/dist/logging/logger.d.ts +37 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +145 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/logging/types.d.ts +19 -0
- package/dist/logging/types.d.ts.map +1 -0
- package/dist/logging/types.js +2 -0
- package/dist/logging/types.js.map +1 -0
- package/dist/loop/loop-manager.d.ts +100 -0
- package/dist/loop/loop-manager.d.ts.map +1 -0
- package/dist/loop/loop-manager.js +379 -0
- package/dist/loop/loop-manager.js.map +1 -0
- package/dist/loop/types.d.ts +103 -0
- package/dist/loop/types.d.ts.map +1 -0
- package/dist/loop/types.js +11 -0
- package/dist/loop/types.js.map +1 -0
- package/dist/persistence/index.d.ts +3 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +2 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/sqlite-provider.d.ts +25 -0
- package/dist/persistence/sqlite-provider.d.ts.map +1 -0
- package/dist/persistence/sqlite-provider.js +59 -0
- package/dist/persistence/sqlite-provider.js.map +1 -0
- package/dist/persistence/types.d.ts +36 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +8 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/planning/gap-analysis.d.ts +72 -0
- package/dist/planning/gap-analysis.d.ts.map +1 -0
- package/dist/planning/gap-analysis.js +442 -0
- package/dist/planning/gap-analysis.js.map +1 -0
- package/dist/planning/gap-types.d.ts +29 -0
- package/dist/planning/gap-types.d.ts.map +1 -0
- package/dist/planning/gap-types.js +28 -0
- package/dist/planning/gap-types.js.map +1 -0
- package/dist/planning/planner.d.ts +421 -4
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +949 -21
- package/dist/planning/planner.js.map +1 -1
- package/dist/playbooks/generic/brainstorming.d.ts +9 -0
- package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
- package/dist/playbooks/generic/brainstorming.js +105 -0
- package/dist/playbooks/generic/brainstorming.js.map +1 -0
- package/dist/playbooks/generic/code-review.d.ts +11 -0
- package/dist/playbooks/generic/code-review.d.ts.map +1 -0
- package/dist/playbooks/generic/code-review.js +176 -0
- package/dist/playbooks/generic/code-review.js.map +1 -0
- package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
- package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
- package/dist/playbooks/generic/subagent-execution.js +68 -0
- package/dist/playbooks/generic/subagent-execution.js.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
- package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
- package/dist/playbooks/generic/systematic-debugging.js +87 -0
- package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
- package/dist/playbooks/generic/tdd.d.ts +9 -0
- package/dist/playbooks/generic/tdd.d.ts.map +1 -0
- package/dist/playbooks/generic/tdd.js +70 -0
- package/dist/playbooks/generic/tdd.js.map +1 -0
- package/dist/playbooks/generic/verification.d.ts +9 -0
- package/dist/playbooks/generic/verification.d.ts.map +1 -0
- package/dist/playbooks/generic/verification.js +74 -0
- package/dist/playbooks/generic/verification.js.map +1 -0
- package/dist/playbooks/index.d.ts +4 -0
- package/dist/playbooks/index.d.ts.map +1 -0
- package/dist/playbooks/index.js +5 -0
- package/dist/playbooks/index.js.map +1 -0
- package/dist/playbooks/playbook-registry.d.ts +42 -0
- package/dist/playbooks/playbook-registry.d.ts.map +1 -0
- package/dist/playbooks/playbook-registry.js +227 -0
- package/dist/playbooks/playbook-registry.js.map +1 -0
- package/dist/playbooks/playbook-seeder.d.ts +47 -0
- package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
- package/dist/playbooks/playbook-seeder.js +104 -0
- package/dist/playbooks/playbook-seeder.js.map +1 -0
- package/dist/playbooks/playbook-types.d.ts +132 -0
- package/dist/playbooks/playbook-types.d.ts.map +1 -0
- package/dist/playbooks/playbook-types.js +12 -0
- package/dist/playbooks/playbook-types.js.map +1 -0
- package/dist/project/project-registry.d.ts +79 -0
- package/dist/project/project-registry.d.ts.map +1 -0
- package/dist/project/project-registry.js +274 -0
- package/dist/project/project-registry.js.map +1 -0
- package/dist/project/types.d.ts +28 -0
- package/dist/project/types.d.ts.map +1 -0
- package/dist/project/types.js +5 -0
- package/dist/project/types.js.map +1 -0
- package/dist/prompts/index.d.ts +4 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +3 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/parser.d.ts +17 -0
- package/dist/prompts/parser.d.ts.map +1 -0
- package/dist/prompts/parser.js +47 -0
- package/dist/prompts/parser.js.map +1 -0
- package/dist/prompts/template-manager.d.ts +25 -0
- package/dist/prompts/template-manager.d.ts.map +1 -0
- package/dist/prompts/template-manager.js +71 -0
- package/dist/prompts/template-manager.js.map +1 -0
- package/dist/prompts/types.d.ts +26 -0
- package/dist/prompts/types.d.ts.map +1 -0
- package/dist/prompts/types.js +5 -0
- package/dist/prompts/types.js.map +1 -0
- package/dist/runtime/admin-extra-ops.d.ts +15 -0
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -0
- package/dist/runtime/admin-extra-ops.js +595 -0
- package/dist/runtime/admin-extra-ops.js.map +1 -0
- package/dist/runtime/admin-ops.d.ts +15 -0
- package/dist/runtime/admin-ops.d.ts.map +1 -0
- package/dist/runtime/admin-ops.js +329 -0
- package/dist/runtime/admin-ops.js.map +1 -0
- package/dist/runtime/capture-ops.d.ts +15 -0
- package/dist/runtime/capture-ops.d.ts.map +1 -0
- package/dist/runtime/capture-ops.js +363 -0
- package/dist/runtime/capture-ops.js.map +1 -0
- package/dist/runtime/cognee-sync-ops.d.ts +12 -0
- package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
- package/dist/runtime/cognee-sync-ops.js +55 -0
- package/dist/runtime/cognee-sync-ops.js.map +1 -0
- package/dist/runtime/core-ops.d.ts +9 -3
- package/dist/runtime/core-ops.d.ts.map +1 -1
- package/dist/runtime/core-ops.js +693 -10
- package/dist/runtime/core-ops.js.map +1 -1
- package/dist/runtime/curator-extra-ops.d.ts +9 -0
- package/dist/runtime/curator-extra-ops.d.ts.map +1 -0
- package/dist/runtime/curator-extra-ops.js +71 -0
- package/dist/runtime/curator-extra-ops.js.map +1 -0
- package/dist/runtime/domain-ops.d.ts.map +1 -1
- package/dist/runtime/domain-ops.js +61 -15
- package/dist/runtime/domain-ops.js.map +1 -1
- package/dist/runtime/grading-ops.d.ts +14 -0
- package/dist/runtime/grading-ops.d.ts.map +1 -0
- package/dist/runtime/grading-ops.js +105 -0
- package/dist/runtime/grading-ops.js.map +1 -0
- package/dist/runtime/intake-ops.d.ts +14 -0
- package/dist/runtime/intake-ops.d.ts.map +1 -0
- package/dist/runtime/intake-ops.js +110 -0
- package/dist/runtime/intake-ops.js.map +1 -0
- package/dist/runtime/loop-ops.d.ts +14 -0
- package/dist/runtime/loop-ops.d.ts.map +1 -0
- package/dist/runtime/loop-ops.js +251 -0
- package/dist/runtime/loop-ops.js.map +1 -0
- package/dist/runtime/memory-cross-project-ops.d.ts +12 -0
- package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -0
- package/dist/runtime/memory-cross-project-ops.js +165 -0
- package/dist/runtime/memory-cross-project-ops.js.map +1 -0
- package/dist/runtime/memory-extra-ops.d.ts +13 -0
- package/dist/runtime/memory-extra-ops.d.ts.map +1 -0
- package/dist/runtime/memory-extra-ops.js +173 -0
- package/dist/runtime/memory-extra-ops.js.map +1 -0
- package/dist/runtime/orchestrate-ops.d.ts +17 -0
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -0
- package/dist/runtime/orchestrate-ops.js +246 -0
- package/dist/runtime/orchestrate-ops.js.map +1 -0
- package/dist/runtime/planning-extra-ops.d.ts +25 -0
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -0
- package/dist/runtime/planning-extra-ops.js +663 -0
- package/dist/runtime/planning-extra-ops.js.map +1 -0
- package/dist/runtime/playbook-ops.d.ts +14 -0
- package/dist/runtime/playbook-ops.d.ts.map +1 -0
- package/dist/runtime/playbook-ops.js +141 -0
- package/dist/runtime/playbook-ops.js.map +1 -0
- package/dist/runtime/project-ops.d.ts +15 -0
- package/dist/runtime/project-ops.d.ts.map +1 -0
- package/dist/runtime/project-ops.js +186 -0
- package/dist/runtime/project-ops.js.map +1 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +65 -3
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +29 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-extra-ops.d.ts +10 -0
- package/dist/runtime/vault-extra-ops.d.ts.map +1 -0
- package/dist/runtime/vault-extra-ops.js +536 -0
- package/dist/runtime/vault-extra-ops.js.map +1 -0
- package/dist/telemetry/telemetry.d.ts +48 -0
- package/dist/telemetry/telemetry.d.ts.map +1 -0
- package/dist/telemetry/telemetry.js +87 -0
- package/dist/telemetry/telemetry.js.map +1 -0
- package/dist/vault/playbook.d.ts +34 -0
- package/dist/vault/playbook.d.ts.map +1 -0
- package/dist/vault/playbook.js +60 -0
- package/dist/vault/playbook.js.map +1 -0
- package/dist/vault/vault.d.ts +97 -4
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +424 -65
- package/dist/vault/vault.js.map +1 -1
- package/package.json +7 -3
- package/src/__tests__/admin-extra-ops.test.ts +467 -0
- package/src/__tests__/admin-ops.test.ts +271 -0
- package/src/__tests__/brain-intelligence.test.ts +205 -0
- package/src/__tests__/brain.test.ts +134 -3
- package/src/__tests__/capture-ops.test.ts +509 -0
- package/src/__tests__/cognee-integration.test.ts +80 -0
- package/src/__tests__/cognee-sync-manager.test.ts +103 -0
- package/src/__tests__/core-ops.test.ts +292 -2
- package/src/__tests__/curator-extra-ops.test.ts +381 -0
- package/src/__tests__/domain-ops.test.ts +66 -0
- package/src/__tests__/errors.test.ts +388 -0
- package/src/__tests__/governance.test.ts +522 -0
- package/src/__tests__/grading-ops.test.ts +361 -0
- package/src/__tests__/identity-manager.test.ts +243 -0
- package/src/__tests__/intake-pipeline.test.ts +162 -0
- package/src/__tests__/intent-router.test.ts +222 -0
- package/src/__tests__/logger.test.ts +200 -0
- package/src/__tests__/loop-ops.test.ts +469 -0
- package/src/__tests__/memory-cross-project-ops.test.ts +248 -0
- package/src/__tests__/memory-extra-ops.test.ts +352 -0
- package/src/__tests__/orchestrate-ops.test.ts +289 -0
- package/src/__tests__/persistence.test.ts +225 -0
- package/src/__tests__/planner.test.ts +416 -7
- package/src/__tests__/planning-extra-ops.test.ts +706 -0
- package/src/__tests__/playbook-registry.test.ts +326 -0
- package/src/__tests__/playbook-seeder.test.ts +163 -0
- package/src/__tests__/playbook.test.ts +389 -0
- package/src/__tests__/project-ops.test.ts +381 -0
- package/src/__tests__/template-manager.test.ts +222 -0
- package/src/__tests__/vault-extra-ops.test.ts +482 -0
- package/src/brain/brain.ts +185 -16
- package/src/brain/intelligence.ts +179 -10
- package/src/brain/types.ts +40 -2
- package/src/cognee/client.ts +18 -0
- package/src/cognee/sync-manager.ts +389 -0
- package/src/control/identity-manager.ts +354 -0
- package/src/control/intent-router.ts +326 -0
- package/src/control/types.ts +102 -0
- package/src/curator/curator.ts +295 -1
- package/src/errors/classify.ts +102 -0
- package/src/errors/index.ts +5 -0
- package/src/errors/retry.ts +132 -0
- package/src/errors/types.ts +81 -0
- package/src/governance/governance.ts +698 -0
- package/src/governance/index.ts +18 -0
- package/src/governance/types.ts +111 -0
- package/src/index.ts +213 -2
- package/src/intake/content-classifier.ts +146 -0
- package/src/intake/dedup-gate.ts +92 -0
- package/src/intake/intake-pipeline.ts +503 -0
- package/src/intake/types.ts +69 -0
- package/src/intelligence/loader.ts +1 -1
- package/src/intelligence/types.ts +3 -1
- package/src/logging/logger.ts +154 -0
- package/src/logging/types.ts +21 -0
- package/src/loop/loop-manager.ts +448 -0
- package/src/loop/types.ts +115 -0
- package/src/persistence/index.ts +7 -0
- package/src/persistence/sqlite-provider.ts +62 -0
- package/src/persistence/types.ts +44 -0
- package/src/planning/gap-analysis.ts +775 -0
- package/src/planning/gap-types.ts +61 -0
- package/src/planning/planner.ts +1273 -24
- package/src/playbooks/generic/brainstorming.ts +110 -0
- package/src/playbooks/generic/code-review.ts +181 -0
- package/src/playbooks/generic/subagent-execution.ts +74 -0
- package/src/playbooks/generic/systematic-debugging.ts +92 -0
- package/src/playbooks/generic/tdd.ts +75 -0
- package/src/playbooks/generic/verification.ts +79 -0
- package/src/playbooks/index.ts +27 -0
- package/src/playbooks/playbook-registry.ts +284 -0
- package/src/playbooks/playbook-seeder.ts +119 -0
- package/src/playbooks/playbook-types.ts +162 -0
- package/src/project/project-registry.ts +370 -0
- package/src/project/types.ts +31 -0
- package/src/prompts/index.ts +3 -0
- package/src/prompts/parser.ts +59 -0
- package/src/prompts/template-manager.ts +77 -0
- package/src/prompts/types.ts +28 -0
- package/src/runtime/admin-extra-ops.ts +652 -0
- package/src/runtime/admin-ops.ts +340 -0
- package/src/runtime/capture-ops.ts +404 -0
- package/src/runtime/cognee-sync-ops.ts +63 -0
- package/src/runtime/core-ops.ts +787 -9
- package/src/runtime/curator-extra-ops.ts +85 -0
- package/src/runtime/domain-ops.ts +67 -15
- package/src/runtime/grading-ops.ts +130 -0
- package/src/runtime/intake-ops.ts +126 -0
- package/src/runtime/loop-ops.ts +277 -0
- package/src/runtime/memory-cross-project-ops.ts +191 -0
- package/src/runtime/memory-extra-ops.ts +186 -0
- package/src/runtime/orchestrate-ops.ts +278 -0
- package/src/runtime/planning-extra-ops.ts +718 -0
- package/src/runtime/playbook-ops.ts +169 -0
- package/src/runtime/project-ops.ts +202 -0
- package/src/runtime/runtime.ts +77 -3
- package/src/runtime/types.ts +29 -0
- package/src/runtime/vault-extra-ops.ts +606 -0
- package/src/telemetry/telemetry.ts +118 -0
- package/src/vault/playbook.ts +87 -0
- package/src/vault/vault.ts +575 -98
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { LoopManager } from '../loop/loop-manager.js';
|
|
3
|
+
import type { LoopConfig } from '../loop/types.js';
|
|
4
|
+
|
|
5
|
+
describe('LoopManager', () => {
|
|
6
|
+
let manager: LoopManager;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
manager = new LoopManager();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// ─── startLoop ─────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
it('starts a loop and returns initial state', () => {
|
|
15
|
+
const config: LoopConfig = {
|
|
16
|
+
mode: 'token-migration',
|
|
17
|
+
prompt: 'Migrate all hardcoded colors to semantic tokens',
|
|
18
|
+
maxIterations: 20,
|
|
19
|
+
targetScore: 95,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const state = manager.startLoop(config);
|
|
23
|
+
|
|
24
|
+
expect(state.id).toMatch(/^loop-\d+$/);
|
|
25
|
+
expect(state.config).toEqual(config);
|
|
26
|
+
expect(state.iterations).toEqual([]);
|
|
27
|
+
expect(state.status).toBe('active');
|
|
28
|
+
expect(state.startedAt).toBeTruthy();
|
|
29
|
+
expect(state.completedAt).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('throws if a loop is already active', () => {
|
|
33
|
+
manager.startLoop({
|
|
34
|
+
mode: 'custom',
|
|
35
|
+
prompt: 'first loop',
|
|
36
|
+
maxIterations: 5,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(() =>
|
|
40
|
+
manager.startLoop({
|
|
41
|
+
mode: 'custom',
|
|
42
|
+
prompt: 'second loop',
|
|
43
|
+
maxIterations: 5,
|
|
44
|
+
}),
|
|
45
|
+
).toThrow(/Loop already active/);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// ─── isActive ──────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
it('reports active state correctly', () => {
|
|
51
|
+
expect(manager.isActive()).toBe(false);
|
|
52
|
+
|
|
53
|
+
manager.startLoop({
|
|
54
|
+
mode: 'contrast-fix',
|
|
55
|
+
prompt: 'Fix contrast',
|
|
56
|
+
maxIterations: 10,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(manager.isActive()).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// ─── iterate ───────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
it('records iterations with incrementing numbers', () => {
|
|
65
|
+
manager.startLoop({
|
|
66
|
+
mode: 'component-build',
|
|
67
|
+
prompt: 'Build button',
|
|
68
|
+
maxIterations: 20,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const iter1 = manager.iterate({ passed: false, validationScore: 60 });
|
|
72
|
+
expect(iter1.iteration).toBe(1);
|
|
73
|
+
expect(iter1.passed).toBe(false);
|
|
74
|
+
expect(iter1.validationScore).toBe(60);
|
|
75
|
+
expect(iter1.timestamp).toBeTruthy();
|
|
76
|
+
|
|
77
|
+
const iter2 = manager.iterate({
|
|
78
|
+
passed: true,
|
|
79
|
+
validationScore: 95,
|
|
80
|
+
validationResult: 'All checks pass',
|
|
81
|
+
});
|
|
82
|
+
expect(iter2.iteration).toBe(2);
|
|
83
|
+
expect(iter2.passed).toBe(true);
|
|
84
|
+
expect(iter2.validationResult).toBe('All checks pass');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('throws if iterating with no active loop', () => {
|
|
88
|
+
expect(() => manager.iterate({ passed: true })).toThrow(/No active loop/);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('auto-closes loop on max iterations with failing result', () => {
|
|
92
|
+
manager.startLoop({
|
|
93
|
+
mode: 'custom',
|
|
94
|
+
prompt: 'Limited loop',
|
|
95
|
+
maxIterations: 3,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
manager.iterate({ passed: false, validationScore: 30 });
|
|
99
|
+
manager.iterate({ passed: false, validationScore: 50 });
|
|
100
|
+
const iter3 = manager.iterate({ passed: false, validationScore: 70 });
|
|
101
|
+
|
|
102
|
+
expect(iter3.passed).toBe(false);
|
|
103
|
+
expect(manager.isActive()).toBe(false);
|
|
104
|
+
|
|
105
|
+
const history = manager.getHistory();
|
|
106
|
+
expect(history).toHaveLength(1);
|
|
107
|
+
expect(history[0].status).toBe('max-iterations');
|
|
108
|
+
expect(history[0].completedAt).toBeTruthy();
|
|
109
|
+
expect(history[0].iterations).toHaveLength(3);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('does NOT auto-close on max iterations if last iteration passes', () => {
|
|
113
|
+
manager.startLoop({
|
|
114
|
+
mode: 'custom',
|
|
115
|
+
prompt: 'Might pass at the end',
|
|
116
|
+
maxIterations: 2,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
manager.iterate({ passed: false, validationScore: 40 });
|
|
120
|
+
// This passes, so auto-close should NOT trigger
|
|
121
|
+
manager.iterate({ passed: true, validationScore: 100 });
|
|
122
|
+
|
|
123
|
+
// Loop should still be active (user must call completeLoop explicitly)
|
|
124
|
+
expect(manager.isActive()).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ─── completeLoop ──────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
it('completes an active loop', () => {
|
|
130
|
+
manager.startLoop({
|
|
131
|
+
mode: 'plan-iteration',
|
|
132
|
+
prompt: 'Iterate plan to A+',
|
|
133
|
+
maxIterations: 10,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
manager.iterate({ passed: false, validationScore: 70 });
|
|
137
|
+
manager.iterate({ passed: true, validationScore: 95 });
|
|
138
|
+
|
|
139
|
+
const completed = manager.completeLoop();
|
|
140
|
+
|
|
141
|
+
expect(completed.status).toBe('completed');
|
|
142
|
+
expect(completed.completedAt).toBeTruthy();
|
|
143
|
+
expect(completed.iterations).toHaveLength(2);
|
|
144
|
+
expect(manager.isActive()).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('throws if completing with no active loop', () => {
|
|
148
|
+
expect(() => manager.completeLoop()).toThrow(/No active loop to complete/);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// ─── cancelLoop ────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
it('cancels an active loop', () => {
|
|
154
|
+
manager.startLoop({
|
|
155
|
+
mode: 'token-migration',
|
|
156
|
+
prompt: 'Migrate tokens',
|
|
157
|
+
maxIterations: 20,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
manager.iterate({ passed: false, validationScore: 30 });
|
|
161
|
+
|
|
162
|
+
const cancelled = manager.cancelLoop();
|
|
163
|
+
|
|
164
|
+
expect(cancelled.status).toBe('cancelled');
|
|
165
|
+
expect(cancelled.completedAt).toBeTruthy();
|
|
166
|
+
expect(cancelled.iterations).toHaveLength(1);
|
|
167
|
+
expect(manager.isActive()).toBe(false);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('throws if cancelling with no active loop', () => {
|
|
171
|
+
expect(() => manager.cancelLoop()).toThrow(/No active loop to cancel/);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// ─── getStatus ─────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
it('returns null when no loop is active', () => {
|
|
177
|
+
expect(manager.getStatus()).toBeNull();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('returns the active loop state', () => {
|
|
181
|
+
const config: LoopConfig = {
|
|
182
|
+
mode: 'contrast-fix',
|
|
183
|
+
prompt: 'Fix contrast',
|
|
184
|
+
maxIterations: 15,
|
|
185
|
+
};
|
|
186
|
+
manager.startLoop(config);
|
|
187
|
+
|
|
188
|
+
const status = manager.getStatus();
|
|
189
|
+
expect(status).not.toBeNull();
|
|
190
|
+
expect(status!.config.mode).toBe('contrast-fix');
|
|
191
|
+
expect(status!.status).toBe('active');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// ─── getHistory ────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
it('returns empty history initially', () => {
|
|
197
|
+
expect(manager.getHistory()).toEqual([]);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('accumulates completed loops in history', () => {
|
|
201
|
+
// First loop — complete
|
|
202
|
+
manager.startLoop({ mode: 'custom', prompt: 'loop 1', maxIterations: 10 });
|
|
203
|
+
manager.iterate({ passed: true, validationScore: 100 });
|
|
204
|
+
manager.completeLoop();
|
|
205
|
+
|
|
206
|
+
// Second loop — cancel
|
|
207
|
+
manager.startLoop({ mode: 'custom', prompt: 'loop 2', maxIterations: 10 });
|
|
208
|
+
manager.cancelLoop();
|
|
209
|
+
|
|
210
|
+
// Third loop — max-iterations
|
|
211
|
+
manager.startLoop({ mode: 'custom', prompt: 'loop 3', maxIterations: 1 });
|
|
212
|
+
manager.iterate({ passed: false, validationScore: 10 });
|
|
213
|
+
|
|
214
|
+
const history = manager.getHistory();
|
|
215
|
+
expect(history).toHaveLength(3);
|
|
216
|
+
expect(history[0].status).toBe('completed');
|
|
217
|
+
expect(history[1].status).toBe('cancelled');
|
|
218
|
+
expect(history[2].status).toBe('max-iterations');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('returns a copy of history (not internal array reference)', () => {
|
|
222
|
+
manager.startLoop({ mode: 'custom', prompt: 'test', maxIterations: 10 });
|
|
223
|
+
manager.completeLoop();
|
|
224
|
+
|
|
225
|
+
const history1 = manager.getHistory();
|
|
226
|
+
const history2 = manager.getHistory();
|
|
227
|
+
expect(history1).not.toBe(history2);
|
|
228
|
+
expect(history1).toEqual(history2);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// ─── can start a new loop after completing one ─────────────────
|
|
232
|
+
|
|
233
|
+
it('allows starting a new loop after completing the previous', () => {
|
|
234
|
+
manager.startLoop({ mode: 'custom', prompt: 'first', maxIterations: 5 });
|
|
235
|
+
manager.completeLoop();
|
|
236
|
+
|
|
237
|
+
const second = manager.startLoop({ mode: 'custom', prompt: 'second', maxIterations: 5 });
|
|
238
|
+
expect(second.status).toBe('active');
|
|
239
|
+
expect(manager.isActive()).toBe(true);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('allows starting a new loop after cancelling the previous', () => {
|
|
243
|
+
manager.startLoop({ mode: 'custom', prompt: 'first', maxIterations: 5 });
|
|
244
|
+
manager.cancelLoop();
|
|
245
|
+
|
|
246
|
+
const second = manager.startLoop({ mode: 'custom', prompt: 'second', maxIterations: 5 });
|
|
247
|
+
expect(second.status).toBe('active');
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe('createLoopOps', () => {
|
|
252
|
+
// Integration-style tests using the ops factory directly.
|
|
253
|
+
// We create a minimal runtime mock with just the loop manager.
|
|
254
|
+
|
|
255
|
+
let manager: LoopManager;
|
|
256
|
+
let ops: Awaited<ReturnType<typeof import('../runtime/loop-ops.js').createLoopOps>>;
|
|
257
|
+
|
|
258
|
+
beforeEach(async () => {
|
|
259
|
+
manager = new LoopManager();
|
|
260
|
+
const { createLoopOps } = await import('../runtime/loop-ops.js');
|
|
261
|
+
// Minimal runtime mock — only `loop` is needed for loop ops
|
|
262
|
+
const mockRuntime = { loop: manager } as import('../runtime/types.js').AgentRuntime;
|
|
263
|
+
ops = createLoopOps(mockRuntime);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
function findOp(name: string) {
|
|
267
|
+
const op = ops.find((o) => o.name === name);
|
|
268
|
+
if (!op) throw new Error(`Op not found: ${name}`);
|
|
269
|
+
return op;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
it('exports 9 loop ops', () => {
|
|
273
|
+
expect(ops).toHaveLength(9);
|
|
274
|
+
const names = ops.map((o) => o.name).sort();
|
|
275
|
+
expect(names).toEqual([
|
|
276
|
+
'loop_anomaly_check',
|
|
277
|
+
'loop_cancel',
|
|
278
|
+
'loop_complete',
|
|
279
|
+
'loop_history',
|
|
280
|
+
'loop_is_active',
|
|
281
|
+
'loop_iterate',
|
|
282
|
+
'loop_iterate_gate',
|
|
283
|
+
'loop_start',
|
|
284
|
+
'loop_status',
|
|
285
|
+
]);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('loop_start creates a new loop with default max iterations', async () => {
|
|
289
|
+
const op = findOp('loop_start');
|
|
290
|
+
const result = (await op.handler({
|
|
291
|
+
mode: 'token-migration',
|
|
292
|
+
prompt: 'Migrate tokens',
|
|
293
|
+
})) as Record<string, unknown>;
|
|
294
|
+
|
|
295
|
+
expect(result.started).toBe(true);
|
|
296
|
+
expect(result.loopId).toMatch(/^loop-\d+$/);
|
|
297
|
+
expect(result.mode).toBe('token-migration');
|
|
298
|
+
expect(result.maxIterations).toBe(20);
|
|
299
|
+
expect(result.targetScore).toBe(95);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('loop_start uses custom max iterations and target score', async () => {
|
|
303
|
+
const op = findOp('loop_start');
|
|
304
|
+
const result = (await op.handler({
|
|
305
|
+
mode: 'custom',
|
|
306
|
+
prompt: 'Custom task',
|
|
307
|
+
maxIterations: 5,
|
|
308
|
+
targetScore: 80,
|
|
309
|
+
})) as Record<string, unknown>;
|
|
310
|
+
|
|
311
|
+
expect(result.maxIterations).toBe(5);
|
|
312
|
+
expect(result.targetScore).toBe(80);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('loop_iterate records results', async () => {
|
|
316
|
+
await findOp('loop_start').handler({ mode: 'custom', prompt: 'Test' });
|
|
317
|
+
|
|
318
|
+
const result = (await findOp('loop_iterate').handler({
|
|
319
|
+
passed: false,
|
|
320
|
+
validationScore: 50,
|
|
321
|
+
validationResult: 'Needs work',
|
|
322
|
+
})) as Record<string, unknown>;
|
|
323
|
+
|
|
324
|
+
expect(result.iteration).toBe(1);
|
|
325
|
+
expect(result.passed).toBe(false);
|
|
326
|
+
expect(result.validationScore).toBe(50);
|
|
327
|
+
expect(result.loopActive).toBe(true);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('loop_status returns null when no loop active', async () => {
|
|
331
|
+
const result = (await findOp('loop_status').handler({})) as Record<string, unknown>;
|
|
332
|
+
expect(result.active).toBe(false);
|
|
333
|
+
expect(result.loop).toBeNull();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('loop_status returns active loop data', async () => {
|
|
337
|
+
await findOp('loop_start').handler({
|
|
338
|
+
mode: 'contrast-fix',
|
|
339
|
+
prompt: 'Fix contrast issues',
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const result = (await findOp('loop_status').handler({})) as Record<string, unknown>;
|
|
343
|
+
expect(result.active).toBe(true);
|
|
344
|
+
const loop = result.loop as Record<string, unknown>;
|
|
345
|
+
expect((loop.config as Record<string, unknown>).mode).toBe('contrast-fix');
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('loop_cancel cancels the active loop', async () => {
|
|
349
|
+
await findOp('loop_start').handler({ mode: 'custom', prompt: 'Test' });
|
|
350
|
+
|
|
351
|
+
const result = (await findOp('loop_cancel').handler({})) as Record<string, unknown>;
|
|
352
|
+
expect(result.cancelled).toBe(true);
|
|
353
|
+
expect(result.status).toBe('cancelled');
|
|
354
|
+
|
|
355
|
+
const status = (await findOp('loop_is_active').handler({})) as Record<string, unknown>;
|
|
356
|
+
expect(status.active).toBe(false);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('loop_complete marks the loop as completed', async () => {
|
|
360
|
+
await findOp('loop_start').handler({ mode: 'custom', prompt: 'Test' });
|
|
361
|
+
await findOp('loop_iterate').handler({ passed: true, validationScore: 100 });
|
|
362
|
+
|
|
363
|
+
const result = (await findOp('loop_complete').handler({})) as Record<string, unknown>;
|
|
364
|
+
expect(result.completed).toBe(true);
|
|
365
|
+
expect(result.status).toBe('completed');
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('loop_history returns past loops', async () => {
|
|
369
|
+
// Complete a loop
|
|
370
|
+
await findOp('loop_start').handler({ mode: 'custom', prompt: 'Loop 1' });
|
|
371
|
+
await findOp('loop_complete').handler({});
|
|
372
|
+
|
|
373
|
+
// Cancel a loop
|
|
374
|
+
await findOp('loop_start').handler({ mode: 'custom', prompt: 'Loop 2' });
|
|
375
|
+
await findOp('loop_cancel').handler({});
|
|
376
|
+
|
|
377
|
+
const result = (await findOp('loop_history').handler({})) as Record<string, unknown>;
|
|
378
|
+
expect(result.count).toBe(2);
|
|
379
|
+
const loops = result.loops as Array<Record<string, unknown>>;
|
|
380
|
+
expect(loops[0].status).toBe('completed');
|
|
381
|
+
expect(loops[1].status).toBe('cancelled');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('loop_is_active returns false when no loop', async () => {
|
|
385
|
+
const result = (await findOp('loop_is_active').handler({})) as Record<string, unknown>;
|
|
386
|
+
expect(result.active).toBe(false);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('loop_iterate_gate accepts durationMs parameter', async () => {
|
|
390
|
+
await findOp('loop_start').handler({ mode: 'token-migration', prompt: 'Test with timing' });
|
|
391
|
+
|
|
392
|
+
const result = (await findOp('loop_iterate_gate').handler({
|
|
393
|
+
lastOutput: 'Some LLM output that does not complete',
|
|
394
|
+
durationMs: 15000,
|
|
395
|
+
})) as Record<string, unknown>;
|
|
396
|
+
|
|
397
|
+
expect(result.decision).toBeDefined();
|
|
398
|
+
// Should not have anomaly warning for normal duration
|
|
399
|
+
expect(result.anomalyWarning).toBeUndefined();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('loop_iterate_gate reports anomaly for fast + low-score iteration', async () => {
|
|
403
|
+
await findOp('loop_start').handler({ mode: 'token-migration', prompt: 'Test anomaly' });
|
|
404
|
+
|
|
405
|
+
// First iterate to set a low score baseline, then gate with fast duration
|
|
406
|
+
await findOp('loop_iterate').handler({ passed: false, validationScore: 20 });
|
|
407
|
+
|
|
408
|
+
const result = (await findOp('loop_iterate_gate').handler({
|
|
409
|
+
lastOutput: 'Quick response with low quality',
|
|
410
|
+
durationMs: 500, // Very fast — below 5000ms threshold for token-migration
|
|
411
|
+
})) as Record<string, unknown>;
|
|
412
|
+
|
|
413
|
+
// The gate still works, anomaly warning may or may not appear depending on score
|
|
414
|
+
expect(result.decision).toBeDefined();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('loop_anomaly_check returns inactive when no loop', async () => {
|
|
418
|
+
const result = (await findOp('loop_anomaly_check').handler({})) as Record<string, unknown>;
|
|
419
|
+
expect(result.active).toBe(false);
|
|
420
|
+
expect(result.anomalies).toEqual([]);
|
|
421
|
+
expect(result.summary).toBe('No active loop');
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('loop_anomaly_check detects consecutive failures', async () => {
|
|
425
|
+
await findOp('loop_start').handler({ mode: 'custom', prompt: 'Failing loop' });
|
|
426
|
+
await findOp('loop_iterate').handler({ passed: false, validationScore: 20 });
|
|
427
|
+
await findOp('loop_iterate').handler({ passed: false, validationScore: 25 });
|
|
428
|
+
await findOp('loop_iterate').handler({ passed: false, validationScore: 30 });
|
|
429
|
+
|
|
430
|
+
const result = (await findOp('loop_anomaly_check').handler({})) as {
|
|
431
|
+
active: boolean;
|
|
432
|
+
hasAnomalies: boolean;
|
|
433
|
+
anomalies: string[];
|
|
434
|
+
totalIterations: number;
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
expect(result.active).toBe(true);
|
|
438
|
+
expect(result.totalIterations).toBe(3);
|
|
439
|
+
expect(result.hasAnomalies).toBe(true);
|
|
440
|
+
expect(result.anomalies.some((a) => a.includes('consecutive failing'))).toBe(true);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('loop_anomaly_check returns no anomalies for passing loop', async () => {
|
|
444
|
+
await findOp('loop_start').handler({ mode: 'custom', prompt: 'Good loop' });
|
|
445
|
+
await findOp('loop_iterate').handler({ passed: true, validationScore: 90 });
|
|
446
|
+
|
|
447
|
+
const result = (await findOp('loop_anomaly_check').handler({})) as {
|
|
448
|
+
active: boolean;
|
|
449
|
+
hasAnomalies: boolean;
|
|
450
|
+
anomalies: string[];
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
expect(result.active).toBe(true);
|
|
454
|
+
expect(result.hasAnomalies).toBe(false);
|
|
455
|
+
expect(result.anomalies).toEqual([]);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('assigns correct auth levels', () => {
|
|
459
|
+
const readOps = ['loop_status', 'loop_history', 'loop_is_active', 'loop_anomaly_check'];
|
|
460
|
+
const writeOps = ['loop_start', 'loop_iterate', 'loop_cancel', 'loop_complete'];
|
|
461
|
+
|
|
462
|
+
for (const name of readOps) {
|
|
463
|
+
expect(findOp(name).auth).toBe('read');
|
|
464
|
+
}
|
|
465
|
+
for (const name of writeOps) {
|
|
466
|
+
expect(findOp(name).auth).toBe('write');
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
});
|