@lucashca/claudecontrol 0.3.30 → 0.3.32
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/backend/agents/canUseTool.ts +0 -5
- package/backend/agents/lifecycle.ts +2 -1
- package/backend/routes/agents.ts +5 -0
- package/backend/routes/workspaces.ts +28 -1
- package/backend/types.ts +1 -0
- package/backend/workspaces.ts +7 -0
- package/frontend/src/api.ts +6 -0
- package/frontend/src/components/ChatPanel.tsx +17 -1
- package/frontend/src/components/side/RoleItem.tsx +136 -3
- package/frontend/src/types/index.ts +8 -0
- package/package.json +1 -1
- package/version.json +1 -1
|
@@ -45,11 +45,6 @@ export function createBypassCanUseTool(agentId: string, workspaceId: string) {
|
|
|
45
45
|
*/
|
|
46
46
|
export function createCanUseTool(agentId: string, workspaceId: string, isOrchestrator = false) {
|
|
47
47
|
return (toolName: string, input: Record<string, unknown>, options: CanUseToolOptions) => {
|
|
48
|
-
if (toolName === 'Task' && !isOrchestrator) {
|
|
49
|
-
const session = agents.get(agentId);
|
|
50
|
-
if (session) pushLog(session, { type: 'system', text: 'Subagente bloqueado: apenas orquestradores podem iniciar subagentes', timestamp: new Date().toISOString() });
|
|
51
|
-
return Promise.resolve({ behavior: 'deny' as const, message: 'Apenas orquestradores podem iniciar subagentes' });
|
|
52
|
-
}
|
|
53
48
|
const requestId = genId();
|
|
54
49
|
const session = agents.get(agentId);
|
|
55
50
|
|
|
@@ -368,8 +368,9 @@ export async function runAgentQuery(session: AgentSession, task: string, bypass
|
|
|
368
368
|
const isOrchestrator = session.isOrchestrator === true || session.role === getOrchestratorRole(session.workspaceId);
|
|
369
369
|
const subagents = (isOrchestrator && runtime.enableSubagents) ? buildSubagents(cwd, session.role) : {};
|
|
370
370
|
const poolContext = (!runtime.enableSubagents && isOrchestrator) ? buildPoolContext(session.workspaceId, session.id) : '';
|
|
371
|
+
const specialistCtx = !isOrchestrator && !session.canSpawnSubagents ? '[INSTRUCAO] Voce e um especialista. Execute suas tarefas diretamente usando suas proprias ferramentas. Nao use o Agent tool nem inicie subagentes.' : '';
|
|
371
372
|
// poolContext first so the model sees the delegation rules before the role instructions
|
|
372
|
-
const systemAppend = [poolContext, customContext, context].filter(Boolean).join('\n\n');
|
|
373
|
+
const systemAppend = [poolContext, specialistCtx, customContext, context].filter(Boolean).join('\n\n');
|
|
373
374
|
const prompt = task;
|
|
374
375
|
|
|
375
376
|
const abortController = new AbortController();
|
package/backend/routes/agents.ts
CHANGED
|
@@ -24,6 +24,7 @@ export async function handleAgentRoutes(
|
|
|
24
24
|
const role = body.role as string;
|
|
25
25
|
if (!role) { jsonError(res, 'role is required'); return true; }
|
|
26
26
|
|
|
27
|
+
const template = workspace.roleTemplates?.[role];
|
|
27
28
|
const task = (body.task as string) || `Voce e o agente ${role}. Leia o CLAUDE.md. Diga que esta pronto.`;
|
|
28
29
|
const bypass = body.bypass === true;
|
|
29
30
|
const spawnedBy = (body.spawnedBy as string) || undefined;
|
|
@@ -54,6 +55,9 @@ export async function handleAgentRoutes(
|
|
|
54
55
|
lastActivity: new Date().toISOString(),
|
|
55
56
|
currentTask: task,
|
|
56
57
|
log: [initEntry],
|
|
58
|
+
...(template?.model && { model: template.model }),
|
|
59
|
+
...(template?.isOrchestrator !== undefined && { isOrchestrator: template.isOrchestrator }),
|
|
60
|
+
...(template?.canSpawnSubagents !== undefined && { canSpawnSubagents: template.canSpawnSubagents }),
|
|
57
61
|
};
|
|
58
62
|
|
|
59
63
|
addAgentToIndex(agentSession);
|
|
@@ -75,6 +79,7 @@ export async function handleAgentRoutes(
|
|
|
75
79
|
const body = await parseBody(req);
|
|
76
80
|
if (body.customName !== undefined) agent.customName = body.customName as string;
|
|
77
81
|
if (body.isOrchestrator !== undefined) agent.isOrchestrator = body.isOrchestrator as boolean;
|
|
82
|
+
if (body.canSpawnSubagents !== undefined) agent.canSpawnSubagents = body.canSpawnSubagents as boolean;
|
|
78
83
|
persistAgents();
|
|
79
84
|
broadcast({ type: 'agent_updated', agentId: agent.id, workspaceId: agent.workspaceId, agent });
|
|
80
85
|
json(res, agent);
|
|
@@ -3,7 +3,7 @@ import { readdirSync, statSync, mkdirSync } from 'fs';
|
|
|
3
3
|
import { resolve } from 'path';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
5
|
import { listExistingSessions } from '../sessions.js';
|
|
6
|
-
import { loadWorkspaces, addWorkspace, removeWorkspace, updateWorkspace, getWorkspaceById, type Workspace } from '../workspaces.js';
|
|
6
|
+
import { loadWorkspaces, addWorkspace, removeWorkspace, updateWorkspace, getWorkspaceById, type Workspace, type RoleTemplate } from '../workspaces.js';
|
|
7
7
|
import { json, jsonError, parseBody, matchRoute } from '../http.js';
|
|
8
8
|
import { broadcast } from '../ws.js';
|
|
9
9
|
import { genId, agentsByWorkspace, removeAgentFromIndex, getAgentsForWorkspace } from '../agents/store.js';
|
|
@@ -142,6 +142,33 @@ export async function handleWorkspaceRoutes(
|
|
|
142
142
|
return true;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
// ── GET /api/workspaces/:id/role-template/:role ──
|
|
146
|
+
// ── PUT /api/workspaces/:id/role-template/:role ──
|
|
147
|
+
const roleTemplateParams = matchRoute(path, '/api/workspaces/:id/role-template/:role');
|
|
148
|
+
if (roleTemplateParams) {
|
|
149
|
+
const workspace = getWorkspaceById(roleTemplateParams.id);
|
|
150
|
+
if (!workspace) { jsonError(res, 'Workspace not found', 404); return true; }
|
|
151
|
+
|
|
152
|
+
if (method === 'GET') {
|
|
153
|
+
const tpl = workspace.roleTemplates?.[roleTemplateParams.role] ?? {};
|
|
154
|
+
json(res, tpl);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (method === 'PUT') {
|
|
159
|
+
const body = await parseBody(req);
|
|
160
|
+
const tpl: RoleTemplate = {
|
|
161
|
+
model: (body.model as string) || undefined,
|
|
162
|
+
isOrchestrator: body.isOrchestrator as boolean | undefined,
|
|
163
|
+
canSpawnSubagents: body.canSpawnSubagents as boolean | undefined,
|
|
164
|
+
};
|
|
165
|
+
const templates = { ...(workspace.roleTemplates || {}), [roleTemplateParams.role]: tpl };
|
|
166
|
+
updateWorkspace(roleTemplateParams.id, { roleTemplates: templates });
|
|
167
|
+
json(res, tpl);
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
145
172
|
// ── DELETE /api/workspaces/:id ──
|
|
146
173
|
const wsDeleteParams = matchRoute(path, '/api/workspaces/:id');
|
|
147
174
|
if (wsDeleteParams && method === 'DELETE') {
|
package/backend/types.ts
CHANGED
|
@@ -92,4 +92,5 @@ export interface AgentSession {
|
|
|
92
92
|
lastContextTokens?: number; // cache_read_input_tokens from last turn = current context size
|
|
93
93
|
model?: string; // per-agent model override
|
|
94
94
|
isOrchestrator?: boolean; // only orchestrators can delegate to other agents
|
|
95
|
+
canSpawnSubagents?: boolean; // allows using Agent tool even for non-orchestrators
|
|
95
96
|
}
|
package/backend/workspaces.ts
CHANGED
|
@@ -6,6 +6,12 @@ function workspacesFile(): string {
|
|
|
6
6
|
return resolve(getDataDir(), 'workspaces.json');
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export interface RoleTemplate {
|
|
10
|
+
model?: string;
|
|
11
|
+
isOrchestrator?: boolean;
|
|
12
|
+
canSpawnSubagents?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
export interface Workspace {
|
|
10
16
|
id: string;
|
|
11
17
|
name: string;
|
|
@@ -13,6 +19,7 @@ export interface Workspace {
|
|
|
13
19
|
agents: string[]; // roles disponiveis neste workspace
|
|
14
20
|
color: string;
|
|
15
21
|
orchestratorRole?: string; // role that acts as orchestrator (gets subagent tools + pool context)
|
|
22
|
+
roleTemplates?: Record<string, RoleTemplate>; // per-role default config
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
const DEFAULT_WORKSPACES: Workspace[] = [];
|
package/frontend/src/api.ts
CHANGED
|
@@ -116,6 +116,12 @@ export const api = {
|
|
|
116
116
|
getClaudeAccount: () => get('/settings/claude-account'),
|
|
117
117
|
openVSCode: (wsId: string) => post(`/workspaces/${wsId}/open-vscode`),
|
|
118
118
|
|
|
119
|
+
getRoleTemplate: (wsId: string, role: string) =>
|
|
120
|
+
get(`/workspaces/${wsId}/role-template/${encodeURIComponent(role)}`) as Promise<import('./types').RoleTemplate>,
|
|
121
|
+
|
|
122
|
+
updateRoleTemplate: (wsId: string, role: string, template: import('./types').RoleTemplate) =>
|
|
123
|
+
put(`/workspaces/${wsId}/role-template/${encodeURIComponent(role)}`, template) as Promise<import('./types').RoleTemplate>,
|
|
124
|
+
|
|
119
125
|
respondSpawnRequest: (requestId: string, approved: boolean, maxCount?: number, approvedRoles?: string[]) =>
|
|
120
126
|
post(`/agents/spawn-request/${requestId}/respond`, { approved, maxCount, approvedRoles }),
|
|
121
127
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LucideIcon } from 'lucide-react';
|
|
2
|
-
import { Blocks, Code2, TestTube2, Cloud, Database, Layout, ChevronRight, Loader2, GitBranch, Square } from 'lucide-react';
|
|
2
|
+
import { Blocks, Code2, TestTube2, Cloud, Database, Layout, ChevronRight, Loader2, GitBranch, Square, Network } from 'lucide-react';
|
|
3
3
|
import { useAtomValue } from 'jotai';
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
import { agentsMapAtom } from '@/atoms';
|
|
@@ -59,6 +59,10 @@ export function ChatPanel({ agentId, compact }: { agentId: string; compact?: boo
|
|
|
59
59
|
api.patchAgent(agentId, { isOrchestrator: !agent!.isOrchestrator }).catch(() => {});
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
function handleToggleSubagents() {
|
|
63
|
+
api.patchAgent(agentId, { canSpawnSubagents: !agent!.canSpawnSubagents }).catch(() => {});
|
|
64
|
+
}
|
|
65
|
+
|
|
62
66
|
return (
|
|
63
67
|
<div className="flex flex-1 flex-col min-w-0 min-h-0 h-full overflow-hidden bg-cc-panel">
|
|
64
68
|
{/* Header */}
|
|
@@ -79,6 +83,18 @@ export function ChatPanel({ agentId, compact }: { agentId: string; compact?: boo
|
|
|
79
83
|
</div>
|
|
80
84
|
</div>
|
|
81
85
|
<div className="flex items-center gap-2">
|
|
86
|
+
<Tooltip text={agent.canSpawnSubagents ? 'Subagentes ativados' : 'Ativar subagentes'} position="bottom">
|
|
87
|
+
<button
|
|
88
|
+
onClick={handleToggleSubagents}
|
|
89
|
+
className={`inline-flex items-center justify-center w-6 h-6 rounded-md border-none cursor-pointer transition-colors ${
|
|
90
|
+
agent.canSpawnSubagents
|
|
91
|
+
? 'text-cc-info bg-cc-info/15 hover:bg-cc-info/25'
|
|
92
|
+
: 'text-cc-muted hover:text-cc-text-secondary hover:bg-cc-surface'
|
|
93
|
+
}`}
|
|
94
|
+
>
|
|
95
|
+
<Network size={12} />
|
|
96
|
+
</button>
|
|
97
|
+
</Tooltip>
|
|
82
98
|
<Tooltip text={agent.isOrchestrator ? 'Orquestrador — pode delegar' : 'Marcar como orquestrador'} position="bottom">
|
|
83
99
|
<button
|
|
84
100
|
onClick={handleToggleOrchestrator}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
2
|
import type { LucideIcon } from 'lucide-react';
|
|
3
3
|
import {
|
|
4
4
|
Blocks, Code2, TestTube2, Cloud, Database, Layout,
|
|
5
|
-
MessageCircle, Bot, Plus, ArrowRightFromLine,
|
|
5
|
+
MessageCircle, Bot, Plus, ArrowRightFromLine, Settings, Check,
|
|
6
6
|
} from 'lucide-react';
|
|
7
|
+
import { useAtomValue } from 'jotai';
|
|
8
|
+
import { getDefaultStore } from 'jotai';
|
|
7
9
|
import { AGENT_LABELS } from '@/types';
|
|
8
|
-
import type { RoleInfo } from '@/types';
|
|
10
|
+
import type { RoleInfo, RoleTemplate } from '@/types';
|
|
11
|
+
import { activeWorkspaceAtom, workspacesAtom } from '@/atoms';
|
|
12
|
+
import { api } from '@/api';
|
|
13
|
+
import { Dialog } from '@/ui';
|
|
14
|
+
|
|
15
|
+
const store = getDefaultStore();
|
|
9
16
|
|
|
10
17
|
const ROLE_ICON: Record<string, LucideIcon> = {
|
|
11
18
|
arquiteto: Blocks, dev: Code2, qa: TestTube2,
|
|
@@ -13,6 +20,13 @@ const ROLE_ICON: Record<string, LucideIcon> = {
|
|
|
13
20
|
chat: MessageCircle,
|
|
14
21
|
};
|
|
15
22
|
|
|
23
|
+
const MODEL_OPTIONS = [
|
|
24
|
+
{ value: '', label: 'Padrão do sistema' },
|
|
25
|
+
{ value: 'claude-opus-4-5', label: 'Opus 4.5 — mais capaz' },
|
|
26
|
+
{ value: 'claude-sonnet-4-5', label: 'Sonnet 4.5 — equilibrado' },
|
|
27
|
+
{ value: 'claude-haiku-4-5', label: 'Haiku 4.5 — mais rápido' },
|
|
28
|
+
];
|
|
29
|
+
|
|
16
30
|
function label(name: string) {
|
|
17
31
|
return AGENT_LABELS[name] || name.replace(/^agent-/, '').replace(/-/g, ' ');
|
|
18
32
|
}
|
|
@@ -23,10 +37,114 @@ interface RoleItemProps {
|
|
|
23
37
|
onInject: (name: string, type?: 'agent' | 'command') => void;
|
|
24
38
|
}
|
|
25
39
|
|
|
40
|
+
function Toggle({ value, onChange, children }: { value: boolean; onChange: (v: boolean) => void; children: string }) {
|
|
41
|
+
return (
|
|
42
|
+
<label className="flex items-center justify-between cursor-pointer py-0.5">
|
|
43
|
+
<span className="text-sm text-cc-text-secondary">{children}</span>
|
|
44
|
+
<div
|
|
45
|
+
onClick={() => onChange(!value)}
|
|
46
|
+
className={`w-8 h-4.5 rounded-full transition-colors flex items-center px-0.5 cursor-pointer shrink-0 ${value ? 'bg-cc-accent' : 'bg-cc-border'}`}
|
|
47
|
+
style={{ height: '18px', width: '32px' }}
|
|
48
|
+
>
|
|
49
|
+
<div className={`w-3 h-3 rounded-full bg-white transition-transform ${value ? 'translate-x-3.5' : 'translate-x-0'}`} />
|
|
50
|
+
</div>
|
|
51
|
+
</label>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function TemplateDialog({ roleName, wsId, open, onClose }: {
|
|
56
|
+
roleName: string;
|
|
57
|
+
wsId: string;
|
|
58
|
+
open: boolean;
|
|
59
|
+
onClose: () => void;
|
|
60
|
+
}) {
|
|
61
|
+
const [model, setModel] = useState('');
|
|
62
|
+
const [isOrch, setIsOrch] = useState(false);
|
|
63
|
+
const [subagents, setSubagents] = useState(false);
|
|
64
|
+
const [loading, setLoading] = useState(false);
|
|
65
|
+
const [saving, setSaving] = useState(false);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!open) return;
|
|
69
|
+
setLoading(true);
|
|
70
|
+
api.getRoleTemplate(wsId, roleName).then(tpl => {
|
|
71
|
+
setModel(tpl.model || '');
|
|
72
|
+
setIsOrch(tpl.isOrchestrator ?? false);
|
|
73
|
+
setSubagents(tpl.canSpawnSubagents ?? false);
|
|
74
|
+
}).finally(() => setLoading(false));
|
|
75
|
+
}, [open, wsId, roleName]);
|
|
76
|
+
|
|
77
|
+
async function save() {
|
|
78
|
+
setSaving(true);
|
|
79
|
+
try {
|
|
80
|
+
const tpl: RoleTemplate = {
|
|
81
|
+
model: model || undefined,
|
|
82
|
+
isOrchestrator: isOrch,
|
|
83
|
+
canSpawnSubagents: subagents,
|
|
84
|
+
};
|
|
85
|
+
const saved = await api.updateRoleTemplate(wsId, roleName, tpl);
|
|
86
|
+
const ws = store.get(workspacesAtom);
|
|
87
|
+
store.set(workspacesAtom, ws.map(w =>
|
|
88
|
+
w.id === wsId ? { ...w, roleTemplates: { ...(w.roleTemplates || {}), [roleName]: saved } } : w,
|
|
89
|
+
));
|
|
90
|
+
onClose();
|
|
91
|
+
} finally {
|
|
92
|
+
setSaving(false);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Dialog open={open} onClose={onClose} title={`Template — ${label(roleName)}`}>
|
|
98
|
+
{loading ? (
|
|
99
|
+
<div className="py-6 flex justify-center text-sm text-cc-muted">Carregando...</div>
|
|
100
|
+
) : (
|
|
101
|
+
<div className="flex flex-col gap-4">
|
|
102
|
+
<div className="flex flex-col gap-1.5">
|
|
103
|
+
<label className="text-xs font-medium text-cc-text-secondary">Modelo</label>
|
|
104
|
+
<select
|
|
105
|
+
value={model}
|
|
106
|
+
onChange={e => setModel(e.target.value)}
|
|
107
|
+
className="text-sm bg-cc-bg border border-cc-border rounded-lg px-3 py-2 text-cc-text outline-none focus:border-cc-accent/60 cursor-pointer"
|
|
108
|
+
>
|
|
109
|
+
{MODEL_OPTIONS.map(o => (
|
|
110
|
+
<option key={o.value} value={o.value}>{o.label}</option>
|
|
111
|
+
))}
|
|
112
|
+
</select>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div className="flex flex-col gap-2 border-t border-cc-border/40 pt-3">
|
|
116
|
+
<Toggle value={isOrch} onChange={setIsOrch}>Orquestrador — pode delegar tarefas</Toggle>
|
|
117
|
+
<Toggle value={subagents} onChange={setSubagents}>Subagentes — pode usar Agent tool</Toggle>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div className="flex justify-end gap-2 pt-1">
|
|
121
|
+
<button
|
|
122
|
+
onClick={onClose}
|
|
123
|
+
className="px-4 py-1.5 rounded-lg text-sm text-cc-muted hover:text-cc-text-secondary hover:bg-cc-surface transition-colors"
|
|
124
|
+
>
|
|
125
|
+
Cancelar
|
|
126
|
+
</button>
|
|
127
|
+
<button
|
|
128
|
+
onClick={save}
|
|
129
|
+
disabled={saving}
|
|
130
|
+
className="flex items-center gap-1.5 px-4 py-1.5 rounded-lg text-sm bg-cc-accent text-cc-bg font-medium hover:opacity-90 transition-opacity disabled:opacity-50"
|
|
131
|
+
>
|
|
132
|
+
<Check size={13} /> {saving ? 'Salvando...' : 'Salvar'}
|
|
133
|
+
</button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
</Dialog>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
26
141
|
export function RoleItem({ role, onSpawn, onInject }: RoleItemProps) {
|
|
27
142
|
const [open, setOpen] = useState(false);
|
|
143
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
144
|
+
const workspace = useAtomValue(activeWorkspaceAtom);
|
|
28
145
|
const Icon = ROLE_ICON[role.name] || Bot;
|
|
29
146
|
const t = role.type as 'agent' | 'command';
|
|
147
|
+
const hasTemplate = workspace?.roleTemplates?.[role.name] !== undefined;
|
|
30
148
|
|
|
31
149
|
return (
|
|
32
150
|
<div>
|
|
@@ -36,6 +154,7 @@ export function RoleItem({ role, onSpawn, onInject }: RoleItemProps) {
|
|
|
36
154
|
>
|
|
37
155
|
<Icon size={12} className="text-cc-muted shrink-0" />
|
|
38
156
|
<span className="flex-1 text-[11px] truncate">{label(role.name)}</span>
|
|
157
|
+
{hasTemplate && <span className="w-1.5 h-1.5 rounded-full bg-cc-accent/60 shrink-0" title="Template configurado" />}
|
|
39
158
|
</div>
|
|
40
159
|
{open && (
|
|
41
160
|
<div className="ml-5 mb-1 flex flex-col gap-0.5">
|
|
@@ -51,8 +170,22 @@ export function RoleItem({ role, onSpawn, onInject }: RoleItemProps) {
|
|
|
51
170
|
>
|
|
52
171
|
<Plus size={10} /> Novo chat
|
|
53
172
|
</button>
|
|
173
|
+
<button
|
|
174
|
+
onClick={() => { setDialogOpen(true); setOpen(false); }}
|
|
175
|
+
className="flex items-center gap-1.5 px-2 py-1 rounded text-[10px] text-cc-muted hover:text-cc-text-secondary hover:bg-cc-surface transition-colors text-left"
|
|
176
|
+
>
|
|
177
|
+
<Settings size={10} /> Configurar template
|
|
178
|
+
</button>
|
|
54
179
|
</div>
|
|
55
180
|
)}
|
|
181
|
+
{workspace && (
|
|
182
|
+
<TemplateDialog
|
|
183
|
+
roleName={role.name}
|
|
184
|
+
wsId={workspace.id}
|
|
185
|
+
open={dialogOpen}
|
|
186
|
+
onClose={() => setDialogOpen(false)}
|
|
187
|
+
/>
|
|
188
|
+
)}
|
|
56
189
|
</div>
|
|
57
190
|
);
|
|
58
191
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export interface RoleTemplate {
|
|
2
|
+
model?: string;
|
|
3
|
+
isOrchestrator?: boolean;
|
|
4
|
+
canSpawnSubagents?: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
export interface Workspace {
|
|
2
8
|
id: string;
|
|
3
9
|
name: string;
|
|
@@ -5,6 +11,7 @@ export interface Workspace {
|
|
|
5
11
|
agents: string[];
|
|
6
12
|
color: string;
|
|
7
13
|
orchestratorRole?: string;
|
|
14
|
+
roleTemplates?: Record<string, RoleTemplate>;
|
|
8
15
|
}
|
|
9
16
|
|
|
10
17
|
export interface Agent {
|
|
@@ -19,6 +26,7 @@ export interface Agent {
|
|
|
19
26
|
customName?: string;
|
|
20
27
|
closed?: boolean;
|
|
21
28
|
isOrchestrator?: boolean;
|
|
29
|
+
canSpawnSubagents?: boolean;
|
|
22
30
|
todos?: Array<{ content: string; status: 'pending' | 'in_progress' | 'completed'; activeForm: string }>;
|
|
23
31
|
}
|
|
24
32
|
|
package/package.json
CHANGED
package/version.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.3.
|
|
1
|
+
{"version":"0.3.32","build":"2026-04-04T00:22:18.525Z"}
|