@soleri/core 9.3.0 → 9.4.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/intelligence.d.ts +5 -0
- package/dist/brain/intelligence.d.ts.map +1 -1
- package/dist/brain/intelligence.js +115 -26
- package/dist/brain/intelligence.js.map +1 -1
- package/dist/brain/learning-radar.d.ts +3 -3
- package/dist/brain/learning-radar.d.ts.map +1 -1
- package/dist/brain/learning-radar.js +8 -4
- package/dist/brain/learning-radar.js.map +1 -1
- package/dist/control/intent-router.d.ts +2 -2
- package/dist/control/intent-router.d.ts.map +1 -1
- package/dist/control/intent-router.js +35 -1
- package/dist/control/intent-router.js.map +1 -1
- package/dist/control/types.d.ts +10 -2
- package/dist/control/types.d.ts.map +1 -1
- package/dist/curator/curator.d.ts +4 -0
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +23 -1
- package/dist/curator/curator.js.map +1 -1
- package/dist/curator/schema.d.ts +1 -1
- package/dist/curator/schema.d.ts.map +1 -1
- package/dist/curator/schema.js +8 -0
- package/dist/curator/schema.js.map +1 -1
- package/dist/domain-packs/types.d.ts +6 -0
- package/dist/domain-packs/types.d.ts.map +1 -1
- package/dist/domain-packs/types.js +1 -0
- package/dist/domain-packs/types.js.map +1 -1
- package/dist/engine/module-manifest.d.ts +2 -0
- package/dist/engine/module-manifest.d.ts.map +1 -1
- package/dist/engine/module-manifest.js +117 -2
- package/dist/engine/module-manifest.js.map +1 -1
- package/dist/engine/register-engine.d.ts +9 -0
- package/dist/engine/register-engine.d.ts.map +1 -1
- package/dist/engine/register-engine.js +59 -1
- package/dist/engine/register-engine.js.map +1 -1
- package/dist/facades/types.d.ts +5 -1
- package/dist/facades/types.d.ts.map +1 -1
- package/dist/facades/types.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/operator/operator-context-store.d.ts +54 -0
- package/dist/operator/operator-context-store.d.ts.map +1 -0
- package/dist/operator/operator-context-store.js +434 -0
- package/dist/operator/operator-context-store.js.map +1 -0
- package/dist/operator/operator-context-types.d.ts +101 -0
- package/dist/operator/operator-context-types.d.ts.map +1 -0
- package/dist/operator/operator-context-types.js +27 -0
- package/dist/operator/operator-context-types.js.map +1 -0
- package/dist/packs/index.d.ts +2 -2
- package/dist/packs/index.d.ts.map +1 -1
- package/dist/packs/index.js +1 -1
- package/dist/packs/index.js.map +1 -1
- package/dist/packs/lockfile.d.ts +3 -0
- package/dist/packs/lockfile.d.ts.map +1 -1
- package/dist/packs/lockfile.js.map +1 -1
- package/dist/packs/types.d.ts +8 -2
- package/dist/packs/types.d.ts.map +1 -1
- package/dist/packs/types.js +6 -0
- package/dist/packs/types.js.map +1 -1
- package/dist/planning/plan-lifecycle.d.ts +12 -1
- package/dist/planning/plan-lifecycle.d.ts.map +1 -1
- package/dist/planning/plan-lifecycle.js +52 -19
- package/dist/planning/plan-lifecycle.js.map +1 -1
- package/dist/planning/planner-types.d.ts +6 -0
- package/dist/planning/planner-types.d.ts.map +1 -1
- package/dist/planning/planner.d.ts +21 -1
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +62 -3
- package/dist/planning/planner.js.map +1 -1
- package/dist/planning/task-complexity-assessor.d.ts +42 -0
- package/dist/planning/task-complexity-assessor.d.ts.map +1 -0
- package/dist/planning/task-complexity-assessor.js +132 -0
- package/dist/planning/task-complexity-assessor.js.map +1 -0
- package/dist/plugins/types.d.ts +18 -18
- package/dist/runtime/admin-ops.d.ts +1 -1
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +118 -3
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
- package/dist/runtime/admin-setup-ops.js +19 -9
- 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 +35 -7
- 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 +4 -2
- package/dist/runtime/facades/brain-facade.js.map +1 -1
- package/dist/runtime/facades/control-facade.d.ts.map +1 -1
- package/dist/runtime/facades/control-facade.js +8 -2
- package/dist/runtime/facades/control-facade.js.map +1 -1
- package/dist/runtime/facades/curator-facade.d.ts.map +1 -1
- package/dist/runtime/facades/curator-facade.js +13 -0
- package/dist/runtime/facades/curator-facade.js.map +1 -1
- package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
- package/dist/runtime/facades/memory-facade.js +10 -12
- 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 +36 -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 +20 -4
- package/dist/runtime/facades/plan-facade.js.map +1 -1
- package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
- package/dist/runtime/orchestrate-ops.js +109 -31
- package/dist/runtime/orchestrate-ops.js.map +1 -1
- package/dist/runtime/plan-feedback-helper.d.ts +21 -0
- package/dist/runtime/plan-feedback-helper.d.ts.map +1 -0
- package/dist/runtime/plan-feedback-helper.js +52 -0
- package/dist/runtime/plan-feedback-helper.js.map +1 -0
- package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
- package/dist/runtime/planning-extra-ops.js +73 -34
- package/dist/runtime/planning-extra-ops.js.map +1 -1
- package/dist/runtime/session-briefing.d.ts.map +1 -1
- package/dist/runtime/session-briefing.js +9 -1
- package/dist/runtime/session-briefing.js.map +1 -1
- package/dist/runtime/types.d.ts +3 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/skills/sync-skills.d.ts.map +1 -1
- package/dist/skills/sync-skills.js +13 -7
- package/dist/skills/sync-skills.js.map +1 -1
- package/package.json +1 -1
- package/src/brain/brain-intelligence.test.ts +30 -0
- package/src/brain/extraction-quality.test.ts +323 -0
- package/src/brain/intelligence.ts +133 -30
- package/src/brain/learning-radar.ts +8 -5
- package/src/brain/second-brain-features.test.ts +1 -1
- package/src/control/intent-router.test.ts +73 -3
- package/src/control/intent-router.ts +38 -1
- package/src/control/types.ts +13 -2
- package/src/curator/curator.test.ts +92 -0
- package/src/curator/curator.ts +29 -1
- package/src/curator/schema.ts +8 -0
- package/src/domain-packs/types.ts +8 -0
- package/src/engine/module-manifest.test.ts +51 -2
- package/src/engine/module-manifest.ts +119 -2
- package/src/engine/register-engine.test.ts +73 -1
- package/src/engine/register-engine.ts +61 -1
- package/src/facades/types.ts +5 -0
- package/src/index.ts +30 -0
- package/src/operator/operator-context-store.test.ts +698 -0
- package/src/operator/operator-context-store.ts +569 -0
- package/src/operator/operator-context-types.ts +139 -0
- package/src/packs/index.ts +3 -1
- package/src/packs/lockfile.ts +3 -0
- package/src/packs/types.ts +9 -0
- package/src/planning/plan-lifecycle.ts +80 -22
- package/src/planning/planner-types.ts +6 -0
- package/src/planning/planner.ts +74 -4
- package/src/planning/task-complexity-assessor.test.ts +302 -0
- package/src/planning/task-complexity-assessor.ts +180 -0
- package/src/runtime/admin-ops.test.ts +159 -3
- package/src/runtime/admin-ops.ts +123 -3
- package/src/runtime/admin-setup-ops.ts +30 -10
- package/src/runtime/capture-ops.test.ts +84 -0
- package/src/runtime/capture-ops.ts +35 -7
- package/src/runtime/facades/admin-facade.test.ts +1 -1
- package/src/runtime/facades/brain-facade.ts +6 -3
- package/src/runtime/facades/control-facade.ts +10 -2
- package/src/runtime/facades/curator-facade.ts +18 -0
- package/src/runtime/facades/memory-facade.test.ts +14 -12
- package/src/runtime/facades/memory-facade.ts +10 -12
- package/src/runtime/facades/orchestrate-facade.ts +33 -1
- package/src/runtime/facades/plan-facade.test.ts +213 -0
- package/src/runtime/facades/plan-facade.ts +23 -4
- package/src/runtime/orchestrate-ops.test.ts +404 -0
- package/src/runtime/orchestrate-ops.ts +129 -37
- package/src/runtime/plan-feedback-helper.test.ts +173 -0
- package/src/runtime/plan-feedback-helper.ts +63 -0
- package/src/runtime/planning-extra-ops.test.ts +43 -1
- package/src/runtime/planning-extra-ops.ts +96 -33
- package/src/runtime/session-briefing.test.ts +1 -0
- package/src/runtime/session-briefing.ts +10 -1
- package/src/runtime/types.ts +3 -0
- package/src/skills/sync-skills.ts +14 -7
- package/src/vault/vault-scaling.test.ts +5 -5
- package/vitest.config.ts +1 -0
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
OperationalMode,
|
|
16
16
|
IntentClassification,
|
|
17
17
|
ModeConfig,
|
|
18
|
+
MorphOptions,
|
|
18
19
|
MorphResult,
|
|
19
20
|
RoutingAccuracyReport,
|
|
20
21
|
} from './types.js';
|
|
@@ -132,6 +133,27 @@ const DEFAULT_MODES: ModeConfig[] = [
|
|
|
132
133
|
behaviorRules: ['Be helpful', 'Ask clarifying questions when needed'],
|
|
133
134
|
keywords: [],
|
|
134
135
|
},
|
|
136
|
+
{
|
|
137
|
+
mode: 'YOLO-MODE',
|
|
138
|
+
intent: 'yolo',
|
|
139
|
+
description: 'Autonomous execution — skip approval gates, execute directly',
|
|
140
|
+
behaviorRules: [
|
|
141
|
+
'Skip plan approval gates — execute tasks directly',
|
|
142
|
+
'Still run orchestrate_complete — knowledge capture is non-negotiable',
|
|
143
|
+
'Still run vault gather-before-execute — decisions must be informed',
|
|
144
|
+
'Hook pack must be installed — refuse to activate without yolo-safety hooks',
|
|
145
|
+
'User can exit with "exit YOLO" or session end',
|
|
146
|
+
],
|
|
147
|
+
keywords: [
|
|
148
|
+
'yolo',
|
|
149
|
+
'autonomous',
|
|
150
|
+
'fire-and-forget',
|
|
151
|
+
'hands-off',
|
|
152
|
+
'no-approval',
|
|
153
|
+
'skip-gates',
|
|
154
|
+
'full-auto',
|
|
155
|
+
],
|
|
156
|
+
},
|
|
135
157
|
];
|
|
136
158
|
|
|
137
159
|
// ─── Class ──────────────────────────────────────────────────────────
|
|
@@ -267,7 +289,7 @@ export class IntentRouter {
|
|
|
267
289
|
|
|
268
290
|
// ─── Mode Management ───────────────────────────────────────────────
|
|
269
291
|
|
|
270
|
-
morph(mode: OperationalMode): MorphResult {
|
|
292
|
+
morph(mode: OperationalMode, options?: MorphOptions): MorphResult {
|
|
271
293
|
// Handle "reset" as a built-in alias for GENERAL-MODE
|
|
272
294
|
const resolvedMode: OperationalMode = (mode as string) === 'reset' ? 'GENERAL-MODE' : mode;
|
|
273
295
|
|
|
@@ -283,6 +305,21 @@ export class IntentRouter {
|
|
|
283
305
|
throw new Error(`Unknown mode: ${mode}. Available: ${available}`);
|
|
284
306
|
}
|
|
285
307
|
|
|
308
|
+
// ─── YOLO-MODE activation gate ────────────────────────────────
|
|
309
|
+
// YOLO-MODE requires the yolo-safety hook pack to be installed.
|
|
310
|
+
// The CLI/facade layer provides hookPackInstalled based on filesystem check.
|
|
311
|
+
if (resolvedMode === 'YOLO-MODE' && !options?.hookPackInstalled) {
|
|
312
|
+
return {
|
|
313
|
+
previousMode: this.currentMode,
|
|
314
|
+
currentMode: this.currentMode, // unchanged — mode switch blocked
|
|
315
|
+
behaviorRules: this.getBehaviorRules(),
|
|
316
|
+
blocked: true,
|
|
317
|
+
error:
|
|
318
|
+
'YOLO-MODE requires the yolo-safety hook pack. ' +
|
|
319
|
+
'Install it with: soleri hooks add-pack yolo-safety',
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
286
323
|
const previousMode = this.currentMode;
|
|
287
324
|
this.currentMode = resolvedMode;
|
|
288
325
|
const behaviorRules = JSON.parse(row.behavior_rules) as string[];
|
package/src/control/types.ts
CHANGED
|
@@ -65,7 +65,8 @@ export type IntentType =
|
|
|
65
65
|
| 'explore'
|
|
66
66
|
| 'plan'
|
|
67
67
|
| 'review'
|
|
68
|
-
| 'general'
|
|
68
|
+
| 'general'
|
|
69
|
+
| 'yolo';
|
|
69
70
|
|
|
70
71
|
export type OperationalMode =
|
|
71
72
|
| 'BUILD-MODE'
|
|
@@ -77,7 +78,8 @@ export type OperationalMode =
|
|
|
77
78
|
| 'EXPLORE-MODE'
|
|
78
79
|
| 'PLAN-MODE'
|
|
79
80
|
| 'REVIEW-MODE'
|
|
80
|
-
| 'GENERAL-MODE'
|
|
81
|
+
| 'GENERAL-MODE'
|
|
82
|
+
| 'YOLO-MODE';
|
|
81
83
|
|
|
82
84
|
export interface IntentClassification {
|
|
83
85
|
intent: IntentType;
|
|
@@ -95,10 +97,19 @@ export interface ModeConfig {
|
|
|
95
97
|
keywords: string[];
|
|
96
98
|
}
|
|
97
99
|
|
|
100
|
+
export interface MorphOptions {
|
|
101
|
+
/** Whether the yolo-safety hook pack is installed. Required for YOLO-MODE activation. */
|
|
102
|
+
hookPackInstalled?: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
98
105
|
export interface MorphResult {
|
|
99
106
|
previousMode: OperationalMode;
|
|
100
107
|
currentMode: OperationalMode;
|
|
101
108
|
behaviorRules: string[];
|
|
109
|
+
/** Present when activation is refused (e.g. missing hook pack). */
|
|
110
|
+
error?: string;
|
|
111
|
+
/** When true, the mode switch was blocked. */
|
|
112
|
+
blocked?: boolean;
|
|
102
113
|
}
|
|
103
114
|
|
|
104
115
|
export interface RoutingAccuracyReport {
|
|
@@ -647,4 +647,96 @@ describe('Curator', () => {
|
|
|
647
647
|
expect(result.metrics.tagHealth).toBeLessThan(1);
|
|
648
648
|
});
|
|
649
649
|
});
|
|
650
|
+
|
|
651
|
+
// ─── Duplicate Dismissal ──────────────────────────────────────
|
|
652
|
+
|
|
653
|
+
describe('Duplicate Dismissal', () => {
|
|
654
|
+
it('dismissDuplicate stores a pair and detectDuplicates filters it out', () => {
|
|
655
|
+
// Add two similar entries that will be flagged as duplicates
|
|
656
|
+
vault.add(
|
|
657
|
+
makeEntry({
|
|
658
|
+
id: 'dup-a',
|
|
659
|
+
title: 'React hook cleanup pattern',
|
|
660
|
+
description:
|
|
661
|
+
'Always clean up useEffect hooks with return cleanup function to prevent memory leaks and stale closures.',
|
|
662
|
+
}),
|
|
663
|
+
);
|
|
664
|
+
vault.add(
|
|
665
|
+
makeEntry({
|
|
666
|
+
id: 'dup-b',
|
|
667
|
+
title: 'React hook cleanup pattern for effects',
|
|
668
|
+
description:
|
|
669
|
+
'Clean up useEffect hooks by returning a cleanup function to avoid memory leaks and stale closures.',
|
|
670
|
+
}),
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
// Verify they show up as duplicates
|
|
674
|
+
const before = curator.detectDuplicates();
|
|
675
|
+
const dupBefore = before.find(
|
|
676
|
+
(d) => d.entryId === 'dup-a' && d.matches.some((m) => m.entryId === 'dup-b'),
|
|
677
|
+
);
|
|
678
|
+
expect(dupBefore).toBeDefined();
|
|
679
|
+
|
|
680
|
+
// Dismiss the pair
|
|
681
|
+
const result = curator.dismissDuplicate('dup-a', 'dup-b', 'intentionally distinct');
|
|
682
|
+
expect(result.dismissed).toBe(true);
|
|
683
|
+
|
|
684
|
+
// Verify they no longer show up
|
|
685
|
+
const after = curator.detectDuplicates();
|
|
686
|
+
const dupAfter = after.find(
|
|
687
|
+
(d) => d.entryId === 'dup-a' && d.matches.some((m) => m.entryId === 'dup-b'),
|
|
688
|
+
);
|
|
689
|
+
expect(dupAfter).toBeUndefined();
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('dismissDuplicate works with swapped ID order', () => {
|
|
693
|
+
vault.add(
|
|
694
|
+
makeEntry({
|
|
695
|
+
id: 'sw-a',
|
|
696
|
+
title: 'Semantic color tokens for error states',
|
|
697
|
+
description: 'Use semantic tokens like text-error and bg-error-subtle for error UI.',
|
|
698
|
+
}),
|
|
699
|
+
);
|
|
700
|
+
vault.add(
|
|
701
|
+
makeEntry({
|
|
702
|
+
id: 'sw-b',
|
|
703
|
+
title: 'Semantic color tokens for error UI',
|
|
704
|
+
description:
|
|
705
|
+
'Always prefer semantic tokens text-error and bg-error-subtle over raw colors.',
|
|
706
|
+
}),
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
// Dismiss with b,a order
|
|
710
|
+
curator.dismissDuplicate('sw-b', 'sw-a');
|
|
711
|
+
|
|
712
|
+
// Should still be filtered (stored as sorted pair)
|
|
713
|
+
const results = curator.detectDuplicates('sw-a');
|
|
714
|
+
const match = results.find((r) => r.matches.some((m) => m.entryId === 'sw-b'));
|
|
715
|
+
expect(match).toBeUndefined();
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
it('dismissDuplicate is idempotent', () => {
|
|
719
|
+
vault.add(
|
|
720
|
+
makeEntry({
|
|
721
|
+
id: 'idem-a',
|
|
722
|
+
title: 'Pattern A idempotent test',
|
|
723
|
+
description: 'First entry for idempotency test of duplicate dismissal.',
|
|
724
|
+
}),
|
|
725
|
+
);
|
|
726
|
+
vault.add(
|
|
727
|
+
makeEntry({
|
|
728
|
+
id: 'idem-b',
|
|
729
|
+
title: 'Pattern A idempotent test duplicate',
|
|
730
|
+
description: 'Second entry for idempotency test of duplicate dismissal.',
|
|
731
|
+
}),
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
const first = curator.dismissDuplicate('idem-a', 'idem-b');
|
|
735
|
+
expect(first.dismissed).toBe(true);
|
|
736
|
+
|
|
737
|
+
// Second call should be a no-op (INSERT OR IGNORE)
|
|
738
|
+
const second = curator.dismissDuplicate('idem-a', 'idem-b');
|
|
739
|
+
expect(second.dismissed).toBe(false);
|
|
740
|
+
});
|
|
741
|
+
});
|
|
650
742
|
});
|
package/src/curator/curator.ts
CHANGED
|
@@ -151,7 +151,35 @@ export class Curator {
|
|
|
151
151
|
// ─── Duplicates (delegates to duplicate-detector) ─────────────
|
|
152
152
|
|
|
153
153
|
detectDuplicates(entryId?: string, threshold?: number): DuplicateDetectionResult[] {
|
|
154
|
-
|
|
154
|
+
const results = detectDuplicatesPure(this.vault.list({ limit: 100000 }), entryId, threshold);
|
|
155
|
+
// Filter out dismissed pairs
|
|
156
|
+
const dismissed = this.getDismissedPairs();
|
|
157
|
+
if (dismissed.size === 0) return results;
|
|
158
|
+
return results
|
|
159
|
+
.map((r) => ({
|
|
160
|
+
...r,
|
|
161
|
+
matches: r.matches.filter((m) => {
|
|
162
|
+
const key = [r.entryId, m.entryId].sort().join('::');
|
|
163
|
+
return !dismissed.has(key);
|
|
164
|
+
}),
|
|
165
|
+
}))
|
|
166
|
+
.filter((r) => r.matches.length > 0);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
dismissDuplicate(entryIdA: string, entryIdB: string, reason?: string): { dismissed: boolean } {
|
|
170
|
+
const [a, b] = [entryIdA, entryIdB].sort();
|
|
171
|
+
const result = this.provider.run(
|
|
172
|
+
'INSERT OR IGNORE INTO curator_duplicate_dismissals (entry_id_a, entry_id_b, reason) VALUES (?, ?, ?)',
|
|
173
|
+
[a, b, reason ?? 'reviewed — not duplicate'],
|
|
174
|
+
);
|
|
175
|
+
return { dismissed: result.changes > 0 };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private getDismissedPairs(): Set<string> {
|
|
179
|
+
const rows = this.provider.all<{ entry_id_a: string; entry_id_b: string }>(
|
|
180
|
+
'SELECT entry_id_a, entry_id_b FROM curator_duplicate_dismissals',
|
|
181
|
+
);
|
|
182
|
+
return new Set(rows.map((r) => `${r.entry_id_a}::${r.entry_id_b}`));
|
|
155
183
|
}
|
|
156
184
|
|
|
157
185
|
// ─── Contradictions (delegates to contradiction-detector) ─────
|
package/src/curator/schema.ts
CHANGED
|
@@ -56,6 +56,14 @@ export const CURATOR_SCHEMA = `
|
|
|
56
56
|
resolved_at INTEGER,
|
|
57
57
|
UNIQUE(pattern_id, antipattern_id)
|
|
58
58
|
);
|
|
59
|
+
CREATE TABLE IF NOT EXISTS curator_duplicate_dismissals (
|
|
60
|
+
entry_id_a TEXT NOT NULL,
|
|
61
|
+
entry_id_b TEXT NOT NULL,
|
|
62
|
+
dismissed_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
63
|
+
reason TEXT,
|
|
64
|
+
PRIMARY KEY (entry_id_a, entry_id_b)
|
|
65
|
+
);
|
|
66
|
+
|
|
59
67
|
CREATE INDEX IF NOT EXISTS idx_curator_state_status ON curator_entry_state(status);
|
|
60
68
|
CREATE INDEX IF NOT EXISTS idx_curator_changelog_entry ON curator_changelog(entry_id);
|
|
61
69
|
`;
|
|
@@ -67,12 +67,17 @@ export interface PackSkillDefinition {
|
|
|
67
67
|
// DomainPack — the main interface
|
|
68
68
|
// ---------------------------------------------------------------------------
|
|
69
69
|
|
|
70
|
+
/** Pack tier: determines visibility, licensing, and install behavior */
|
|
71
|
+
export type DomainPackTier = 'default' | 'community' | 'premium';
|
|
72
|
+
|
|
70
73
|
/** The contract every domain pack must implement. */
|
|
71
74
|
export interface DomainPack {
|
|
72
75
|
/** Unique pack name (e.g., 'design', 'security-intelligence') */
|
|
73
76
|
name: string;
|
|
74
77
|
/** Semver version */
|
|
75
78
|
version: string;
|
|
79
|
+
/** Tier: 'default' (ships with engine), 'community' (free, npm), 'premium' (unlocked today, gated later) */
|
|
80
|
+
tier?: DomainPackTier;
|
|
76
81
|
/** Domains this pack claims. Ops inject into these domain facades. */
|
|
77
82
|
domains: string[];
|
|
78
83
|
/** Custom operations with real logic — injected into claimed domain facades. */
|
|
@@ -115,6 +120,8 @@ export interface DomainPackRef {
|
|
|
115
120
|
package: string;
|
|
116
121
|
/** Optional version constraint */
|
|
117
122
|
version?: string;
|
|
123
|
+
/** Pack tier (inherited from pack if not set) */
|
|
124
|
+
tier?: DomainPackTier;
|
|
118
125
|
}
|
|
119
126
|
|
|
120
127
|
// ---------------------------------------------------------------------------
|
|
@@ -148,6 +155,7 @@ const packSkillSchema = z.object({
|
|
|
148
155
|
const domainPackSchema = z.object({
|
|
149
156
|
name: z.string().min(1),
|
|
150
157
|
version: z.string().min(1),
|
|
158
|
+
tier: z.enum(['default', 'community', 'premium']).optional(),
|
|
151
159
|
domains: z.array(z.string().min(1)).min(1),
|
|
152
160
|
ops: z.array(
|
|
153
161
|
z.object({
|
|
@@ -49,7 +49,7 @@ describe('ENGINE_MODULE_MANIFEST', () => {
|
|
|
49
49
|
expect(entry.description.length).toBeGreaterThan(0);
|
|
50
50
|
expect(Array.isArray(entry.keyOps)).toBe(true);
|
|
51
51
|
expect(entry.keyOps.length).toBeGreaterThan(0);
|
|
52
|
-
expect(entry.keyOps.length).toBeLessThanOrEqual(
|
|
52
|
+
expect(entry.keyOps.length).toBeLessThanOrEqual(5);
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
|
|
@@ -69,7 +69,13 @@ describe('ENGINE_MODULE_MANIFEST', () => {
|
|
|
69
69
|
|
|
70
70
|
it('plan module has expected keyOps', () => {
|
|
71
71
|
const plan = ENGINE_MODULE_MANIFEST.find((m) => m.suffix === 'plan')!;
|
|
72
|
-
expect(plan.keyOps).toEqual([
|
|
72
|
+
expect(plan.keyOps).toEqual([
|
|
73
|
+
'create_plan',
|
|
74
|
+
'approve_plan',
|
|
75
|
+
'plan_split',
|
|
76
|
+
'plan_reconcile',
|
|
77
|
+
'plan_close_stale',
|
|
78
|
+
]);
|
|
73
79
|
});
|
|
74
80
|
|
|
75
81
|
it('conditional field is optional and boolean when present', () => {
|
|
@@ -89,6 +95,49 @@ describe('ENGINE_MODULE_MANIFEST', () => {
|
|
|
89
95
|
expect(testEntry.suffix).toBe('test');
|
|
90
96
|
expect(testEntry.conditional).toBeUndefined();
|
|
91
97
|
});
|
|
98
|
+
|
|
99
|
+
it('intentSignals is optional and a Record<string, string> when present', () => {
|
|
100
|
+
for (const entry of ENGINE_MODULE_MANIFEST) {
|
|
101
|
+
if (entry.intentSignals !== undefined) {
|
|
102
|
+
expect(typeof entry.intentSignals).toBe('object');
|
|
103
|
+
for (const [phrase, op] of Object.entries(entry.intentSignals)) {
|
|
104
|
+
expect(typeof phrase).toBe('string');
|
|
105
|
+
expect(phrase.length).toBeGreaterThan(0);
|
|
106
|
+
expect(typeof op).toBe('string');
|
|
107
|
+
expect(op.length).toBeGreaterThan(0);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('every module has intentSignals defined', () => {
|
|
114
|
+
for (const entry of ENGINE_MODULE_MANIFEST) {
|
|
115
|
+
expect(entry.intentSignals).toBeDefined();
|
|
116
|
+
expect(Object.keys(entry.intentSignals!).length).toBeGreaterThanOrEqual(2);
|
|
117
|
+
expect(Object.keys(entry.intentSignals!).length).toBeLessThanOrEqual(6);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('intentSignals values reference known keyOps or valid op names', () => {
|
|
122
|
+
for (const entry of ENGINE_MODULE_MANIFEST) {
|
|
123
|
+
if (entry.intentSignals) {
|
|
124
|
+
for (const op of Object.values(entry.intentSignals)) {
|
|
125
|
+
// Op should be a non-empty snake_case string
|
|
126
|
+
expect(op).toMatch(/^[a-z][a-z0-9_]*$/);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('intentSignals phrases are unique across all modules', () => {
|
|
133
|
+
const allPhrases: string[] = [];
|
|
134
|
+
for (const entry of ENGINE_MODULE_MANIFEST) {
|
|
135
|
+
if (entry.intentSignals) {
|
|
136
|
+
allPhrases.push(...Object.keys(entry.intentSignals));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
expect(new Set(allPhrases).size).toBe(allPhrases.length);
|
|
140
|
+
});
|
|
92
141
|
});
|
|
93
142
|
|
|
94
143
|
describe('CORE_KEY_OPS', () => {
|
|
@@ -16,6 +16,8 @@ export interface ModuleManifestEntry {
|
|
|
16
16
|
keyOps: string[];
|
|
17
17
|
/** If true, module requires a runtime condition to register */
|
|
18
18
|
conditional?: boolean;
|
|
19
|
+
/** Intent phrases that map to this module's ops — used for dynamic tool routing */
|
|
20
|
+
intentSignals?: Record<string, string>;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -27,102 +29,217 @@ export const ENGINE_MODULE_MANIFEST: ModuleManifestEntry[] = [
|
|
|
27
29
|
suffix: 'vault',
|
|
28
30
|
description: 'Knowledge management — search, CRUD, capture, sharing scope.',
|
|
29
31
|
keyOps: ['search_intelligent', 'capture_knowledge', 'capture_quick'],
|
|
32
|
+
intentSignals: {
|
|
33
|
+
'search knowledge': 'search_intelligent',
|
|
34
|
+
'find pattern': 'search_intelligent',
|
|
35
|
+
'best practice': 'search_intelligent',
|
|
36
|
+
'save this': 'capture_knowledge',
|
|
37
|
+
'remember this': 'capture_knowledge',
|
|
38
|
+
'capture this': 'capture_quick',
|
|
39
|
+
},
|
|
30
40
|
},
|
|
31
41
|
{
|
|
32
42
|
suffix: 'plan',
|
|
33
43
|
description: 'Plan lifecycle — create, approve, execute, reconcile, complete, grading.',
|
|
34
|
-
keyOps: ['create_plan', 'approve_plan', 'plan_split', 'plan_reconcile'],
|
|
44
|
+
keyOps: ['create_plan', 'approve_plan', 'plan_split', 'plan_reconcile', 'plan_close_stale'],
|
|
45
|
+
intentSignals: {
|
|
46
|
+
'plan this': 'create_plan',
|
|
47
|
+
'break this down': 'create_plan',
|
|
48
|
+
'approve the plan': 'approve_plan',
|
|
49
|
+
'split into tasks': 'plan_split',
|
|
50
|
+
'how did it go': 'plan_reconcile',
|
|
51
|
+
},
|
|
35
52
|
},
|
|
36
53
|
{
|
|
37
54
|
suffix: 'brain',
|
|
38
55
|
description: 'Learning system — intelligence pipeline, strengths, feedback, sessions.',
|
|
39
56
|
keyOps: ['recommend', 'strengths', 'feedback'],
|
|
57
|
+
intentSignals: {
|
|
58
|
+
'what works': 'recommend',
|
|
59
|
+
recommendations: 'recommend',
|
|
60
|
+
'pattern strengths': 'strengths',
|
|
61
|
+
'give feedback': 'feedback',
|
|
62
|
+
},
|
|
40
63
|
},
|
|
41
64
|
{
|
|
42
65
|
suffix: 'memory',
|
|
43
66
|
description: 'Session & cross-project memory — capture, search, dedup, promote.',
|
|
44
67
|
keyOps: ['memory_search', 'memory_capture', 'session_capture'],
|
|
68
|
+
intentSignals: {
|
|
69
|
+
'recall past work': 'memory_search',
|
|
70
|
+
'what did we do': 'memory_search',
|
|
71
|
+
'last time': 'memory_search',
|
|
72
|
+
'wrap up session': 'session_capture',
|
|
73
|
+
'summarize session': 'session_capture',
|
|
74
|
+
},
|
|
45
75
|
},
|
|
46
76
|
{
|
|
47
77
|
suffix: 'admin',
|
|
48
78
|
description: 'Infrastructure — health, config, telemetry, tokens, LLM, prompts.',
|
|
49
79
|
keyOps: ['admin_health', 'admin_tool_list', 'admin_diagnostic'],
|
|
80
|
+
intentSignals: {
|
|
81
|
+
'health check': 'admin_health',
|
|
82
|
+
'system status': 'admin_health',
|
|
83
|
+
'what tools exist': 'admin_tool_list',
|
|
84
|
+
'run diagnostics': 'admin_diagnostic',
|
|
85
|
+
},
|
|
50
86
|
},
|
|
51
87
|
{
|
|
52
88
|
suffix: 'curator',
|
|
53
89
|
description: 'Quality — duplicate detection, contradictions, grooming, health audit.',
|
|
54
|
-
keyOps: ['curator_groom', 'curator_status', 'curator_health'],
|
|
90
|
+
keyOps: ['curator_groom', 'curator_status', 'curator_health', 'curator_dismiss_duplicate'],
|
|
91
|
+
intentSignals: {
|
|
92
|
+
'clean up vault': 'curator_groom',
|
|
93
|
+
'find duplicates': 'curator_groom',
|
|
94
|
+
'vault quality': 'curator_status',
|
|
95
|
+
'audit knowledge': 'curator_health',
|
|
96
|
+
},
|
|
55
97
|
},
|
|
56
98
|
{
|
|
57
99
|
suffix: 'loop',
|
|
58
100
|
description: 'Iterative validation loops — start, iterate, cancel, complete, history.',
|
|
59
101
|
keyOps: ['loop_start', 'loop_status', 'loop_cancel'],
|
|
102
|
+
intentSignals: {
|
|
103
|
+
'start a loop': 'loop_start',
|
|
104
|
+
'iterate on this': 'loop_start',
|
|
105
|
+
'loop status': 'loop_status',
|
|
106
|
+
'stop the loop': 'loop_cancel',
|
|
107
|
+
},
|
|
60
108
|
},
|
|
61
109
|
{
|
|
62
110
|
suffix: 'orchestrate',
|
|
63
111
|
description:
|
|
64
112
|
'Execution orchestration — project registration, playbooks, plan/execute/complete.',
|
|
65
113
|
keyOps: ['orchestrate_plan', 'orchestrate_execute', 'orchestrate_complete'],
|
|
114
|
+
intentSignals: {
|
|
115
|
+
'do this task': 'orchestrate_plan',
|
|
116
|
+
'build this': 'orchestrate_plan',
|
|
117
|
+
'execute the plan': 'orchestrate_execute',
|
|
118
|
+
'finish up': 'orchestrate_complete',
|
|
119
|
+
},
|
|
66
120
|
},
|
|
67
121
|
{
|
|
68
122
|
suffix: 'control',
|
|
69
123
|
description: 'Agent behavior — identity, intent routing, morphing, guidelines, governance.',
|
|
70
124
|
keyOps: ['route_intent', 'morph', 'get_behavior_rules'],
|
|
125
|
+
intentSignals: {
|
|
126
|
+
'what should I do': 'route_intent',
|
|
127
|
+
'change persona': 'morph',
|
|
128
|
+
'behavior rules': 'get_behavior_rules',
|
|
129
|
+
},
|
|
71
130
|
},
|
|
72
131
|
{
|
|
73
132
|
suffix: 'context',
|
|
74
133
|
description: 'Context analysis — entity extraction, knowledge retrieval, confidence scoring.',
|
|
75
134
|
keyOps: ['context_extract_entities', 'context_retrieve_knowledge', 'context_analyze'],
|
|
135
|
+
intentSignals: {
|
|
136
|
+
'extract entities': 'context_extract_entities',
|
|
137
|
+
'get context': 'context_retrieve_knowledge',
|
|
138
|
+
'analyze this': 'context_analyze',
|
|
139
|
+
},
|
|
76
140
|
},
|
|
77
141
|
{
|
|
78
142
|
suffix: 'agency',
|
|
79
143
|
description: 'Proactive intelligence — file watching, pattern surfacing, warnings.',
|
|
80
144
|
keyOps: ['agency_scan_file', 'agency_surface_patterns', 'agency_warnings'],
|
|
145
|
+
intentSignals: {
|
|
146
|
+
'scan this file': 'agency_scan_file',
|
|
147
|
+
'surface patterns': 'agency_surface_patterns',
|
|
148
|
+
'any warnings': 'agency_warnings',
|
|
149
|
+
},
|
|
81
150
|
},
|
|
82
151
|
{
|
|
83
152
|
suffix: 'chat',
|
|
84
153
|
description: 'Chat transport — session management, response chunking, authentication.',
|
|
85
154
|
keyOps: ['chat_send', 'chat_history', 'chat_session'],
|
|
155
|
+
intentSignals: {
|
|
156
|
+
'send message': 'chat_send',
|
|
157
|
+
'chat history': 'chat_history',
|
|
158
|
+
'start chat': 'chat_session',
|
|
159
|
+
},
|
|
86
160
|
},
|
|
87
161
|
{
|
|
88
162
|
suffix: 'operator',
|
|
89
163
|
description: 'Operator profile — personality learning, signals, adaptation.',
|
|
90
164
|
keyOps: ['profile_get', 'signal_accumulate', 'synthesis_check'],
|
|
165
|
+
intentSignals: {
|
|
166
|
+
'who am I': 'profile_get',
|
|
167
|
+
'my profile': 'profile_get',
|
|
168
|
+
'learn my style': 'signal_accumulate',
|
|
169
|
+
},
|
|
91
170
|
},
|
|
92
171
|
{
|
|
93
172
|
suffix: 'archive',
|
|
94
173
|
description: 'Archival, lifecycle, and knowledge maintenance.',
|
|
95
174
|
keyOps: ['vault_archive', 'vault_restore', 'vault_optimize', 'knowledge_audit'],
|
|
175
|
+
intentSignals: {
|
|
176
|
+
'archive entries': 'vault_archive',
|
|
177
|
+
'restore archived': 'vault_restore',
|
|
178
|
+
'optimize vault': 'vault_optimize',
|
|
179
|
+
'knowledge audit': 'knowledge_audit',
|
|
180
|
+
},
|
|
96
181
|
},
|
|
97
182
|
{
|
|
98
183
|
suffix: 'sync',
|
|
99
184
|
description: 'Git, Obsidian, and pack sync operations.',
|
|
100
185
|
keyOps: ['vault_git_push', 'vault_git_pull', 'obsidian_sync'],
|
|
186
|
+
intentSignals: {
|
|
187
|
+
'push vault': 'vault_git_push',
|
|
188
|
+
'pull vault': 'vault_git_pull',
|
|
189
|
+
'sync obsidian': 'obsidian_sync',
|
|
190
|
+
},
|
|
101
191
|
},
|
|
102
192
|
{
|
|
103
193
|
suffix: 'review',
|
|
104
194
|
description: 'Knowledge review workflow.',
|
|
105
195
|
keyOps: ['vault_submit_review', 'vault_approve', 'vault_reject'],
|
|
196
|
+
intentSignals: {
|
|
197
|
+
'submit for review': 'vault_submit_review',
|
|
198
|
+
'approve entry': 'vault_approve',
|
|
199
|
+
'reject entry': 'vault_reject',
|
|
200
|
+
},
|
|
106
201
|
},
|
|
107
202
|
{
|
|
108
203
|
suffix: 'intake',
|
|
109
204
|
description: 'Content ingestion — books, URLs, text, batch import.',
|
|
110
205
|
keyOps: ['intake_ingest_book', 'ingest_url', 'ingest_text', 'ingest_batch'],
|
|
206
|
+
intentSignals: {
|
|
207
|
+
'ingest a book': 'intake_ingest_book',
|
|
208
|
+
'import from URL': 'ingest_url',
|
|
209
|
+
'ingest text': 'ingest_text',
|
|
210
|
+
'batch import': 'ingest_batch',
|
|
211
|
+
},
|
|
111
212
|
},
|
|
112
213
|
{
|
|
113
214
|
suffix: 'links',
|
|
114
215
|
description: 'Entry linking — create, traverse, suggest, orphan detection.',
|
|
115
216
|
keyOps: ['link_entries', 'traverse', 'suggest_links', 'get_orphans'],
|
|
217
|
+
intentSignals: {
|
|
218
|
+
'link entries': 'link_entries',
|
|
219
|
+
'explore connections': 'traverse',
|
|
220
|
+
'suggest links': 'suggest_links',
|
|
221
|
+
'find orphans': 'get_orphans',
|
|
222
|
+
},
|
|
116
223
|
},
|
|
117
224
|
{
|
|
118
225
|
suffix: 'branching',
|
|
119
226
|
description: 'Vault branching — create, list, merge, delete branches.',
|
|
120
227
|
keyOps: ['vault_branch', 'vault_branch_list', 'vault_merge_branch'],
|
|
228
|
+
intentSignals: {
|
|
229
|
+
'create branch': 'vault_branch',
|
|
230
|
+
'list branches': 'vault_branch_list',
|
|
231
|
+
'merge branch': 'vault_merge_branch',
|
|
232
|
+
},
|
|
121
233
|
},
|
|
122
234
|
{
|
|
123
235
|
suffix: 'tier',
|
|
124
236
|
description: 'Multi-vault tiers — connect, disconnect, search across sources.',
|
|
125
237
|
keyOps: ['vault_connect_source', 'vault_search_all', 'vault_list_sources'],
|
|
238
|
+
intentSignals: {
|
|
239
|
+
'connect source': 'vault_connect_source',
|
|
240
|
+
'search all vaults': 'vault_search_all',
|
|
241
|
+
'list sources': 'vault_list_sources',
|
|
242
|
+
},
|
|
126
243
|
},
|
|
127
244
|
];
|
|
128
245
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
16
16
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
17
17
|
import { createAgentRuntime } from '../runtime/runtime.js';
|
|
18
|
-
import { registerEngine, ENGINE_MODULES } from './register-engine.js';
|
|
18
|
+
import { registerEngine, ENGINE_MODULES, INTERNAL_OPS } from './register-engine.js';
|
|
19
19
|
import { ENGINE_MODULE_MANIFEST } from './module-manifest.js';
|
|
20
20
|
import type { AgentRuntime } from '../runtime/types.js';
|
|
21
21
|
import type { OpDefinition } from '../facades/types.js';
|
|
@@ -233,3 +233,75 @@ describe('ENGINE_MODULES descriptions match manifest', () => {
|
|
|
233
233
|
}
|
|
234
234
|
});
|
|
235
235
|
});
|
|
236
|
+
|
|
237
|
+
describe('registerEngine — op visibility', () => {
|
|
238
|
+
it('INTERNAL_OPS set contains expected ops', () => {
|
|
239
|
+
// Spot-check known internal ops
|
|
240
|
+
expect(INTERNAL_OPS.has('admin_create_token')).toBe(true);
|
|
241
|
+
expect(INTERNAL_OPS.has('vault_bulk_add')).toBe(true);
|
|
242
|
+
expect(INTERNAL_OPS.has('plan_auto_reconcile')).toBe(true);
|
|
243
|
+
expect(INTERNAL_OPS.has('telemetry_errors')).toBe(true);
|
|
244
|
+
// User-facing ops should NOT be in the set
|
|
245
|
+
expect(INTERNAL_OPS.has('admin_health')).toBe(false);
|
|
246
|
+
expect(INTERNAL_OPS.has('search_intelligent')).toBe(false);
|
|
247
|
+
expect(INTERNAL_OPS.has('create_plan')).toBe(false);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('INTERNAL_OPS has at least 25 entries', () => {
|
|
251
|
+
expect(INTERNAL_OPS.size).toBeGreaterThanOrEqual(25);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('ops without visibility field default to user (backward compat)', () => {
|
|
255
|
+
const server = makeServer();
|
|
256
|
+
const userOp: OpDefinition = {
|
|
257
|
+
name: 'my_visible_op',
|
|
258
|
+
description: 'Visible op',
|
|
259
|
+
auth: 'read',
|
|
260
|
+
handler: async () => 'ok',
|
|
261
|
+
};
|
|
262
|
+
const result = registerEngine(server, runtime, {
|
|
263
|
+
agentId: 'vis',
|
|
264
|
+
domainPacks: [{ name: 'test', facades: [{ name: 'test', ops: [userOp] }] }],
|
|
265
|
+
});
|
|
266
|
+
expect(result.tools).toContain('vis_test');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('ops with visibility: internal are excluded from MCP tool description but remain callable', () => {
|
|
270
|
+
const server = makeServer();
|
|
271
|
+
const visibleOp: OpDefinition = {
|
|
272
|
+
name: 'public_op',
|
|
273
|
+
description: 'Public op',
|
|
274
|
+
auth: 'read',
|
|
275
|
+
handler: async () => 'visible',
|
|
276
|
+
};
|
|
277
|
+
const internalOp: OpDefinition = {
|
|
278
|
+
name: 'secret_op',
|
|
279
|
+
description: 'Internal op',
|
|
280
|
+
auth: 'admin',
|
|
281
|
+
visibility: 'internal',
|
|
282
|
+
handler: async () => 'hidden',
|
|
283
|
+
};
|
|
284
|
+
// Register both ops under a pack facade
|
|
285
|
+
registerEngine(server, runtime, {
|
|
286
|
+
agentId: 'vt',
|
|
287
|
+
domainPacks: [{ name: 'test', facades: [{ name: 'check', ops: [visibleOp, internalOp] }] }],
|
|
288
|
+
});
|
|
289
|
+
// We can't easily inspect the MCP schema description string, but we verify
|
|
290
|
+
// that registration succeeds with mixed visibility ops
|
|
291
|
+
expect(true).toBe(true);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('every INTERNAL_OPS entry corresponds to a real op in some facade', () => {
|
|
295
|
+
// Collect all op names across all engine modules
|
|
296
|
+
const allOpNames = new Set<string>();
|
|
297
|
+
for (const mod of ENGINE_MODULES) {
|
|
298
|
+
const ops = mod.createOps(runtime);
|
|
299
|
+
for (const op of ops) {
|
|
300
|
+
allOpNames.add(op.name);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const internalOp of INTERNAL_OPS) {
|
|
304
|
+
expect(allOpNames.has(internalOp)).toBe(true);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
});
|