@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
@@ -9,24 +9,59 @@ import { createChatState } from './chat-state.js';
9
9
  import type { ChatState } from './chat-state.js';
10
10
  import type { OpDefinition } from '../../facades/types.js';
11
11
 
12
+ // ─── Mock instances ─────────────────────────────────────────────────
13
+
14
+ const mockCancellation = {
15
+ create: vi.fn().mockReturnValue({ aborted: false }),
16
+ cancel: vi.fn().mockReturnValue({ description: 'test', startedAt: 1000 }),
17
+ getInfo: vi.fn().mockReturnValue({ description: 'test', startedAt: 1000 }),
18
+ listRunning: vi.fn().mockReturnValue(['chat-1']),
19
+ size: 1,
20
+ };
21
+
22
+ const mockSelfUpdate = {
23
+ loadContext: vi.fn().mockReturnValue(null),
24
+ clearContext: vi.fn(),
25
+ requestRestart: vi.fn().mockReturnValue({ scheduled: true }),
26
+ };
27
+
28
+ const mockNotification = {
29
+ start: vi.fn(),
30
+ stop: vi.fn(),
31
+ poll: vi.fn().mockResolvedValue(2),
32
+ stats: vi.fn().mockReturnValue({ checks: 3, running: true, sent: 5, lastPollAt: 1000 }),
33
+ };
34
+
35
+ const mockBrowserSession = {
36
+ process: { pid: 1234 },
37
+ };
38
+
39
+ const mockBrowser = {
40
+ acquire: vi.fn().mockReturnValue(mockBrowserSession),
41
+ release: vi.fn().mockReturnValue(true),
42
+ size: 1,
43
+ listSessions: vi.fn().mockReturnValue(['c1']),
44
+ getInfo: vi.fn().mockReturnValue({ startedAt: 1000 }),
45
+ };
46
+
12
47
  // ─── Mock chat modules ─────────────────────────────────────────────
13
48
 
14
49
  vi.mock('../../chat/cancellation.js', () => ({
15
- TaskCancellationManager: vi.fn().mockImplementation(() => ({
16
- create: vi.fn().mockReturnValue({ aborted: false }),
17
- cancel: vi.fn().mockReturnValue({ description: 'test', startedAt: 1000 }),
18
- getInfo: vi.fn().mockReturnValue({ description: 'test', startedAt: 1000 }),
19
- listRunning: vi.fn().mockReturnValue(['chat-1']),
20
- size: 1,
21
- })),
50
+ TaskCancellationManager: class {
51
+ create = mockCancellation.create;
52
+ cancel = mockCancellation.cancel;
53
+ getInfo = mockCancellation.getInfo;
54
+ listRunning = mockCancellation.listRunning;
55
+ size = mockCancellation.size;
56
+ },
22
57
  }));
23
58
 
24
59
  vi.mock('../../chat/self-update.js', () => ({
25
- SelfUpdateManager: vi.fn().mockImplementation(() => ({
26
- loadContext: vi.fn().mockReturnValue(null),
27
- clearContext: vi.fn(),
28
- requestRestart: vi.fn().mockReturnValue({ scheduled: true }),
29
- })),
60
+ SelfUpdateManager: class {
61
+ loadContext = mockSelfUpdate.loadContext;
62
+ clearContext = mockSelfUpdate.clearContext;
63
+ requestRestart = mockSelfUpdate.requestRestart;
64
+ },
30
65
  }));
31
66
 
