groove-dev 0.26.38 → 0.27.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 (171) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/CLAUDE.md +24 -19
  3. package/node_modules/@groove-dev/cli/bin/groove.js +2 -0
  4. package/node_modules/@groove-dev/cli/package.json +1 -1
  5. package/node_modules/@groove-dev/cli/src/commands/nuke.js +16 -4
  6. package/node_modules/@groove-dev/cli/src/commands/stop.js +17 -2
  7. package/node_modules/@groove-dev/daemon/integrations-registry.json +681 -75
  8. package/node_modules/@groove-dev/daemon/package.json +1 -1
  9. package/node_modules/@groove-dev/daemon/src/adaptive.js +23 -25
  10. package/node_modules/@groove-dev/daemon/src/api.js +346 -22
  11. package/node_modules/@groove-dev/daemon/src/classifier.js +53 -6
  12. package/node_modules/@groove-dev/daemon/src/firstrun.js +14 -1
  13. package/node_modules/@groove-dev/daemon/src/gateways/manager.js +2 -2
  14. package/node_modules/@groove-dev/daemon/src/index.js +28 -4
  15. package/node_modules/@groove-dev/daemon/src/integrations.js +215 -14
  16. package/node_modules/@groove-dev/daemon/src/introducer.js +84 -11
  17. package/node_modules/@groove-dev/daemon/src/journalist.js +43 -1
  18. package/node_modules/@groove-dev/daemon/src/lockmanager.js +60 -0
  19. package/node_modules/@groove-dev/daemon/src/mcp-manager.js +270 -0
  20. package/node_modules/@groove-dev/daemon/src/memory.js +370 -0
  21. package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
  22. package/node_modules/@groove-dev/daemon/src/process.js +141 -9
  23. package/node_modules/@groove-dev/daemon/src/registry.js +1 -1
  24. package/node_modules/@groove-dev/daemon/src/rotator.js +334 -31
  25. package/node_modules/@groove-dev/daemon/src/router.js +43 -0
  26. package/node_modules/@groove-dev/daemon/src/tokentracker.js +70 -18
  27. package/node_modules/@groove-dev/daemon/src/validate.js +5 -13
  28. package/node_modules/@groove-dev/daemon/templates/groove-slides.cjs +306 -0
  29. package/node_modules/@groove-dev/daemon/test/classifier.test.js +3 -5
  30. package/node_modules/@groove-dev/daemon/test/lockmanager.test.js +64 -0
  31. package/node_modules/@groove-dev/daemon/test/memory.test.js +252 -0
  32. package/node_modules/@groove-dev/daemon/test/rotator.test.js +108 -0
  33. package/node_modules/@groove-dev/daemon/test/router.test.js +64 -0
  34. package/node_modules/@groove-dev/daemon/test/slides-engine.test.js +230 -0
  35. package/node_modules/@groove-dev/daemon/test/tokentracker.test.js +78 -0
  36. package/node_modules/@groove-dev/gui/dist/assets/index-DjORRpF0.css +1 -0
  37. package/node_modules/@groove-dev/gui/dist/assets/index-eCrVowF0.js +652 -0
  38. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  39. package/node_modules/@groove-dev/gui/package.json +1 -4
  40. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +26 -17
  41. package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +22 -1
  42. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +53 -21
  43. package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +132 -90
  44. package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +212 -1
  45. package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +6 -2
  46. package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +495 -174
  47. package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +12 -2
  48. package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +55 -0
  49. package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +3 -3
  50. package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +24 -19
  51. package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +2 -2
  52. package/node_modules/@groove-dev/gui/src/components/marketplace/integration-wizard.jsx +391 -61
  53. package/node_modules/@groove-dev/gui/src/components/marketplace/marketplace-card.jsx +29 -7
  54. package/node_modules/@groove-dev/gui/src/lib/format.js +0 -6
  55. package/node_modules/@groove-dev/gui/src/lib/hooks/use-dashboard.js +23 -5
  56. package/node_modules/@groove-dev/gui/src/stores/groove.js +59 -9
  57. package/node_modules/@groove-dev/gui/src/views/agents.jsx +84 -10
  58. package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +24 -21
  59. package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +153 -85
  60. package/package.json +2 -8
  61. package/packages/cli/bin/groove.js +2 -0
  62. package/packages/cli/package.json +1 -1
  63. package/packages/cli/src/commands/nuke.js +16 -4
  64. package/packages/cli/src/commands/stop.js +17 -2
  65. package/packages/daemon/integrations-registry.json +681 -75
  66. package/packages/daemon/package.json +1 -1
  67. package/packages/daemon/src/adaptive.js +23 -25
  68. package/packages/daemon/src/api.js +346 -22
  69. package/packages/daemon/src/classifier.js +53 -6
  70. package/packages/daemon/src/firstrun.js +14 -1
  71. package/packages/daemon/src/gateways/manager.js +2 -2
  72. package/packages/daemon/src/index.js +28 -4
  73. package/packages/daemon/src/integrations.js +215 -14
  74. package/packages/daemon/src/introducer.js +84 -11
  75. package/packages/daemon/src/journalist.js +43 -1
  76. package/packages/daemon/src/lockmanager.js +60 -0
  77. package/packages/daemon/src/mcp-manager.js +270 -0
  78. package/packages/daemon/src/memory.js +370 -0
  79. package/packages/daemon/src/pm.js +1 -1
  80. package/packages/daemon/src/process.js +141 -9
  81. package/packages/daemon/src/registry.js +1 -1
  82. package/packages/daemon/src/rotator.js +334 -31
  83. package/packages/daemon/src/router.js +43 -0
  84. package/packages/daemon/src/tokentracker.js +70 -18
  85. package/packages/daemon/src/validate.js +5 -13
  86. package/packages/daemon/templates/groove-slides.cjs +306 -0
  87. package/packages/gui/dist/assets/index-DjORRpF0.css +1 -0
  88. package/packages/gui/dist/assets/index-eCrVowF0.js +652 -0
  89. package/packages/gui/dist/index.html +2 -2
  90. package/packages/gui/package.json +1 -4
  91. package/packages/gui/src/components/agents/agent-chat.jsx +26 -17
  92. package/packages/gui/src/components/agents/agent-config.jsx +22 -1
  93. package/packages/gui/src/components/agents/agent-feed.jsx +53 -21
  94. package/packages/gui/src/components/agents/agent-node.jsx +132 -90
  95. package/packages/gui/src/components/agents/spawn-wizard.jsx +212 -1
  96. package/packages/gui/src/components/dashboard/cache-ring.jsx +6 -2
  97. package/packages/gui/src/components/dashboard/intel-panel.jsx +495 -174
  98. package/packages/gui/src/components/dashboard/kpi-card.jsx +12 -2
  99. package/packages/gui/src/components/dashboard/team-burn-panel.jsx +55 -0
  100. package/packages/gui/src/components/layout/activity-bar.jsx +3 -3
  101. package/packages/gui/src/components/layout/app-shell.jsx +24 -19
  102. package/packages/gui/src/components/layout/command-palette.jsx +2 -2
  103. package/packages/gui/src/components/marketplace/integration-wizard.jsx +391 -61
  104. package/packages/gui/src/components/marketplace/marketplace-card.jsx +29 -7
  105. package/packages/gui/src/lib/format.js +0 -6
  106. package/packages/gui/src/lib/hooks/use-dashboard.js +23 -5
  107. package/packages/gui/src/stores/groove.js +59 -9
  108. package/packages/gui/src/views/agents.jsx +84 -10
  109. package/packages/gui/src/views/dashboard.jsx +24 -21
  110. package/packages/gui/src/views/marketplace.jsx +153 -85
  111. package/node_modules/@groove-dev/gui/dist/assets/index-CEFKgLGB.css +0 -1
  112. package/node_modules/@groove-dev/gui/dist/assets/index-CaKBNWcK.js +0 -638
  113. package/node_modules/@groove-dev/gui/dist/groove-logo-short.png +0 -0
  114. package/node_modules/@groove-dev/gui/dist/groove-logo.png +0 -0
  115. package/node_modules/@groove-dev/gui/public/groove-logo-short.png +0 -0
  116. package/node_modules/@groove-dev/gui/public/groove-logo.png +0 -0
  117. package/node_modules/@groove-dev/gui/src/components/ui/dropdown-menu.jsx +0 -60
  118. package/node_modules/@groove-dev/gui/src/lib/hooks/use-media-query.js +0 -18
  119. package/node_modules/@radix-ui/react-dropdown-menu/LICENSE +0 -21
  120. package/node_modules/@radix-ui/react-dropdown-menu/README.md +0 -3
  121. package/node_modules/@radix-ui/react-dropdown-menu/dist/index.d.mts +0 -97
  122. package/node_modules/@radix-ui/react-dropdown-menu/dist/index.d.ts +0 -97
  123. package/node_modules/@radix-ui/react-dropdown-menu/dist/index.js +0 -337
  124. package/node_modules/@radix-ui/react-dropdown-menu/dist/index.js.map +0 -7
  125. package/node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs +0 -305
  126. package/node_modules/@radix-ui/react-dropdown-menu/dist/index.mjs.map +0 -7
  127. package/node_modules/@radix-ui/react-dropdown-menu/package.json +0 -75
  128. package/node_modules/@radix-ui/react-popover/LICENSE +0 -21
  129. package/node_modules/@radix-ui/react-popover/README.md +0 -3
  130. package/node_modules/@radix-ui/react-popover/dist/index.d.mts +0 -85
  131. package/node_modules/@radix-ui/react-popover/dist/index.d.ts +0 -85
  132. package/node_modules/@radix-ui/react-popover/dist/index.js +0 -352
  133. package/node_modules/@radix-ui/react-popover/dist/index.js.map +0 -7
  134. package/node_modules/@radix-ui/react-popover/dist/index.mjs +0 -320
  135. package/node_modules/@radix-ui/react-popover/dist/index.mjs.map +0 -7
  136. package/node_modules/@radix-ui/react-popover/package.json +0 -82
  137. package/node_modules/@radix-ui/react-separator/LICENSE +0 -21
  138. package/node_modules/@radix-ui/react-separator/README.md +0 -3
  139. package/node_modules/@radix-ui/react-separator/dist/index.d.mts +0 -21
  140. package/node_modules/@radix-ui/react-separator/dist/index.d.ts +0 -21
  141. package/node_modules/@radix-ui/react-separator/dist/index.js +0 -65
  142. package/node_modules/@radix-ui/react-separator/dist/index.js.map +0 -7
  143. package/node_modules/@radix-ui/react-separator/dist/index.mjs +0 -32
  144. package/node_modules/@radix-ui/react-separator/dist/index.mjs.map +0 -7
  145. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/LICENSE +0 -21
  146. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/README.md +0 -3
  147. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.d.mts +0 -52
  148. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.d.ts +0 -52
  149. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.js +0 -80
  150. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.js.map +0 -7
  151. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.mjs +0 -47
  152. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/dist/index.mjs.map +0 -7
  153. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive/package.json +0 -69
  154. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/LICENSE +0 -21
  155. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/README.md +0 -3
  156. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.d.mts +0 -22
  157. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.d.ts +0 -22
  158. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.js +0 -152
  159. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.js.map +0 -7
  160. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.mjs +0 -119
  161. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/dist/index.mjs.map +0 -7
  162. package/node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-slot/package.json +0 -64
  163. package/node_modules/@radix-ui/react-separator/package.json +0 -69
  164. package/packages/gui/dist/assets/index-CEFKgLGB.css +0 -1
  165. package/packages/gui/dist/assets/index-CaKBNWcK.js +0 -638
  166. package/packages/gui/dist/groove-logo-short.png +0 -0
  167. package/packages/gui/dist/groove-logo.png +0 -0
  168. package/packages/gui/public/groove-logo-short.png +0 -0
  169. package/packages/gui/public/groove-logo.png +0 -0
  170. package/packages/gui/src/components/ui/dropdown-menu.jsx +0 -60
  171. package/packages/gui/src/lib/hooks/use-media-query.js +0 -18
