groove-dev 0.27.134 → 0.27.136

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 (85) hide show
  1. package/moe-training/client/domain-tagger.js +1 -1
  2. package/moe-training/scripts/retag-delegate-yield.js +303 -0
  3. package/moe-training/test/shared/envelope-schema.test.js +3 -3
  4. package/node_modules/@groove-dev/cli/package.json +1 -1
  5. package/node_modules/@groove-dev/daemon/package.json +1 -1
  6. package/node_modules/@groove-dev/daemon/src/adaptive.js +77 -0
  7. package/node_modules/@groove-dev/daemon/src/api.js +35 -5
  8. package/node_modules/@groove-dev/daemon/src/journalist.js +28 -12
  9. package/node_modules/@groove-dev/daemon/src/model-lab.js +53 -76
  10. package/node_modules/@groove-dev/daemon/src/process.js +91 -2
  11. package/node_modules/@groove-dev/daemon/src/rotator.js +45 -3
  12. package/node_modules/@groove-dev/gui/dist/assets/{index-Dozp69tK.js → index-BrZHF7pK.js} +1770 -1766
  13. package/node_modules/@groove-dev/gui/dist/assets/index-DIfiwdKl.css +1 -0
  14. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  15. package/node_modules/@groove-dev/gui/package.json +1 -1
  16. package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +60 -18
  17. package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +42 -20
  18. package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +1 -1
  19. package/node_modules/@groove-dev/gui/src/components/agents/workspace-mode.jsx +1 -1
  20. package/node_modules/@groove-dev/gui/src/components/chat/chat-messages.jsx +2 -22
  21. package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +9 -9
  22. package/node_modules/@groove-dev/gui/src/components/editor/file-tree.jsx +1 -1
  23. package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +7 -0
  24. package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +59 -51
  25. package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +48 -48
  26. package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +39 -38
  27. package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +4 -5
  28. package/node_modules/@groove-dev/gui/src/components/lab/preset-manager.jsx +11 -11
  29. package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +66 -62
  30. package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +13 -13
  31. package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +1 -1
  32. package/node_modules/@groove-dev/gui/src/components/preview/preview-workspace.jsx +62 -22
  33. package/node_modules/@groove-dev/gui/src/components/ui/slider.jsx +16 -17
  34. package/node_modules/@groove-dev/gui/src/components/ui/table-tree.jsx +38 -0
  35. package/node_modules/@groove-dev/gui/src/stores/groove.js +23 -9
  36. package/node_modules/@groove-dev/gui/src/views/editor.jsx +1 -1
  37. package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +101 -87
  38. package/node_modules/moe-training/client/domain-tagger.js +1 -1
  39. package/node_modules/moe-training/scripts/retag-delegate-yield.js +303 -0
  40. package/node_modules/moe-training/test/shared/envelope-schema.test.js +3 -3
  41. package/package.json +1 -1
  42. package/packages/cli/package.json +1 -1
  43. package/packages/daemon/package.json +1 -1
  44. package/packages/daemon/src/adaptive.js +77 -0
  45. package/packages/daemon/src/api.js +35 -5
  46. package/packages/daemon/src/journalist.js +28 -12
  47. package/packages/daemon/src/model-lab.js +53 -76
  48. package/packages/daemon/src/process.js +91 -2
  49. package/packages/daemon/src/rotator.js +45 -3
  50. package/packages/gui/dist/assets/{index-Dozp69tK.js → index-BrZHF7pK.js} +1770 -1766
  51. package/packages/gui/dist/assets/index-DIfiwdKl.css +1 -0
  52. package/packages/gui/dist/index.html +2 -2
  53. package/packages/gui/package.json +1 -1
  54. package/packages/gui/src/components/agents/agent-chat.jsx +60 -18
  55. package/packages/gui/src/components/agents/agent-feed.jsx +42 -20
  56. package/packages/gui/src/components/agents/agent-file-tree.jsx +1 -1
  57. package/packages/gui/src/components/agents/workspace-mode.jsx +1 -1
  58. package/packages/gui/src/components/chat/chat-messages.jsx +2 -22
  59. package/packages/gui/src/components/editor/code-editor.jsx +9 -9
  60. package/packages/gui/src/components/editor/file-tree.jsx +1 -1
  61. package/packages/gui/src/components/editor/terminal.jsx +7 -0
  62. package/packages/gui/src/components/lab/chat-playground.jsx +59 -51
  63. package/packages/gui/src/components/lab/lab-assistant.jsx +48 -48
  64. package/packages/gui/src/components/lab/metrics-panel.jsx +39 -38
  65. package/packages/gui/src/components/lab/parameter-panel.jsx +4 -5
  66. package/packages/gui/src/components/lab/preset-manager.jsx +11 -11
  67. package/packages/gui/src/components/lab/runtime-config.jsx +66 -62
  68. package/packages/gui/src/components/lab/system-prompt-editor.jsx +13 -13
  69. package/packages/gui/src/components/layout/breadcrumb-bar.jsx +1 -1
  70. package/packages/gui/src/components/preview/preview-workspace.jsx +62 -22
  71. package/packages/gui/src/components/ui/slider.jsx +16 -17
  72. package/packages/gui/src/components/ui/table-tree.jsx +38 -0
  73. package/packages/gui/src/stores/groove.js +23 -9
  74. package/packages/gui/src/views/editor.jsx +1 -1
  75. package/packages/gui/src/views/model-lab.jsx +101 -87
  76. package/plan_files/DELEGATE_YIELD_TRAINING_TAGS.md +135 -0
  77. package/plan_files/session-quality-rotation-fixes.md +218 -0
  78. package/test.py +571 -0
  79. package/node_modules/@groove-dev/gui/dist/assets/index-BgQL4bNl.css +0 -1
  80. package/packages/gui/dist/assets/index-BgQL4bNl.css +0 -1
  81. /package/{AGENT_ORCHESTRATION.md → plan_files/AGENT_ORCHESTRATION.md} +0 -0
  82. /package/{DYNAMIC_LEAF_ARCH.md → plan_files/DYNAMIC_LEAF_ARCH.md} +0 -0
  83. /package/{EMBEDDING_DIAGNOSTIC.md → plan_files/EMBEDDING_DIAGNOSTIC.md} +0 -0
  84. /package/{EMBEDDING_SERVICE_BUILD_PLAN.md → plan_files/EMBEDDING_SERVICE_BUILD_PLAN.md} +0 -0
  85. /package/{MOE_TRAINING_PIPELINE.md → plan_files/MOE_TRAINING_PIPELINE.md} +0 -0