32
67
  vi.mock('../../chat/file-handler.js', () => ({
@@ -36,31 +71,35 @@ vi.mock('../../chat/file-handler.js', () => ({
36
71
  }));
37
72
 
38
73
  vi.mock('../../chat/notifications.js', () => ({
39
- NotificationEngine: vi.fn().mockImplementation(() => ({
40
- start: vi.fn(),
41
- stop: vi.fn(),
42
- poll: vi.fn().mockResolvedValue(2),
43
- stats: vi.fn().mockReturnValue({ checks: 3, running: true, sent: 5, lastPollAt: 1000 }),
44
- })),
74
+ NotificationEngine: class {
75
+ start = mockNotification.start;
76
+ stop = mockNotification.stop;
77
+ poll = mockNotification.poll;
78
+ stats = mockNotification.stats;
79
+ },
45
80
  }));
46
81
 
47
- const mockBrowserSession = {
48
- process: { pid: 1234 },
49
- };
50
-
51
82
  vi.mock('../../chat/browser-session.js', () => ({
52
- BrowserSessionManager: vi.fn().mockImplementation(() => ({
53
- acquire: vi.fn().mockReturnValue(mockBrowserSession),
54
- release: vi.fn().mockReturnValue(true),
55
- size: 1,
56
- listSessions: vi.fn().mockReturnValue(['c1']),
57
- getInfo: vi.fn().mockReturnValue({ startedAt: 1000 }),
58
- })),
83
+ BrowserSessionManager: class {
84
+ acquire = mockBrowser.acquire;
85
+ release = mockBrowser.release;
86
+ size = mockBrowser.size;
87
+ listSessions = mockBrowser.listSessions;
88
+ getInfo = mockBrowser.getInfo;
89
+ },
59
90
  }));
60
91
 
61
92
  // Needed via chat-state imports
62
- vi.mock('../../chat/chat-session.js', () => ({ ChatSessionManager: vi.fn() }));
63
- vi.mock('../../chat/auth-manager.js', () => ({ ChatAuthManager: vi.fn() }));
93
+ vi.mock('../../chat/chat-session.js', () => ({
94
+ ChatSessionManager: class {
95
+ _stub = true;
96
+ },
97
+ }));
98
+ vi.mock('../../chat/auth-manager.js', () => ({
99
+ ChatAuthManager: class {
100
+ _stub = true;
101
+ },
102
+ }));
64
103
 
65
104
  // ─── Helpers ────────────────────────────────────────────────────────
66
105
 
@@ -85,12 +124,24 @@ describe('chat-service-ops', () => {
85
124
  it('exports 18 service ops', () => {
86
125
  const names = ops.map((o) => o.name);
87
126
  expect(names).toEqual([
88
- 'chat_cancel_create', 'chat_cancel_stop', 'chat_cancel_status',
89
- 'chat_update_init', 'chat_update_request', 'chat_update_confirm',
90
- 'chat_file_detect_intent', 'chat_file_build_content', 'chat_file_cleanup',
91
- 'chat_notify_init', 'chat_notify_start', 'chat_notify_stop',
92
- 'chat_notify_poll', 'chat_notify_status',
93
- 'chat_browser_init', 'chat_browser_acquire', 'chat_browser_release', 'chat_browser_status',
127
+ 'chat_cancel_create',
128
+ 'chat_cancel_stop',
129
+ 'chat_cancel_status',
130
+ 'chat_update_init',
131
+ 'chat_update_request',
132
+ 'chat_update_confirm',
133
+ 'chat_file_detect_intent',
134
+ 'chat_file_build_content',
135
+ 'chat_file_cleanup',
136
+ 'chat_notify_init',
137
+ 'chat_notify_start',
138
+ 'chat_notify_stop',
139
+ 'chat_notify_poll',
140
+ 'chat_notify_status',
141
+ 'chat_browser_init',
142
+ 'chat_browser_acquire',
143
+ 'chat_browser_release',
144
+ 'chat_browser_status',
94
145
  ]);
95
146
  });
96
147
 
@@ -98,7 +149,10 @@ describe('chat-service-ops', () => {
98
149
 
99
150
  describe('chat_cancel_create', () => {
100
151
  it('creates cancellation signal', async () => {
101
- const result = await findOp(ops, 'chat_cancel_create').handler({ chatId: 'chat-1', description: 'processing' });
152
+ const result = await findOp(ops, 'chat_cancel_create').handler({
153
+ chatId: 'chat-1',
154
+ description: 'processing',
155
+ });
102
156
  expect(result).toEqual({ chatId: 'chat-1', created: true, aborted: false, activeTasks: 1 });
103
157
  });
104
158
  });
@@ -111,25 +165,35 @@ describe('chat-service-ops', () => {
111
165
 
112
166
  it('cancels task after create', async () => {
113
167
  await findOp(ops, 'chat_cancel_create').handler({ chatId: 'chat-1' });
114
- const result = (await findOp(ops, 'chat_cancel_stop').handler({ chatId: 'chat-1' })) as Record<string, unknown>;
168
+ const result = (await findOp(ops, 'chat_cancel_stop').handler({
169
+ chatId: 'chat-1',
170
+ })) as Record<string, unknown>;
115
171
  expect(result.cancelled).toBe(true);
116
172
  });
117
173
  });
118
174
 
119
175
  describe('chat_cancel_status', () => {
120
176
  it('returns empty when no manager', async () => {
121
- expect(await findOp(ops, 'chat_cancel_status').handler({})).toEqual({ activeTasks: 0, running: [] });
177
+ expect(await findOp(ops, 'chat_cancel_status').handler({})).toEqual({
178
+ activeTasks: 0,
179
+ running: [],
180
+ });
122
181
  });
123
182
 
124
183
  it('returns per-chat status', async () => {
125
184
  await findOp(ops, 'chat_cancel_create').handler({ chatId: 'chat-1' });
126
- const result = (await findOp(ops, 'chat_cancel_status').handler({ chatId: 'chat-1' })) as Record<string, unknown>;
185
+ const result = (await findOp(ops, 'chat_cancel_status').handler({
186
+ chatId: 'chat-1',
187
+ })) as Record<string, unknown>;
127
188
  expect(result.running).toBe(true);
128
189
  });
129
190
 
130
191
  it('returns all running tasks when no chatId', async () => {
131
192
  await findOp(ops, 'chat_cancel_create').handler({ chatId: 'chat-1' });
132
- const result = (await findOp(ops, 'chat_cancel_status').handler({})) as { activeTasks: number; running: unknown[] };
193
+ const result = (await findOp(ops, 'chat_cancel_status').handler({})) as {
194
+ activeTasks: number;
195
+ running: unknown[];
196
+ };
133
197
  expect(result.activeTasks).toBe(1);
134
198
  expect(result.running).toHaveLength(1);
135
199
  });
@@ -139,23 +203,29 @@ describe('chat-service-ops', () => {
139
203
 
140
204
  describe('chat_update_init', () => {
141
205
  it('initializes updater', async () => {
142
- expect(await findOp(ops, 'chat_update_init').handler({ contextPath: '/tmp/ctx.json' }))
143
- .toEqual({ initialized: true, hasPendingRestart: false, pendingContext: null });
206
+ expect(
207
+ await findOp(ops, 'chat_update_init').handler({ contextPath: '/tmp/ctx.json' }),
208
+ ).toEqual({ initialized: true, hasPendingRestart: false, pendingContext: null });
144
209
  });
145
210
  });
146
211
 
147
212
  describe('chat_update_request', () => {
148
213
  it('requests restart', async () => {
149
- expect(await findOp(ops, 'chat_update_request').handler({
150
- chatId: 'chat-1', reason: 'self-update', contextPath: '/tmp/ctx.json',
151
- })).toEqual({ scheduled: true });
214
+ expect(
215
+ await findOp(ops, 'chat_update_request').handler({
216
+ chatId: 'chat-1',
217
+ reason: 'self-update',
218
+ contextPath: '/tmp/ctx.json',
219
+ }),
220
+ ).toEqual({ scheduled: true });
152
221
  });
153
222
  });
154
223
 
155
224
  describe('chat_update_confirm', () => {
156
225
  it('confirms and clears context', async () => {
157
- expect(await findOp(ops, 'chat_update_confirm').handler({ contextPath: '/tmp/ctx.json' }))
158
- .toEqual({ confirmed: true, previousContext: null });
226
+ expect(
227
+ await findOp(ops, 'chat_update_confirm').handler({ contextPath: '/tmp/ctx.json' }),
228
+ ).toEqual({ confirmed: true, previousContext: null });
159
229
  });
160
230
  });
161
231
 
@@ -163,16 +233,20 @@ describe('chat-service-ops', () => {
163
233
 
164
234
  describe('chat_file_detect_intent', () => {
165
235
  it('detects file intent', async () => {
166
- expect(await findOp(ops, 'chat_file_detect_intent').handler({
167
- filename: 'photo.jpg', mimeType: 'image/jpeg',
168
- })).toEqual({ filename: 'photo.jpg', mimeType: 'image/jpeg', intent: 'vision' });
236
+ expect(
237
+ await findOp(ops, 'chat_file_detect_intent').handler({
238
+ filename: 'photo.jpg',
239
+ mimeType: 'image/jpeg',
240
+ }),
241
+ ).toEqual({ filename: 'photo.jpg', mimeType: 'image/jpeg', intent: 'vision' });
169
242
  });
170
243
  });
171
244
 
172
245
  describe('chat_file_cleanup', () => {
173
246
  it('cleans up temp files', async () => {
174
- expect(await findOp(ops, 'chat_file_cleanup').handler({ uploadDir: '/tmp/uploads' }))
175
- .toEqual({ removed: 3, uploadDir: '/tmp/uploads' });
247
+ expect(await findOp(ops, 'chat_file_cleanup').handler({ uploadDir: '/tmp/uploads' })).toEqual(
248
+ { removed: 3, uploadDir: '/tmp/uploads' },
249
+ );
176
250
  });
177
251
  });
178
252
 
@@ -186,28 +260,37 @@ describe('chat-service-ops', () => {
186
260
 
187
261
  describe('chat_notify_start', () => {
188
262
  it('returns error when not initialized', async () => {
189
- expect(await findOp(ops, 'chat_notify_start').handler({}))
190
- .toEqual({ started: false, reason: 'Notification engine not initialized.' });
263
+ expect(await findOp(ops, 'chat_notify_start').handler({})).toEqual({
264
+ started: false,
265
+ reason: 'Notification engine not initialized.',
266
+ });
191
267
  });
192
268
 
193
269
  it('starts after init', async () => {
194
270
  await findOp(ops, 'chat_notify_init').handler({});
195
- const result = (await findOp(ops, 'chat_notify_start').handler({})) as Record<string, unknown>;
271
+ const result = (await findOp(ops, 'chat_notify_start').handler({})) as Record<
272
+ string,
273
+ unknown
274
+ >;
196
275
  expect(result.started).toBe(true);
197
276
  });
198
277
  });
199
278
 
200
279
  describe('chat_notify_stop', () => {
201
280
  it('returns error when not initialized', async () => {
202
- expect(await findOp(ops, 'chat_notify_stop').handler({}))
203
- .toEqual({ stopped: false, reason: 'Notification engine not initialized.' });
281
+ expect(await findOp(ops, 'chat_notify_stop').handler({})).toEqual({
282
+ stopped: false,
283
+ reason: 'Notification engine not initialized.',
284
+ });
204
285
  });
205
286
  });
206
287
 
207
288
  describe('chat_notify_poll', () => {
208
289
  it('returns error when not initialized', async () => {
209
- expect(await findOp(ops, 'chat_notify_poll').handler({}))
210
- .toEqual({ polled: false, reason: 'Notification engine not initialized.' });
290
+ expect(await findOp(ops, 'chat_notify_poll').handler({})).toEqual({
291
+ polled: false,
292
+ reason: 'Notification engine not initialized.',
293
+ });
211
294
  });
212
295
 
213
296
  it('polls after init', async () => {
@@ -220,13 +303,21 @@ describe('chat-service-ops', () => {
220
303
 
221
304
  describe('chat_notify_status', () => {
222
305
  it('returns uninitialized status', async () => {
223
- expect(await findOp(ops, 'chat_notify_status').handler({}))
224
- .toEqual({ initialized: false, checks: 0, running: false, sent: 0, lastPollAt: null });
306
+ expect(await findOp(ops, 'chat_notify_status').handler({})).toEqual({
307
+ initialized: false,
308
+ checks: 0,
309
+ running: false,
310
+ sent: 0,
311
+ lastPollAt: null,
312
+ });
225
313
  });
226
314
 
227
315
  it('returns stats after init', async () => {
228
316
  await findOp(ops, 'chat_notify_init').handler({});
229
- const result = (await findOp(ops, 'chat_notify_status').handler({})) as Record<string, unknown>;
317
+ const result = (await findOp(ops, 'chat_notify_status').handler({})) as Record<
318
+ string,
319
+ unknown
320
+ >;
230
321
  expect(result.initialized).toBe(true);
231
322
  });
232
323
  });
@@ -235,14 +326,18 @@ describe('chat-service-ops', () => {
235
326
 
236
327
  describe('chat_browser_init', () => {
237
328
  it('initializes browser manager', async () => {
238
- expect(await findOp(ops, 'chat_browser_init').handler({ maxSessions: 5 }))
239
- .toEqual({ initialized: true, maxSessions: 5 });
329
+ expect(await findOp(ops, 'chat_browser_init').handler({ maxSessions: 5 })).toEqual({
330
+ initialized: true,
331
+ maxSessions: 5,
332
+ });
240
333
  });
241
334
  });
242
335
 
243
336
  describe('chat_browser_acquire', () => {
244
337
  it('acquires browser session', async () => {
245
- const result = (await findOp(ops, 'chat_browser_acquire').handler({ chatId: 'chat-1' })) as Record<string, unknown>;
338
+ const result = (await findOp(ops, 'chat_browser_acquire').handler({
339
+ chatId: 'chat-1',
340
+ })) as Record<string, unknown>;
246
341
  expect(result.chatId).toBe('chat-1');
247
342
  expect(result.pid).toBe(1234);
248
343
  });
@@ -250,26 +345,36 @@ describe('chat-service-ops', () => {
250
345
 
251
346
  describe('chat_browser_release', () => {
252
347
  it('returns false when no browser manager', async () => {
253
- expect(await findOp(ops, 'chat_browser_release').handler({ chatId: 'chat-1' }))
254
- .toEqual({ released: false, reason: 'No browser manager.' });
348
+ expect(await findOp(ops, 'chat_browser_release').handler({ chatId: 'chat-1' })).toEqual({
349
+ released: false,
350
+ reason: 'No browser manager.',
351
+ });
255
352
  });
256
353
 
257
354
  it('releases after init', async () => {
258
355
  await findOp(ops, 'chat_browser_init').handler({});
259
- const result = (await findOp(ops, 'chat_browser_release').handler({ chatId: 'chat-1' })) as Record<string, unknown>;
356
+ const result = (await findOp(ops, 'chat_browser_release').handler({
357
+ chatId: 'chat-1',
358
+ })) as Record<string, unknown>;
260
359
  expect(result.released).toBe(true);
261
360
  });
262
361
  });
263
362
 
264
363
  describe('chat_browser_status', () => {
265
364
  it('returns uninitialized status', async () => {
266
- expect(await findOp(ops, 'chat_browser_status').handler({}))
267
- .toEqual({ initialized: false, activeSessions: 0, sessions: [] });
365
+ expect(await findOp(ops, 'chat_browser_status').handler({})).toEqual({
366
+ initialized: false,
367
+ activeSessions: 0,
368
+ sessions: [],
369
+ });
268
370
  });
269
371
 
270
372
  it('returns status after init', async () => {
271
373
  await findOp(ops, 'chat_browser_init').handler({});
272
- const result = (await findOp(ops, 'chat_browser_status').handler({})) as Record<string, unknown>;
374
+ const result = (await findOp(ops, 'chat_browser_status').handler({})) as Record<
375
+ string,
376
+ unknown
377
+ >;
273
378
  expect(result.initialized).toBe(true);
274
379
  });
275
380
  });
@@ -366,7 +366,9 @@ export function createChatServiceOps(state: ChatState): OpDefinition[] {
366
366
  auth: 'read',
367
367
  handler: async () => {
368
368
  if (!state.browser) return { initialized: false, activeSessions: 0, sessions: [] };
369
- const sessions = state.browser.listSessions().map((id) => (Object.assign({chatId:id}, state.browser! .getInfo(id))));
369
+ const sessions = state.browser
370
+ .listSessions()
371
+ .map((id) => Object.assign({ chatId: id }, state.browser!.getInfo(id)));
370
372
  return { initialized: true, activeSessions: state.browser.size, sessions };
371
373
  },
372
374
  },
@@ -29,8 +29,22 @@ const mockSessionManager = {
29
29
  listAll: vi.fn().mockReturnValue(['sess-1', 'sess-2']),
30
30
  };
31
31
 
32
+ const mockConstructorCalls: unknown[][] = [];
33
+
32
34
  vi.mock('../../chat/chat-session.js', () => ({
33
- ChatSessionManager: vi.fn().mockImplementation(() => mockSessionManager),
35
+ ChatSessionManager: class {
36
+ size = mockSessionManager.size;
37
+ startReaper = mockSessionManager.startReaper;
38
+ getOrCreate = mockSessionManager.getOrCreate;
39
+ appendMessage = mockSessionManager.appendMessage;
40
+ messageCount = mockSessionManager.messageCount;
41
+ clear = mockSessionManager.clear;
42
+ delete = mockSessionManager.delete;
43
+ listAll = mockSessionManager.listAll;
44
+ constructor(...args: unknown[]) {
45
+ mockConstructorCalls.push(args);
46
+ }
47
+ },
34
48
  }));
35
49
 
36
50
  // ─── Helpers ────────────────────────────────────────────────────────
@@ -49,6 +63,7 @@ describe('chat-session-ops', () => {
49
63
 
50
64
  beforeEach(() => {
51
65
  vi.clearAllMocks();
66
+ mockConstructorCalls.length = 0;
52
67
  state = createChatState();
53
68
  ops = createChatSessionOps(state);
54
69
  });
@@ -78,7 +93,6 @@ describe('chat-session-ops', () => {
78
93
  });
79
94
 
80
95
  it('passes optional config params', async () => {
81
- const { ChatSessionManager } = await import('../../chat/chat-session.js');
82
96
  const op = findOp(ops, 'chat_session_init');
83
97
  await op.handler({
84
98
  storageDir: '/tmp/s',
@@ -86,12 +100,14 @@ describe('chat-session-ops', () => {
86
100
  compactionThreshold: 50,
87
101
  compactionKeep: 20,
88
102
  });
89
- expect(ChatSessionManager).toHaveBeenCalledWith({
90
- storageDir: '/tmp/s',
91
- ttlMs: 5000,
92
- compactionThreshold: 50,
93
- compactionKeep: 20,
94
- });
103
+ expect(mockConstructorCalls[0]).toEqual([
104
+ {
105
+ storageDir: '/tmp/s',
106
+ ttlMs: 5000,
107
+ compactionThreshold: 50,
108
+ compactionKeep: 20,
109
+ },
110
+ ]);
95
111
  });
96
112
 
97
113
  it('has write auth level', () => {
@@ -190,8 +206,7 @@ describe('chat-session-ops', () => {
190
206
  await getOp.handler({ sessionId: 'x', storageDir: '/tmp/s' });
191
207
 
192
208
  // ChatSessionManager should be constructed only once
193
- const { ChatSessionManager } = await import('../../chat/chat-session.js');
194
- expect(ChatSessionManager).toHaveBeenCalledTimes(1);
209
+ expect(mockConstructorCalls).toHaveLength(1);
195
210
  });
196
211
  });
197
212
  });