@@ -2,12 +2,19 @@
2
2
  // FSL-1.1-Apache-2.0 — see LICENSE
3
3
 
4
4
  import { spawn as cpSpawn } from 'child_process';
5
- import { createWriteStream, mkdirSync, chmodSync, existsSync, readFileSync, unlinkSync, readdirSync } from 'fs';
6
- import { resolve } from 'path';
5
+ import { createWriteStream, mkdirSync, chmodSync, existsSync, readFileSync, unlinkSync, readdirSync, copyFileSync } from 'fs';
6
+ import { resolve, dirname } from 'path';
7
+ import { fileURLToPath } from 'url';
7
8
  import { getProvider, getInstalledProviders } from './providers/index.js';
8
9
  import { AgentLoop } from './agent-loop.js';
9
10
  import { validateAgentConfig } from './validate.js';
10
11
 
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const SLIDES_ENGINE_SRC = resolve(__dirname, '../templates/groove-slides.cjs');
14
+
15
+ const COMPACTION_DROP_THRESHOLD = 0.25; // 25% drop from peak = natural compaction
16
+ const COMPACTION_MIN_PEAK = 0.15; // Ignore compaction if peak was below 15%
17
+
11
18
  // Role-specific prompt prefixes — applied during spawn regardless of entry point
12
19
  // (SpawnPanel, chat continue, CLI, API) for consistency
