groove-dev 0.27.143 → 0.27.145

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 (251) hide show
  1. package/CLAUDE.md +0 -7
  2. package/node_modules/@groove-dev/cli/package.json +1 -1
  3. package/node_modules/@groove-dev/daemon/package.json +1 -1
  4. package/node_modules/@groove-dev/daemon/src/api.js +1086 -6532
  5. package/node_modules/@groove-dev/daemon/src/conversations.js +18 -48
  6. package/node_modules/@groove-dev/daemon/src/gateways/manager.js +35 -1
  7. package/node_modules/@groove-dev/daemon/src/index.js +3 -0
  8. package/node_modules/@groove-dev/daemon/src/journalist.js +23 -13
  9. package/node_modules/@groove-dev/daemon/src/mlx-server.js +365 -0
  10. package/node_modules/@groove-dev/daemon/src/model-lab.js +308 -12
  11. package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
  12. package/node_modules/@groove-dev/daemon/src/process.js +2 -2
  13. package/node_modules/@groove-dev/daemon/src/providers/local.js +36 -8
  14. package/node_modules/@groove-dev/daemon/src/registry.js +21 -5
  15. package/node_modules/@groove-dev/daemon/src/routes/agents.js +812 -0
  16. package/node_modules/@groove-dev/daemon/src/routes/coordination.js +318 -0
  17. package/node_modules/@groove-dev/daemon/src/routes/files.js +751 -0
  18. package/node_modules/@groove-dev/daemon/src/routes/integrations.js +485 -0
  19. package/node_modules/@groove-dev/daemon/src/routes/network.js +1784 -0
  20. package/node_modules/@groove-dev/daemon/src/routes/providers.js +755 -0
  21. package/node_modules/@groove-dev/daemon/src/routes/schedules.js +110 -0
  22. package/node_modules/@groove-dev/daemon/src/routes/teams.js +650 -0
  23. package/node_modules/@groove-dev/daemon/src/scheduler.js +456 -24
  24. package/node_modules/@groove-dev/daemon/src/teams.js +1 -1
  25. package/node_modules/@groove-dev/daemon/src/validate.js +38 -1
  26. package/node_modules/@groove-dev/daemon/templates/mlx-setup.json +12 -0
  27. package/node_modules/@groove-dev/daemon/templates/tgi-setup.json +1 -1
  28. package/node_modules/@groove-dev/daemon/templates/vllm-setup.json +1 -1
  29. package/node_modules/@groove-dev/daemon/test/introducer.test.js +3 -3
  30. package/node_modules/@groove-dev/daemon/test/journalist.test.js +7 -10
  31. package/node_modules/@groove-dev/daemon/test/registry.test.js +38 -0
  32. package/node_modules/@groove-dev/gui/dist/assets/index-Bxc0gU06.js +1006 -0
  33. package/node_modules/@groove-dev/gui/dist/assets/index-C0pztKBn.css +1 -0
  34. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  35. package/node_modules/@groove-dev/gui/package.json +1 -1
  36. package/node_modules/@groove-dev/gui/src/{app.jsx → App.jsx} +0 -2
  37. package/node_modules/@groove-dev/gui/src/app.css +35 -0
  38. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +1 -128
  39. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +210 -112
  40. package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +8 -13
  41. package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +2 -70
  42. package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +159 -122
  43. package/node_modules/@groove-dev/gui/src/components/agents/diff-viewer.jsx +23 -23
  44. package/node_modules/@groove-dev/gui/src/components/agents/journalist-panel.jsx +1 -1
  45. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -135
  46. package/node_modules/@groove-dev/gui/src/components/automations/automation-card.jsx +274 -0
  47. package/node_modules/@groove-dev/gui/src/components/automations/automation-wizard.jsx +1136 -0
  48. package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +2 -0
  49. package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +68 -66
  50. package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +4 -8
  51. package/node_modules/@groove-dev/gui/src/components/dashboard/activity-feed.jsx +3 -3
  52. package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +5 -5
  53. package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +6 -8
  54. package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  55. package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +238 -656
  56. package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +3 -3
  57. package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
  58. package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  59. package/node_modules/@groove-dev/gui/src/components/dashboard/token-chart.jsx +4 -4
  60. package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +39 -31
  61. package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +316 -82
  62. package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +187 -32
  63. package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +200 -18
  64. package/node_modules/@groove-dev/gui/src/components/lab/preset-manager.jsx +17 -14
  65. package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +335 -152
  66. package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +10 -8
  67. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -4
  68. package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +4 -2
  69. package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +137 -108
  70. package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +2 -2
  71. package/node_modules/@groove-dev/gui/src/components/network/performance-dashboard.jsx +4 -4
  72. package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +81 -99
  73. package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +5 -2
  74. package/node_modules/@groove-dev/gui/src/components/ui/slider.jsx +8 -8
  75. package/node_modules/@groove-dev/gui/src/lib/cron.js +64 -0
  76. package/node_modules/@groove-dev/gui/src/lib/status.js +25 -24
  77. package/node_modules/@groove-dev/gui/src/lib/theme-hex.js +1 -0
  78. package/node_modules/@groove-dev/gui/src/stores/groove.js +51 -3144
  79. package/node_modules/@groove-dev/gui/src/stores/helpers.js +10 -0
  80. package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +459 -0
  81. package/node_modules/@groove-dev/gui/src/stores/slices/automations-slice.js +96 -0
  82. package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +226 -0
  83. package/node_modules/@groove-dev/gui/src/stores/slices/editor-slice.js +285 -0
  84. package/node_modules/@groove-dev/gui/src/stores/slices/marketplace-slice.js +461 -0
  85. package/node_modules/@groove-dev/gui/src/stores/slices/network-slice.js +361 -0
  86. package/node_modules/@groove-dev/gui/src/stores/slices/preview-slice.js +109 -0
  87. package/node_modules/@groove-dev/gui/src/stores/slices/providers-slice.js +897 -0
  88. package/node_modules/@groove-dev/gui/src/stores/slices/teams-slice.js +413 -0
  89. package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +98 -0
  90. package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -5
  91. package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +12 -13
  92. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +191 -3
  93. package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +54 -12
  94. package/node_modules/@groove-dev/gui/src/views/models.jsx +419 -496
  95. package/node_modules/@groove-dev/gui/src/views/network.jsx +3 -3
  96. package/node_modules/@groove-dev/gui/src/views/settings.jsx +81 -94
  97. package/node_modules/@groove-dev/gui/src/views/teams.jsx +40 -483
  98. package/node_modules/axios/CHANGELOG.md +260 -0
  99. package/node_modules/axios/README.md +595 -223
  100. package/node_modules/axios/dist/axios.js +1460 -1090
  101. package/node_modules/axios/dist/axios.js.map +1 -1
  102. package/node_modules/axios/dist/axios.min.js +3 -3
  103. package/node_modules/axios/dist/axios.min.js.map +1 -1
  104. package/node_modules/axios/dist/browser/axios.cjs +1560 -1132
  105. package/node_modules/axios/dist/browser/axios.cjs.map +1 -1
  106. package/node_modules/axios/dist/esm/axios.js +1557 -1128
  107. package/node_modules/axios/dist/esm/axios.js.map +1 -1
  108. package/node_modules/axios/dist/esm/axios.min.js +2 -2
  109. package/node_modules/axios/dist/esm/axios.min.js.map +1 -1
  110. package/node_modules/axios/dist/node/axios.cjs +1594 -1057
  111. package/node_modules/axios/dist/node/axios.cjs.map +1 -1
  112. package/node_modules/axios/index.d.cts +40 -41
  113. package/node_modules/axios/index.d.ts +151 -227
  114. package/node_modules/axios/index.js +2 -0
  115. package/node_modules/axios/lib/adapters/adapters.js +4 -2
  116. package/node_modules/axios/lib/adapters/fetch.js +147 -16
  117. package/node_modules/axios/lib/adapters/http.js +306 -58
  118. package/node_modules/axios/lib/adapters/xhr.js +6 -2
  119. package/node_modules/axios/lib/core/Axios.js +7 -3
  120. package/node_modules/axios/lib/core/AxiosError.js +120 -34
  121. package/node_modules/axios/lib/core/AxiosHeaders.js +27 -25
  122. package/node_modules/axios/lib/core/buildFullPath.js +1 -1
  123. package/node_modules/axios/lib/core/dispatchRequest.js +19 -7
  124. package/node_modules/axios/lib/core/mergeConfig.js +21 -4
  125. package/node_modules/axios/lib/core/settle.js +7 -11
  126. package/node_modules/axios/lib/defaults/index.js +14 -9
  127. package/node_modules/axios/lib/env/data.js +1 -1
  128. package/node_modules/axios/lib/helpers/AxiosURLSearchParams.js +1 -2
  129. package/node_modules/axios/lib/helpers/buildURL.js +1 -1
  130. package/node_modules/axios/lib/helpers/cookies.js +14 -2
  131. package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +28 -1
  132. package/node_modules/axios/lib/helpers/formDataToJSON.js +3 -1
  133. package/node_modules/axios/lib/helpers/formDataToStream.js +3 -2
  134. package/node_modules/axios/lib/helpers/parseProtocol.js +1 -1
  135. package/node_modules/axios/lib/helpers/progressEventReducer.js +5 -5
  136. package/node_modules/axios/lib/helpers/resolveConfig.js +54 -18
  137. package/node_modules/axios/lib/helpers/shouldBypassProxy.js +74 -2
  138. package/node_modules/axios/lib/helpers/toFormData.js +10 -2
  139. package/node_modules/axios/lib/helpers/validator.js +3 -1
  140. package/node_modules/axios/lib/utils.js +33 -21
  141. package/node_modules/axios/package.json +17 -24
  142. package/node_modules/follow-redirects/README.md +7 -5
  143. package/node_modules/follow-redirects/index.js +24 -1
  144. package/node_modules/follow-redirects/package.json +1 -1
  145. package/package.json +1 -1
  146. package/packages/cli/package.json +1 -1
  147. package/packages/daemon/package.json +1 -1
  148. package/packages/daemon/src/api.js +1086 -6532
  149. package/packages/daemon/src/conversations.js +18 -48
  150. package/packages/daemon/src/gateways/manager.js +35 -1
  151. package/packages/daemon/src/index.js +3 -0
  152. package/packages/daemon/src/journalist.js +23 -13
  153. package/packages/daemon/src/mlx-server.js +365 -0
  154. package/packages/daemon/src/model-lab.js +308 -12
  155. package/packages/daemon/src/pm.js +1 -1
  156. package/packages/daemon/src/process.js +2 -2
  157. package/packages/daemon/src/providers/local.js +36 -8
  158. package/packages/daemon/src/registry.js +21 -5
  159. package/packages/daemon/src/routes/agents.js +812 -0
  160. package/packages/daemon/src/routes/coordination.js +318 -0
  161. package/packages/daemon/src/routes/files.js +751 -0
  162. package/packages/daemon/src/routes/integrations.js +485 -0
  163. package/packages/daemon/src/routes/network.js +1784 -0
  164. package/packages/daemon/src/routes/providers.js +755 -0
  165. package/packages/daemon/src/routes/schedules.js +110 -0
  166. package/packages/daemon/src/routes/teams.js +650 -0
  167. package/packages/daemon/src/scheduler.js +456 -24
  168. package/packages/daemon/src/teams.js +1 -1
  169. package/packages/daemon/src/validate.js +38 -1
  170. package/packages/daemon/templates/mlx-setup.json +12 -0
  171. package/packages/daemon/templates/tgi-setup.json +1 -1
  172. package/packages/daemon/templates/vllm-setup.json +1 -1
  173. package/packages/gui/dist/assets/index-Bxc0gU06.js +1006 -0
  174. package/packages/gui/dist/assets/index-C0pztKBn.css +1 -0
  175. package/packages/gui/dist/index.html +2 -2
  176. package/packages/gui/package.json +1 -1
  177. package/packages/gui/src/{app.jsx → App.jsx} +0 -2
  178. package/packages/gui/src/app.css +35 -0
  179. package/packages/gui/src/components/agents/agent-config.jsx +1 -128
  180. package/packages/gui/src/components/agents/agent-feed.jsx +210 -112
  181. package/packages/gui/src/components/agents/agent-node.jsx +8 -13
  182. package/packages/gui/src/components/agents/agent-panel.jsx +2 -70
  183. package/packages/gui/src/components/agents/code-review.jsx +159 -122
  184. package/packages/gui/src/components/agents/diff-viewer.jsx +23 -23
  185. package/packages/gui/src/components/agents/journalist-panel.jsx +1 -1
  186. package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -135
  187. package/packages/gui/src/components/automations/automation-card.jsx +274 -0
  188. package/packages/gui/src/components/automations/automation-wizard.jsx +1136 -0
  189. package/packages/gui/src/components/chat/chat-header.jsx +2 -0
  190. package/packages/gui/src/components/chat/chat-input.jsx +68 -66
  191. package/packages/gui/src/components/chat/chat-view.jsx +4 -8
  192. package/packages/gui/src/components/dashboard/activity-feed.jsx +3 -3
  193. package/packages/gui/src/components/dashboard/cache-ring.jsx +5 -5
  194. package/packages/gui/src/components/dashboard/context-gauges.jsx +6 -8
  195. package/packages/gui/src/components/dashboard/fleet-panel.jsx +8 -14
  196. package/packages/gui/src/components/dashboard/intel-panel.jsx +238 -656
  197. package/packages/gui/src/components/dashboard/kpi-card.jsx +3 -3
  198. package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
  199. package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
  200. package/packages/gui/src/components/dashboard/token-chart.jsx +4 -4
  201. package/packages/gui/src/components/lab/chat-playground.jsx +39 -31
  202. package/packages/gui/src/components/lab/lab-assistant.jsx +316 -82
  203. package/packages/gui/src/components/lab/metrics-panel.jsx +187 -32
  204. package/packages/gui/src/components/lab/parameter-panel.jsx +200 -18
  205. package/packages/gui/src/components/lab/preset-manager.jsx +17 -14
  206. package/packages/gui/src/components/lab/runtime-config.jsx +335 -152
  207. package/packages/gui/src/components/lab/system-prompt-editor.jsx +10 -8
  208. package/packages/gui/src/components/layout/activity-bar.jsx +2 -4
  209. package/packages/gui/src/components/layout/terminal-panel.jsx +4 -2
  210. package/packages/gui/src/components/layout/welcome-splash.jsx +137 -108
  211. package/packages/gui/src/components/network/network-health.jsx +2 -2
  212. package/packages/gui/src/components/network/performance-dashboard.jsx +4 -4
  213. package/packages/gui/src/components/settings/ssh-wizard.jsx +81 -99
  214. package/packages/gui/src/components/ui/sheet.jsx +5 -2
  215. package/packages/gui/src/components/ui/slider.jsx +8 -8
  216. package/packages/gui/src/lib/cron.js +64 -0
  217. package/packages/gui/src/lib/status.js +25 -24
  218. package/packages/gui/src/lib/theme-hex.js +1 -0
  219. package/packages/gui/src/stores/groove.js +51 -3144
  220. package/packages/gui/src/stores/helpers.js +10 -0
  221. package/packages/gui/src/stores/slices/agents-slice.js +459 -0
  222. package/packages/gui/src/stores/slices/automations-slice.js +96 -0
  223. package/packages/gui/src/stores/slices/chat-slice.js +226 -0
  224. package/packages/gui/src/stores/slices/editor-slice.js +285 -0
  225. package/packages/gui/src/stores/slices/marketplace-slice.js +461 -0
  226. package/packages/gui/src/stores/slices/network-slice.js +361 -0
  227. package/packages/gui/src/stores/slices/preview-slice.js +109 -0
  228. package/packages/gui/src/stores/slices/providers-slice.js +897 -0
  229. package/packages/gui/src/stores/slices/teams-slice.js +413 -0
  230. package/packages/gui/src/stores/slices/ui-slice.js +98 -0
  231. package/packages/gui/src/views/agents.jsx +5 -5
  232. package/packages/gui/src/views/dashboard.jsx +12 -13
  233. package/packages/gui/src/views/marketplace.jsx +191 -3
  234. package/packages/gui/src/views/model-lab.jsx +54 -12
  235. package/packages/gui/src/views/models.jsx +419 -496
  236. package/packages/gui/src/views/network.jsx +3 -3
  237. package/packages/gui/src/views/settings.jsx +81 -94
  238. package/packages/gui/src/views/teams.jsx +40 -483
  239. package/SECURITY_SWEEP.md +0 -228
  240. package/TRAINING_DATA_v4.md +0 -6
  241. package/node_modules/@groove-dev/gui/dist/assets/index-CCVvAoQn.css +0 -1
  242. package/node_modules/@groove-dev/gui/dist/assets/index-DGIv_TRm.js +0 -984
  243. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +0 -379
  244. package/node_modules/@groove-dev/gui/src/views/preview.jsx +0 -6
  245. package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +0 -327
  246. package/packages/gui/dist/assets/index-CCVvAoQn.css +0 -1
  247. package/packages/gui/dist/assets/index-DGIv_TRm.js +0 -984
  248. package/packages/gui/src/components/agents/agent-chat.jsx +0 -379
  249. package/packages/gui/src/views/preview.jsx +0 -6
  250. package/packages/gui/src/views/subscription-panel.jsx +0 -327
  251. package/test.py +0 -571
