@soleri/core 9.2.0 → 9.3.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.
Files changed (298) hide show
  1. package/data/flows/build.flow.yaml +8 -9
  2. package/data/flows/deliver.flow.yaml +9 -10
  3. package/data/flows/design.flow.yaml +3 -4
  4. package/data/flows/enhance.flow.yaml +5 -6
  5. package/data/flows/explore.flow.yaml +3 -4
  6. package/data/flows/fix.flow.yaml +5 -6
  7. package/data/flows/plan.flow.yaml +4 -5
  8. package/data/flows/review.flow.yaml +3 -4
  9. package/dist/curator/curator.d.ts.map +1 -1
  10. package/dist/curator/curator.js +98 -22
  11. package/dist/curator/curator.js.map +1 -1
  12. package/dist/engine/bin/soleri-engine.js.map +1 -1
  13. package/dist/engine/module-manifest.d.ts.map +1 -1
  14. package/dist/engine/module-manifest.js +21 -1
  15. package/dist/engine/module-manifest.js.map +1 -1
  16. package/dist/engine/register-engine.d.ts.map +1 -1
  17. package/dist/engine/register-engine.js +25 -1
  18. package/dist/engine/register-engine.js.map +1 -1
  19. package/dist/flows/gate-evaluator.js.map +1 -1
  20. package/dist/operator/operator-profile.d.ts.map +1 -1
  21. package/dist/operator/operator-profile.js +11 -5
  22. package/dist/operator/operator-profile.js.map +1 -1
  23. package/dist/operator/operator-signals.d.ts.map +1 -1
  24. package/dist/operator/operator-signals.js.map +1 -1
  25. package/dist/planning/evidence-collector.js.map +1 -1
  26. package/dist/planning/gap-passes.d.ts.map +1 -1
  27. package/dist/planning/gap-passes.js +23 -6
  28. package/dist/planning/gap-passes.js.map +1 -1
  29. package/dist/planning/gap-patterns.d.ts.map +1 -1
  30. package/dist/planning/gap-patterns.js +57 -11
  31. package/dist/planning/gap-patterns.js.map +1 -1
  32. package/dist/planning/github-projection.d.ts.map +1 -1
  33. package/dist/planning/github-projection.js +39 -20
  34. package/dist/planning/github-projection.js.map +1 -1
  35. package/dist/planning/impact-analyzer.d.ts.map +1 -1
  36. package/dist/planning/impact-analyzer.js +20 -18
  37. package/dist/planning/impact-analyzer.js.map +1 -1
  38. package/dist/planning/plan-lifecycle.d.ts.map +1 -1
  39. package/dist/planning/plan-lifecycle.js +22 -9
  40. package/dist/planning/plan-lifecycle.js.map +1 -1
  41. package/dist/planning/planner.d.ts.map +1 -1
  42. package/dist/planning/planner.js +60 -17
  43. package/dist/planning/planner.js.map +1 -1
  44. package/dist/planning/rationalization-detector.d.ts.map +1 -1
  45. package/dist/planning/rationalization-detector.js.map +1 -1
  46. package/dist/planning/reconciliation-engine.d.ts.map +1 -1
  47. package/dist/planning/reconciliation-engine.js.map +1 -1
  48. package/dist/planning/task-verifier.d.ts.map +1 -1
  49. package/dist/planning/task-verifier.js +14 -6
  50. package/dist/planning/task-verifier.js.map +1 -1
  51. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  52. package/dist/runtime/admin-setup-ops.js +2 -1
  53. package/dist/runtime/admin-setup-ops.js.map +1 -1
  54. package/dist/runtime/branching-ops.d.ts +12 -0
  55. package/dist/runtime/branching-ops.d.ts.map +1 -0
  56. package/dist/runtime/branching-ops.js +100 -0
  57. package/dist/runtime/branching-ops.js.map +1 -0
  58. package/dist/runtime/context-health.d.ts.map +1 -1
  59. package/dist/runtime/context-health.js.map +1 -1
  60. package/dist/runtime/facades/branching-facade.d.ts +7 -0
  61. package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
  62. package/dist/runtime/facades/branching-facade.js +8 -0
  63. package/dist/runtime/facades/branching-facade.js.map +1 -0
  64. package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -1
  65. package/dist/runtime/facades/chat-service-ops.js +3 -1
  66. package/dist/runtime/facades/chat-service-ops.js.map +1 -1
  67. package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -1
  68. package/dist/runtime/facades/chat-transport-ops.js.map +1 -1
  69. package/dist/runtime/facades/index.d.ts.map +1 -1
  70. package/dist/runtime/facades/index.js +42 -0
  71. package/dist/runtime/facades/index.js.map +1 -1
  72. package/dist/runtime/facades/intake-facade.d.ts +9 -0
  73. package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
  74. package/dist/runtime/facades/intake-facade.js +11 -0
  75. package/dist/runtime/facades/intake-facade.js.map +1 -0
  76. package/dist/runtime/facades/links-facade.d.ts +9 -0
  77. package/dist/runtime/facades/links-facade.d.ts.map +1 -0
  78. package/dist/runtime/facades/links-facade.js +10 -0
  79. package/dist/runtime/facades/links-facade.js.map +1 -0
  80. package/dist/runtime/facades/operator-facade.d.ts.map +1 -1
  81. package/dist/runtime/facades/operator-facade.js.map +1 -1
  82. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  83. package/dist/runtime/facades/plan-facade.js +4 -1
  84. package/dist/runtime/facades/plan-facade.js.map +1 -1
  85. package/dist/runtime/facades/tier-facade.d.ts +7 -0
  86. package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
  87. package/dist/runtime/facades/tier-facade.js +8 -0
  88. package/dist/runtime/facades/tier-facade.js.map +1 -0
  89. package/dist/runtime/facades/vault-facade.d.ts +9 -1
  90. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  91. package/dist/runtime/facades/vault-facade.js +44 -187
  92. package/dist/runtime/facades/vault-facade.js.map +1 -1
  93. package/dist/runtime/github-integration.d.ts.map +1 -1
  94. package/dist/runtime/github-integration.js +11 -4
  95. package/dist/runtime/github-integration.js.map +1 -1
  96. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  97. package/dist/runtime/orchestrate-ops.js +32 -10
  98. package/dist/runtime/orchestrate-ops.js.map +1 -1
  99. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  100. package/dist/runtime/planning-extra-ops.js.map +1 -1
  101. package/dist/runtime/runtime.d.ts.map +1 -1
  102. package/dist/runtime/runtime.js +3 -1
  103. package/dist/runtime/runtime.js.map +1 -1
  104. package/dist/runtime/session-briefing.d.ts.map +1 -1
  105. package/dist/runtime/session-briefing.js +5 -1
  106. package/dist/runtime/session-briefing.js.map +1 -1
  107. package/dist/runtime/tier-ops.d.ts +13 -0
  108. package/dist/runtime/tier-ops.d.ts.map +1 -0
  109. package/dist/runtime/tier-ops.js +110 -0
  110. package/dist/runtime/tier-ops.js.map +1 -0
  111. package/dist/skills/sync-skills.d.ts.map +1 -1
  112. package/dist/skills/sync-skills.js +1 -1
  113. package/dist/skills/sync-skills.js.map +1 -1
  114. package/dist/vault/linking.d.ts.map +1 -1
  115. package/dist/vault/linking.js +41 -5
  116. package/dist/vault/linking.js.map +1 -1
  117. package/dist/vault/vault-entries.d.ts.map +1 -1
  118. package/dist/vault/vault-entries.js +68 -26
  119. package/dist/vault/vault-entries.js.map +1 -1
  120. package/dist/vault/vault-maintenance.d.ts.map +1 -1
  121. package/dist/vault/vault-maintenance.js +6 -2
  122. package/dist/vault/vault-maintenance.js.map +1 -1
  123. package/dist/vault/vault-markdown-sync.d.ts.map +1 -1
  124. package/dist/vault/vault-markdown-sync.js.map +1 -1
  125. package/dist/vault/vault-memories.d.ts.map +1 -1
  126. package/dist/vault/vault-memories.js +3 -1
  127. package/dist/vault/vault-memories.js.map +1 -1
  128. package/dist/vault/vault-schema.js +36 -10
  129. package/dist/vault/vault-schema.js.map +1 -1
  130. package/dist/vault/vault.d.ts.map +1 -1
  131. package/dist/vault/vault.js +5 -1
  132. package/dist/vault/vault.js.map +1 -1
  133. package/package.json +7 -7
  134. package/src/agency/agency-manager.test.ts +60 -40
  135. package/src/agency/default-rules.test.ts +17 -9
  136. package/src/capabilities/registry.test.ts +2 -12
  137. package/src/chat/agent-loop.test.ts +33 -43
  138. package/src/chat/mcp-bridge.test.ts +7 -2
  139. package/src/claudemd/inject.test.ts +2 -12
  140. package/src/context/context-engine.test.ts +96 -51
  141. package/src/control/intent-router.test.ts +3 -3
  142. package/src/curator/classifier.test.ts +14 -8
  143. package/src/curator/contradiction-detector.test.ts +30 -5
  144. package/src/curator/curator.ts +278 -56
  145. package/src/curator/duplicate-detector.test.ts +77 -15
  146. package/src/curator/quality-gate.test.ts +71 -31
  147. package/src/curator/tag-manager.test.ts +12 -4
  148. package/src/domain-packs/knowledge-installer.test.ts +2 -10
  149. package/src/domain-packs/token-resolver.test.ts +1 -3
  150. package/src/domain-packs/types.test.ts +16 -2
  151. package/src/enforcement/registry.test.ts +2 -8
  152. package/src/engine/bin/soleri-engine.ts +3 -1
  153. package/src/engine/module-manifest.test.ts +5 -4
  154. package/src/engine/module-manifest.ts +21 -1
  155. package/src/engine/register-engine.test.ts +6 -1
  156. package/src/engine/register-engine.ts +26 -3
  157. package/src/errors/classify.test.ts +6 -2
  158. package/src/errors/retry.test.ts +1 -4
  159. package/src/facades/facade-factory.test.ts +110 -64
  160. package/src/flows/epilogue.test.ts +16 -10
  161. package/src/flows/gate-evaluator.test.ts +12 -6
  162. package/src/flows/gate-evaluator.ts +1 -3
  163. package/src/governance/governance.test.ts +137 -21
  164. package/src/health/health-registry.test.ts +8 -1
  165. package/src/intake/content-classifier.test.ts +121 -51
  166. package/src/intake/dedup-gate.test.ts +38 -22
  167. package/src/intake/intake-pipeline.test.ts +5 -3
  168. package/src/intake/text-ingester.test.ts +26 -20
  169. package/src/llm/key-pool.test.ts +1 -3
  170. package/src/llm/llm-client.test.ts +1 -4
  171. package/src/llm/oauth-discovery.test.ts +16 -16
  172. package/src/llm/utils.test.ts +62 -18
  173. package/src/logging/logger.test.ts +4 -1
  174. package/src/loop/loop-manager.test.ts +2 -6
  175. package/src/migrations/migration-runner.edge-cases.test.ts +2 -7
  176. package/src/operator/operator-profile-extended.test.ts +15 -5
  177. package/src/operator/operator-profile.test.ts +26 -8
  178. package/src/operator/operator-profile.ts +38 -22
  179. package/src/operator/operator-signals-extended.test.ts +35 -23
  180. package/src/operator/operator-signals.test.ts +6 -10
  181. package/src/operator/operator-signals.ts +2 -1
  182. package/src/operator/prompts/hook-precompact-operator-dispatch.md +10 -6
  183. package/src/operator/prompts/subagent-soft-signal-extractor.md +5 -0
  184. package/src/operator/prompts/subagent-synthesis-cognition.md +19 -10
  185. package/src/operator/prompts/subagent-synthesis-communication.md +13 -7
  186. package/src/operator/prompts/subagent-synthesis-technical.md +19 -9
  187. package/src/operator/prompts/subagent-synthesis-trust.md +27 -21
  188. package/src/persona/defaults.test.ts +1 -5
  189. package/src/planning/evidence-collector.test.ts +147 -38
  190. package/src/planning/evidence-collector.ts +1 -4
  191. package/src/planning/gap-analysis-alternatives.test.ts +41 -11
  192. package/src/planning/gap-passes.test.ts +215 -33
  193. package/src/planning/gap-passes.ts +115 -46
  194. package/src/planning/gap-patterns.test.ts +87 -13
  195. package/src/planning/gap-patterns.ts +114 -31
  196. package/src/planning/github-projection.test.ts +6 -1
  197. package/src/planning/github-projection.ts +41 -20
  198. package/src/planning/impact-analyzer.test.ts +10 -23
  199. package/src/planning/impact-analyzer.ts +33 -46
  200. package/src/planning/plan-lifecycle.test.ts +103 -36
  201. package/src/planning/plan-lifecycle.ts +49 -18
  202. package/src/planning/planner.test.ts +12 -2
  203. package/src/planning/planner.ts +198 -58
  204. package/src/planning/rationalization-detector.test.ts +5 -20
  205. package/src/planning/rationalization-detector.ts +14 -16
  206. package/src/planning/reconciliation-engine.test.ts +20 -3
  207. package/src/planning/reconciliation-engine.ts +1 -2
  208. package/src/planning/task-verifier.test.ts +59 -27
  209. package/src/planning/task-verifier.ts +15 -9
  210. package/src/playbooks/playbook-executor.test.ts +1 -3
  211. package/src/plugins/plugin-loader.test.ts +19 -14
  212. package/src/plugins/plugin-registry.test.ts +45 -33
  213. package/src/project/project-registry.test.ts +23 -12
  214. package/src/prompts/template-manager.test.ts +4 -1
  215. package/src/queue/job-queue.test.ts +10 -14
  216. package/src/runtime/admin-extra-ops.test.ts +5 -19
  217. package/src/runtime/admin-ops.test.ts +1 -3
  218. package/src/runtime/admin-setup-ops.test.ts +3 -4
  219. package/src/runtime/admin-setup-ops.ts +9 -2
  220. package/src/runtime/archive-ops.test.ts +4 -1
  221. package/src/runtime/branching-ops.test.ts +144 -0
  222. package/src/runtime/branching-ops.ts +107 -0
  223. package/src/runtime/capture-ops.test.ts +7 -21
  224. package/src/runtime/chain-ops.test.ts +16 -6
  225. package/src/runtime/claude-md-helpers.test.ts +1 -3
  226. package/src/runtime/context-health.test.ts +1 -3
  227. package/src/runtime/context-health.ts +1 -3
  228. package/src/runtime/curator-extra-ops.test.ts +3 -1
  229. package/src/runtime/domain-ops.test.ts +46 -36
  230. package/src/runtime/facades/admin-facade.test.ts +1 -4
  231. package/src/runtime/facades/archive-facade.test.ts +21 -7
  232. package/src/runtime/facades/brain-facade.test.ts +176 -72
  233. package/src/runtime/facades/branching-facade.test.ts +43 -0
  234. package/src/runtime/facades/branching-facade.ts +11 -0
  235. package/src/runtime/facades/chat-facade.test.ts +81 -28
  236. package/src/runtime/facades/chat-service-ops.test.ts +178 -73
  237. package/src/runtime/facades/chat-service-ops.ts +3 -1
  238. package/src/runtime/facades/chat-session-ops.test.ts +25 -10
  239. package/src/runtime/facades/chat-transport-ops.test.ts +101 -34
  240. package/src/runtime/facades/chat-transport-ops.ts +0 -1
  241. package/src/runtime/facades/context-facade.test.ts +19 -4
  242. package/src/runtime/facades/control-facade.test.ts +3 -3
  243. package/src/runtime/facades/index.ts +42 -0
  244. package/src/runtime/facades/intake-facade.test.ts +215 -0
  245. package/src/runtime/facades/intake-facade.ts +14 -0
  246. package/src/runtime/facades/links-facade.test.ts +203 -0
  247. package/src/runtime/facades/links-facade.ts +13 -0
  248. package/src/runtime/facades/loop-facade.test.ts +22 -5
  249. package/src/runtime/facades/memory-facade.test.ts +19 -5
  250. package/src/runtime/facades/operator-facade.test.ts +17 -4
  251. package/src/runtime/facades/operator-facade.ts +11 -3
  252. package/src/runtime/facades/orchestrate-facade.test.ts +7 -1
  253. package/src/runtime/facades/plan-facade.test.ts +29 -12
  254. package/src/runtime/facades/plan-facade.ts +7 -2
  255. package/src/runtime/facades/tier-facade.test.ts +47 -0
  256. package/src/runtime/facades/tier-facade.ts +11 -0
  257. package/src/runtime/facades/vault-facade.test.ts +174 -242
  258. package/src/runtime/facades/vault-facade.ts +55 -199
  259. package/src/runtime/github-integration.ts +11 -8
  260. package/src/runtime/grading-ops.test.ts +39 -8
  261. package/src/runtime/intake-ops.test.ts +69 -16
  262. package/src/runtime/loop-ops.test.ts +16 -6
  263. package/src/runtime/memory-cross-project-ops.test.ts +25 -14
  264. package/src/runtime/orchestrate-ops.ts +54 -27
  265. package/src/runtime/pack-ops.test.ts +23 -6
  266. package/src/runtime/planning-extra-ops.test.ts +17 -7
  267. package/src/runtime/planning-extra-ops.ts +3 -1
  268. package/src/runtime/playbook-ops.test.ts +26 -3
  269. package/src/runtime/plugin-ops.test.ts +83 -25
  270. package/src/runtime/project-ops.test.ts +26 -6
  271. package/src/runtime/runtime.ts +3 -1
  272. package/src/runtime/session-briefing.test.ts +183 -54
  273. package/src/runtime/session-briefing.ts +8 -2
  274. package/src/runtime/sync-ops.test.ts +3 -12
  275. package/src/runtime/telemetry-ops.test.ts +31 -6
  276. package/src/runtime/tier-ops.test.ts +159 -0
  277. package/src/runtime/tier-ops.ts +119 -0
  278. package/src/runtime/vault-extra-ops.test.ts +32 -8
  279. package/src/runtime/vault-sharing-ops.test.ts +1 -4
  280. package/src/skills/sync-skills.ts +2 -12
  281. package/src/transport/ws-server.test.ts +7 -4
  282. package/src/vault/__tests__/vault-characterization.test.ts +492 -81
  283. package/src/vault/linking.test.ts +50 -17
  284. package/src/vault/linking.ts +48 -7
  285. package/src/vault/obsidian-sync.test.ts +6 -3
  286. package/src/vault/scope-detector.test.ts +1 -3
  287. package/src/vault/vault-branching.test.ts +9 -7
  288. package/src/vault/vault-entries.ts +209 -65
  289. package/src/vault/vault-maintenance.ts +7 -12
  290. package/src/vault/vault-manager.test.ts +10 -10
  291. package/src/vault/vault-markdown-sync.ts +4 -1
  292. package/src/vault/vault-memories.ts +7 -7
  293. package/src/vault/vault-schema.ts +72 -15
  294. package/src/vault/vault.ts +55 -9
  295. package/src/brain/strength-scorer.ts +0 -404
  296. package/src/engine/index.ts +0 -21
  297. package/src/persona/index.ts +0 -9
  298. package/src/vault/vault-interfaces.ts +0 -56
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Colocated contract tests for links-facade.ts.
3
+ * Verifies the facade delegates to createVaultLinkingOps and
4
+ * exposes all 9 linking ops with correct auth levels.
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
8
+ import { createLinksFacadeOps } from './links-facade.js';
9
+ import { captureOps, executeOp } from '../../engine/test-helpers.js';
10
+ import type { CapturedOp } from '../../engine/test-helpers.js';
11
+ import type { AgentRuntime } from '../types.js';
12
+
13
+ // ─── Mock runtime ────────────────────────────────────────────────────
14
+
15
+ function makeRuntime(): AgentRuntime {
16
+ return {
17
+ vault: {
18
+ getProvider: vi.fn().mockReturnValue({
19
+ get: vi.fn().mockReturnValue({ id: 'x' }),
20
+ all: vi.fn().mockReturnValue([]),
21
+ run: vi.fn(),
22
+ }),
23
+ },
24
+ linkManager: {
25
+ addLink: vi.fn(),
26
+ removeLink: vi.fn(),
27
+ getLinks: vi.fn().mockReturnValue([]),
28
+ getBacklinks: vi.fn().mockReturnValue([]),
29
+ getLinkCount: vi.fn().mockReturnValue(1),
30
+ traverse: vi.fn().mockReturnValue([]),
31
+ suggestLinks: vi.fn().mockReturnValue([]),
32
+ getOrphans: vi.fn().mockReturnValue([]),
33
+ backfillLinks: vi.fn().mockReturnValue({ created: 0, preview: [] }),
34
+ },
35
+ llmClient: {
36
+ isAvailable: vi.fn().mockReturnValue({ anthropic: false, openai: false }),
37
+ complete: vi.fn(),
38
+ },
39
+ } as unknown as AgentRuntime;
40
+ }
41
+
42
+ // ─── Tests ───────────────────────────────────────────────────────────
43
+
44
+ describe('links-facade', () => {
45
+ let runtime: AgentRuntime;
46
+ let ops: Map<string, CapturedOp>;
47
+
48
+ beforeEach(() => {
49
+ runtime = makeRuntime();
50
+ ops = captureOps(createLinksFacadeOps(runtime));
51
+ });
52
+
53
+ // ─── Registration ──────────────────────────────────────────────────
54
+
55
+ it('registers exactly 9 ops', () => {
56
+ expect(ops.size).toBe(9);
57
+ });
58
+
59
+ it('includes all expected op names', () => {
60
+ const expected = [
61
+ 'link_entries',
62
+ 'unlink_entries',
63
+ 'get_links',
64
+ 'traverse',
65
+ 'suggest_links',
66
+ 'get_orphans',
67
+ 'relink_vault',
68
+ 'backfill_links',
69
+ 'link_stats',
70
+ ];
71
+ for (const name of expected) {
72
+ expect(ops.has(name), `missing op: ${name}`).toBe(true);
73
+ }
74
+ });
75
+
76
+ // ─── Auth levels ───────────────────────────────────────────────────
77
+
78
+ it('has correct auth levels', () => {
79
+ expect(ops.get('link_entries')!.auth).toBe('write');
80
+ expect(ops.get('unlink_entries')!.auth).toBe('write');
81
+ expect(ops.get('get_links')!.auth).toBe('read');
82
+ expect(ops.get('traverse')!.auth).toBe('read');
83
+ expect(ops.get('suggest_links')!.auth).toBe('read');
84
+ expect(ops.get('get_orphans')!.auth).toBe('read');
85
+ expect(ops.get('relink_vault')!.auth).toBe('write');
86
+ expect(ops.get('backfill_links')!.auth).toBe('write');
87
+ expect(ops.get('link_stats')!.auth).toBe('read');
88
+ });
89
+
90
+ // ─── Handler delegation ───────────────────────────────────────────
91
+
92
+ describe('link_entries', () => {
93
+ it('creates a link via linkManager', async () => {
94
+ const result = await executeOp(ops, 'link_entries', {
95
+ sourceId: 'a',
96
+ targetId: 'b',
97
+ linkType: 'extends',
98
+ note: 'test',
99
+ });
100
+ expect(result.success).toBe(true);
101
+ const data = result.data as { success: boolean };
102
+ expect(data.success).toBe(true);
103
+ const lm = runtime.linkManager as { addLink: ReturnType<typeof vi.fn> };
104
+ expect(lm.addLink).toHaveBeenCalledWith('a', 'b', 'extends', 'test');
105
+ });
106
+ });
107
+
108
+ describe('unlink_entries', () => {
109
+ it('removes a link', async () => {
110
+ const result = await executeOp(ops, 'unlink_entries', {
111
+ sourceId: 'a',
112
+ targetId: 'b',
113
+ });
114
+ expect(result.success).toBe(true);
115
+ const lm = runtime.linkManager as { removeLink: ReturnType<typeof vi.fn> };
116
+ expect(lm.removeLink).toHaveBeenCalledWith('a', 'b');
117
+ });
118
+ });
119
+
120
+ describe('get_links', () => {
121
+ it('returns outgoing and incoming links', async () => {
122
+ const result = await executeOp(ops, 'get_links', { entryId: 'a' });
123
+ expect(result.success).toBe(true);
124
+ const data = result.data as { totalLinks: number };
125
+ expect(data.totalLinks).toBe(0);
126
+ });
127
+ });
128
+
129
+ describe('traverse', () => {
130
+ it('traverses with default depth', async () => {
131
+ const result = await executeOp(ops, 'traverse', { entryId: 'a' });
132
+ expect(result.success).toBe(true);
133
+ const lm = runtime.linkManager as { traverse: ReturnType<typeof vi.fn> };
134
+ expect(lm.traverse).toHaveBeenCalledWith('a', 2);
135
+ });
136
+ });
137
+
138
+ describe('suggest_links', () => {
139
+ it('returns suggestions', async () => {
140
+ const result = await executeOp(ops, 'suggest_links', { entryId: 'a' });
141
+ expect(result.success).toBe(true);
142
+ const data = result.data as { totalSuggestions: number };
143
+ expect(data.totalSuggestions).toBe(0);
144
+ });
145
+ });
146
+
147
+ describe('get_orphans', () => {
148
+ it('returns orphaned entries', async () => {
149
+ const result = await executeOp(ops, 'get_orphans', {});
150
+ expect(result.success).toBe(true);
151
+ const data = result.data as { totalOrphans: number };
152
+ expect(data.totalOrphans).toBe(0);
153
+ });
154
+ });
155
+
156
+ describe('backfill_links', () => {
157
+ it('delegates to linkManager', async () => {
158
+ const result = await executeOp(ops, 'backfill_links', {
159
+ threshold: 0.7,
160
+ maxLinks: 3,
161
+ dryRun: false,
162
+ batchSize: 50,
163
+ });
164
+ expect(result.success).toBe(true);
165
+ const data = result.data as { created: number };
166
+ expect(data.created).toBe(0);
167
+ });
168
+ });
169
+
170
+ describe('link_stats', () => {
171
+ it('returns graph statistics', async () => {
172
+ const provider = (runtime.vault as { getProvider: ReturnType<typeof vi.fn> }).getProvider();
173
+ vi.mocked(provider.get)
174
+ .mockReturnValueOnce({ c: 10 }) // totalLinks
175
+ .mockReturnValueOnce({ c: 50 }) // totalEntries
176
+ .mockReturnValueOnce({ c: 3 }) // orphans
177
+ .mockReturnValueOnce({ c: 5 }); // withNotes
178
+ vi.mocked(provider.all)
179
+ .mockReturnValueOnce([{ link_type: 'extends', c: 7 }])
180
+ .mockReturnValueOnce([{ title: 'Top', links: 5 }]);
181
+
182
+ const result = await executeOp(ops, 'link_stats', {});
183
+ expect(result.success).toBe(true);
184
+ const data = result.data as { totalLinks: number; totalEntries: number };
185
+ expect(data.totalLinks).toBe(10);
186
+ expect(data.totalEntries).toBe(50);
187
+ });
188
+ });
189
+
190
+ describe('relink_vault', () => {
191
+ it('returns error when no LLM available', async () => {
192
+ const result = await executeOp(ops, 'relink_vault', {
193
+ batchSize: 10,
194
+ limit: 0,
195
+ dryRun: false,
196
+ });
197
+ expect(result.success).toBe(true);
198
+ const data = result.data as { success: boolean; error: string };
199
+ expect(data.success).toBe(false);
200
+ expect(data.error).toContain('No LLM');
201
+ });
202
+ });
203
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Links facade — Zettelkasten entry linking ops.
3
+ * link_entries, unlink_entries, get_links, traverse, suggest_links,
4
+ * get_orphans, relink_vault, backfill_links, link_stats.
5
+ */
6
+
7
+ import type { OpDefinition } from '../../facades/types.js';
8
+ import type { AgentRuntime } from '../types.js';
9
+ import { createVaultLinkingOps } from '../vault-linking-ops.js';
10
+
11
+ export function createLinksFacadeOps(runtime: AgentRuntime): OpDefinition[] {
12
+ return createVaultLinkingOps(runtime);
13
+ }
@@ -7,7 +7,14 @@ import type { AgentRuntime } from '../types.js';
7
7
  // ─── Mock LoopManager ──────────────────────────────────────────────
8
8
 
9
9
  function makeMockLoop() {
10
- const activeLoop: { id: string; config: Record<string, unknown>; iterations: unknown[]; status: string; startedAt: string; completedAt?: string } = {
10
+ const activeLoop: {
11
+ id: string;
12
+ config: Record<string, unknown>;
13
+ iterations: unknown[];
14
+ status: string;
15
+ startedAt: string;
16
+ completedAt?: string;
17
+ } = {
11
18
  id: 'loop-1',
12
19
  config: { mode: 'custom', prompt: 'test task', maxIterations: 20 },
13
20
  iterations: [],
@@ -16,7 +23,7 @@ function makeMockLoop() {
16
23
  };
17
24
 
18
25
  let isActive = false;
19
- const history: typeof activeLoop[] = [];
26
+ const history: (typeof activeLoop)[] = [];
20
27
 
21
28
  return {
22
29
  startLoop: vi.fn().mockImplementation((config: Record<string, unknown>) => {
@@ -118,7 +125,10 @@ describe('loop-facade', () => {
118
125
  });
119
126
 
120
127
  it('loop_start uses mode-specific defaults for token-migration', async () => {
121
- const result = await executeOp(ops, 'loop_start', { mode: 'token-migration', prompt: 'migrate tokens' });
128
+ const result = await executeOp(ops, 'loop_start', {
129
+ mode: 'token-migration',
130
+ prompt: 'migrate tokens',
131
+ });
122
132
  expect(result.success).toBe(true);
123
133
  const data = result.data as Record<string, unknown>;
124
134
  expect(data.maxIterations).toBe(20);
@@ -126,7 +136,11 @@ describe('loop-facade', () => {
126
136
  });
127
137
 
128
138
  it('loop_start respects custom maxIterations', async () => {
129
- const result = await executeOp(ops, 'loop_start', { mode: 'custom', prompt: 'test', maxIterations: 5 });
139
+ const result = await executeOp(ops, 'loop_start', {
140
+ mode: 'custom',
141
+ prompt: 'test',
142
+ maxIterations: 5,
143
+ });
130
144
  expect(result.success).toBe(true);
131
145
  expect((result.data as Record<string, unknown>).maxIterations).toBe(5);
132
146
  });
@@ -144,7 +158,10 @@ describe('loop-facade', () => {
144
158
 
145
159
  it('loop_iterate records a failing iteration', async () => {
146
160
  await executeOp(ops, 'loop_start', { mode: 'custom', prompt: 'test' });
147
- const result = await executeOp(ops, 'loop_iterate', { passed: false, validationResult: 'score too low' });
161
+ const result = await executeOp(ops, 'loop_iterate', {
162
+ passed: false,
163
+ validationResult: 'score too low',
164
+ });
148
165
  expect(result.success).toBe(true);
149
166
  expect((result.data as Record<string, unknown>).passed).toBe(false);
150
167
  });
@@ -127,10 +127,16 @@ describe('memory-facade', () => {
127
127
 
128
128
  it('memory_list filters by type', async () => {
129
129
  await executeOp(ops, 'memory_capture', {
130
- projectPath: '/test', type: 'lesson', context: 'c', summary: 'lesson',
130
+ projectPath: '/test',
131
+ type: 'lesson',
132
+ context: 'c',
133
+ summary: 'lesson',
131
134
  });
132
135
  await executeOp(ops, 'memory_capture', {
133
- projectPath: '/test', type: 'preference', context: 'c', summary: 'pref',
136
+ projectPath: '/test',
137
+ type: 'preference',
138
+ context: 'c',
139
+ summary: 'pref',
134
140
  });
135
141
  const result = await executeOp(ops, 'memory_list', { type: 'lesson' });
136
142
  expect(result.success).toBe(true);
@@ -140,7 +146,10 @@ describe('memory-facade', () => {
140
146
 
141
147
  it('memory_list verbose returns full objects', async () => {
142
148
  await executeOp(ops, 'memory_capture', {
143
- projectPath: '/test', type: 'lesson', context: 'c', summary: 'verbose list',
149
+ projectPath: '/test',
150
+ type: 'lesson',
151
+ context: 'c',
152
+ summary: 'verbose list',
144
153
  });
145
154
  const result = await executeOp(ops, 'memory_list', { verbose: true });
146
155
  expect(result.success).toBe(true);
@@ -192,9 +201,14 @@ describe('memory-facade', () => {
192
201
 
193
202
  it('memory_delete removes a memory', async () => {
194
203
  const captureResult = await executeOp(ops, 'memory_capture', {
195
- projectPath: '/test', type: 'lesson', context: 'c', summary: 'to delete',
204
+ projectPath: '/test',
205
+ type: 'lesson',
206
+ context: 'c',
207
+ summary: 'to delete',
196
208
  });
197
- const memoryId = ((captureResult.data as Record<string, unknown>).memory as Record<string, unknown>).id as string;
209
+ const memoryId = (
210
+ (captureResult.data as Record<string, unknown>).memory as Record<string, unknown>
211
+ ).id as string;
198
212
  const result = await executeOp(ops, 'memory_delete', { memoryId });
199
213
  expect(result.success).toBe(true);
200
214
  expect((result.data as Record<string, unknown>).deleted).toBe(true);
@@ -45,9 +45,16 @@ describe('operator-facade (colocated)', () => {
45
45
  expect(ops.size).toBe(10);
46
46
  expect([...ops.keys()]).toEqual(
47
47
  expect.arrayContaining([
48
- 'profile_get', 'profile_update_section', 'profile_correct',
49
- 'profile_delete', 'profile_export', 'signal_accumulate',
50
- 'signal_list', 'signal_stats', 'synthesis_check', 'profile_snapshot',
48
+ 'profile_get',
49
+ 'profile_update_section',
50
+ 'profile_correct',
51
+ 'profile_delete',
52
+ 'profile_export',
53
+ 'signal_accumulate',
54
+ 'signal_list',
55
+ 'signal_stats',
56
+ 'synthesis_check',
57
+ 'profile_snapshot',
51
58
  ]),
52
59
  );
53
60
  });
@@ -85,7 +92,13 @@ describe('operator-facade (colocated)', () => {
85
92
  it('profile_update_section updates section', async () => {
86
93
  const result = await executeOp(ops, 'profile_update_section', {
87
94
  section: 'communication',
88
- data: { style: 'concise', signalWords: ['yo'], formality: 0.3, patience: 0.9, adaptationRules: [] },
95
+ data: {
96
+ style: 'concise',
97
+ signalWords: ['yo'],
98
+ formality: 0.3,
99
+ patience: 0.9,
100
+ adaptationRules: [],
101
+ },
89
102
  });
90
103
  expect(result.success).toBe(true);
91
104
  expect((result.data as Record<string, unknown>).updated).toBe(true);
@@ -67,7 +67,8 @@ function profileToMarkdown(profile: Record<string, unknown>): string {
67
67
  if (evidence.length > 0) {
68
68
  lines.push(`**Evidence (${evidence.length}):**`, '');
69
69
  for (const e of evidence.slice(0, 10)) {
70
- const label = e.source === 'observed' ? '[observed]' : e.source === 'reported' ? '[reported]' : '';
70
+ const label =
71
+ e.source === 'observed' ? '[observed]' : e.source === 'reported' ? '[reported]' : '';
71
72
  lines.push(`- ${label} ${e.summary} (confidence: ${e.confidence}, ${e.timestamp})`);
72
73
  }
73
74
  lines.push('');
@@ -77,10 +78,17 @@ function profileToMarkdown(profile: Record<string, unknown>): string {
77
78
  return lines.join('\n');
78
79
  }
79
80
 
80
- function extractEvidence(obj: Record<string, unknown>): Array<{ summary: string; confidence: number; timestamp: string; source?: string }> {
81
+ function extractEvidence(
82
+ obj: Record<string, unknown>,
83
+ ): Array<{ summary: string; confidence: number; timestamp: string; source?: string }> {
81
84
  const evidence = obj.evidence;
82
85
  if (!Array.isArray(evidence)) return [];
83
- return evidence.filter((e) => e && typeof e === 'object' && 'summary' in e) as Array<{ summary: string; confidence: number; timestamp: string; source?: string }>;
86
+ return evidence.filter((e) => e && typeof e === 'object' && 'summary' in e) as Array<{
87
+ summary: string;
88
+ confidence: number;
89
+ timestamp: string;
90
+ source?: string;
91
+ }>;
84
92
  }
85
93
 
86
94
  // ─── Facade Creator ─────────────────────────────────────────────────
@@ -25,7 +25,13 @@ function makeRuntime(vault: Vault): AgentRuntime {
25
25
  const playbookExecutor = new PlaybookExecutor();
26
26
  const contextHealth = {
27
27
  track: vi.fn(),
28
- check: vi.fn().mockReturnValue({ level: 'green', estimatedFill: 0.1, toolCallCount: 5, estimatedTokens: 1000, recommendation: '' }),
28
+ check: vi.fn().mockReturnValue({
29
+ level: 'green',
30
+ estimatedFill: 0.1,
31
+ toolCallCount: 5,
32
+ estimatedTokens: 1000,
33
+ recommendation: '',
34
+ }),
29
35
  };
30
36
 
31
37
  return {
@@ -23,7 +23,13 @@ function makeRuntime(vault: Vault): AgentRuntime {
23
23
  const linkManager = null;
24
24
  const chainRunner = new ChainRunner(vault.getProvider());
25
25
  return {
26
- vault, planner, brain, brainIntelligence, curator, linkManager, chainRunner,
26
+ vault,
27
+ planner,
28
+ brain,
29
+ brainIntelligence,
30
+ curator,
31
+ linkManager,
32
+ chainRunner,
27
33
  } as unknown as AgentRuntime;
28
34
  }
29
35
 
@@ -92,9 +98,11 @@ describe('plan-facade', () => {
92
98
 
93
99
  it('get_plan returns plan by ID', async () => {
94
100
  const createResult = await executeOp(ops, 'create_plan', {
95
- objective: 'Test', scope: 'test',
101
+ objective: 'Test',
102
+ scope: 'test',
96
103
  });
97
- const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>).id as string;
104
+ const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>)
105
+ .id as string;
98
106
  const result = await executeOp(ops, 'get_plan', { planId });
99
107
  expect(result.success).toBe(true);
100
108
  expect((result.data as Record<string, unknown>).objective).toBe('Test');
@@ -126,7 +134,8 @@ describe('plan-facade', () => {
126
134
  { title: 'Task 2', description: 'desc 2' },
127
135
  ],
128
136
  });
129
- const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>).id as string;
137
+ const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>)
138
+ .id as string;
130
139
 
131
140
  const result = await executeOp(ops, 'approve_plan', { planId });
132
141
  expect(result.success).toBe(true);
@@ -150,7 +159,8 @@ describe('plan-facade', () => {
150
159
  { approach: 'Alt B', pros: ['safe'], cons: ['slow'], rejected_reason: 'Too slow' },
151
160
  ],
152
161
  });
153
- const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>).id as string;
162
+ const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>)
163
+ .id as string;
154
164
 
155
165
  const result = await executeOp(ops, 'approve_plan', { planId, startExecution: true });
156
166
  expect(result.success).toBe(true);
@@ -171,13 +181,15 @@ describe('plan-facade', () => {
171
181
  });
172
182
  const plan = (createResult.data as Record<string, unknown>).plan as Record<string, unknown>;
173
183
  const planId = plan.id as string;
174
- const taskId = ((plan.tasks as Array<Record<string, unknown>>)[0]).id as string;
184
+ const taskId = (plan.tasks as Array<Record<string, unknown>>)[0].id as string;
175
185
 
176
186
  // Approve and start execution first
177
187
  await executeOp(ops, 'approve_plan', { planId, startExecution: true });
178
188
 
179
189
  const result = await executeOp(ops, 'update_task', {
180
- planId, taskId, status: 'completed',
190
+ planId,
191
+ taskId,
192
+ status: 'completed',
181
193
  });
182
194
  expect(result.success).toBe(true);
183
195
  const data = result.data as { updated: boolean };
@@ -197,7 +209,8 @@ describe('plan-facade', () => {
197
209
  { approach: 'B', pros: ['a'], cons: ['b'], rejected_reason: 'c' },
198
210
  ],
199
211
  });
200
- const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>).id as string;
212
+ const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>)
213
+ .id as string;
201
214
  await executeOp(ops, 'approve_plan', { planId, startExecution: true });
202
215
 
203
216
  const result = await executeOp(ops, 'complete_plan', { planId });
@@ -211,12 +224,15 @@ describe('plan-facade', () => {
211
224
 
212
225
  it('plan_iterate revises a draft plan', async () => {
213
226
  const createResult = await executeOp(ops, 'create_plan', {
214
- objective: 'Initial', scope: 'test',
227
+ objective: 'Initial',
228
+ scope: 'test',
215
229
  });
216
- const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>).id as string;
230
+ const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>)
231
+ .id as string;
217
232
 
218
233
  const result = await executeOp(ops, 'plan_iterate', {
219
- planId, objective: 'Revised objective',
234
+ planId,
235
+ objective: 'Revised objective',
220
236
  });
221
237
  expect(result.success).toBe(true);
222
238
  const data = result.data as { iterated?: boolean; error?: string };
@@ -240,7 +256,8 @@ describe('plan-facade', () => {
240
256
  decisions: ['Use pattern X'],
241
257
  tasks: [{ title: 'Task 1', description: 'desc' }],
242
258
  });
243
- const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>).id as string;
259
+ const planId = ((createResult.data as Record<string, unknown>).plan as Record<string, unknown>)
260
+ .id as string;
244
261
 
245
262
  const result = await executeOp(ops, 'plan_grade', { planId });
246
263
  expect(result.success).toBe(true);
@@ -24,7 +24,10 @@ export function createPlanFacadeOps(runtime: AgentRuntime): OpDefinition[] {
24
24
  schema: z.object({
25
25
  objective: z.string().describe('What the plan aims to achieve'),
26
26
  scope: z.string().describe('Which parts of the codebase are affected'),
27
- decisions: z.array(z.union([z.string(), z.object({ decision: z.string(), rationale: z.string() })])).optional().default([]),
27
+ decisions: z
28
+ .array(z.union([z.string(), z.object({ decision: z.string(), rationale: z.string() })]))
29
+ .optional()
30
+ .default([]),
28
31
  tasks: z
29
32
  .array(z.object({ title: z.string(), description: z.string() }))
30
33
  .optional()
@@ -47,7 +50,9 @@ export function createPlanFacadeOps(runtime: AgentRuntime): OpDefinition[] {
47
50
  scope: params.scope as string,
48
51
  decisions: (params.decisions as string[]) ?? [],
49
52
  tasks: (params.tasks as Array<{ title: string; description: string }>) ?? [],
50
- alternatives: params.alternatives as Array<{ approach: string; pros: string[]; cons: string[]; rejected_reason: string }> | undefined,
53
+ alternatives: params.alternatives as
54
+ | Array<{ approach: string; pros: string[]; cons: string[]; rejected_reason: string }>
55
+ | undefined,
51
56
  });
52
57
  return { created: true, plan };
53
58
  },
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Colocated contract tests for tier-facade.ts.
3
+ * Verifies the facade wrapper delegates to tier-ops.
4
+ */
5
+
6
+ import { describe, it, expect, vi } from 'vitest';
7
+ import { createTierFacadeOps } from './tier-facade.js';
8
+ import type { AgentRuntime } from '../types.js';
9
+
10
+ function makeRuntime(): AgentRuntime {
11
+ return {
12
+ vaultManager: {
13
+ open: vi.fn(),
14
+ disconnect: vi.fn().mockReturnValue(true),
15
+ listTiers: vi.fn().mockReturnValue([]),
16
+ search: vi.fn().mockReturnValue([]),
17
+ connect: vi.fn(),
18
+ disconnectNamed: vi.fn().mockReturnValue(true),
19
+ listConnected: vi.fn().mockReturnValue([]),
20
+ },
21
+ config: { agentId: 'test-agent' },
22
+ } as unknown as AgentRuntime;
23
+ }
24
+
25
+ describe('tier-facade', () => {
26
+ it('returns all 7 tier/source ops', () => {
27
+ const ops = createTierFacadeOps(makeRuntime());
28
+ expect(ops).toHaveLength(7);
29
+ const names = ops.map((o) => o.name);
30
+ expect(names).toContain('vault_connect');
31
+ expect(names).toContain('vault_disconnect');
32
+ expect(names).toContain('vault_tiers');
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');
37
+ });
38
+
39
+ it('every op has name, handler, and auth', () => {
40
+ const ops = createTierFacadeOps(makeRuntime());
41
+ for (const op of ops) {
42
+ expect(typeof op.name).toBe('string');
43
+ expect(typeof op.handler).toBe('function');
44
+ expect(typeof op.auth).toBe('string');
45
+ }
46
+ });
47
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tier facade — multi-vault tier and named source operations.
3
+ */
4
+
5
+ import type { OpDefinition } from '../../facades/types.js';
6
+ import type { AgentRuntime } from '../types.js';
7
+ import { createTierOps } from '../tier-ops.js';
8
+
9
+ export function createTierFacadeOps(runtime: AgentRuntime): OpDefinition[] {
10
+ return [...createTierOps(runtime)];
11
+ }