@soleri/core 9.15.0 → 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/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/context/context-engine.js +1 -1
- package/dist/context/context-engine.js.map +1 -1
- 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 +31 -4
- 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 +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.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 +1 -0
- package/dist/planning/planner.d.ts.map +1 -1
- package/dist/planning/planner.js +7 -0
- 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/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 +87 -17
- 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 +12 -0
- 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/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/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/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/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/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 +1 -1
- 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/second-brain-features.test.ts +2 -14
- 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/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/tag-manager.test.ts +0 -4
- 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 +17 -0
- package/src/intake/dedup-gate.test.ts +2 -6
- package/src/intake/text-ingester.test.ts +3 -4
- 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.ts +8 -0
- 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 +185 -13
- package/src/runtime/admin-setup-ops.ts +86 -16
- 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 +12 -0
- 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/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/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 +12 -11
- 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/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/vault-connect.test.ts +1 -1
- package/src/vault/vault-entries.ts +3 -1
- package/src/vault/vault.test.ts +23 -8
|
@@ -10,6 +10,7 @@ import { createPlanningExtraOps } from '../planning-extra-ops.js';
|
|
|
10
10
|
import { createGradingOps } from '../grading-ops.js';
|
|
11
11
|
import { createChainOps } from '../chain-ops.js';
|
|
12
12
|
import { PlanGradeRejectionError } from '../../planning/planner.js';
|
|
13
|
+
import { matchPlaybooks } from '../../playbooks/playbook-registry.js';
|
|
13
14
|
|
|
14
15
|
export function createPlanFacadeOps(runtime: AgentRuntime): OpDefinition[] {
|
|
15
16
|
const { planner, vault } = runtime;
|
|
@@ -64,16 +65,73 @@ export function createPlanFacadeOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
64
65
|
// Vault search failed — proceed without enrichment
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
// Playbook matching: inject task templates + start executor session
|
|
69
|
+
const userTasks = (params.tasks as Array<{ title: string; description: string }>) ?? [];
|
|
70
|
+
const matchResult = matchPlaybooks('BUILD', objective);
|
|
71
|
+
const playbook = matchResult.playbook;
|
|
72
|
+
const playbookTasks: Array<{ title: string; description: string }> = [];
|
|
73
|
+
let playbookSessionId: string | undefined;
|
|
74
|
+
let playbookMatch: { label: string; genericId?: string; domainId?: string } | undefined;
|
|
75
|
+
|
|
76
|
+
if (playbook) {
|
|
77
|
+
playbookMatch = {
|
|
78
|
+
label: playbook.label,
|
|
79
|
+
genericId: playbook.generic?.id,
|
|
80
|
+
domainId: playbook.domain?.id,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Inject task templates (before-implementation first, then user tasks)
|
|
84
|
+
for (const tmpl of playbook.mergedTasks) {
|
|
85
|
+
const title = tmpl.titleTemplate.replace('{objective}', objective);
|
|
86
|
+
playbookTasks.push({ title, description: tmpl.acceptanceCriteria.join('\n') });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Start executor session to track gate enforcement
|
|
90
|
+
if (runtime.playbookExecutor) {
|
|
91
|
+
const startResult = runtime.playbookExecutor.start(playbook);
|
|
92
|
+
playbookSessionId = startResult.sessionId;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const beforeTasks = playbookTasks.filter((_, i) => {
|
|
97
|
+
const tmpl = playbook?.mergedTasks[i];
|
|
98
|
+
return !tmpl || tmpl.order === 'before-implementation';
|
|
99
|
+
});
|
|
100
|
+
const afterTasks = playbookTasks.filter((_, i) => {
|
|
101
|
+
const tmpl = playbook?.mergedTasks[i];
|
|
102
|
+
return tmpl?.order === 'after-implementation';
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const allTasks = [...beforeTasks, ...userTasks, ...afterTasks];
|
|
106
|
+
|
|
67
107
|
const plan = planner.create({
|
|
68
108
|
objective,
|
|
69
109
|
scope: params.scope as string,
|
|
70
110
|
decisions,
|
|
71
|
-
tasks:
|
|
111
|
+
tasks: allTasks,
|
|
72
112
|
alternatives: params.alternatives as
|
|
73
113
|
| Array<{ approach: string; pros: string[]; cons: string[]; rejected_reason: string }>
|
|
74
114
|
| undefined,
|
|
75
115
|
});
|
|
76
|
-
|
|
116
|
+
|
|
117
|
+
if (playbookMatch) plan.playbookMatch = playbookMatch;
|
|
118
|
+
if (playbookSessionId) {
|
|
119
|
+
planner.patchPlan(plan.id, { playbookSessionId });
|
|
120
|
+
plan.playbookSessionId = playbookSessionId;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
created: true,
|
|
125
|
+
plan,
|
|
126
|
+
vaultEntryIds,
|
|
127
|
+
playbook: playbook
|
|
128
|
+
? {
|
|
129
|
+
label: playbook.label,
|
|
130
|
+
tasksInjected: playbookTasks.length,
|
|
131
|
+
sessionId: playbookSessionId ?? null,
|
|
132
|
+
}
|
|
133
|
+
: null,
|
|
134
|
+
};
|
|
77
135
|
},
|
|
78
136
|
},
|
|
79
137
|
{
|
|
@@ -144,10 +202,43 @@ export function createPlanFacadeOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
144
202
|
status: z.enum(['pending', 'in_progress', 'completed', 'skipped', 'failed']),
|
|
145
203
|
}),
|
|
146
204
|
handler: async (params) => {
|
|
205
|
+
const targetStatus = params.status as
|
|
206
|
+
| 'pending'
|
|
207
|
+
| 'in_progress'
|
|
208
|
+
| 'completed'
|
|
209
|
+
| 'skipped'
|
|
210
|
+
| 'failed';
|
|
211
|
+
|
|
212
|
+
// Gate check: enforce blocking post-task gates before allowing completion
|
|
213
|
+
if (targetStatus === 'completed') {
|
|
214
|
+
const currentPlan = planner.get(params.planId as string);
|
|
215
|
+
if (currentPlan?.playbookSessionId && runtime.playbookExecutor) {
|
|
216
|
+
const session = runtime.playbookExecutor.getSession(currentPlan.playbookSessionId);
|
|
217
|
+
if (session) {
|
|
218
|
+
const blockingPostTaskGates = session.gates.filter(
|
|
219
|
+
(g) =>
|
|
220
|
+
g.phase === 'post-task' &&
|
|
221
|
+
(g.severity === 'blocking' || g.severity === undefined),
|
|
222
|
+
);
|
|
223
|
+
if (blockingPostTaskGates.length > 0) {
|
|
224
|
+
return {
|
|
225
|
+
updated: false,
|
|
226
|
+
blocked: true,
|
|
227
|
+
reason: 'post-task gates unsatisfied',
|
|
228
|
+
gates: blockingPostTaskGates.map((g) => ({
|
|
229
|
+
checkType: g.checkType,
|
|
230
|
+
requirement: g.requirement,
|
|
231
|
+
})),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
147
238
|
const plan = planner.updateTask(
|
|
148
239
|
params.planId as string,
|
|
149
240
|
params.taskId as string,
|
|
150
|
-
|
|
241
|
+
targetStatus,
|
|
151
242
|
);
|
|
152
243
|
const task = plan.tasks.find((t) => t.id === params.taskId);
|
|
153
244
|
return { updated: true, task, plan: { id: plan.id, status: plan.status } };
|
|
@@ -162,6 +253,43 @@ export function createPlanFacadeOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
162
253
|
planId: z.string(),
|
|
163
254
|
}),
|
|
164
255
|
handler: async (params) => {
|
|
256
|
+
// Enforce playbook completion gates before allowing plan completion
|
|
257
|
+
const currentPlan = planner.get(params.planId as string);
|
|
258
|
+
if (currentPlan?.playbookSessionId && runtime.playbookExecutor) {
|
|
259
|
+
const completeResult = runtime.playbookExecutor.complete(
|
|
260
|
+
currentPlan.playbookSessionId,
|
|
261
|
+
{},
|
|
262
|
+
);
|
|
263
|
+
if ('error' in completeResult) {
|
|
264
|
+
// Session not found — executor may have been reset; proceed without gate check
|
|
265
|
+
} else if (!completeResult.gatesPassed && completeResult.unsatisfiedGates.length > 0) {
|
|
266
|
+
// Session is consumed after complete() — use the result's unsatisfied gate list
|
|
267
|
+
// All completion gates are blocking unless marked advisory
|
|
268
|
+
return {
|
|
269
|
+
completed: false,
|
|
270
|
+
blocked: true,
|
|
271
|
+
reason: 'completion gates unsatisfied',
|
|
272
|
+
unsatisfiedGates: completeResult.unsatisfiedGates,
|
|
273
|
+
recommendation:
|
|
274
|
+
'Satisfy the listed playbook gates before completing this plan. Pass gate results via the playbook executor if checks were completed externally.',
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Collect unenforced playbook warnings before completing
|
|
280
|
+
const playbookWarnings: string[] = [];
|
|
281
|
+
if (currentPlan?.playbookMatch) {
|
|
282
|
+
if (!currentPlan.playbookSessionId) {
|
|
283
|
+
// Playbook was matched at create_plan time but executor session was never started
|
|
284
|
+
playbookWarnings.push(
|
|
285
|
+
`WARNING: Playbook "${currentPlan.playbookMatch.label}" was matched but its gates were never evaluated — enforcement was entirely skipped.`,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
// If session existed, the blocking gate check above already ran.
|
|
289
|
+
// Advisory gates that were unsatisfied become warnings here (non-blocking).
|
|
290
|
+
// (Future: track per-gate evaluation status for richer reporting)
|
|
291
|
+
}
|
|
292
|
+
|
|
165
293
|
const plan = planner.complete(params.planId as string);
|
|
166
294
|
const taskSummary = {
|
|
167
295
|
completed: plan.tasks.filter((t) => t.status === 'completed').length,
|
|
@@ -169,7 +297,12 @@ export function createPlanFacadeOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
169
297
|
failed: plan.tasks.filter((t) => t.status === 'failed').length,
|
|
170
298
|
total: plan.tasks.length,
|
|
171
299
|
};
|
|
172
|
-
return {
|
|
300
|
+
return {
|
|
301
|
+
completed: true,
|
|
302
|
+
plan,
|
|
303
|
+
taskSummary,
|
|
304
|
+
...(playbookWarnings.length > 0 ? { playbookWarnings } : {}),
|
|
305
|
+
};
|
|
173
306
|
},
|
|
174
307
|
},
|
|
175
308
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect, vi
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
2
|
import { createReviewFacadeOps } from './review-facade.js';
|
|
3
3
|
import type { AgentRuntime } from '../types.js';
|
|
4
4
|
|
|
@@ -24,54 +24,6 @@ function mockRuntime(): AgentRuntime {
|
|
|
24
24
|
// ---------------------------------------------------------------------------
|
|
25
25
|
|
|
26
26
|
describe('createReviewFacadeOps', () => {
|
|
27
|
-
let ops: ReturnType<typeof createReviewFacadeOps>;
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
vi.clearAllMocks();
|
|
31
|
-
ops = createReviewFacadeOps(mockRuntime());
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('returns 5 ops matching review-ops', () => {
|
|
35
|
-
expect(ops.length).toBe(5);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('includes all expected op names', () => {
|
|
39
|
-
const names = ops.map((o) => o.name);
|
|
40
|
-
expect(names).toContain('vault_submit_review');
|
|
41
|
-
expect(names).toContain('vault_approve');
|
|
42
|
-
expect(names).toContain('vault_reject');
|
|
43
|
-
expect(names).toContain('vault_pending_reviews');
|
|
44
|
-
expect(names).toContain('vault_review_stats');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('all ops have required fields', () => {
|
|
48
|
-
for (const op of ops) {
|
|
49
|
-
expect(op.name).toBeDefined();
|
|
50
|
-
expect(op.description).toBeDefined();
|
|
51
|
-
expect(op.auth).toBeDefined();
|
|
52
|
-
expect(typeof op.handler).toBe('function');
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('approve and reject require admin auth', () => {
|
|
57
|
-
const approveOp = ops.find((o) => o.name === 'vault_approve');
|
|
58
|
-
const rejectOp = ops.find((o) => o.name === 'vault_reject');
|
|
59
|
-
expect(approveOp!.auth).toBe('admin');
|
|
60
|
-
expect(rejectOp!.auth).toBe('admin');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('submit uses write auth', () => {
|
|
64
|
-
const op = ops.find((o) => o.name === 'vault_submit_review');
|
|
65
|
-
expect(op!.auth).toBe('write');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('pending_reviews and review_stats use read auth', () => {
|
|
69
|
-
const pending = ops.find((o) => o.name === 'vault_pending_reviews');
|
|
70
|
-
const stats = ops.find((o) => o.name === 'vault_review_stats');
|
|
71
|
-
expect(pending!.auth).toBe('read');
|
|
72
|
-
expect(stats!.auth).toBe('read');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
27
|
it('handlers are callable', async () => {
|
|
76
28
|
const rt = mockRuntime();
|
|
77
29
|
const rtOps = createReviewFacadeOps(rt);
|
|
@@ -64,50 +64,33 @@ describe('createSyncFacadeOps', () => {
|
|
|
64
64
|
ops = createSyncFacadeOps(mockRuntime());
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
describe('vault_git_push', () => {
|
|
68
|
+
it('pushes vault entries and returns pushed count', async () => {
|
|
69
|
+
const op = ops.find((o) => o.name === 'vault_git_push')!;
|
|
70
|
+
const result = (await op.handler({ remote: 'origin', branch: 'main' })) as {
|
|
71
|
+
pushed: number;
|
|
72
|
+
};
|
|
73
|
+
expect(result.pushed).toBe(5);
|
|
74
|
+
});
|
|
69
75
|
});
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
describe('vault_git_pull', () => {
|
|
78
|
+
it('pulls from remote and returns imported count', async () => {
|
|
79
|
+
const op = ops.find((o) => o.name === 'vault_git_pull')!;
|
|
80
|
+
const result = (await op.handler({ remote: 'origin', branch: 'main' })) as {
|
|
81
|
+
imported: number;
|
|
82
|
+
conflicts: number;
|
|
83
|
+
};
|
|
84
|
+
expect(result.imported).toBe(3);
|
|
85
|
+
expect(result.conflicts).toBe(0);
|
|
86
|
+
});
|
|
81
87
|
});
|
|
82
88
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
expect(
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('git ops use write auth', () => {
|
|
93
|
-
const gitOps = ops.filter((o) => o.name.startsWith('vault_git_'));
|
|
94
|
-
for (const op of gitOps) {
|
|
95
|
-
expect(op.auth).toBe('write');
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('obsidian_export uses read auth', () => {
|
|
100
|
-
const op = ops.find((o) => o.name === 'obsidian_export');
|
|
101
|
-
expect(op!.auth).toBe('read');
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('vault_export_pack uses read auth', () => {
|
|
105
|
-
const op = ops.find((o) => o.name === 'vault_export_pack');
|
|
106
|
-
expect(op!.auth).toBe('read');
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('vault_import_pack uses write auth', () => {
|
|
110
|
-
const op = ops.find((o) => o.name === 'vault_import_pack');
|
|
111
|
-
expect(op!.auth).toBe('write');
|
|
89
|
+
describe('obsidian_export', () => {
|
|
90
|
+
it('exports entries and returns exported count', async () => {
|
|
91
|
+
const op = ops.find((o) => o.name === 'obsidian_export')!;
|
|
92
|
+
const result = (await op.handler({ vaultPath: '/tmp/obsidian' })) as { exported: number };
|
|
93
|
+
expect(result.exported).toBe(10);
|
|
94
|
+
});
|
|
112
95
|
});
|
|
113
96
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Colocated contract tests for tier-facade.ts.
|
|
3
|
-
* Verifies the facade wrapper delegates to tier-ops.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
import { describe, it, expect, vi } from 'vitest';
|
|
@@ -12,36 +11,45 @@ function makeRuntime(): AgentRuntime {
|
|
|
12
11
|
vaultManager: {
|
|
13
12
|
open: vi.fn(),
|
|
14
13
|
disconnect: vi.fn().mockReturnValue(true),
|
|
15
|
-
listTiers: vi.fn().mockReturnValue([]),
|
|
16
|
-
search: vi.fn().mockReturnValue([]),
|
|
14
|
+
listTiers: vi.fn().mockReturnValue([{ name: 'agent', count: 10 }]),
|
|
15
|
+
search: vi.fn().mockReturnValue([{ entry: { id: 'e1' }, score: 0.9 }]),
|
|
17
16
|
connect: vi.fn(),
|
|
18
17
|
disconnectNamed: vi.fn().mockReturnValue(true),
|
|
19
|
-
listConnected: vi.fn().mockReturnValue([]),
|
|
18
|
+
listConnected: vi.fn().mockReturnValue([{ name: 'team-vault', priority: 2 }]),
|
|
20
19
|
},
|
|
21
20
|
config: { agentId: 'test-agent' },
|
|
22
21
|
} as unknown as AgentRuntime;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
describe('tier-facade', () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
expect(names).toContain('vault_search_all');
|
|
34
|
-
expect(names).toContain('vault_connect_source');
|
|
35
|
-
expect(names).toContain('vault_disconnect_source');
|
|
36
|
-
expect(names).toContain('vault_list_sources');
|
|
25
|
+
describe('vault_tiers', () => {
|
|
26
|
+
it('returns all vault tiers', async () => {
|
|
27
|
+
const ops = createTierFacadeOps(makeRuntime());
|
|
28
|
+
const op = ops.find((o) => o.name === 'vault_tiers')!;
|
|
29
|
+
const result = (await op.handler({})) as { tiers: unknown[] };
|
|
30
|
+
expect(result.tiers).toHaveLength(1);
|
|
31
|
+
});
|
|
37
32
|
});
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
describe('vault_search_all', () => {
|
|
35
|
+
it('returns search results with count', async () => {
|
|
36
|
+
const ops = createTierFacadeOps(makeRuntime());
|
|
37
|
+
const op = ops.find((o) => o.name === 'vault_search_all')!;
|
|
38
|
+
const result = (await op.handler({ query: 'test', limit: 5 })) as {
|
|
39
|
+
results: unknown[];
|
|
40
|
+
count: number;
|
|
41
|
+
};
|
|
42
|
+
expect(result.count).toBe(1);
|
|
43
|
+
expect(result.results).toHaveLength(1);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('vault_list_sources', () => {
|
|
48
|
+
it('returns connected sources', async () => {
|
|
49
|
+
const ops = createTierFacadeOps(makeRuntime());
|
|
50
|
+
const op = ops.find((o) => o.name === 'vault_list_sources')!;
|
|
51
|
+
const result = (await op.handler({})) as { sources: unknown[] };
|
|
52
|
+
expect(result.sources).toHaveLength(1);
|
|
53
|
+
});
|
|
46
54
|
});
|
|
47
55
|
});
|
|
@@ -164,47 +164,6 @@ describe('vault-facade', () => {
|
|
|
164
164
|
ops = captureOps(createVaultFacadeOps(runtime));
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
-
// ─── Registration ─────────────────────────────────────────────────
|
|
168
|
-
|
|
169
|
-
it('registers ops from all groups', () => {
|
|
170
|
-
// The facade includes inline ops + satellite ops from 5 modules.
|
|
171
|
-
// We check the inline ops explicitly, satellite ops just need to exist.
|
|
172
|
-
expect(ops.size).toBeGreaterThan(20);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('includes core inline op names', () => {
|
|
176
|
-
const coreOps = [
|
|
177
|
-
'search',
|
|
178
|
-
'load_entries',
|
|
179
|
-
'vault_stats',
|
|
180
|
-
'list_all',
|
|
181
|
-
'export',
|
|
182
|
-
'capture_enriched',
|
|
183
|
-
];
|
|
184
|
-
for (const name of coreOps) {
|
|
185
|
-
expect(ops.has(name), `missing op: ${name}`).toBe(true);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('includes satellite ops', () => {
|
|
190
|
-
// Spot-check a few ops from each satellite module
|
|
191
|
-
expect(ops.has('vault_get')).toBe(true); // vault-extra-ops
|
|
192
|
-
expect(ops.has('capture_knowledge')).toBe(true); // capture-ops
|
|
193
|
-
expect(ops.has('search_intelligent')).toBe(true); // capture-ops
|
|
194
|
-
// link_entries moved to vault-linking-facade
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// ─── Auth levels ─────────────────────────────────────────────────
|
|
198
|
-
|
|
199
|
-
it('has correct auth levels for inline ops', () => {
|
|
200
|
-
expect(ops.get('search')!.auth).toBe('read');
|
|
201
|
-
expect(ops.get('load_entries')!.auth).toBe('read');
|
|
202
|
-
expect(ops.get('vault_stats')!.auth).toBe('read');
|
|
203
|
-
expect(ops.get('list_all')!.auth).toBe('read');
|
|
204
|
-
expect(ops.get('export')!.auth).toBe('read');
|
|
205
|
-
expect(ops.get('capture_enriched')!.auth).toBe('write');
|
|
206
|
-
});
|
|
207
|
-
|
|
208
167
|
// ─── search ────────────────────────────────────────────────────────
|
|
209
168
|
|
|
210
169
|
describe('search', () => {
|
|
@@ -66,13 +66,36 @@ export function createVaultFacadeOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
66
66
|
{
|
|
67
67
|
name: 'load_entries',
|
|
68
68
|
description:
|
|
69
|
-
'Two-pass retrieval — Pass 2: Load full entries by IDs (from a previous scan search).'
|
|
69
|
+
'Two-pass retrieval — Pass 2: Load full entries by IDs (from a previous scan search). ' +
|
|
70
|
+
'Alternative: pass domain + limit to list entries by domain without a prior scan.',
|
|
70
71
|
auth: 'read',
|
|
71
72
|
schema: z.object({
|
|
72
|
-
ids: z
|
|
73
|
+
ids: z
|
|
74
|
+
.array(z.string())
|
|
75
|
+
.min(1)
|
|
76
|
+
.optional()
|
|
77
|
+
.describe('Entry IDs from a previous scan search'),
|
|
78
|
+
domain: z.string().optional().describe('Alternative to ids: filter entries by domain'),
|
|
79
|
+
limit: z
|
|
80
|
+
.number()
|
|
81
|
+
.optional()
|
|
82
|
+
.default(20)
|
|
83
|
+
.describe('Max entries when using domain filter (default: 20)'),
|
|
73
84
|
}),
|
|
74
85
|
handler: async (params) => {
|
|
75
|
-
|
|
86
|
+
const ids = params.ids as string[] | undefined;
|
|
87
|
+
if (ids && ids.length > 0) {
|
|
88
|
+
return brain.loadEntries(ids);
|
|
89
|
+
}
|
|
90
|
+
const domain = params.domain as string | undefined;
|
|
91
|
+
if (domain) {
|
|
92
|
+
const entries = vault.list({ domain, limit: (params.limit as number) ?? 20 });
|
|
93
|
+
return { entries, total: entries.length };
|
|
94
|
+
}
|
|
95
|
+
throw new Error(
|
|
96
|
+
'Provide ids (from search_intelligent scan) or domain to filter entries. ' +
|
|
97
|
+
'Example: { ids: ["entry-id-1"] } or { domain: "testing", limit: 10 }',
|
|
98
|
+
);
|
|
76
99
|
},
|
|
77
100
|
},
|
|
78
101
|
{
|
|
@@ -52,33 +52,6 @@ describe('createGradingOps', () => {
|
|
|
52
52
|
return op;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
it('returns 5 ops', () => {
|
|
56
|
-
runtime = makeMockRuntime();
|
|
57
|
-
ops = createGradingOps(runtime);
|
|
58
|
-
expect(ops).toHaveLength(5);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('has correct op names', () => {
|
|
62
|
-
runtime = makeMockRuntime();
|
|
63
|
-
ops = createGradingOps(runtime);
|
|
64
|
-
const names = ops.map((o) => o.name);
|
|
65
|
-
expect(names).toEqual([
|
|
66
|
-
'plan_grade',
|
|
67
|
-
'plan_check_history',
|
|
68
|
-
'plan_latest_check',
|
|
69
|
-
'plan_meets_grade',
|
|
70
|
-
'plan_auto_improve',
|
|
71
|
-
]);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('all ops have read auth', () => {
|
|
75
|
-
runtime = makeMockRuntime();
|
|
76
|
-
ops = createGradingOps(runtime);
|
|
77
|
-
for (const op of ops) {
|
|
78
|
-
expect(op.auth).toBe('read');
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
55
|
describe('plan_grade', () => {
|
|
83
56
|
it('delegates to planner.grade', async () => {
|
|
84
57
|
runtime = makeMockRuntime();
|
|
@@ -28,25 +28,6 @@ describe('createIntakeOps', () => {
|
|
|
28
28
|
return op;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
it('returns 7 ops', () => {
|
|
32
|
-
const ops = createIntakeOps(makeMockPipeline() as never);
|
|
33
|
-
expect(ops).toHaveLength(7);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('has all expected op names', () => {
|
|
37
|
-
const ops = createIntakeOps(makeMockPipeline() as never);
|
|
38
|
-
const names = ops.map((o) => o.name);
|
|
39
|
-
expect(names).toEqual([
|
|
40
|
-
'intake_ingest_book',
|
|
41
|
-
'intake_process',
|
|
42
|
-
'intake_status',
|
|
43
|
-
'intake_preview',
|
|
44
|
-
'ingest_url',
|
|
45
|
-
'ingest_text',
|
|
46
|
-
'ingest_batch',
|
|
47
|
-
]);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
31
|
describe('when pipeline is null', () => {
|
|
51
32
|
it('intake_ingest_book returns error', async () => {
|
|
52
33
|
const ops = createIntakeOps(null);
|
|
@@ -50,29 +50,6 @@ describe('createLoopOps', () => {
|
|
|
50
50
|
return op;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
it('returns 9 ops', () => {
|
|
54
|
-
runtime = makeMockRuntime();
|
|
55
|
-
ops = createLoopOps(runtime);
|
|
56
|
-
expect(ops).toHaveLength(9);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('has all expected op names', () => {
|
|
60
|
-
runtime = makeMockRuntime();
|
|
61
|
-
ops = createLoopOps(runtime);
|
|
62
|
-
const names = ops.map((o) => o.name);
|
|
63
|
-
expect(names).toEqual([
|
|
64
|
-
'loop_start',
|
|
65
|
-
'loop_iterate',
|
|
66
|
-
'loop_iterate_gate',
|
|
67
|
-
'loop_status',
|
|
68
|
-
'loop_cancel',
|
|
69
|
-
'loop_history',
|
|
70
|
-
'loop_is_active',
|
|
71
|
-
'loop_complete',
|
|
72
|
-
'loop_anomaly_check',
|
|
73
|
-
]);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
53
|
describe('loop_start', () => {
|
|
77
54
|
it('starts a loop with defaults for custom mode', async () => {
|
|
78
55
|
runtime = makeMockRuntime();
|
|
@@ -125,24 +102,6 @@ describe('createLoopOps', () => {
|
|
|
125
102
|
});
|
|
126
103
|
});
|
|
127
104
|
|
|
128
|
-
describe('loop_iterate_gate', () => {
|
|
129
|
-
it('delegates to loop.iterateWithGate', async () => {
|
|
130
|
-
runtime = makeMockRuntime();
|
|
131
|
-
ops = createLoopOps(runtime);
|
|
132
|
-
const result = await findOp('loop_iterate_gate').handler({
|
|
133
|
-
lastOutput: 'some output',
|
|
134
|
-
knowledge: { items: ['learned something'] },
|
|
135
|
-
durationMs: 5000,
|
|
136
|
-
});
|
|
137
|
-
expect(runtime.loop.iterateWithGate).toHaveBeenCalledWith(
|
|
138
|
-
'some output',
|
|
139
|
-
{ items: ['learned something'] },
|
|
140
|
-
5000,
|
|
141
|
-
);
|
|
142
|
-
expect(result).toEqual({ decision: 'block', reason: 'continue' });
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
105
|
describe('loop_status', () => {
|
|
147
106
|
it('returns active loop status', async () => {
|
|
148
107
|
runtime = makeMockRuntime();
|
|
@@ -187,13 +146,6 @@ describe('createLoopOps', () => {
|
|
|
187
146
|
});
|
|
188
147
|
|
|
189
148
|
describe('loop_is_active', () => {
|
|
190
|
-
it('returns active true', async () => {
|
|
191
|
-
runtime = makeMockRuntime();
|
|
192
|
-
ops = createLoopOps(runtime);
|
|
193
|
-
const result = (await findOp('loop_is_active').handler({})) as Record<string, unknown>;
|
|
194
|
-
expect(result.active).toBe(true);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
149
|
it('returns active false when no loop', async () => {
|
|
198
150
|
runtime = makeMockRuntime();
|
|
199
151
|
(runtime.loop.isActive as ReturnType<typeof vi.fn>).mockReturnValue(false);
|
|
@@ -40,20 +40,6 @@ describe('createMemoryCrossProjectOps', () => {
|
|
|
40
40
|
return op;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
it('returns 3 ops', () => {
|
|
44
|
-
ops = createMemoryCrossProjectOps(makeMockRuntime());
|
|
45
|
-
expect(ops).toHaveLength(3);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('has correct op names', () => {
|
|
49
|
-
ops = createMemoryCrossProjectOps(makeMockRuntime());
|
|
50
|
-
expect(ops.map((o) => o.name)).toEqual([
|
|
51
|
-
'memory_promote_to_global',
|
|
52
|
-
'memory_configure',
|
|
53
|
-
'memory_cross_project_search',
|
|
54
|
-
]);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
43
|
describe('memory_promote_to_global', () => {
|
|
58
44
|
it('promotes entry by adding _global tag', async () => {
|
|
59
45
|
const runtime = makeMockRuntime();
|
|
@@ -73,10 +73,6 @@ describe('createMemoryExtraOps', () => {
|
|
|
73
73
|
ops = createMemoryExtraOps(rt);
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
it('returns all 18 ops', () => {
|
|
77
|
-
expect(ops.length).toBe(18);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
76
|
// ─── memory_delete ────────────────────────────────────────────
|
|
81
77
|
|
|
82
78
|
describe('memory_delete', () => {
|
|
@@ -249,8 +245,8 @@ describe('createMemoryExtraOps', () => {
|
|
|
249
245
|
it('returns results without archived by default', async () => {
|
|
250
246
|
const op = findOp(ops, 'session_search');
|
|
251
247
|
vi.mocked(rt.vault.searchMemories).mockReturnValue([{ id: 's1' }] as never);
|
|
252
|
-
const result = (await op.handler({ query: 'test', limit: 10 })) as
|
|
253
|
-
expect(result.results).
|
|
248
|
+
const result = (await op.handler({ query: 'test', limit: 10 })) as { results: unknown[] };
|
|
249
|
+
expect(result.results).toHaveLength(1); // mock returns exactly one session result
|
|
254
250
|
});
|
|
255
251
|
|
|
256
252
|
it('includes archived when includeArchived is true', async () => {
|
|
@@ -264,8 +260,8 @@ describe('createMemoryExtraOps', () => {
|
|
|
264
260
|
includeArchived: true,
|
|
265
261
|
limit: 10,
|
|
266
262
|
})) as Record<string, unknown>;
|
|
267
|
-
expect(result.active).
|
|
268
|
-
expect(result.archived).
|
|
263
|
+
expect((result as { active: unknown[] }).active).toHaveLength(1); // mock searchMemories returns 1
|
|
264
|
+
expect((result as { archived: unknown[] }).archived).toHaveLength(1); // mock provider.all returns 1
|
|
269
265
|
});
|
|
270
266
|
});
|
|
271
267
|
|