@@ -0,0 +1,318 @@
1
+ // FSL-1.1-Apache-2.0 — see LICENSE
2
+ import { Keeper, KEEPER_COMMANDS } from '../keeper.js';
3
+
4
+ const FILE_READ_TOOLS = new Set(['Read', 'read_file']);
5
+ const FILE_WRITE_TOOLS = new Set(['Write', 'Edit', 'write_file', 'edit_file', 'create_file']);
6
+
7
+ export function registerCoordinationRoutes(app, daemon) {
8
+ // Lock management
9
+ app.get('/api/locks', (req, res) => {
10
+ res.json(daemon.locks.getAll());
11
+ });
12
+
13
+ // Knock protocol: Claude Code PreToolUse hook POSTs every Bash/Write/Edit
14
+ // tool call here. The daemon checks the target path (for file ops) against
15
+ // the agent's declared scope and against other agents' active locks, and
16
+ // allows or denies. Non-Claude providers don't hit this path.
17
+ app.post('/api/knock', (req, res) => {
18
+ const body = req.body || {};
19
+ const agentId = body.grooveAgentId;
20
+ const toolName = body.tool_name || body.toolName || '';
21
+ const toolInput = body.tool_input || body.toolInput || {};
22
+
23
+ // Unknown / no agent id → fail open (don't wedge an agent we can't identify)
24
+ if (!agentId) return res.json({ allow: true });
25
+ const agent = daemon.registry.get(agentId);
26
+ if (!agent) return res.json({ allow: true });
27
+
28
+ // Extract the target file paths from the tool input
29
+ const targets = [];
30
+ if (toolInput.file_path) targets.push(String(toolInput.file_path));
31
+ if (toolInput.path) targets.push(String(toolInput.path));
32
+ if (Array.isArray(toolInput.edits)) {
33
+ for (const e of toolInput.edits) if (e?.file_path) targets.push(String(e.file_path));
34
+ }
35
+
36
+ // Scope guard: if agent has a declared scope and the op targets a path,
37
+ // verify the path matches the scope or belongs to no one.
38
+ if (agent.scope && agent.scope.length > 0 && targets.length > 0) {
39
+ for (const target of targets) {
40
+ const conflict = daemon.locks.check(agentId, target, agent.workingDir);
41
+ if (conflict.conflict) {
42
+ daemon.audit.log('knock.denied', { agentId, toolName, target, owner: conflict.owner, pattern: conflict.pattern });
43
+ daemon.broadcast({ type: 'knock:denied', agentId, agentName: agent.name, toolName, target, owner: conflict.owner, reason: 'scope_conflict' });
44
+ return res.json({
45
+ allow: false,
46
+ reason: `GROOVE PM: ${target} is owned by another agent (pattern ${conflict.pattern}). Use the handoff protocol (write .groove/handoffs/<role>.md) or request approval instead of editing it directly.`,
47
+ });
48
+ }
49
+ }
50
+ }
51
+
52
+ // Track file operations for the files-touched API
53
+ if (targets.length > 0) {
54
+ const op = FILE_WRITE_TOOLS.has(toolName) ? 'write' : FILE_READ_TOOLS.has(toolName) ? 'read' : null;
55
+ if (op) {
56
+ for (const t of targets) daemon.registry.trackFileOp(agentId, t, op);
57
+ }
58
+ }
59
+
60
+ daemon.audit.log('knock.allowed', { agentId, toolName, targets });
61
+ res.json({ allow: true });
62
+ });
63
+
64
+ // Coordination protocol — agents declare intent on shared resources
65
+ // (npm install, server restart, package.json edit) to prevent races.
66
+ // Returns 423 Locked if another agent holds a conflicting resource.
67
+ app.post('/api/coordination/declare', (req, res) => {
68
+ const { agentId, operation, resources, ttlMs } = req.body || {};
69
+ if (!agentId || !operation || !Array.isArray(resources) || resources.length === 0) {
70
+ return res.status(400).json({ error: 'agentId, operation, and resources[] required' });
71
+ }
72
+ const result = daemon.locks.declareOperation(agentId, operation, resources, ttlMs);
73
+ if (result.conflict) {
74
+ daemon.audit.log('coordination.conflict', { agentId, operation, resource: result.resource, owner: result.owner });
75
+ return res.status(423).json(result);
76
+ }
77
+ daemon.audit.log('coordination.declared', { agentId, operation, resources });
78
+ daemon.broadcast({ type: 'coordination:declared', agentId, operation, resources });
79
+ res.json({ declared: true, operation, resources });
80
+ });
81
+
82
+ app.post('/api/coordination/complete', (req, res) => {
83
+ const { agentId } = req.body || {};
84
+ if (!agentId) return res.status(400).json({ error: 'agentId required' });
85
+ const removed = daemon.locks.completeOperation(agentId);
86
+ daemon.audit.log('coordination.completed', { agentId });
87
+ daemon.broadcast({ type: 'coordination:completed', agentId });
88
+ res.json({ completed: removed });
89
+ });
90
+
91
+ app.get('/api/coordination', (req, res) => {
92
+ res.json({ operations: daemon.locks.getOperations() });
93
+ });
94
+
95
+ // --- Persistent Memory (Layer 7) ---
96
+ // Constraints: project rules discovered by agents / set by user
97
+ app.get('/api/memory/constraints', (req, res) => {
98
+ res.json({ constraints: daemon.memory.listConstraints() });
99
+ });
100
+
101
+ app.post('/api/memory/constraints', (req, res) => {
102
+ const { text, category } = req.body || {};
103
+ const result = daemon.memory.addConstraint({ text, category });
104
+ if (!result.added && result.error) {
105
+ return res.status(400).json(result);
106
+ }
107
+ if (result.added) {
108
+ daemon.audit.log('memory.constraint.added', { hash: result.hash, category });
109
+ daemon.broadcast({ type: 'memory:constraint:added', hash: result.hash });
110
+ }
111
+ res.json(result);
112
+ });
113
+
114
+ app.delete('/api/memory/constraints/:hash', (req, res) => {
115
+ const removed = daemon.memory.removeConstraint(req.params.hash);
116
+ if (removed) {
117
+ daemon.audit.log('memory.constraint.removed', { hash: req.params.hash });
118
+ daemon.broadcast({ type: 'memory:constraint:removed', hash: req.params.hash });
119
+ }
120
+ res.json({ removed });
121
+ });
122
+
123
+ // Handoff chains (per role, optionally scoped by workspace)
124
+ app.get('/api/memory/handoff-chain/:role', (req, res) => {
125
+ res.json({
126
+ role: req.params.role,
127
+ workspace: req.query.workspace || null,
128
+ entries: daemon.memory.getHandoffChain(req.params.role, req.query.workspace),
129
+ });
130
+ });
131
+
132
+ app.get('/api/memory/handoff-chain/:role/recent', (req, res) => {
133
+ const count = Math.min(parseInt(req.query.count) || 3, 10);
134
+ res.json({
135
+ role: req.params.role,
136
+ workspace: req.query.workspace || null,
137
+ markdown: daemon.memory.getRecentHandoffMarkdown(req.params.role, count, 10_000, req.query.workspace),
138
+ });
139
+ });
140
+
141
+ app.get('/api/memory/handoff-chain', (req, res) => {
142
+ res.json({ roles: daemon.memory.listHandoffRoles(req.query.workspace) });
143
+ });
144
+
145
+ // Discoveries (error → fix pairs)
146
+ app.get('/api/memory/discoveries', (req, res) => {
147
+ const role = req.query.role;
148
+ const teamId = req.query.teamId;
149
+ const limit = Math.min(parseInt(req.query.limit) || 100, 500);
150
+ res.json({ discoveries: daemon.memory.listDiscoveries({ role, teamId, limit }) });
151
+ });
152
+
153
+ app.post('/api/memory/discoveries', (req, res) => {
154
+ const { agentId, role, trigger, fix, outcome, teamId } = req.body || {};
155
+ const result = daemon.memory.addDiscovery({ agentId, role, trigger, fix, outcome, teamId });
156
+ if (!result.added && result.error) {
157
+ return res.status(400).json(result);
158
+ }
159
+ if (result.added) {
160
+ daemon.audit.log('memory.discovery.added', { agentId, role });
161
+ daemon.broadcast({ type: 'memory:discovery:added', agentId, role });
162
+ }
163
+ res.json(result);
164
+ });
165
+
166
+ // Specializations (per-agent + per-role quality profiles)
167
+ app.get('/api/memory/specializations', (req, res) => {
168
+ res.json(daemon.memory.getAllSpecializations());
169
+ });
170
+
171
+ app.get('/api/memory/specializations/:agentId', (req, res) => {
172
+ const spec = daemon.memory.getSpecialization(req.params.agentId);
173
+ if (!spec) return res.status(404).json({ error: 'No specialization data for this agent' });
174
+ res.json(spec);
175
+ });
176
+
177
+ // ── Keeper (tagged memory) ──────────────────────────────────
178
+
179
+ app.get('/api/keeper', (req, res) => {
180
+ res.json({ items: daemon.keeper.list() });
181
+ });
182
+
183
+ app.get('/api/keeper/tree', (req, res) => {
184
+ res.json({ tree: daemon.keeper.tree() });
185
+ });
186
+
187
+ app.get('/api/keeper/search', (req, res) => {
188
+ const q = req.query.q || '';
189
+ res.json({ results: daemon.keeper.search(q) });
190
+ });
191
+
192
+ app.get('/api/keeper/commands', (_req, res) => {
193
+ res.json({ commands: KEEPER_COMMANDS });
194
+ });
195
+
196
+ app.get('/api/keeper/:tag(*)', (req, res) => {
197
+ const item = daemon.keeper.get(req.params.tag);
198
+ if (!item) return res.status(404).json({ error: `Memory #${req.params.tag} not found` });
199
+ res.json(item);
200
+ });
201
+
202
+ app.post('/api/keeper', (req, res) => {
203
+ try {
204
+ const { tag, content } = req.body || {};
205
+ const item = daemon.keeper.save(tag, content);
206
+ daemon.audit.log('keeper.save', { tag: item.tag });
207
+ daemon.broadcast({ type: 'keeper:saved', item });
208
+ res.status(201).json(item);
209
+ } catch (err) {
210
+ res.status(400).json({ error: err.message });
211
+ }
212
+ });
213
+
214
+ app.post('/api/keeper/append', (req, res) => {
215
+ try {
216
+ const { tag, content } = req.body || {};
217
+ const item = daemon.keeper.append(tag, content);
218
+ daemon.audit.log('keeper.append', { tag: item.tag });
219
+ daemon.broadcast({ type: 'keeper:updated', item });
220
+ res.json(item);
221
+ } catch (err) {
222
+ res.status(400).json({ error: err.message });
223
+ }
224
+ });
225
+
226
+ app.post('/api/keeper/pull', (req, res) => {
227
+ try {
228
+ const { tags } = req.body || {};
229
+ if (!Array.isArray(tags) || tags.length === 0) {
230
+ return res.status(400).json({ error: 'Tags array is required' });
231
+ }
232
+ const brief = daemon.keeper.pull(tags);
233
+ if (!brief) return res.status(404).json({ error: 'No memories found for the given tags' });
234
+ res.json({ brief, tags });
235
+ } catch (err) {
236
+ res.status(400).json({ error: err.message });
237
+ }
238
+ });
239
+
240
+ app.patch('/api/keeper/:tag(*)', (req, res) => {
241
+ try {
242
+ const { content } = req.body || {};
243
+ const item = daemon.keeper.update(req.params.tag, content);
244
+ daemon.audit.log('keeper.update', { tag: item.tag });
245
+ daemon.broadcast({ type: 'keeper:updated', item });
246
+ res.json(item);
247
+ } catch (err) {
248
+ res.status(err.message.includes('does not exist') ? 404 : 400).json({ error: err.message });
249
+ }
250
+ });
251
+
252
+ app.delete('/api/keeper/link/:tag(*)', (req, res) => {
253
+ try {
254
+ const { docPath } = req.body || {};
255
+ daemon.keeper.unlink(req.params.tag, docPath);
256
+ daemon.audit.log('keeper.unlink', { tag: req.params.tag, docPath });
257
+ res.json({ ok: true });
258
+ } catch (err) {
259
+ res.status(400).json({ error: err.message });
260
+ }
261
+ });
262
+
263
+ app.delete('/api/keeper/:tag(*)', (req, res) => {
264
+ try {
265
+ const removed = daemon.keeper.delete(req.params.tag);
266
+ if (!removed) return res.status(404).json({ error: `Memory #${req.params.tag} not found` });
267
+ daemon.audit.log('keeper.delete', { tag: req.params.tag });
268
+ daemon.broadcast({ type: 'keeper:deleted', tag: req.params.tag });
269
+ res.json({ ok: true });
270
+ } catch (err) {
271
+ res.status(400).json({ error: err.message });
272
+ }
273
+ });
274
+
275
+ app.post('/api/keeper/doc', async (req, res) => {
276
+ try {
277
+ const { tag, chatHistory, agentId } = req.body || {};
278
+ if (!tag) return res.status(400).json({ error: 'Tag is required' });
279
+ if (!chatHistory || !Array.isArray(chatHistory) || chatHistory.length === 0) {
280
+ return res.status(400).json({ error: 'Chat history is required' });
281
+ }
282
+ const transcript = chatHistory
283
+ .map(m => `**${m.from === 'user' ? 'User' : 'Agent'}:** ${m.text}`)
284
+ .join('\n\n');
285
+ const prompt = `You are a technical writer. Below is a conversation exploring an idea or feature. Write a comprehensive document that captures:\n\n1. The core idea and motivation\n2. Key decisions made during the discussion\n3. Architecture / design choices\n4. Implementation plan (if discussed)\n5. Open questions or next steps\n\nWrite in clear, structured markdown with headers. Be thorough — this document will be the reference for future work on this topic. Do not include a meta-summary about the conversation itself.\n\n---\n\nConversation:\n\n${transcript.slice(0, 40000)}`;
286
+ let doc;
287
+ if (daemon.journalist && typeof daemon.journalist.callHeadless === 'function') {
288
+ doc = await daemon.journalist.callHeadless(prompt, { trackAs: '__keeper_doc__' });
289
+ } else {
290
+ doc = `# ${tag}\n\n*Auto-generated document from conversation*\n\n${transcript.slice(0, 5000)}`;
291
+ }
292
+ const item = daemon.keeper.saveDoc(tag, doc);
293
+ daemon.audit.log('keeper.doc', { tag: item.tag, agentId });
294
+ daemon.broadcast({ type: 'keeper:saved', item });
295
+ res.status(201).json({ ...item, content: doc });
296
+ } catch (err) {
297
+ res.status(500).json({ error: err.message });
298
+ }
299
+ });
300
+
301
+ app.post('/api/keeper/link', (req, res) => {
302
+ try {
303
+ const { tag, docPath } = req.body || {};
304
+ const item = daemon.keeper.link(tag, docPath);
305
+ daemon.audit.log('keeper.link', { tag: item.tag, docPath });
306
+ daemon.broadcast({ type: 'keeper:updated', item });
307
+ res.json(item);
308
+ } catch (err) {
309
+ res.status(400).json({ error: err.message });
310
+ }
311
+ });
312
+
313
+ app.post('/api/keeper/parse', (req, res) => {
314
+ const { text } = req.body || {};
315
+ const parsed = Keeper.parseCommand(text || '');
316
+ res.json({ parsed });
317
+ });
318
+ }