13
20
  const ROLE_PROMPTS = {
@@ -84,7 +91,14 @@ When making visual changes, always read app.css for the color palette and existi
84
91
  - Running tests (npm test) and builds (npm run build) to catch regressions
85
92
  - Auditing changes from other agents for correctness and consistency
86
93
  - Fixing integration issues that span the daemon/GUI boundary
87
- IMPORTANT: Do NOT start long-running dev servers (vite dev, npm start, next dev, etc.) unless explicitly asked. Verify builds compile with "npm run build", not by running a dev server. Rogue servers waste resources and block agent completion.
94
+
95
+ CRITICAL — NEVER DO THESE:
96
+ - NEVER restart the Groove daemon. No "groove stop", "groove start", "groove nuke", "groove restart".
97
+ - NEVER kill the daemon process. No "kill <pid>", "pkill groove", "killall node", etc.
98
+ - NEVER run "./promote.sh", "./promote-local.sh", or any publish/deploy script.
99
+ - NEVER start long-running dev servers (vite dev, npm start, next dev, etc.).
100
+
101
+ Restarting the daemon destroys ALL other agents currently running in other teams. Verification is done via "npm run build" and "npm test", which exit cleanly. If code changes require a daemon restart to take effect, report that in your output so the user can restart manually — do NOT do it yourself.
88
102
 
89
103
  `,
90
104
  testing: `You are a Testing agent. You write and maintain test suites, improve coverage, and verify quality. Focus on:
@@ -127,12 +141,36 @@ IMPORTANT: Do NOT start long-running dev servers (vite dev, npm start, next dev,
127
141
  - Writing database-related tests and verifying migration safety
128
142
 
129
143
  `,
130
- slides: `You are a Slide Deck agent. You build presentation decks as HTML slides (Reveal.js) with optional PPTX export. Focus on:
144
+ slides: `You are a Slide Deck agent. You build presentation decks as HTML slides (Reveal.js) with a matching PPTX export. Focus on:
131
145
  - Creating clean, professional slide layouts with strong visual hierarchy
132
- - Structuring content into clear sections with concise bullet points
146
+ - Structuring content into clear sections with concise, editorial copy
133
147
  - Building responsive HTML slides that look polished in the browser
134
- - Generating a slides.json data file alongside HTML for PPTX conversion
135
- For best results, apply a slide deck skill from the Marketplace. The skill provides templates, styling, and export automation.
148
+ - Generating a slides.json data file alongside HTML so PPTX export stays in sync
149
+
150
+ MANDATORY PPTX LAYOUT ENGINE — baked into Groove, not optional
151
+
152
+ A file called groove-slides.cjs has been written into your working directory. It is the layout engine — it does NOT define any theme or style. You MUST use it in your convert-slides.js (which must also be CommonJS — use require, not import). Do not re-implement its functions. Do not copy its contents into your own code.
153
+
154
+ const {
155
+ LAYOUT, estimateTextHeight, fitFontSize,
156
+ hardGate, tracker,
157
+ } = require('./groove-slides.cjs');
158
+
159
+ What the engine gives you:
160
+ - LAYOUT constants — SLIDE_W (10), SLIDE_H (5.625), SAFE_X (0.6), SAFE_W (8.8), SAFE_BOT (5.125), CONTENT_START (0.65), plus standard gaps. Any element whose y + h exceeds SAFE_BOT (5.125) is clipped off the slide — treat that as a build failure.
161
+ - estimateTextHeight(text, fontSize, boxW, {bold}) — returns the height the text will actually need.
162
+ - fitFontSize(text, maxSize, minSize, boxW, boxH, {bold}) — returns the largest integer font size that fits the text in the box. Use this for every headline, hero number, price, or any text that could wrap. It replaces shrinkText.
163
+ - tracker() — returns { track, placed }. Wrap EVERY slide.addText / slide.addShape call's options with track('element-name', {x, y, w, h, text, fontSize, bold, ...}) so the gate can inspect it.
164
+ - hardGate(pairs) — runs collision, bounds, and wrap checks over every slide. Calls process.exit(1) on any issue. Call it once, before pres.writeFile(), with allSlides.map(s => [s.name, s.elements]).
165
+
166
+ Non-negotiable rules:
167
+ 1. Do NOT use shrinkText: true. PowerPoint honors it; Google Slides and LibreOffice PDF export largely do not — the text renders at its declared size and overflows. Compute a real font size with fitFontSize() instead.
168
+ 2. Do NOT invent skipCollision, skipBounds, skipWrap, or any other opt-out flag on tracked elements. The engine rejects them with an error. If a design element legitimately extends past the safe area (e.g., an orb bleeding off-slide as atmosphere), call slide.addImage directly WITHOUT tracking it — the gate only validates elements you declare placed.
169
+ 3. Y advances come from estimateTextHeight() or an explicit box budget (const titleBoxH = 1.2). Never write a hardcoded magic number like Y += 0.95.
170
+ 4. Do NOT wrap hardGate() in try/catch. Do NOT run pres.writeFile() after a failing gate. If the gate fails, the deck is not shippable — fix the slide generator.
171
+ 5. Track the brand watermark and category label you draw on every slide. If they are not in placed[], the gate cannot detect a title bleeding into them.
172
+
173
+ A marketplace skill (e.g., modern-pitch-deck) layers theme on top — color palette, typography choices, gradient backgrounds, visual components (orbs, charts, heatmaps), slide archetype patterns. Skills must NOT redefine the engine functions, reset LAYOUT constants, or introduce their own gate. If you see a skill doing any of those, ignore that part and keep using the engine as documented here.
136
174
 
137
175
  `,
138
176
  home: `You are a Smart Home automation agent. You have MCP integrations for Home Assistant. Focus on:
@@ -225,6 +263,7 @@ export class ProcessManager {
225
263
  constructor(daemon) {
226
264
  this.daemon = daemon;
227
265
  this.handles = new Map(); // agentId -> { proc, logStream }
266
+ this.peakContextUsage = new Map(); // agentId -> highest contextUsage seen
228
267
  }
229
268
 
230
269
  async spawn(config) {
@@ -298,6 +337,20 @@ export class ProcessManager {
298
337
  locks.register(agent.id, agent.scope);
299
338
  }
300
339
 
340
+ // For slides-role agents, write the baked-in layout engine into the working
341
+ // directory. The agent imports it as `./groove-slides.cjs`. Core layout rules
342
+ // (Y-cursor, fitFontSize, collision/bounds/wrap gates) ship with Groove so
343
+ // they apply without a skill attached — skills only provide theme/style.
344
+ if (config.role === 'slides') {
345
+ try {
346
+ const dest = resolve(agent.workingDir || this.daemon.projectDir, 'groove-slides.cjs');
347
+ mkdirSync(dirname(dest), { recursive: true });
348
+ copyFileSync(SLIDES_ENGINE_SRC, dest);
349
+ } catch (err) {
350
+ console.log(`[Groove:Spawn] Failed to write groove-slides.cjs: ${err.message}`);
351
+ }
352
+ }
353
+
301
354
  // Pre-spawn task negotiation — if same-role agents are running,
302
355
  // query them about current work so the new agent gets a clear assignment
303
356
  const sameRole = registry.getAll().filter(
@@ -469,9 +522,12 @@ For normal file edits within your scope, proceed without review.
469
522
 
470
523
  // Write MCP config for agent integrations (command/args only, no secrets)
471
524
  // Credentials are injected via process environment below
525
+ // Only write .mcp.json for claude-code — other providers use GROOVE's exec API
472
526
  let integrationEnv = {};
473
527
  if (config.integrations?.length > 0 && this.daemon.integrations) {
474
- this.daemon.integrations.writeMcpJson(config.integrations);
528
+ if (agent.provider === 'claude-code') {
529
+ this.daemon.integrations.writeMcpJson(config.integrations);
530
+ }
475
531
  integrationEnv = this.daemon.integrations.getSpawnEnv(config.integrations);
476
532
  }
477
533
 
@@ -571,6 +627,17 @@ For normal file edits within your scope, proceed without review.
571
627
  }
572
628
  if (output.contextUsage !== undefined) {
573
629
  updates.contextUsage = output.contextUsage;
630
+
631
+ const peak = this.peakContextUsage.get(agent.id) || 0;
632
+ if (output.contextUsage > peak) {
633
+ this.peakContextUsage.set(agent.id, output.contextUsage);
634
+ } else if (peak >= COMPACTION_MIN_PEAK) {
635
+ const drop = peak - output.contextUsage;
636
+ if (drop >= COMPACTION_DROP_THRESHOLD * peak) {
637
+ this.daemon.rotator.recordNaturalCompaction(agent, peak, output.contextUsage);
638
+ this.peakContextUsage.set(agent.id, output.contextUsage);
639
+ }
640
+ }
574
641
  }
575
642
  registry.update(agent.id, updates);
576
643
 
@@ -652,6 +719,25 @@ For normal file edits within your scope, proceed without review.
652
719
  // Process cross-scope handoff requests from this agent
653
720
  this._processHandoffs(agent);
654
721
  }
722
+
723
+ // Update Layer 7 specialization profile for this agent's session
724
+ if (this.daemon.memory && (finalStatus === 'completed' || finalStatus === 'crashed')) {
725
+ try {
726
+ const events = this.daemon.classifier?.agentWindows?.[agent.id] || [];
727
+ const signals = events.length >= 6
728
+ ? this.daemon.adaptive.extractSignals(events, agent.scope)
729
+ : null;
730
+ const score = signals ? this.daemon.adaptive.scoreSession(signals) : null;
731
+ const files = this.daemon.journalist?.getAgentFiles(agent) || [];
732
+ this.daemon.memory.updateSpecialization(agent.id, {
733
+ role: agent.role,
734
+ qualityScore: score,
735
+ filesTouched: files,
736
+ signals,
737
+ threshold: this.daemon.adaptive?.getThreshold(agent.provider, agent.role),
738
+ });
739
+ } catch { /* best-effort */ }
740
+ }
655
741
  });
656
742
 
657
743
  proc.on('error', (err) => {
@@ -716,6 +802,17 @@ For normal file edits within your scope, proceed without review.
716
802
  // Context window usage (0-1 scale) — drives rotation threshold
717
803
  if (output.contextUsage !== undefined) {
718
804
  updates.contextUsage = output.contextUsage;
805
+
806
+ const peak = this.peakContextUsage.get(agentId) || 0;
807
+ if (output.contextUsage > peak) {
808
+ this.peakContextUsage.set(agentId, output.contextUsage);
809
+ } else if (peak >= COMPACTION_MIN_PEAK) {
810
+ const drop = peak - output.contextUsage;
811
+ if (drop >= COMPACTION_DROP_THRESHOLD * peak) {
812
+ this.daemon.rotator.recordNaturalCompaction(agent, peak, output.contextUsage);
813
+ this.peakContextUsage.set(agentId, output.contextUsage);
814
+ }
815
+ }
719
816
  }
720
817
 
721
818
  // Session ID for resume support
@@ -1042,7 +1139,42 @@ For normal file edits within your scope, proceed without review.
1042
1139
  return newAgent;
1043
1140
  }
1044
1141
 
1142
+ /**
1143
+ * Stop the agent's current work without killing the agent.
1144
+ * The process is terminated but the agent stays in the registry with its
1145
+ * sessionId intact, so you can resume it later with a new message.
1146
+ */
1147
+ async stop(agentId) {
1148
+ const handle = this.handles.get(agentId);
1149
+ if (!handle) return;
1150
+
1151
+ const { proc, loop } = handle;
1152
+
1153
+ if (loop) {
1154
+ await loop.stop();
1155
+ this.handles.delete(agentId);
1156
+ this.daemon.registry.update(agentId, { status: 'stopped', pid: null });
1157
+ return;
1158
+ }
1159
+
1160
+ return new Promise((resolveStop) => {
1161
+ const forceTimer = setTimeout(() => {
1162
+ try { proc.kill('SIGKILL'); } catch {}
1163
+ }, 5000);
1164
+
1165
+ proc.on('exit', () => {
1166
+ clearTimeout(forceTimer);
1167
+ this.handles.delete(agentId);
1168
+ this.daemon.registry.update(agentId, { status: 'stopped', pid: null });
1169
+ resolveStop();
1170
+ });
1171
+
1172
+ try { proc.kill('SIGTERM'); } catch { resolveStop(); }
1173
+ });
1174
+ }
1175
+
1045
1176
  async kill(agentId) {
1177
+ this.peakContextUsage.delete(agentId);
1046
1178
  const handle = this.handles.get(agentId);
1047
1179
 
1048
1180
  if (!handle) {
@@ -1165,7 +1297,7 @@ For normal file edits within your scope, proceed without review.
1165
1297
  ].join('\n');
1166
1298
 
1167
1299
  try {
1168
- const response = await journalist.callHeadless(prompt);
1300
+ const response = await journalist.callHeadless(prompt, { trackAs: '__negotiator__' });
1169
1301
  return response;
1170
1302
  } catch {
1171
1303
  // Fallback: return raw data for the agent to interpret
@@ -51,7 +51,7 @@ export class Registry extends EventEmitter {
51
51
  if (!agent) return null;
52
52
 
53
53
  // Only allow known fields to prevent prototype pollution
54
- const SAFE_FIELDS = ['status', 'pid', 'tokensUsed', 'contextUsage', 'lastActivity', 'model', 'provider', 'name', 'routingMode', 'routingReason', 'sessionId', 'skills', 'integrations', 'workingDir', 'effort', 'costUsd', 'durationMs', 'turns', 'inputTokens', 'outputTokens', 'teamId', 'permission', 'scope'];
54
+ const SAFE_FIELDS = ['status', 'pid', 'tokensUsed', 'contextUsage', 'lastActivity', 'model', 'provider', 'name', 'routingMode', 'routingReason', 'sessionId', 'skills', 'integrations', 'workingDir', 'effort', 'costUsd', 'durationMs', 'turns', 'inputTokens', 'outputTokens', 'teamId', 'permission', 'scope', 'integrationApproval'];
55
55
  for (const key of Object.keys(updates)) {
56
56
  if (SAFE_FIELDS.includes(key)) {
57
57
  agent[key] = updates[key];