@@ -220,6 +220,7 @@ export const useGrooveStore = create((set, get) => ({
220
220
  }),
221
221
  labSystemPrompt: localStorage.getItem('groove:labSystemPrompt') || '',
222
222
  labStreaming: false,
223
+ labAbortController: null,
223
224
  labLocalModels: [],
224
225
  labLaunching: null,
225
226
  labLlamaInstalled: null,
@@ -3501,6 +3502,9 @@ export const useGrooveStore = create((set, get) => ({
3501
3502
  return { labSessions: sessions };
3502
3503
  });
3503
3504
 
3505
+ const abortController = new AbortController();
3506
+ set({ labAbortController: abortController });
3507
+
3504
3508
  const startTime = performance.now();
3505
3509
  let firstTokenTime = null;
3506
3510
  let tokenCount = 0;
@@ -3536,6 +3540,7 @@ export const useGrooveStore = create((set, get) => ({
3536
3540
  method: 'POST',
3537
3541
  headers: { 'Content-Type': 'application/json' },
3538
3542
  body: JSON.stringify(body),
3543
+ signal: abortController.signal,
3539
3544
  });
3540
3545
 
3541
3546
  if (!res.ok) {
@@ -3639,20 +3644,29 @@ export const useGrooveStore = create((set, get) => ({
3639
3644
  });
3640
3645
  }
3641
3646
  } catch (err) {
3642
- set((s) => {
3643
- const sessions = s.labSessions.map((sess) => {
3644
- if (sess.id !== sessionId) return sess;
3645
- const msgs = [...sess.messages];
3646
- msgs[msgs.length - 1] = { ...msgs[msgs.length - 1], content: `Error: ${err.message}`, error: true };
3647
- return { ...sess, messages: msgs };
3647
+ if (err.name === 'AbortError') {
3648
+ // User cancelled keep whatever content was already streamed
3649
+ } else {
3650
+ set((s) => {
3651
+ const sessions = s.labSessions.map((sess) => {
3652
+ if (sess.id !== sessionId) return sess;
3653
+ const msgs = [...sess.messages];
3654
+ msgs[msgs.length - 1] = { ...msgs[msgs.length - 1], content: `Error: ${err.message}`, error: true };
3655
+ return { ...sess, messages: msgs };
3656
+ });
3657
+ return { labSessions: sessions };
3648
3658
  });
3649
- return { labSessions: sessions };
3650
- });
3659
+ }
3651
3660
  } finally {
3652
- set({ labStreaming: false });
3661
+ set({ labStreaming: false, labAbortController: null });
3653
3662
  }
3654
3663
  },
3655
3664
 
3665
+ stopLabInference() {
3666
+ const ctrl = get().labAbortController;
3667
+ if (ctrl) ctrl.abort();
3668
+ },
3669
+
3656
3670
  saveLabPreset(name) {
3657
3671
  const st = get();
3658
3672
  const preset = {
@@ -121,7 +121,7 @@ export default function EditorView() {
121
121
  {sidebarCollapsed && (
122
122
  <button
123
123
  onClick={() => setSidebarCollapsed(false)}
124
- className="flex-shrink-0 w-6 flex items-center justify-center border-r border-border bg-surface-2 text-text-4 hover:text-text-0 hover:bg-surface-3 transition-colors cursor-pointer"
124
+ className="flex-shrink-0 w-6 flex items-start justify-center pt-2 border-r border-border bg-surface-2 text-text-4 hover:text-text-0 hover:bg-surface-3 transition-colors cursor-pointer"
125
125
  title="Show sidebar"
126
126
  >
127
127
  <PanelLeftOpen size={14} />
@@ -2,7 +2,6 @@
2
2
  import { useState, useCallback, useRef, useEffect } from 'react';
3
3
  import { useGrooveStore } from '../stores/groove';
4
4
  import { ScrollArea } from '../components/ui/scroll-area';
5
- import { Badge } from '../components/ui/badge';
6
5
  import { Tooltip } from '../components/ui/tooltip';
7
6
  import { Combobox } from '../components/ui/combobox';
8
7
  import { RuntimeConfig, LaunchModel } from '../components/lab/runtime-config';
@@ -13,7 +12,7 @@ import { LabAssistant } from '../components/lab/lab-assistant';
13
12
  import { MetricsPanel } from '../components/lab/metrics-panel';
14
13
  import { PresetManager } from '../components/lab/preset-manager';
15
14
  import { cn } from '../lib/cn';
16
- import { FlaskConical, PanelLeftOpen, PanelRightOpen, Box } from 'lucide-react';
15
+ import { FlaskConical, PanelLeftClose, PanelLeftOpen, PanelRightClose, PanelRightOpen, Box } from 'lucide-react';
17
16
 
18
17
  const LEFT_DEFAULT = 280;
19
18
  const LEFT_MIN = 220;
@@ -32,7 +31,7 @@ function ModelSelector() {
32
31
 
33
32
  return (
34
33
  <div className="space-y-1.5">
35
- <span className="text-xs font-semibold font-sans text-text-2 uppercase tracking-wider">Model</span>
34
+ <span className="text-2xs font-semibold font-sans text-text-3 uppercase tracking-wider">Model</span>
36
35
  <Combobox
37
36
  value={activeModel || ''}
38
37
  onChange={setActiveModel}
@@ -53,15 +52,38 @@ function ModelSelector() {
53
52
  );
54
53
  }
55
54
 
56
- function ResizeHandle({ onMouseDown, direction = 'vertical' }) {
55
+ function ResizeHandle({ onMouseDown }) {
57
56
  return (
58
57
  <div
59
58
  onMouseDown={onMouseDown}
60
- className={cn(
61
- 'flex-shrink-0 bg-border hover:bg-accent/40 transition-colors',
62
- direction === 'vertical' ? 'w-px cursor-col-resize hover:w-0.5' : 'h-px cursor-row-resize hover:h-0.5',
63
- )}
64
- />
59
+ className="flex-shrink-0 w-[3px] cursor-col-resize group relative"
60
+ >
61
+ <div className="absolute inset-y-0 -left-1 -right-1" />
62
+ <div className="absolute inset-y-0 left-[1px] w-px bg-border group-hover:bg-accent/50 transition-colors" />
63
+ </div>
64
+ );
65
+ }
66
+
67
+ function PanelToggle({ collapsed, onClick, side }) {
68
+ const Icon = side === 'left'
69
+ ? (collapsed ? PanelLeftOpen : PanelLeftClose)
70
+ : (collapsed ? PanelRightOpen : PanelRightClose);
71
+ const label = side === 'left'
72
+ ? (collapsed ? 'Show config' : 'Hide config')
73
+ : (collapsed ? 'Show metrics' : 'Hide metrics');
74
+
75
+ return (
76
+ <Tooltip content={label}>
77
+ <button
78
+ onClick={onClick}
79
+ className={cn(
80
+ 'p-1 transition-colors cursor-pointer',
81
+ collapsed ? 'text-text-4 hover:text-text-1' : 'text-text-3 hover:text-text-1',
82
+ )}
83
+ >
84
+ <Icon size={14} />
85
+ </button>
86
+ </Tooltip>
65
87
  );
66
88
  }
67
89
 
@@ -130,93 +152,79 @@ export default function ModelLabView() {
130
152
 
131
153
  return (
132
154
  <div className="h-full flex flex-col bg-surface-0">
133
- {/* Header */}
134
- <div className="flex-shrink-0 flex items-center justify-between px-5 py-2.5 border-b border-border">
135
- <div className="flex items-center gap-2.5">
136
- <FlaskConical size={16} className="text-accent" />
137
- <h1 className="text-sm font-bold font-sans text-text-0">Model Lab</h1>
138
- <Badge variant="accent">Beta</Badge>
139
- </div>
140
- <div className="flex items-center gap-1">
141
- <Tooltip content={leftCollapsed ? 'Show config panel' : 'Hide config panel'}>
142
- <button
143
- onClick={() => setLeftCollapsed(!leftCollapsed)}
144
- className={cn(
145
- 'p-1.5 rounded-md transition-colors cursor-pointer',
146
- leftCollapsed ? 'text-text-3 hover:text-accent hover:bg-accent/10' : 'text-accent bg-accent/10',
147
- )}
148
- >
149
- <PanelLeftOpen size={14} />
150
- </button>
151
- </Tooltip>
152
- <Tooltip content={rightCollapsed ? 'Show metrics panel' : 'Hide metrics panel'}>
153
- <button
154
- onClick={() => setRightCollapsed(!rightCollapsed)}
155
- className={cn(
156
- 'p-1.5 rounded-md transition-colors cursor-pointer',
157
- rightCollapsed ? 'text-text-3 hover:text-accent hover:bg-accent/10' : 'text-accent bg-accent/10',
158
- )}
159
- >
160
- <PanelRightOpen size={14} />
161
- </button>
162
- </Tooltip>
163
- </div>
164
- </div>
165
-
166
155
  {/* 3-panel layout */}
167
156
  <div className="flex-1 flex min-h-0">
168
157
  {/* Left panel — config */}
169
158
  <div
170
159
  className={cn(
171
- 'flex-shrink-0 border-r border-border transition-all duration-200 overflow-hidden',
172
- leftCollapsed && 'w-0 border-r-0',
160
+ 'flex-shrink-0 transition-all duration-200 overflow-hidden',
161
+ leftCollapsed && 'w-0',
173
162
  )}
174
163
  style={leftCollapsed ? undefined : { width: leftWidth }}
175
164
  >
176
- <ScrollArea className="h-full">
177
- <div className="px-4 py-4 space-y-5">
178
- <LaunchModel />
179
- <div className="border-t border-border-subtle" />
180
- <RuntimeConfig />
181
- <div className="border-t border-border-subtle" />
182
- <ModelSelector />
183
- <div className="border-t border-border-subtle" />
184
- <ParameterPanel />
185
- <div className="border-t border-border-subtle" />
186
- <PresetManager />
187
- <div className="border-t border-border-subtle" />
188
- <SystemPromptEditor />
165
+ <div className="h-full flex flex-col">
166
+ <div className="flex-shrink-0 flex items-center justify-between px-4 h-10">
167
+ <div className="flex items-center gap-2">
168
+ <FlaskConical size={13} className="text-accent" />
169
+ <span className="text-xs font-semibold font-sans text-text-1">Model Lab</span>
170
+ </div>
171
+ <PanelToggle collapsed={false} onClick={() => setLeftCollapsed(true)} side="left" />
189
172
  </div>
190
- </ScrollArea>
173
+ <ScrollArea className="flex-1 min-h-0">
174
+ <div className="px-4 pb-4 space-y-5">
175
+ <LaunchModel />
176
+ <div className="h-px bg-border-subtle" />
177
+ <RuntimeConfig />
178
+ <div className="h-px bg-border-subtle" />
179
+ <ModelSelector />
180
+ <div className="h-px bg-border-subtle" />
181
+ <ParameterPanel />
182
+ <div className="h-px bg-border-subtle" />
183
+ <PresetManager />
184
+ <div className="h-px bg-border-subtle" />
185
+ <SystemPromptEditor />
186
+ </div>
187
+ </ScrollArea>
188
+ </div>
191
189
  </div>
192
190
 
193
191
  {!leftCollapsed && <ResizeHandle onMouseDown={onLeftMouseDown} />}
194
192
 
195
193
  {/* Center panel — chat playground / assistant */}
196
194
  <div className="flex-1 min-w-0 flex flex-col">
197
- {labAssistantAgentId && (
198
- <div className="flex-shrink-0 flex items-center gap-1 px-3 pt-2">
199
- <button
200
- onClick={() => setLabAssistantMode(false)}
201
- className={cn(
202
- 'px-3 py-1.5 text-xs font-sans font-medium rounded-t-md transition-colors cursor-pointer',
203
- !labAssistantMode ? 'text-accent bg-accent/10' : 'text-text-3 hover:text-text-1',
204
- )}
205
- >
206
- Playground
207
- </button>
208
- <button
209
- onClick={() => setLabAssistantMode(true)}
210
- className={cn(
211
- 'px-3 py-1.5 text-xs font-sans font-medium rounded-t-md transition-colors cursor-pointer',
212
- labAssistantMode ? 'text-accent bg-accent/10' : 'text-text-3 hover:text-text-1',
213
- )}
214
- >
215
- Assistant
216
- </button>
217
- </div>
218
- )}
219
- <div className="flex-1 min-h-0 p-3">
195
+ {/* Center header bar */}
196
+ <div className="flex-shrink-0 flex items-center h-10 px-3 gap-2">
197
+ {leftCollapsed && (
198
+ <PanelToggle collapsed onClick={() => setLeftCollapsed(false)} side="left" />
199
+ )}
200
+ {labAssistantAgentId && (
201
+ <div className="flex items-center gap-px bg-surface-2 rounded p-px">
202
+ <button
203
+ onClick={() => setLabAssistantMode(false)}
204
+ className={cn(
205
+ 'px-3 py-1 text-2xs font-sans font-medium rounded-sm transition-colors cursor-pointer',
206
+ !labAssistantMode ? 'text-text-0 bg-surface-4' : 'text-text-3 hover:text-text-1',
207
+ )}
208
+ >
209
+ Playground
210
+ </button>
211
+ <button
212
+ onClick={() => setLabAssistantMode(true)}
213
+ className={cn(
214
+ 'px-3 py-1 text-2xs font-sans font-medium rounded-sm transition-colors cursor-pointer',
215
+ labAssistantMode ? 'text-text-0 bg-surface-4' : 'text-text-3 hover:text-text-1',
216
+ )}
217
+ >
218
+ Assistant
219
+ </button>
220
+ </div>
221
+ )}
222
+ <div className="flex-1" />
223
+ {rightCollapsed && (
224
+ <PanelToggle collapsed onClick={() => setRightCollapsed(false)} side="right" />
225
+ )}
226
+ </div>
227
+ <div className="flex-1 min-h-0">
220
228
  {labAssistantMode && labAssistantAgentId ? <LabAssistant /> : <ChatPlayground />}
221
229
  </div>
222
230
  </div>
@@ -226,16 +234,22 @@ export default function ModelLabView() {
226
234
  {/* Right panel — metrics */}
227
235
  <div
228
236
  className={cn(
229
- 'flex-shrink-0 border-l border-border transition-all duration-200 overflow-hidden',
230
- rightCollapsed && 'w-0 border-l-0',
237
+ 'flex-shrink-0 transition-all duration-200 overflow-hidden',
238
+ rightCollapsed && 'w-0',
231
239
  )}
232
240
  style={rightCollapsed ? undefined : { width: rightWidth }}
233
241
  >
234
- <ScrollArea className="h-full">
235
- <div className="px-4 py-4">
236
- <MetricsPanel />
242
+ <div className="h-full flex flex-col">
243
+ <div className="flex-shrink-0 flex items-center justify-between px-4 h-10">
244
+ <span className="text-2xs font-semibold font-sans text-text-3 uppercase tracking-wider">Metrics</span>
245
+ <PanelToggle collapsed={false} onClick={() => setRightCollapsed(true)} side="right" />
237
246
  </div>
238
- </ScrollArea>
247
+ <ScrollArea className="flex-1 min-h-0">
248
+ <div className="px-4 pb-4">
249
+ <MetricsPanel />
250
+ </div>
251
+ </ScrollArea>
252
+ </div>
239
253
  </div>
240
254
  </div>
241
255
  </div>
@@ -0,0 +1,135 @@
1
+ # Groove Team: Delegate/Yield Tag Extraction from Planner Telemetry
2
+
3
+ ## Context
4
+
5
+ The Hummingbird chassis is being trained on Groove planner telemetry data to learn orchestration — decomposing tasks, sequencing work, dispatching to specialists. The chassis already learns this well from the existing ReAct tag format (thought, action, observation, resolution).
6
+
7
+ We're adding two new coordination tags to the protocol:
8
+
9
+ | Tag | Purpose | Example |
10
+ |-----|---------|---------|
11
+ | `<delegate>` | "This needs a different specialist — re-route" | `<delegate>Create a PostgreSQL schema for user auth with sessions table</delegate>` |
12
+ | `<yield>` | "My part is done, here's my artifact for the next agent" | `<yield path="phase_01/api.py">Flask REST API with auth endpoints</yield>` |
13
+
14
+ The delegation behavior already exists in Groove planner sessions — it just isn't captured with these tags. Every time a planner agent reads `recommended-team.json` and dispatches work to a specialist agent, that's a delegation. Every time an agent produces an artifact that the next agent consumes, that's a yield.
15
+
16
+ ## What We Need
17
+
18
+ ### 1. Retroactive Tagging of Existing Planner Telemetry
19
+
20
+ Go through existing planner session trajectory_logs and identify two patterns:
21
+
22
+ **Pattern A — Delegation (dispatch to specialist):**
23
+
24
+ Look for sequences where the planner:
25
+ 1. Reads `recommended-team.json` or `AGENTS_REGISTRY.md`
26
+ 2. Reasons about which specialist to use (in a thought step)
27
+ 3. Dispatches work to a specialist agent
28
+
29
+ The dispatch moment should become a `<delegate>` step. The content is the rewritten task — what the planner told the specialist to do.
30
+
31
+ Before (current format):
32
+ ```json
33
+ {"step": 4, "type": "thought", "content": "This needs a backend specialist to implement the API endpoints..."}
34
+ {"step": 5, "type": "action", "tool": "Agent", "content": "Dispatch to backend-2: Implement REST endpoints for user authentication with JWT tokens and session management"}
35
+ {"step": 6, "type": "observation", "content": "Agent dispatched to backend-2"}
36
+ ```
37
+
38
+ After (with delegate tag):
39
+ ```json
40
+ {"step": 4, "type": "thought", "content": "This needs a backend specialist to implement the API endpoints..."}
41
+ {"step": 5, "type": "delegate", "content": "Implement REST endpoints for user authentication with JWT tokens and session management"}
42
+ ```
43
+
44
+ Key rules:
45
+ - The delegate content is the TASK, not "dispatch to backend-2." The delegating agent doesn't choose the target — the router does.
46
+ - Strip any agent ID references (backend-2, fullstack-7, etc.) from the content — just the task description.
47
+ - The thought step before the delegate should contain the reasoning about WHY this needs a different specialist.
48
+ - A delegate is a terminal step — it replaces resolution. Nothing comes after it in that firing.
49
+
50
+ **Pattern B — Yield (artifact handoff):**
51
+
52
+ Look for sequences where an agent:
53
+ 1. Produces a file or artifact (Write, Edit)
54
+ 2. That artifact is subsequently read by a different agent in the same pipeline
55
+
56
+ The artifact production should become a `<yield>` step.
57
+
58
+ Before (current format):
59
+ ```json
60
+ {"step": 7, "type": "action", "tool": "Write", "content": "Writing api.py with Flask endpoints..."}
61
+ {"step": 8, "type": "observation", "content": "File written successfully"}
62
+ {"step": 9, "type": "resolution", "content": "API endpoints implemented. The backend agent can now build on this."}
63
+ ```
64
+
65
+ After (with yield tag):
66
+ ```json
67
+ {"step": 7, "type": "action", "tool": "Write", "content": "Writing api.py with Flask endpoints..."}
68
+ {"step": 8, "type": "observation", "content": "File written successfully"}
69
+ {"step": 9, "type": "yield", "content": "Flask REST API with auth endpoints", "path": "api.py"}
70
+ ```
71
+
72
+ Key rules:
73
+ - The yield content is a SHORT summary (~10-20 tokens) of what the artifact is — enough for the next agent's prompt.
74
+ - The path field is the file path of the artifact.
75
+ - A yield is a terminal step — it replaces resolution. The agent's work is done; the next agent picks up.
76
+ - Only tag as yield if another agent actually consumed the artifact. If the agent resolved directly to the user, keep it as resolution.
77
+
78
+ ### 2. Step Format
79
+
80
+ Both new step types follow the same shape as existing steps:
81
+
82
+ ```json
83
+ {
84
+ "step": 5,
85
+ "type": "delegate",
86
+ "content": "The rewritten task for the next specialist",
87
+ "timestamp": 1777412000.5,
88
+ "token_count": 15
89
+ }
90
+ ```
91
+
92
+ ```json
93
+ {
94
+ "step": 9,
95
+ "type": "yield",
96
+ "content": "Short artifact summary",
97
+ "path": "phase_01/output_file.py",
98
+ "timestamp": 1777412001.2,
99
+ "token_count": 8
100
+ }
101
+ ```
102
+
103
+ They flow through the same `_processStep -> EnvelopeBuilder.addStep -> envelope -> Central Command` path as all other steps.
104
+
105
+ ### 3. Future Telemetry Capture
106
+
107
+ Once nano agents are running and producing `<delegate>` and `<yield>` tags in their output, the chassis parser should emit these as step objects into the trajectory capture:
108
+
109
+ - When parser sees `<delegate>task</delegate>` → emit `{"type": "delegate", "content": "task"}`
110
+ - When parser sees `<yield path="...">summary</yield>` → emit `{"type": "yield", "content": "summary", "path": "..."}`
111
+
112
+ Same as how the current parser emits thought/action/observation/resolution steps.
113
+
114
+ ### 4. What NOT to Do
115
+
116
+ - Do NOT add empty delegate/yield steps to sessions where they don't occur. If a session resolves normally, it ends with resolution. The absence of delegate/yield is correct.
117
+ - Do NOT change the thought or resolution content. Only add/modify the terminal step (replacing resolution with delegate or yield where the pattern matches).
118
+ - Do NOT invent delegation where it didn't happen. Only tag real dispatch-to-specialist moments.
119
+
120
+ ### 5. Output
121
+
122
+ Deliver the retroactively tagged sessions as:
123
+ - Same JSONL format as current planner telemetry
124
+ - Same file location (ingestion/ directory)
125
+ - Filename convention: `2026-05-09-planner-delegated-{hash}.jsonl` or similar
126
+
127
+ We'll run these through the standard `build_production_data.py` pipeline.
128
+
129
+ ### 6. Estimated Yield
130
+
131
+ From the 139 organic planner sessions currently in the training set:
132
+ - Most sessions involve at least one specialist dispatch → expect 80-120 delegate examples
133
+ - Many multi-agent sessions involve artifact handoffs → expect 40-80 yield examples
134
+
135
+ This organic data is highest quality — real frontier model orchestration decisions. It sets the standard that our synthetic generation will match.