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.
- package/moe-training/client/domain-tagger.js +1 -1
- package/moe-training/scripts/retag-delegate-yield.js +303 -0
- package/moe-training/test/shared/envelope-schema.test.js +3 -3
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/adaptive.js +77 -0
- package/node_modules/@groove-dev/daemon/src/api.js +35 -5
- package/node_modules/@groove-dev/daemon/src/journalist.js +28 -12
- package/node_modules/@groove-dev/daemon/src/model-lab.js +53 -76
- package/node_modules/@groove-dev/daemon/src/process.js +91 -2
- package/node_modules/@groove-dev/daemon/src/rotator.js +45 -3
- package/node_modules/@groove-dev/gui/dist/assets/{index-Dozp69tK.js → index-BrZHF7pK.js} +1770 -1766
- package/node_modules/@groove-dev/gui/dist/assets/index-DIfiwdKl.css +1 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +60 -18
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +42 -20
- package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/workspace-mode.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/chat/chat-messages.jsx +2 -22
- package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +9 -9
- package/node_modules/@groove-dev/gui/src/components/editor/file-tree.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +7 -0
- package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +59 -51
- package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +48 -48
- package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +39 -38
- package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +4 -5
- package/node_modules/@groove-dev/gui/src/components/lab/preset-manager.jsx +11 -11
- package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +66 -62
- package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +13 -13
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/preview/preview-workspace.jsx +62 -22
- package/node_modules/@groove-dev/gui/src/components/ui/slider.jsx +16 -17
- package/node_modules/@groove-dev/gui/src/components/ui/table-tree.jsx +38 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +23 -9
- package/node_modules/@groove-dev/gui/src/views/editor.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +101 -87
- package/node_modules/moe-training/client/domain-tagger.js +1 -1
- package/node_modules/moe-training/scripts/retag-delegate-yield.js +303 -0
- package/node_modules/moe-training/test/shared/envelope-schema.test.js +3 -3
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/adaptive.js +77 -0
- package/packages/daemon/src/api.js +35 -5
- package/packages/daemon/src/journalist.js +28 -12
- package/packages/daemon/src/model-lab.js +53 -76
- package/packages/daemon/src/process.js +91 -2
- package/packages/daemon/src/rotator.js +45 -3
- package/packages/gui/dist/assets/{index-Dozp69tK.js → index-BrZHF7pK.js} +1770 -1766
- package/packages/gui/dist/assets/index-DIfiwdKl.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/agents/agent-chat.jsx +60 -18
- package/packages/gui/src/components/agents/agent-feed.jsx +42 -20
- package/packages/gui/src/components/agents/agent-file-tree.jsx +1 -1
- package/packages/gui/src/components/agents/workspace-mode.jsx +1 -1
- package/packages/gui/src/components/chat/chat-messages.jsx +2 -22
- package/packages/gui/src/components/editor/code-editor.jsx +9 -9
- package/packages/gui/src/components/editor/file-tree.jsx +1 -1
- package/packages/gui/src/components/editor/terminal.jsx +7 -0
- package/packages/gui/src/components/lab/chat-playground.jsx +59 -51
- package/packages/gui/src/components/lab/lab-assistant.jsx +48 -48
- package/packages/gui/src/components/lab/metrics-panel.jsx +39 -38
- package/packages/gui/src/components/lab/parameter-panel.jsx +4 -5
- package/packages/gui/src/components/lab/preset-manager.jsx +11 -11
- package/packages/gui/src/components/lab/runtime-config.jsx +66 -62
- package/packages/gui/src/components/lab/system-prompt-editor.jsx +13 -13
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +1 -1
- package/packages/gui/src/components/preview/preview-workspace.jsx +62 -22
- package/packages/gui/src/components/ui/slider.jsx +16 -17
- package/packages/gui/src/components/ui/table-tree.jsx +38 -0
- package/packages/gui/src/stores/groove.js +23 -9
- package/packages/gui/src/views/editor.jsx +1 -1
- package/packages/gui/src/views/model-lab.jsx +101 -87
- package/plan_files/DELEGATE_YIELD_TRAINING_TAGS.md +135 -0
- package/plan_files/session-quality-rotation-fixes.md +218 -0
- package/test.py +571 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BgQL4bNl.css +0 -1
- package/packages/gui/dist/assets/index-BgQL4bNl.css +0 -1
- /package/{AGENT_ORCHESTRATION.md → plan_files/AGENT_ORCHESTRATION.md} +0 -0
- /package/{DYNAMIC_LEAF_ARCH.md → plan_files/DYNAMIC_LEAF_ARCH.md} +0 -0
- /package/{EMBEDDING_DIAGNOSTIC.md → plan_files/EMBEDDING_DIAGNOSTIC.md} +0 -0
- /package/{EMBEDDING_SERVICE_BUILD_PLAN.md → plan_files/EMBEDDING_SERVICE_BUILD_PLAN.md} +0 -0
- /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
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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
|
|
55
|
+
function ResizeHandle({ onMouseDown }) {
|
|
57
56
|
return (
|
|
58
57
|
<div
|
|
59
58
|
onMouseDown={onMouseDown}
|
|
60
|
-
className=
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
172
|
-
leftCollapsed && 'w-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
|
-
<
|
|
177
|
-
<div className="
|
|
178
|
-
<
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
<
|
|
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
|
-
|
|
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
|
-
{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
230
|
-
rightCollapsed && 'w-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
|
-
<
|
|
235
|
-
<div className="px-4
|
|
236
|
-
<
|
|
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
|
-
|
|
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,303 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
//
|
|
3
|
+
// Retroactive tagger: scans planner/fullstack session envelopes and re-tags
|
|
4
|
+
// delegation dispatches as "delegate" steps and artifact handoffs as "yield" steps.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// node retag-delegate-yield.js <input.jsonl> [output.jsonl]
|
|
8
|
+
//
|
|
9
|
+
// If output is omitted, writes to stdout. Input can also be piped via stdin.
|
|
10
|
+
// Only modifies planner/fullstack trajectory envelopes — SESSION_CLOSE and
|
|
11
|
+
// USER_FEEDBACK envelopes pass through unchanged.
|
|
12
|
+
|
|
13
|
+
import { createReadStream, createWriteStream, existsSync } from 'fs';
|
|
14
|
+
import { createInterface } from 'readline';
|
|
15
|
+
|
|
16
|
+
// --- Pattern A: Delegation detection ---
|
|
17
|
+
// A thought step reasoning about needing a specialist, followed by an action
|
|
18
|
+
// that dispatches to another agent. The dispatch action+observation get replaced
|
|
19
|
+
// with a single delegate step.
|
|
20
|
+
|
|
21
|
+
const DELEGATION_THOUGHT_RE = /\b(specialist|dispatch|delegate|hand off|route to|needs? a .*(backend|frontend|fullstack|database|devops|planner)|re-route|different agent|another agent|pass this to)\b/i;
|
|
22
|
+
|
|
23
|
+
const DELEGATION_ACTION_RE = /\b(dispatch|spawn|agent|delegate|hand off|route|assign)\b/i;
|
|
24
|
+
|
|
25
|
+
const AGENT_ID_RE = /\b(backend|frontend|fullstack|planner|devops|database|chat|advisor|qc)[-_]?\d+\b/gi;
|
|
26
|
+
|
|
27
|
+
function isDelegationThought(step) {
|
|
28
|
+
if (step.type !== 'thought') return false;
|
|
29
|
+
return DELEGATION_THOUGHT_RE.test(step.content || '');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isDelegationAction(step) {
|
|
33
|
+
if (step.type !== 'action') return false;
|
|
34
|
+
const tool = (step.tool || '').toLowerCase();
|
|
35
|
+
const content = (step.content || '').toLowerCase();
|
|
36
|
+
if (tool === 'agent' || tool === 'dispatch') return true;
|
|
37
|
+
if (DELEGATION_ACTION_RE.test(content)) return true;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function extractDelegateTask(actionStep, observationStep) {
|
|
42
|
+
let task = '';
|
|
43
|
+
const content = actionStep.content || '';
|
|
44
|
+
const args = actionStep.arguments || {};
|
|
45
|
+
|
|
46
|
+
// Try to extract the task from arguments (e.g. Agent tool input)
|
|
47
|
+
if (args.prompt) {
|
|
48
|
+
task = args.prompt;
|
|
49
|
+
} else if (args.message) {
|
|
50
|
+
task = args.message;
|
|
51
|
+
} else if (args.task) {
|
|
52
|
+
task = args.task;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Fall back to parsing the content after "Dispatch to X:" or similar
|
|
56
|
+
if (!task) {
|
|
57
|
+
const match = content.match(/(?:dispatch(?:ed)? to \S+:\s*|:\s*)(.*)/i);
|
|
58
|
+
task = match ? match[1] : content;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Strip agent ID references — the router picks the target, not the delegator
|
|
62
|
+
task = task.replace(AGENT_ID_RE, '').replace(/\s{2,}/g, ' ').trim();
|
|
63
|
+
|
|
64
|
+
return task;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// --- Pattern B: Yield detection ---
|
|
68
|
+
// An action(Write/Edit) producing a file, followed by observation(success),
|
|
69
|
+
// followed by a resolution whose content suggests artifact handoff to another agent.
|
|
70
|
+
|
|
71
|
+
const WRITE_TOOLS = new Set(['write', 'edit', 'create', 'save']);
|
|
72
|
+
|
|
73
|
+
const YIELD_RESOLUTION_RE = /\b(next agent|can now|build on this|picks? up|hand(?:ed|s|ing)? off|artifact|ready for|phase complete|my part is done|produced|output for)\b/i;
|
|
74
|
+
|
|
75
|
+
function isWriteAction(step) {
|
|
76
|
+
if (step.type !== 'action') return false;
|
|
77
|
+
const tool = (step.tool || '').toLowerCase();
|
|
78
|
+
return WRITE_TOOLS.has(tool);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isSuccessObservation(step) {
|
|
82
|
+
if (step.type !== 'observation') return false;
|
|
83
|
+
const c = (step.content || '').toLowerCase();
|
|
84
|
+
if (step.is_error) return false;
|
|
85
|
+
return !c.includes('error') || c.includes('0 error');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isYieldResolution(step) {
|
|
89
|
+
if (step.type !== 'resolution') return false;
|
|
90
|
+
return YIELD_RESOLUTION_RE.test(step.content || '');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function extractFilePath(writeStep) {
|
|
94
|
+
const args = writeStep.arguments || {};
|
|
95
|
+
if (args.file_path) return args.file_path;
|
|
96
|
+
if (args.path) return args.path;
|
|
97
|
+
|
|
98
|
+
// Try to parse path from content
|
|
99
|
+
const content = writeStep.content || '';
|
|
100
|
+
const match = content.match(/(?:Writing|Wrote|Created?|Saving?|Edit(?:ing|ed)?)\s+(\S+\.\w+)/i);
|
|
101
|
+
return match ? match[1] : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function buildYieldSummary(resolutionStep, maxTokens = 20) {
|
|
105
|
+
const content = (resolutionStep.content || '').trim();
|
|
106
|
+
// Take first sentence, cap at ~80 chars for ~20 tokens
|
|
107
|
+
const firstSentence = content.split(/[.!?\n]/)[0].trim();
|
|
108
|
+
return firstSentence.slice(0, 80);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// --- Main retagging logic ---
|
|
112
|
+
|
|
113
|
+
function retagTrajectory(steps) {
|
|
114
|
+
if (!Array.isArray(steps) || steps.length < 2) return { steps, delegateCount: 0, yieldCount: 0 };
|
|
115
|
+
|
|
116
|
+
const result = [];
|
|
117
|
+
let delegateCount = 0;
|
|
118
|
+
let yieldCount = 0;
|
|
119
|
+
let i = 0;
|
|
120
|
+
|
|
121
|
+
while (i < steps.length) {
|
|
122
|
+
// Pattern A: thought(delegation) → action(dispatch) → observation → ...
|
|
123
|
+
// Replace action+observation with a single delegate step
|
|
124
|
+
if (i + 2 < steps.length &&
|
|
125
|
+
isDelegationThought(steps[i]) &&
|
|
126
|
+
isDelegationAction(steps[i + 1])) {
|
|
127
|
+
|
|
128
|
+
// Keep the thought
|
|
129
|
+
result.push({ ...steps[i] });
|
|
130
|
+
|
|
131
|
+
const actionStep = steps[i + 1];
|
|
132
|
+
const nextStep = steps[i + 2];
|
|
133
|
+
const obsStep = nextStep.type === 'observation' ? nextStep : null;
|
|
134
|
+
|
|
135
|
+
const task = extractDelegateTask(actionStep, obsStep);
|
|
136
|
+
|
|
137
|
+
if (task) {
|
|
138
|
+
result.push({
|
|
139
|
+
step: actionStep.step,
|
|
140
|
+
type: 'delegate',
|
|
141
|
+
content: task,
|
|
142
|
+
timestamp: actionStep.timestamp,
|
|
143
|
+
token_count: Math.max(1, Math.ceil(task.length / 4)),
|
|
144
|
+
});
|
|
145
|
+
delegateCount++;
|
|
146
|
+
|
|
147
|
+
// Skip the action and observation
|
|
148
|
+
i += obsStep ? 3 : 2;
|
|
149
|
+
|
|
150
|
+
// If the next step is a resolution that just confirms dispatch, skip it too
|
|
151
|
+
if (i < steps.length && steps[i].type === 'resolution') {
|
|
152
|
+
const rc = (steps[i].content || '').toLowerCase();
|
|
153
|
+
if (rc.includes('dispatch') || rc.includes('delegat') || rc.includes('handed off') || rc.length < 50) {
|
|
154
|
+
i++;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Pattern B: action(Write/Edit) → observation(success) → resolution(handoff)
|
|
162
|
+
// Replace resolution with yield
|
|
163
|
+
if (i + 2 < steps.length &&
|
|
164
|
+
isWriteAction(steps[i]) &&
|
|
165
|
+
isSuccessObservation(steps[i + 1]) &&
|
|
166
|
+
isYieldResolution(steps[i + 2])) {
|
|
167
|
+
|
|
168
|
+
// Keep the action and observation as-is
|
|
169
|
+
result.push({ ...steps[i] });
|
|
170
|
+
result.push({ ...steps[i + 1] });
|
|
171
|
+
|
|
172
|
+
const writeStep = steps[i];
|
|
173
|
+
const resStep = steps[i + 2];
|
|
174
|
+
const path = extractFilePath(writeStep);
|
|
175
|
+
const summary = buildYieldSummary(resStep);
|
|
176
|
+
|
|
177
|
+
const yieldStep = {
|
|
178
|
+
step: resStep.step,
|
|
179
|
+
type: 'yield',
|
|
180
|
+
content: summary,
|
|
181
|
+
timestamp: resStep.timestamp,
|
|
182
|
+
token_count: Math.max(1, Math.ceil(summary.length / 4)),
|
|
183
|
+
};
|
|
184
|
+
if (path) yieldStep.path = path;
|
|
185
|
+
|
|
186
|
+
result.push(yieldStep);
|
|
187
|
+
yieldCount++;
|
|
188
|
+
i += 3;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// No pattern match — pass through unchanged
|
|
193
|
+
result.push({ ...steps[i] });
|
|
194
|
+
i++;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return { steps: result, delegateCount, yieldCount };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// --- Process envelopes ---
|
|
201
|
+
|
|
202
|
+
async function processStream(input, output) {
|
|
203
|
+
const rl = createInterface({ input, crlfDelay: Infinity });
|
|
204
|
+
|
|
205
|
+
let totalEnvelopes = 0;
|
|
206
|
+
let modifiedEnvelopes = 0;
|
|
207
|
+
let totalDelegates = 0;
|
|
208
|
+
let totalYields = 0;
|
|
209
|
+
let skippedRoles = 0;
|
|
210
|
+
|
|
211
|
+
for await (const line of rl) {
|
|
212
|
+
const trimmed = line.trim();
|
|
213
|
+
if (!trimmed) continue;
|
|
214
|
+
|
|
215
|
+
let envelope;
|
|
216
|
+
try {
|
|
217
|
+
envelope = JSON.parse(trimmed);
|
|
218
|
+
} catch {
|
|
219
|
+
output.write(trimmed + '\n');
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
totalEnvelopes++;
|
|
224
|
+
|
|
225
|
+
// Pass through non-trajectory envelopes unchanged
|
|
226
|
+
if (envelope.type === 'SESSION_CLOSE' || envelope.type === 'USER_FEEDBACK') {
|
|
227
|
+
output.write(JSON.stringify(envelope) + '\n');
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Only retag planner and fullstack sessions (where delegation/yield patterns occur)
|
|
232
|
+
const role = envelope.metadata?.agent_role;
|
|
233
|
+
if (!role || !['planner', 'fullstack', 'advisor'].includes(role)) {
|
|
234
|
+
skippedRoles++;
|
|
235
|
+
output.write(JSON.stringify(envelope) + '\n');
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const steps = envelope.trajectory_log;
|
|
240
|
+
if (!Array.isArray(steps) || steps.length < 2) {
|
|
241
|
+
output.write(JSON.stringify(envelope) + '\n');
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const { steps: retagged, delegateCount, yieldCount } = retagTrajectory(steps);
|
|
246
|
+
|
|
247
|
+
if (delegateCount > 0 || yieldCount > 0) {
|
|
248
|
+
modifiedEnvelopes++;
|
|
249
|
+
totalDelegates += delegateCount;
|
|
250
|
+
totalYields += yieldCount;
|
|
251
|
+
envelope.trajectory_log = retagged;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
output.write(JSON.stringify(envelope) + '\n');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return { totalEnvelopes, modifiedEnvelopes, totalDelegates, totalYields, skippedRoles };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// --- Entry point ---
|
|
261
|
+
|
|
262
|
+
async function main() {
|
|
263
|
+
const args = process.argv.slice(2);
|
|
264
|
+
const inputPath = args[0];
|
|
265
|
+
const outputPath = args[1];
|
|
266
|
+
|
|
267
|
+
let input;
|
|
268
|
+
if (inputPath && inputPath !== '-') {
|
|
269
|
+
if (!existsSync(inputPath)) {
|
|
270
|
+
console.error(`Error: input file not found: ${inputPath}`);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
input = createReadStream(inputPath, 'utf8');
|
|
274
|
+
} else {
|
|
275
|
+
input = process.stdin;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let output;
|
|
279
|
+
if (outputPath) {
|
|
280
|
+
output = createWriteStream(outputPath, 'utf8');
|
|
281
|
+
} else {
|
|
282
|
+
output = process.stdout;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const stats = await processStream(input, output);
|
|
286
|
+
|
|
287
|
+
if (output !== process.stdout) {
|
|
288
|
+
output.end();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Print stats to stderr so they don't mix with JSONL output
|
|
292
|
+
console.error(`\n--- Retag Summary ---`);
|
|
293
|
+
console.error(`Envelopes processed: ${stats.totalEnvelopes}`);
|
|
294
|
+
console.error(`Envelopes modified: ${stats.modifiedEnvelopes}`);
|
|
295
|
+
console.error(`Delegate steps added: ${stats.totalDelegates}`);
|
|
296
|
+
console.error(`Yield steps added: ${stats.totalYields}`);
|
|
297
|
+
console.error(`Skipped (wrong role): ${stats.skippedRoles}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
main().catch((err) => {
|
|
301
|
+
console.error(`Fatal: ${err.message}`);
|
|
302
|
+
process.exit(1);
|
|
303
|
+
});
|
|
@@ -475,7 +475,7 @@ describe('envelope-schema', () => {
|
|
|
475
475
|
it('accepts valid session_embedding object', () => {
|
|
476
476
|
const env = validEnvelope();
|
|
477
477
|
env.metadata.session_embedding = {
|
|
478
|
-
model: 'BAAI/bge-
|
|
478
|
+
model: 'BAAI/bge-base-en-v1.5',
|
|
479
479
|
vector: [0.0234, -0.0891, 0.1247, 0.0562],
|
|
480
480
|
source_text: 'Write a Python decorator that caches function results',
|
|
481
481
|
};
|
|
@@ -486,7 +486,7 @@ describe('envelope-schema', () => {
|
|
|
486
486
|
it('rejects session_embedding with empty vector', () => {
|
|
487
487
|
const env = validEnvelope();
|
|
488
488
|
env.metadata.session_embedding = {
|
|
489
|
-
model: 'BAAI/bge-
|
|
489
|
+
model: 'BAAI/bge-base-en-v1.5',
|
|
490
490
|
vector: [],
|
|
491
491
|
source_text: 'test',
|
|
492
492
|
};
|
|
@@ -498,7 +498,7 @@ describe('envelope-schema', () => {
|
|
|
498
498
|
it('rejects session_embedding with non-numeric vector values', () => {
|
|
499
499
|
const env = validEnvelope();
|
|
500
500
|
env.metadata.session_embedding = {
|
|
501
|
-
model: 'BAAI/bge-
|
|
501
|
+
model: 'BAAI/bge-base-en-v1.5',
|
|
502
502
|
vector: [0.1, 'bad', 0.3],
|
|
503
503
|
source_text: 'test',
|
|
504
504
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.136",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. Local model agent engine (GGUF/Ollama/llama-server), HuggingFace model browser, MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama, any local model.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|