groove-dev 0.27.145 → 0.27.146
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/CLAUDE.md +7 -0
- 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/api.js +12 -6
- package/node_modules/@groove-dev/daemon/src/conversations.js +41 -10
- package/node_modules/@groove-dev/daemon/src/introducer.js +20 -0
- package/node_modules/@groove-dev/daemon/src/process.js +262 -15
- package/node_modules/@groove-dev/daemon/src/providers/groove-network.js +1 -3
- package/node_modules/@groove-dev/daemon/src/rotator.js +15 -3
- package/node_modules/@groove-dev/daemon/src/routes/agents.js +43 -0
- package/node_modules/@groove-dev/daemon/templates/lab-general.json +12 -0
- package/node_modules/@groove-dev/daemon/templates/llama-cpp-setup.json +12 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BKbsE_hn.js +1011 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-CEkPsSAm.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/spawn-wizard.jsx +132 -4
- package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +1 -8
- package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +135 -13
- package/node_modules/@groove-dev/gui/src/components/chat/chat-messages.jsx +21 -4
- package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +6 -5
- package/node_modules/@groove-dev/gui/src/components/chat/model-picker.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +9 -3
- package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +13 -3
- package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +1 -3
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +24 -1
- package/node_modules/@groove-dev/gui/src/components/ui/question-modal.jsx +107 -0
- package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/stores/groove.js +32 -2
- package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +10 -1
- package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +1 -0
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +27 -22
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +12 -6
- package/packages/daemon/src/conversations.js +41 -10
- package/packages/daemon/src/introducer.js +20 -0
- package/packages/daemon/src/process.js +262 -15
- package/packages/daemon/src/providers/groove-network.js +1 -3
- package/packages/daemon/src/rotator.js +15 -3
- package/packages/daemon/src/routes/agents.js +43 -0
- package/packages/daemon/templates/lab-general.json +12 -0
- package/packages/daemon/templates/llama-cpp-setup.json +12 -0
- package/packages/gui/dist/assets/index-BKbsE_hn.js +1011 -0
- package/packages/gui/dist/assets/index-CEkPsSAm.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/agents/spawn-wizard.jsx +132 -4
- package/packages/gui/src/components/chat/chat-header.jsx +1 -8
- package/packages/gui/src/components/chat/chat-input.jsx +135 -13
- package/packages/gui/src/components/chat/chat-messages.jsx +21 -4
- package/packages/gui/src/components/chat/chat-view.jsx +6 -5
- package/packages/gui/src/components/chat/model-picker.jsx +3 -3
- package/packages/gui/src/components/lab/chat-playground.jsx +3 -3
- package/packages/gui/src/components/lab/lab-assistant.jsx +9 -3
- package/packages/gui/src/components/lab/metrics-panel.jsx +13 -3
- package/packages/gui/src/components/lab/parameter-panel.jsx +5 -5
- package/packages/gui/src/components/lab/runtime-config.jsx +1 -3
- package/packages/gui/src/components/layout/app-shell.jsx +2 -0
- package/packages/gui/src/components/layout/status-bar.jsx +24 -1
- package/packages/gui/src/components/ui/question-modal.jsx +107 -0
- package/packages/gui/src/components/ui/sheet.jsx +2 -2
- package/packages/gui/src/stores/groove.js +32 -2
- package/packages/gui/src/stores/slices/agents-slice.js +10 -1
- package/packages/gui/src/stores/slices/chat-slice.js +1 -0
- package/packages/gui/src/views/model-lab.jsx +27 -22
- package/node_modules/@groove-dev/gui/dist/assets/index-Bxc0gU06.js +0 -1006
- package/node_modules/@groove-dev/gui/dist/assets/index-C0pztKBn.css +0 -1
- package/packages/gui/dist/assets/index-Bxc0gU06.js +0 -1006
- package/packages/gui/dist/assets/index-C0pztKBn.css +0 -1
|
@@ -314,6 +314,11 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
case 'agent:exit': {
|
|
317
|
+
if (msg.status === 'waiting_for_input') {
|
|
318
|
+
const waitAgent = get().agents.find((a) => a.id === msg.agentId);
|
|
319
|
+
get().addToast('info', `${waitAgent?.name || msg.agentId.slice(0, 8)} needs your input`, 'Check the question popup below');
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
317
322
|
const agent = get().agents.find((a) => a.id === msg.agentId);
|
|
318
323
|
const name = agent?.name || msg.agentId;
|
|
319
324
|
const isKill = msg.status === 'killed' || msg.code === 143 || msg.code === 137;
|
|
@@ -415,6 +420,14 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
415
420
|
get().addChatMessage(msg.agentId, 'system', 'Agent is working — message will be delivered when it finishes.');
|
|
416
421
|
break;
|
|
417
422
|
|
|
423
|
+
case 'agent:question':
|
|
424
|
+
set((s) => ({ pendingQuestions: [...s.pendingQuestions, msg.data] }));
|
|
425
|
+
break;
|
|
426
|
+
|
|
427
|
+
case 'agent:question:resolved':
|
|
428
|
+
set((s) => ({ pendingQuestions: s.pendingQuestions.filter((q) => q.agentId !== msg.agentId) }));
|
|
429
|
+
break;
|
|
430
|
+
|
|
418
431
|
case 'ollama:pull:progress':
|
|
419
432
|
set({ ollamaPullProgress: { ...get().ollamaPullProgress, [msg.model]: { status: 'pulling', progress: msg.progress } } });
|
|
420
433
|
break;
|
|
@@ -450,6 +463,8 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
450
463
|
case 'lab:runtime:added':
|
|
451
464
|
case 'lab:runtime:updated':
|
|
452
465
|
case 'lab:runtime:removed':
|
|
466
|
+
case 'lab:runtime:started':
|
|
467
|
+
case 'lab:runtime:stopped':
|
|
453
468
|
case 'llama:server:stopped':
|
|
454
469
|
get().fetchLabRuntimes();
|
|
455
470
|
break;
|
|
@@ -888,6 +903,17 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
888
903
|
break;
|
|
889
904
|
}
|
|
890
905
|
|
|
906
|
+
case 'conversation:tool': {
|
|
907
|
+
const { conversationId, name, summary } = msg.data || msg;
|
|
908
|
+
if (!conversationId) break;
|
|
909
|
+
set((s) => {
|
|
910
|
+
const tools = { ...s.conversationActiveTools };
|
|
911
|
+
tools[conversationId] = { name: name || 'Tool', summary: summary || null, timestamp: Date.now() };
|
|
912
|
+
return { conversationActiveTools: tools };
|
|
913
|
+
});
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
916
|
+
|
|
891
917
|
case 'conversation:chunk': {
|
|
892
918
|
const { conversationId, text } = msg.data || msg;
|
|
893
919
|
if (!conversationId || !text) break;
|
|
@@ -902,7 +928,9 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
902
928
|
arr.push({ from: 'assistant', text, timestamp: Date.now() });
|
|
903
929
|
}
|
|
904
930
|
msgs[conversationId] = arr.slice(-200);
|
|
905
|
-
|
|
931
|
+
const tools = { ...s.conversationActiveTools };
|
|
932
|
+
delete tools[conversationId];
|
|
933
|
+
return { conversationMessages: msgs, streamingConversationId: conversationId, conversationActiveTools: tools };
|
|
906
934
|
});
|
|
907
935
|
break;
|
|
908
936
|
}
|
|
@@ -910,7 +938,9 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
910
938
|
case 'conversation:complete': {
|
|
911
939
|
const { conversationId } = msg.data || msg;
|
|
912
940
|
if (conversationId && get().streamingConversationId === conversationId) {
|
|
913
|
-
|
|
941
|
+
const tools = { ...get().conversationActiveTools };
|
|
942
|
+
delete tools[conversationId];
|
|
943
|
+
set({ sendingMessage: false, streamingConversationId: null, conversationActiveTools: tools });
|
|
914
944
|
}
|
|
915
945
|
if (conversationId) persistJSON('groove:conversationMessages', get().conversationMessages);
|
|
916
946
|
break;
|
|
@@ -14,6 +14,15 @@ export const createAgentsSlice = (set, get) => ({
|
|
|
14
14
|
// Track which agents are thinking (sent a message, waiting for response)
|
|
15
15
|
thinkingAgents: new Set(),
|
|
16
16
|
|
|
17
|
+
// Pending questions from agents awaiting user input
|
|
18
|
+
pendingQuestions: [],
|
|
19
|
+
|
|
20
|
+
answerQuestion(agentId, answers) {
|
|
21
|
+
return api.post(`/agents/${agentId}/answer`, { answers }).then(() => {
|
|
22
|
+
set((s) => ({ pendingQuestions: s.pendingQuestions.filter((q) => q.agentId !== agentId) }));
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
|
|
17
26
|
// ── Workspace Mode ────────────────────────────────────────
|
|
18
27
|
workspaceMode: localStorage.getItem('groove:workspaceMode') === 'true',
|
|
19
28
|
workspaceAgentId: null,
|
|
@@ -38,7 +47,7 @@ export const createAgentsSlice = (set, get) => ({
|
|
|
38
47
|
|
|
39
48
|
async spawnAgent(config) {
|
|
40
49
|
try {
|
|
41
|
-
const teamId = get().activeTeamId;
|
|
50
|
+
const teamId = config.teamId || get().activeTeamId;
|
|
42
51
|
const agent = await api.post('/agents', { ...config, teamId });
|
|
43
52
|
get().addToast('success', `Spawned ${agent.name}`);
|
|
44
53
|
return agent;
|
|
@@ -10,6 +10,7 @@ export const createChatSlice = (set, get) => ({
|
|
|
10
10
|
conversations: [],
|
|
11
11
|
activeConversationId: localStorage.getItem('groove:activeConversationId') || null,
|
|
12
12
|
conversationMessages: loadJSON('groove:conversationMessages'),
|
|
13
|
+
conversationActiveTools: {},
|
|
13
14
|
sendingMessage: false,
|
|
14
15
|
streamingConversationId: null,
|
|
15
16
|
conversationRoles: loadJSON('groove:conversationRoles'),
|
|
@@ -135,6 +135,7 @@ export default function ModelLabView() {
|
|
|
135
135
|
const labAssistantAgentId = useGrooveStore((s) => s.labAssistantAgentId);
|
|
136
136
|
const labAssistantMode = useGrooveStore((s) => s.labAssistantMode);
|
|
137
137
|
const setLabAssistantMode = useGrooveStore((s) => s.setLabAssistantMode);
|
|
138
|
+
const launchLabAssistant = useGrooveStore((s) => s.launchLabAssistant);
|
|
138
139
|
|
|
139
140
|
useEffect(() => { fetchLabRuntimes(); }, [fetchLabRuntimes]);
|
|
140
141
|
|
|
@@ -234,28 +235,32 @@ export default function ModelLabView() {
|
|
|
234
235
|
{leftCollapsed && (
|
|
235
236
|
<PanelToggle collapsed onClick={() => setLeftCollapsed(false)} side="left" />
|
|
236
237
|
)}
|
|
237
|
-
|
|
238
|
-
<
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
238
|
+
<div className="flex items-center gap-px bg-surface-2 rounded p-px">
|
|
239
|
+
<button
|
|
240
|
+
onClick={() => setLabAssistantMode(false)}
|
|
241
|
+
className={cn(
|
|
242
|
+
'px-3 py-1 text-2xs font-sans font-medium rounded-sm transition-colors cursor-pointer',
|
|
243
|
+
!labAssistantMode ? 'text-text-0 bg-surface-4' : 'text-text-3 hover:text-text-1',
|
|
244
|
+
)}
|
|
245
|
+
>
|
|
246
|
+
Playground
|
|
247
|
+
</button>
|
|
248
|
+
<button
|
|
249
|
+
onClick={() => {
|
|
250
|
+
if (labAssistantAgentId) {
|
|
251
|
+
setLabAssistantMode(true);
|
|
252
|
+
} else {
|
|
253
|
+
launchLabAssistant('lab-general');
|
|
254
|
+
}
|
|
255
|
+
}}
|
|
256
|
+
className={cn(
|
|
257
|
+
'px-3 py-1 text-2xs font-sans font-medium rounded-sm transition-colors cursor-pointer',
|
|
258
|
+
labAssistantMode ? 'text-text-0 bg-surface-4' : 'text-text-3 hover:text-text-1',
|
|
259
|
+
)}
|
|
260
|
+
>
|
|
261
|
+
Assistant
|
|
262
|
+
</button>
|
|
263
|
+
</div>
|
|
259
264
|
<div className="flex-1" />
|
|
260
265
|
{rightCollapsed && (
|
|
261
266
|
<PanelToggle collapsed onClick={() => setRightCollapsed(false)} side="right" />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.146",
|
|
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)",
|
|
@@ -11,7 +11,7 @@ import { hostname, networkInterfaces, homedir } from 'os';
|
|
|
11
11
|
import { StringDecoder } from 'string_decoder';
|
|
12
12
|
import { request as httpRequest } from 'http';
|
|
13
13
|
import { lookup as mimeLookup } from './mimetypes.js';
|
|
14
|
-
import { listProviders, getProvider, clearInstallCache, getProviderMetadata, getProviderPath, setProviderPaths } from './providers/index.js';
|
|
14
|
+
import { listProviders, getProvider, clearInstallCache, getProviderMetadata, getProviderPath, setProviderPaths, isProviderInstalled } from './providers/index.js';
|
|
15
15
|
import { OllamaProvider } from './providers/ollama.js';
|
|
16
16
|
import { ClaudeCodeProvider } from './providers/claude-code.js';
|
|
17
17
|
import { supportsSignalFlag, compareSemver, parseSemver } from './providers/groove-network.js';
|
|
@@ -1336,7 +1336,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
1336
1336
|
|
|
1337
1337
|
app.post('/api/onboarding/set-default', async (req, res) => {
|
|
1338
1338
|
const { provider, model } = req.body;
|
|
1339
|
-
const validProviders = ['claude-code', 'codex', 'gemini', 'ollama'];
|
|
1339
|
+
const validProviders = ['claude-code', 'codex', 'gemini', 'grok', 'ollama', 'local'];
|
|
1340
1340
|
if (!provider || !validProviders.includes(provider)) {
|
|
1341
1341
|
return res.status(400).json({ error: `Invalid provider. Valid: ${validProviders.join(', ')}` });
|
|
1342
1342
|
}
|
|
@@ -1721,8 +1721,9 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
1721
1721
|
app.post('/api/lab/assistant', async (req, res) => {
|
|
1722
1722
|
try {
|
|
1723
1723
|
const { backend, model } = req.body || {};
|
|
1724
|
-
|
|
1725
|
-
|
|
1724
|
+
const validBackends = ['vllm', 'tgi', 'mlx', 'llama-cpp', 'lab-general'];
|
|
1725
|
+
if (!backend || !validBackends.includes(backend)) {
|
|
1726
|
+
return res.status(400).json({ error: `backend must be one of: ${validBackends.join(', ')}` });
|
|
1726
1727
|
}
|
|
1727
1728
|
const templatePath = resolve(__dirname, `../templates/${backend}-setup.json`);
|
|
1728
1729
|
const template = JSON.parse(readFileSync(templatePath, 'utf8'));
|
|
@@ -1733,14 +1734,19 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
1733
1734
|
const desc = parts.join(', ');
|
|
1734
1735
|
prompt = `The user has selected a local model: ${desc} (id: ${model.id}).\nUse this model for setup instead of recommending a different one. If this exact model isn't available in the runtime's format, find the closest equivalent (same base model, similar quantization).\n\n${prompt}`;
|
|
1735
1736
|
}
|
|
1737
|
+
// Pick best available CLI provider: prefer user's default, fall back through tool-use capable providers
|
|
1738
|
+
const cliProviders = ['claude-code', 'codex', 'gemini'];
|
|
1739
|
+
const defaultProv = daemon.config.defaultProvider;
|
|
1740
|
+
let assistantProvider = cliProviders.includes(defaultProv) && isProviderInstalled(defaultProv)
|
|
1741
|
+
? defaultProv
|
|
1742
|
+
: cliProviders.find((p) => isProviderInstalled(p)) || 'claude-code';
|
|
1736
1743
|
const config = {
|
|
1737
1744
|
role: 'lab-assistant',
|
|
1738
1745
|
scope: agentConfig.scope || [],
|
|
1739
|
-
provider:
|
|
1746
|
+
provider: assistantProvider,
|
|
1740
1747
|
prompt,
|
|
1741
1748
|
metadata: { labAssistant: true, backend },
|
|
1742
1749
|
};
|
|
1743
|
-
if (!config.provider) config.provider = daemon.config.defaultProvider;
|
|
1744
1750
|
const agent = await daemon.processes.spawn(config);
|
|
1745
1751
|
daemon.audit.log('lab.assistant.spawn', { id: agent.id, backend });
|
|
1746
1752
|
res.status(201).json({ agentId: agent.id, backend });
|
|
@@ -475,31 +475,62 @@ export class ConversationManager {
|
|
|
475
475
|
|
|
476
476
|
try {
|
|
477
477
|
const json = JSON.parse(trimmed);
|
|
478
|
+
let matched = false;
|
|
478
479
|
if (json.type === 'assistant' && json.message?.content) {
|
|
479
480
|
for (const block of json.message.content) {
|
|
480
481
|
if (block.type === 'text' && block.text) {
|
|
481
482
|
emitChunk(block.text);
|
|
482
483
|
}
|
|
483
484
|
}
|
|
484
|
-
|
|
485
|
+
matched = true;
|
|
485
486
|
}
|
|
486
|
-
if (json.type === 'content_block_delta' && json.delta?.text) {
|
|
487
|
+
if (!matched && json.type === 'content_block_delta' && json.delta?.text) {
|
|
487
488
|
emitChunk(json.delta.text);
|
|
488
|
-
|
|
489
|
+
matched = true;
|
|
489
490
|
}
|
|
490
|
-
if (json.type === 'result' && json.result)
|
|
491
|
-
|
|
491
|
+
if (!matched && json.type === 'result' && json.result) {
|
|
492
|
+
matched = true;
|
|
493
|
+
}
|
|
494
|
+
if (!matched && json.type === 'token' && json.text != null) {
|
|
492
495
|
emitChunk(json.text);
|
|
493
|
-
|
|
496
|
+
matched = true;
|
|
494
497
|
}
|
|
495
|
-
if ((json.type === 'done' || json.type === 'complete' || json.type === 'result') && json.text) {
|
|
498
|
+
if (!matched && (json.type === 'done' || json.type === 'complete' || json.type === 'result') && json.text) {
|
|
496
499
|
emitChunk(json.text);
|
|
497
|
-
|
|
500
|
+
matched = true;
|
|
498
501
|
}
|
|
499
|
-
if (json.content?.[0]?.text) {
|
|
502
|
+
if (!matched && json.content?.[0]?.text) {
|
|
500
503
|
emitChunk(json.content[0].text);
|
|
501
|
-
|
|
504
|
+
matched = true;
|
|
505
|
+
}
|
|
506
|
+
// Fallback: use provider's parseOutput for provider-specific formats (Gemini tool_use, tool_result, Codex items, etc.)
|
|
507
|
+
if (!matched && provider.parseOutput) {
|
|
508
|
+
const parsed = provider.parseOutput(trimmed);
|
|
509
|
+
if (parsed) {
|
|
510
|
+
const blocks = Array.isArray(parsed.data) ? parsed.data : [];
|
|
511
|
+
for (const block of blocks) {
|
|
512
|
+
if (block.type === 'text' && block.text) {
|
|
513
|
+
emitChunk(block.text);
|
|
514
|
+
} else if (block.type === 'tool_use') {
|
|
515
|
+
const cmd = block.input?.command;
|
|
516
|
+
const path = block.input?.path;
|
|
517
|
+
const summary = cmd || path || (block.input && Object.keys(block.input).length > 0
|
|
518
|
+
? Object.values(block.input)[0]
|
|
519
|
+
: null);
|
|
520
|
+
if (block.name || summary) {
|
|
521
|
+
this.daemon.broadcast({
|
|
522
|
+
type: 'conversation:tool',
|
|
523
|
+
data: { conversationId: id, name: block.name || 'Tool', summary: summary ? String(summary).slice(0, 120) : null },
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (!blocks.length && typeof parsed.data === 'string' && parsed.data) {
|
|
529
|
+
emitChunk(parsed.data);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
502
532
|
}
|
|
533
|
+
continue;
|
|
503
534
|
} catch { /* not JSON */ }
|
|
504
535
|
|
|
505
536
|
if (!trimmed.startsWith('{')) {
|
|
@@ -106,6 +106,26 @@ export class Introducer {
|
|
|
106
106
|
|
|
107
107
|
if (others.length === 0) {
|
|
108
108
|
lines.push('You are the only agent on this project right now.');
|
|
109
|
+
|
|
110
|
+
// Solo agents get full authority — no team coordination, no scope limits.
|
|
111
|
+
// Business roles and planners keep their restrictions (intentional by design).
|
|
112
|
+
const NO_SOLO_EXPAND = new Set([
|
|
113
|
+
'planner', 'cmo', 'cfo', 'ea', 'support', 'analyst', 'home', 'chat', 'ambassador',
|
|
114
|
+
]);
|
|
115
|
+
if (!NO_SOLO_EXPAND.has(newAgent.role)) {
|
|
116
|
+
lines.push('');
|
|
117
|
+
lines.push('## Solo Mode');
|
|
118
|
+
lines.push('');
|
|
119
|
+
lines.push('You are working alone — no team, no scope restrictions, no coordination needed. You have full authority to do whatever the task requires:');
|
|
120
|
+
lines.push('- Install dependencies (npm install, pip install, etc.)');
|
|
121
|
+
lines.push('- Start dev servers and long-running processes when needed');
|
|
122
|
+
lines.push('- Run tests, builds, and linters');
|
|
123
|
+
lines.push('- Create, modify, or delete any project files');
|
|
124
|
+
lines.push('- Commit and manage git operations');
|
|
125
|
+
lines.push('- Perform any shell commands necessary to complete your task');
|
|
126
|
+
lines.push('');
|
|
127
|
+
lines.push('You are not limited to your role\'s typical focus area. If the task requires work outside your specialty, handle it directly.');
|
|
128
|
+
}
|
|
109
129
|
} else {
|
|
110
130
|
lines.push(`## Team (${others.length} other agent${others.length > 1 ? 's' : ''})`);
|
|
111
131
|
lines.push('');
|