attocode 0.1.0 → 0.1.3
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/CHANGELOG.md +64 -1
- package/README.md +138 -10
- package/dist/src/agent.d.ts +75 -1
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +700 -25
- package/dist/src/agent.js.map +1 -1
- package/dist/src/commands/agents-commands.d.ts +24 -0
- package/dist/src/commands/agents-commands.d.ts.map +1 -0
- package/dist/src/commands/agents-commands.js +284 -0
- package/dist/src/commands/agents-commands.js.map +1 -0
- package/dist/src/commands/handler.d.ts.map +1 -1
- package/dist/src/commands/handler.js +135 -19
- package/dist/src/commands/handler.js.map +1 -1
- package/dist/src/commands/init-commands.d.ts +35 -0
- package/dist/src/commands/init-commands.d.ts.map +1 -0
- package/dist/src/commands/init-commands.js +187 -0
- package/dist/src/commands/init-commands.js.map +1 -0
- package/dist/src/commands/skills-commands.d.ts +26 -0
- package/dist/src/commands/skills-commands.d.ts.map +1 -0
- package/dist/src/commands/skills-commands.js +309 -0
- package/dist/src/commands/skills-commands.js.map +1 -0
- package/dist/src/commands/types.d.ts +13 -2
- package/dist/src/commands/types.d.ts.map +1 -1
- package/dist/src/defaults.d.ts +29 -1
- package/dist/src/defaults.d.ts.map +1 -1
- package/dist/src/defaults.js +66 -0
- package/dist/src/defaults.js.map +1 -1
- package/dist/src/integrations/agent-registry.d.ts +68 -2
- package/dist/src/integrations/agent-registry.d.ts.map +1 -1
- package/dist/src/integrations/agent-registry.js +230 -23
- package/dist/src/integrations/agent-registry.js.map +1 -1
- package/dist/src/integrations/cancellation.d.ts +5 -0
- package/dist/src/integrations/cancellation.d.ts.map +1 -1
- package/dist/src/integrations/cancellation.js +7 -0
- package/dist/src/integrations/cancellation.js.map +1 -1
- package/dist/src/integrations/capabilities.d.ts +160 -0
- package/dist/src/integrations/capabilities.d.ts.map +1 -0
- package/dist/src/integrations/capabilities.js +426 -0
- package/dist/src/integrations/capabilities.js.map +1 -0
- package/dist/src/integrations/context-engineering.d.ts +6 -1
- package/dist/src/integrations/context-engineering.d.ts.map +1 -1
- package/dist/src/integrations/context-engineering.js +7 -0
- package/dist/src/integrations/context-engineering.js.map +1 -1
- package/dist/src/integrations/index.d.ts +12 -2
- package/dist/src/integrations/index.d.ts.map +1 -1
- package/dist/src/integrations/index.js +22 -2
- package/dist/src/integrations/index.js.map +1 -1
- package/dist/src/integrations/interactive-planning.d.ts +322 -0
- package/dist/src/integrations/interactive-planning.d.ts.map +1 -0
- package/dist/src/integrations/interactive-planning.js +655 -0
- package/dist/src/integrations/interactive-planning.js.map +1 -0
- package/dist/src/integrations/learning-store.d.ts +291 -0
- package/dist/src/integrations/learning-store.d.ts.map +1 -0
- package/dist/src/integrations/learning-store.js +640 -0
- package/dist/src/integrations/learning-store.js.map +1 -0
- package/dist/src/integrations/pending-plan.d.ts.map +1 -1
- package/dist/src/integrations/pending-plan.js +69 -10
- package/dist/src/integrations/pending-plan.js.map +1 -1
- package/dist/src/integrations/skill-executor.d.ts +113 -0
- package/dist/src/integrations/skill-executor.d.ts.map +1 -0
- package/dist/src/integrations/skill-executor.js +270 -0
- package/dist/src/integrations/skill-executor.js.map +1 -0
- package/dist/src/integrations/skills.d.ts +98 -7
- package/dist/src/integrations/skills.d.ts.map +1 -1
- package/dist/src/integrations/skills.js +210 -11
- package/dist/src/integrations/skills.js.map +1 -1
- package/dist/src/providers/circuit-breaker.d.ts +180 -0
- package/dist/src/providers/circuit-breaker.d.ts.map +1 -0
- package/dist/src/providers/circuit-breaker.js +349 -0
- package/dist/src/providers/circuit-breaker.js.map +1 -0
- package/dist/src/providers/fallback-chain.d.ts +194 -0
- package/dist/src/providers/fallback-chain.d.ts.map +1 -0
- package/dist/src/providers/fallback-chain.js +363 -0
- package/dist/src/providers/fallback-chain.js.map +1 -0
- package/dist/src/providers/llm-resilience.d.ts +126 -0
- package/dist/src/providers/llm-resilience.d.ts.map +1 -0
- package/dist/src/providers/llm-resilience.js +261 -0
- package/dist/src/providers/llm-resilience.js.map +1 -0
- package/dist/src/providers/resilient-provider.d.ts +124 -0
- package/dist/src/providers/resilient-provider.d.ts.map +1 -0
- package/dist/src/providers/resilient-provider.js +242 -0
- package/dist/src/providers/resilient-provider.js.map +1 -0
- package/dist/src/tricks/recursive-context.d.ts +296 -0
- package/dist/src/tricks/recursive-context.d.ts.map +1 -0
- package/dist/src/tricks/recursive-context.js +518 -0
- package/dist/src/tricks/recursive-context.js.map +1 -0
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +226 -29
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/components/ApprovalDialog.d.ts.map +1 -1
- package/dist/src/tui/components/ApprovalDialog.js +1 -1
- package/dist/src/tui/components/ApprovalDialog.js.map +1 -1
- package/dist/src/tui/index.d.ts +1 -0
- package/dist/src/tui/index.d.ts.map +1 -1
- package/dist/src/tui/index.js +2 -0
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/transparency-aggregator.d.ts +100 -0
- package/dist/src/tui/transparency-aggregator.d.ts.map +1 -0
- package/dist/src/tui/transparency-aggregator.js +234 -0
- package/dist/src/tui/transparency-aggregator.js.map +1 -0
- package/dist/src/types.d.ts +155 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/src/tui/app.js
CHANGED
|
@@ -13,6 +13,40 @@ import { Box, Text, useApp, useInput, Static } from 'ink';
|
|
|
13
13
|
import { getTheme, getThemeNames } from './theme/index.js';
|
|
14
14
|
import { ControlledCommandPalette } from './input/CommandPalette.js';
|
|
15
15
|
import { ApprovalDialog } from './components/ApprovalDialog.js';
|
|
16
|
+
import { TransparencyAggregator } from './transparency-aggregator.js';
|
|
17
|
+
import { handleSkillsCommand } from '../commands/skills-commands.js';
|
|
18
|
+
import { handleAgentsCommand } from '../commands/agents-commands.js';
|
|
19
|
+
import { handleInitCommand } from '../commands/init-commands.js';
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// PATTERN GENERATION FOR ALWAYS-ALLOW
|
|
22
|
+
// =============================================================================
|
|
23
|
+
/**
|
|
24
|
+
* Generates an approval pattern for matching future requests.
|
|
25
|
+
* Pattern format: `tool:key_argument`
|
|
26
|
+
*
|
|
27
|
+
* Matching strategy by tool type:
|
|
28
|
+
* - bash: match on base command (first 2 tokens, e.g., "npm test")
|
|
29
|
+
* - file operations: match on file path
|
|
30
|
+
* - other tools: match on first string argument
|
|
31
|
+
*/
|
|
32
|
+
function generateApprovalPattern(request) {
|
|
33
|
+
const tool = request.tool || request.action || 'unknown';
|
|
34
|
+
const args = request.args || {};
|
|
35
|
+
// Tool-specific key extraction
|
|
36
|
+
if (tool === 'bash' && typeof args.command === 'string') {
|
|
37
|
+
// Extract base command: "npm test --coverage" → "npm test"
|
|
38
|
+
const parts = args.command.trim().split(/\s+/);
|
|
39
|
+
const baseCmd = parts.slice(0, 2).join(' '); // First 2 tokens
|
|
40
|
+
return `bash:${baseCmd}`;
|
|
41
|
+
}
|
|
42
|
+
if (['write_file', 'edit_file', 'read_file'].includes(tool)) {
|
|
43
|
+
const path = (args.path || args.file_path || '');
|
|
44
|
+
return `${tool}:${path}`;
|
|
45
|
+
}
|
|
46
|
+
// Default: tool + first string argument
|
|
47
|
+
const firstStringArg = Object.values(args).find(v => typeof v === 'string');
|
|
48
|
+
return `${tool}:${firstStringArg || ''}`;
|
|
49
|
+
}
|
|
16
50
|
const MessageItem = memo(function MessageItem({ msg, colors }) {
|
|
17
51
|
const isUser = msg.role === 'user';
|
|
18
52
|
const isAssistant = msg.role === 'assistant';
|
|
@@ -84,26 +118,26 @@ const ToolCallItem = memo(function ToolCallItem({ tc, expanded, colors }) {
|
|
|
84
118
|
}
|
|
85
119
|
return (_jsxs(Box, { marginLeft: 2, gap: 1, children: [_jsx(Text, { color: statusColor, children: icon }), _jsx(Text, { color: "#DDA0DD", bold: true, children: tc.name }), argsStr ? _jsx(Text, { color: colors.textMuted, dimColor: true, children: argsStr }) : null, tc.duration ? _jsxs(Text, { color: colors.textMuted, dimColor: true, children: ["(", tc.duration, "ms)"] }) : null] }));
|
|
86
120
|
});
|
|
87
|
-
const MemoizedInputArea = memo(function MemoizedInputArea({ onSubmit, disabled, borderColor, textColor, cursorColor, onCtrlC, onCtrlL, onCtrlP, onEscape, onToggleToolExpand, onToggleThinking, onPageUp, onPageDown, onHome, onEnd, commandPaletteOpen, onCommandPaletteInput, approvalDialogOpen, approvalDenyReasonMode, onApprovalApprove, onApprovalDeny, onApprovalDenyWithReason, onApprovalCancelDenyReason, onApprovalDenyReasonInput, }) {
|
|
121
|
+
const MemoizedInputArea = memo(function MemoizedInputArea({ onSubmit, disabled, borderColor, textColor, cursorColor, onCtrlC, onCtrlL, onCtrlP, onEscape, onToggleToolExpand, onToggleThinking, onToggleTransparency, onPageUp, onPageDown, onHome, onEnd, commandPaletteOpen, onCommandPaletteInput, approvalDialogOpen, approvalDenyReasonMode, onApprovalApprove, onApprovalAlwaysAllow, onApprovalDeny, onApprovalDenyWithReason, onApprovalCancelDenyReason, onApprovalDenyReasonInput, }) {
|
|
88
122
|
const [value, setValue] = useState('');
|
|
89
123
|
const [cursorPos, setCursorPos] = useState(0);
|
|
90
124
|
// Store callbacks in refs so useInput doesn't re-subscribe on prop changes
|
|
91
125
|
const callbacksRef = useRef({
|
|
92
126
|
onSubmit, onCtrlC, onCtrlL, onCtrlP, onEscape,
|
|
93
|
-
onToggleToolExpand, onToggleThinking,
|
|
127
|
+
onToggleToolExpand, onToggleThinking, onToggleTransparency,
|
|
94
128
|
onPageUp, onPageDown, onHome, onEnd,
|
|
95
129
|
commandPaletteOpen, onCommandPaletteInput,
|
|
96
130
|
approvalDialogOpen, approvalDenyReasonMode,
|
|
97
|
-
onApprovalApprove, onApprovalDeny, onApprovalDenyWithReason,
|
|
131
|
+
onApprovalApprove, onApprovalAlwaysAllow, onApprovalDeny, onApprovalDenyWithReason,
|
|
98
132
|
onApprovalCancelDenyReason, onApprovalDenyReasonInput,
|
|
99
133
|
});
|
|
100
134
|
callbacksRef.current = {
|
|
101
135
|
onSubmit, onCtrlC, onCtrlL, onCtrlP, onEscape,
|
|
102
|
-
onToggleToolExpand, onToggleThinking,
|
|
136
|
+
onToggleToolExpand, onToggleThinking, onToggleTransparency,
|
|
103
137
|
onPageUp, onPageDown, onHome, onEnd,
|
|
104
138
|
commandPaletteOpen, onCommandPaletteInput,
|
|
105
139
|
approvalDialogOpen, approvalDenyReasonMode,
|
|
106
|
-
onApprovalApprove, onApprovalDeny, onApprovalDenyWithReason,
|
|
140
|
+
onApprovalApprove, onApprovalAlwaysAllow, onApprovalDeny, onApprovalDenyWithReason,
|
|
107
141
|
onApprovalCancelDenyReason, onApprovalDenyReasonInput,
|
|
108
142
|
};
|
|
109
143
|
const disabledRef = useRef(disabled);
|
|
@@ -137,6 +171,11 @@ const MemoizedInputArea = memo(function MemoizedInputArea({ onSubmit, disabled,
|
|
|
137
171
|
cb.onToggleThinking?.();
|
|
138
172
|
return;
|
|
139
173
|
}
|
|
174
|
+
// Alt+I / Option+I - Toggle transparency panel
|
|
175
|
+
if (input === '\u00ee' || input === '\u0131' || (key.meta && input === 'i')) {
|
|
176
|
+
cb.onToggleTransparency?.();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
140
179
|
// Command palette keyboard handling (when open)
|
|
141
180
|
if (cb.commandPaletteOpen && cb.onCommandPaletteInput) {
|
|
142
181
|
cb.onCommandPaletteInput(input, key);
|
|
@@ -165,6 +204,10 @@ const MemoizedInputArea = memo(function MemoizedInputArea({ onSubmit, disabled,
|
|
|
165
204
|
cb.onApprovalApprove?.();
|
|
166
205
|
return;
|
|
167
206
|
}
|
|
207
|
+
if (input === 'a' || input === 'A') {
|
|
208
|
+
cb.onApprovalAlwaysAllow?.();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
168
211
|
if (input === 'n' || input === 'N') {
|
|
169
212
|
cb.onApprovalDeny?.();
|
|
170
213
|
return;
|
|
@@ -264,6 +307,7 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
264
307
|
// Display toggles
|
|
265
308
|
const [toolCallsExpanded, setToolCallsExpanded] = useState(false);
|
|
266
309
|
const [showThinking, setShowThinking] = useState(true);
|
|
310
|
+
const [transparencyExpanded, setTransparencyExpanded] = useState(false);
|
|
267
311
|
// Command palette state
|
|
268
312
|
const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
|
|
269
313
|
const [commandPaletteQuery, setCommandPaletteQuery] = useState('');
|
|
@@ -272,6 +316,11 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
272
316
|
const [pendingApproval, setPendingApproval] = useState(null);
|
|
273
317
|
const [denyReasonMode, setDenyReasonMode] = useState(false);
|
|
274
318
|
const [denyReason, setDenyReason] = useState('');
|
|
319
|
+
// Session-scoped always-allowed patterns (e.g., "bash:npm test", "write_file:/path")
|
|
320
|
+
const [alwaysAllowed, setAlwaysAllowed] = useState(new Set());
|
|
321
|
+
// Transparency state
|
|
322
|
+
const [transparencyState, setTransparencyState] = useState(null);
|
|
323
|
+
const transparencyAggregatorRef = useRef(null);
|
|
275
324
|
// Refs for stable callbacks
|
|
276
325
|
const isProcessingRef = useRef(isProcessing);
|
|
277
326
|
const messagesLengthRef = useRef(messages.length);
|
|
@@ -292,10 +341,18 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
292
341
|
// =========================================================================
|
|
293
342
|
// Handle approval request from bridge
|
|
294
343
|
const handleApprovalRequest = useCallback((request) => {
|
|
344
|
+
// Check if this matches an always-allowed pattern
|
|
345
|
+
const pattern = generateApprovalPattern(request);
|
|
346
|
+
if (alwaysAllowed.has(pattern)) {
|
|
347
|
+
// Auto-approve without showing dialog
|
|
348
|
+
approvalBridge?.resolve({ approved: true });
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
// Show dialog as normal
|
|
295
352
|
setPendingApproval(request);
|
|
296
353
|
setDenyReasonMode(false);
|
|
297
354
|
setDenyReason('');
|
|
298
|
-
}, []);
|
|
355
|
+
}, [alwaysAllowed, approvalBridge]);
|
|
299
356
|
// Approve the pending request
|
|
300
357
|
const handleApprove = useCallback(() => {
|
|
301
358
|
if (approvalBridge && pendingApprovalRef.current) {
|
|
@@ -314,6 +371,17 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
314
371
|
setDenyReason('');
|
|
315
372
|
}
|
|
316
373
|
}, [approvalBridge]);
|
|
374
|
+
// Always allow this pattern for the rest of the session
|
|
375
|
+
const handleAlwaysAllow = useCallback(() => {
|
|
376
|
+
if (approvalBridge && pendingApprovalRef.current) {
|
|
377
|
+
const pattern = generateApprovalPattern(pendingApprovalRef.current);
|
|
378
|
+
setAlwaysAllowed(prev => new Set(prev).add(pattern));
|
|
379
|
+
approvalBridge.resolve({ approved: true });
|
|
380
|
+
setPendingApproval(null);
|
|
381
|
+
setDenyReasonMode(false);
|
|
382
|
+
setDenyReason('');
|
|
383
|
+
}
|
|
384
|
+
}, [approvalBridge]);
|
|
317
385
|
// Enter deny with reason mode
|
|
318
386
|
const handleDenyWithReason = useCallback(() => {
|
|
319
387
|
setDenyReasonMode(true);
|
|
@@ -332,6 +400,23 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
332
400
|
});
|
|
333
401
|
}
|
|
334
402
|
}, [approvalBridge, handleApprovalRequest]);
|
|
403
|
+
// Set up transparency aggregator and subscribe to agent events
|
|
404
|
+
useEffect(() => {
|
|
405
|
+
const aggregator = new TransparencyAggregator();
|
|
406
|
+
transparencyAggregatorRef.current = aggregator;
|
|
407
|
+
// Subscribe to state changes
|
|
408
|
+
const unsubscribeAggregator = aggregator.subscribe((state) => {
|
|
409
|
+
setTransparencyState(state);
|
|
410
|
+
});
|
|
411
|
+
// Subscribe to agent events
|
|
412
|
+
const unsubscribeAgent = agent.subscribe((event) => {
|
|
413
|
+
aggregator.processEvent(event);
|
|
414
|
+
});
|
|
415
|
+
return () => {
|
|
416
|
+
unsubscribeAggregator();
|
|
417
|
+
unsubscribeAgent();
|
|
418
|
+
};
|
|
419
|
+
}, [agent]);
|
|
335
420
|
// =========================================================================
|
|
336
421
|
// COMMAND HANDLER
|
|
337
422
|
// =========================================================================
|
|
@@ -400,10 +485,18 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
400
485
|
' /mcp tools List MCP tools',
|
|
401
486
|
' /mcp search <q> Search & load tools',
|
|
402
487
|
'',
|
|
403
|
-
'>
|
|
404
|
-
' /
|
|
488
|
+
'> SKILLS & AGENTS',
|
|
489
|
+
' /skills List all skills',
|
|
490
|
+
' /skills new <n> Create new skill',
|
|
491
|
+
' /skills info <n> Show skill details',
|
|
492
|
+
' /agents List all agents',
|
|
493
|
+
' /agents new <n> Create new agent',
|
|
494
|
+
' /agents info <n> Show agent details',
|
|
405
495
|
' /spawn <a> <task> Run agent with task',
|
|
406
496
|
'',
|
|
497
|
+
'> INITIALIZATION',
|
|
498
|
+
' /init Setup .attocode/ directory',
|
|
499
|
+
'',
|
|
407
500
|
'> PLAN MODE',
|
|
408
501
|
' /mode Show current mode',
|
|
409
502
|
' /plan Toggle plan mode',
|
|
@@ -417,6 +510,7 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
417
510
|
' Ctrl+P Help',
|
|
418
511
|
' Alt+T Toggle tool details',
|
|
419
512
|
' Alt+O Toggle thinking',
|
|
513
|
+
' Alt+I Toggle transparency panel',
|
|
420
514
|
'========================',
|
|
421
515
|
].join('\n'));
|
|
422
516
|
return;
|
|
@@ -764,16 +858,81 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
764
858
|
case 'model':
|
|
765
859
|
addMessage('system', `Model: ${model || 'auto'}\nRestart to change.`);
|
|
766
860
|
return;
|
|
861
|
+
// Skills commands
|
|
862
|
+
case 'skills': {
|
|
863
|
+
const skillManager = agent.getSkillManager();
|
|
864
|
+
if (skillManager) {
|
|
865
|
+
// Create output adapter for TUI
|
|
866
|
+
const tuiOutput = {
|
|
867
|
+
log: (msg) => addMessage('system', msg),
|
|
868
|
+
error: (msg) => addMessage('error', msg),
|
|
869
|
+
clear: () => setMessages([]),
|
|
870
|
+
};
|
|
871
|
+
const ctx = {
|
|
872
|
+
agent,
|
|
873
|
+
sessionId: currentSessionId,
|
|
874
|
+
output: tuiOutput,
|
|
875
|
+
integrations: {
|
|
876
|
+
sessionStore,
|
|
877
|
+
mcpClient,
|
|
878
|
+
compactor,
|
|
879
|
+
skillManager,
|
|
880
|
+
},
|
|
881
|
+
};
|
|
882
|
+
await handleSkillsCommand(args, ctx, skillManager);
|
|
883
|
+
}
|
|
884
|
+
else {
|
|
885
|
+
addMessage('system', 'Skills not enabled');
|
|
886
|
+
}
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
// Init command
|
|
890
|
+
case 'init': {
|
|
891
|
+
const tuiOutput = {
|
|
892
|
+
log: (msg) => addMessage('system', msg),
|
|
893
|
+
error: (msg) => addMessage('error', msg),
|
|
894
|
+
clear: () => setMessages([]),
|
|
895
|
+
};
|
|
896
|
+
const ctx = {
|
|
897
|
+
agent,
|
|
898
|
+
sessionId: currentSessionId,
|
|
899
|
+
output: tuiOutput,
|
|
900
|
+
integrations: {
|
|
901
|
+
sessionStore,
|
|
902
|
+
mcpClient,
|
|
903
|
+
compactor,
|
|
904
|
+
},
|
|
905
|
+
};
|
|
906
|
+
await handleInitCommand(args, ctx);
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
767
909
|
// Subagent commands
|
|
768
|
-
case 'agents':
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
910
|
+
case 'agents': {
|
|
911
|
+
const agentRegistry = agent.getAgentRegistry();
|
|
912
|
+
if (agentRegistry) {
|
|
913
|
+
const tuiOutput = {
|
|
914
|
+
log: (msg) => addMessage('system', msg),
|
|
915
|
+
error: (msg) => addMessage('error', msg),
|
|
916
|
+
clear: () => setMessages([]),
|
|
917
|
+
};
|
|
918
|
+
const ctx = {
|
|
919
|
+
agent,
|
|
920
|
+
sessionId: currentSessionId,
|
|
921
|
+
output: tuiOutput,
|
|
922
|
+
integrations: {
|
|
923
|
+
sessionStore,
|
|
924
|
+
mcpClient,
|
|
925
|
+
compactor,
|
|
926
|
+
agentRegistry,
|
|
927
|
+
},
|
|
928
|
+
};
|
|
929
|
+
await handleAgentsCommand(args, ctx, agentRegistry);
|
|
772
930
|
}
|
|
773
|
-
|
|
774
|
-
addMessage('
|
|
931
|
+
else {
|
|
932
|
+
addMessage('system', 'Agents not enabled');
|
|
775
933
|
}
|
|
776
934
|
return;
|
|
935
|
+
}
|
|
777
936
|
case 'spawn':
|
|
778
937
|
if (args.length < 2) {
|
|
779
938
|
addMessage('system', 'Usage: /spawn <agent-name> <task>');
|
|
@@ -813,19 +972,31 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
813
972
|
setIsProcessing(true);
|
|
814
973
|
setStatus(s => ({ ...s, mode: 'thinking' }));
|
|
815
974
|
const unsub = agent.subscribe((event) => {
|
|
816
|
-
if
|
|
817
|
-
|
|
975
|
+
// Check if event is from a subagent
|
|
976
|
+
const subagentPrefix = event.subagent ? `[${event.subagent}] ` : '';
|
|
977
|
+
if (event.type === 'agent.spawn') {
|
|
978
|
+
// A subagent is starting
|
|
979
|
+
addMessage('system', `[AGENT] Spawning ${event.name}: ${event.task.slice(0, 100)}${event.task.length > 100 ? '...' : ''}`);
|
|
980
|
+
}
|
|
981
|
+
else if (event.type === 'agent.complete') {
|
|
982
|
+
// A subagent finished
|
|
983
|
+
addMessage('system', `[AGENT] ${event.agentId} ${event.success ? 'completed' : 'failed'}`);
|
|
984
|
+
}
|
|
985
|
+
else if (event.type === 'tool.start') {
|
|
986
|
+
const displayName = event.subagent ? `${event.subagent}:${event.tool}` : event.tool;
|
|
987
|
+
setStatus(s => ({ ...s, mode: `calling ${displayName}` }));
|
|
818
988
|
setToolCalls(prev => [...prev.slice(-4), {
|
|
819
|
-
id: `${
|
|
820
|
-
name:
|
|
989
|
+
id: `${displayName}-${Date.now()}`,
|
|
990
|
+
name: displayName,
|
|
821
991
|
args: event.args || {},
|
|
822
992
|
status: 'running',
|
|
823
993
|
startTime: new Date(),
|
|
824
994
|
}]);
|
|
825
995
|
}
|
|
826
996
|
else if (event.type === 'tool.complete') {
|
|
827
|
-
|
|
828
|
-
|
|
997
|
+
const displayName = event.subagent ? `${event.subagent}:${event.tool}` : event.tool;
|
|
998
|
+
setStatus(s => ({ ...s, mode: event.subagent ? `${event.subagent} thinking` : 'thinking' }));
|
|
999
|
+
setToolCalls(prev => prev.map(t => t.name === displayName ? {
|
|
829
1000
|
...t,
|
|
830
1001
|
status: 'success',
|
|
831
1002
|
result: event.result,
|
|
@@ -833,18 +1004,19 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
833
1004
|
} : t));
|
|
834
1005
|
}
|
|
835
1006
|
else if (event.type === 'tool.blocked') {
|
|
836
|
-
|
|
1007
|
+
const displayName = event.subagent ? `${event.subagent}:${event.tool}` : event.tool;
|
|
1008
|
+
setToolCalls(prev => prev.map(t => t.name === displayName ? {
|
|
837
1009
|
...t,
|
|
838
1010
|
status: 'error',
|
|
839
1011
|
error: event.reason || 'Blocked',
|
|
840
1012
|
} : t));
|
|
841
1013
|
}
|
|
842
1014
|
else if (event.type === 'llm.start') {
|
|
843
|
-
setStatus(s => ({ ...s, mode: 'thinking', iter: s.iter + 1 }));
|
|
1015
|
+
setStatus(s => ({ ...s, mode: event.subagent ? `${event.subagent} thinking` : 'thinking', iter: s.iter + 1 }));
|
|
844
1016
|
}
|
|
845
1017
|
else if (event.type === 'insight.tokens' && showThinking) {
|
|
846
1018
|
const e = event;
|
|
847
|
-
addMessage('system',
|
|
1019
|
+
addMessage('system', `${subagentPrefix}* ${e.inputTokens.toLocaleString()} in, ${e.outputTokens.toLocaleString()} out${e.cost ? ` $${e.cost.toFixed(6)}` : ''}`);
|
|
848
1020
|
}
|
|
849
1021
|
else if (event.type === 'plan.change.queued') {
|
|
850
1022
|
addMessage('system', `[PLAN] Queued: ${event.tool}`);
|
|
@@ -855,13 +1027,25 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
855
1027
|
const metrics = agent.getMetrics();
|
|
856
1028
|
const modeInfo = agent.getModeInfo();
|
|
857
1029
|
setStatus({ iter: metrics.llmCalls, tokens: metrics.totalTokens, cost: metrics.estimatedCost, mode: modeInfo.name === 'Plan' ? 'ready (plan)' : 'ready' });
|
|
1030
|
+
// Calculate current context size (what's actually in the window now)
|
|
1031
|
+
const agentState = agent.getState();
|
|
1032
|
+
const estimateTokens = (str) => Math.ceil(str.length / 3.2);
|
|
1033
|
+
const currentContextTokens = agentState.messages.reduce((sum, m) => sum + estimateTokens(typeof m.content === 'string' ? m.content : JSON.stringify(m.content)), 0);
|
|
1034
|
+
const contextLimit = 80000;
|
|
1035
|
+
const contextPct = Math.round((currentContextTokens / contextLimit) * 100);
|
|
858
1036
|
const durationSec = (metrics.duration / 1000).toFixed(1);
|
|
859
|
-
|
|
1037
|
+
// Format: Session total (cumulative cost) | Current context (compaction awareness)
|
|
1038
|
+
const sessionIn = metrics.inputTokens >= 1000 ? `${(metrics.inputTokens / 1000).toFixed(1)}k` : metrics.inputTokens.toLocaleString();
|
|
1039
|
+
const sessionOut = metrics.outputTokens >= 1000 ? `${(metrics.outputTokens / 1000).toFixed(1)}k` : metrics.outputTokens.toLocaleString();
|
|
1040
|
+
const contextK = (currentContextTokens / 1000).toFixed(1);
|
|
1041
|
+
const metricsLine = `\n---\nSession: ${sessionIn} in / ${sessionOut} out | Context: ${contextK}k/${contextLimit / 1000}k (${contextPct}%) | ${metrics.toolCalls} tools | ${durationSec}s`;
|
|
860
1042
|
if (agent.hasPendingPlan()) {
|
|
861
1043
|
const plan = agent.getPendingPlan();
|
|
862
1044
|
if (plan) {
|
|
863
|
-
|
|
864
|
-
|
|
1045
|
+
// Auto-show the full plan instead of just a count
|
|
1046
|
+
const fullPlan = agent.formatPendingPlan();
|
|
1047
|
+
addMessage('assistant', (result.response || 'Planning complete.') + metricsLine);
|
|
1048
|
+
addMessage('system', fullPlan);
|
|
865
1049
|
}
|
|
866
1050
|
}
|
|
867
1051
|
else {
|
|
@@ -1025,6 +1209,12 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
1025
1209
|
return !prev;
|
|
1026
1210
|
});
|
|
1027
1211
|
}, [addMessage]);
|
|
1212
|
+
const handleToggleTransparency = useCallback(() => {
|
|
1213
|
+
setTransparencyExpanded(prev => {
|
|
1214
|
+
addMessage('system', !prev ? '[v] Transparency panel: visible' : '[^] Transparency panel: hidden');
|
|
1215
|
+
return !prev;
|
|
1216
|
+
});
|
|
1217
|
+
}, [addMessage]);
|
|
1028
1218
|
// Update context tokens
|
|
1029
1219
|
useEffect(() => {
|
|
1030
1220
|
const agentState = agent.getState();
|
|
@@ -1055,13 +1245,18 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
1055
1245
|
// =========================================================================
|
|
1056
1246
|
// RENDER
|
|
1057
1247
|
// =========================================================================
|
|
1058
|
-
return (_jsxs(_Fragment, { children: [_jsx(Static, { items: messages, children: (m) => (_jsx(MessageItem, { msg: m, colors: colors }, m.id)) }), _jsxs(Box, { flexDirection: "column", children: [toolCalls.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: "#DDA0DD", bold: true, children: `Tools ${toolCallsExpanded ? '[-]' : '[+]'}` }), toolCalls.slice(-5).map(tc => (_jsx(ToolCallItem, { tc: tc, expanded: toolCallsExpanded, colors: colors }, `${tc.id}-${tc.status}`)))] })),
|
|
1248
|
+
return (_jsxs(_Fragment, { children: [_jsx(Static, { items: messages, children: (m) => (_jsx(MessageItem, { msg: m, colors: colors }, m.id)) }), _jsxs(Box, { flexDirection: "column", children: [toolCalls.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: "#DDA0DD", bold: true, children: `Tools ${toolCallsExpanded ? '[-]' : '[+]'}` }), toolCalls.slice(-5).map(tc => (_jsx(ToolCallItem, { tc: tc, expanded: toolCallsExpanded, colors: colors }, `${tc.id}-${tc.status}`)))] })), transparencyExpanded && transparencyState && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: colors.border, paddingX: 1, children: [_jsx(Text, { color: colors.accent, bold: true, children: "[v] Transparency Panel" }), _jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { color: colors.text, children: "REASONING" }), transparencyState.lastRouting ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: colors.textMuted, children: [" Routing: ", transparencyState.lastRouting.model] }), _jsxs(Text, { color: colors.textMuted, children: [" ", transparencyState.lastRouting.reason] })] })) : (_jsx(Text, { color: colors.textMuted, children: " Routing: (no routing decisions yet)" })), transparencyState.lastPolicy && (_jsxs(Text, { color: transparencyState.lastPolicy.decision === 'blocked' ? colors.error :
|
|
1249
|
+
transparencyState.lastPolicy.decision === 'prompted' ? colors.warning : colors.success, children: ["Policy: ", transparencyState.lastPolicy.decision === 'allowed' ? '+' :
|
|
1250
|
+
transparencyState.lastPolicy.decision === 'blocked' ? 'x' : '?', " ", transparencyState.lastPolicy.tool] }))] }), _jsxs(Box, { marginLeft: 2, marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.text, children: "CONTEXT" }), transparencyState.contextHealth ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.textMuted, children: ' [' + '='.repeat(Math.round((transparencyState.contextHealth.percentUsed / 100) * 20)) +
|
|
1251
|
+
'-'.repeat(20 - Math.round((transparencyState.contextHealth.percentUsed / 100) * 20)) +
|
|
1252
|
+
'] ' + transparencyState.contextHealth.percentUsed + '%' }), _jsx(Text, { color: colors.textMuted, children: ' ' + (transparencyState.contextHealth.currentTokens / 1000).toFixed(1) + 'k / ' +
|
|
1253
|
+
(transparencyState.contextHealth.maxTokens / 1000).toFixed(0) + 'k tokens' }), _jsx(Text, { color: colors.textMuted, children: ' ~' + transparencyState.contextHealth.estimatedExchanges + ' exchanges remaining' })] })) : (_jsx(Text, { color: colors.textMuted, children: " (no context data yet)" }))] }), transparencyState.activeLearnings.length > 0 && (_jsxs(Box, { marginLeft: 2, marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.text, children: "MEMORY" }), _jsxs(Text, { color: colors.textMuted, children: [" Learnings applied: ", transparencyState.activeLearnings.length] })] }))] })), pendingApproval && (_jsx(ApprovalDialog, { visible: true, request: {
|
|
1059
1254
|
id: pendingApproval.id,
|
|
1060
1255
|
tool: pendingApproval.tool || pendingApproval.action,
|
|
1061
1256
|
args: pendingApproval.args || {},
|
|
1062
1257
|
risk: pendingApproval.risk,
|
|
1063
1258
|
context: pendingApproval.context,
|
|
1064
|
-
}, onApprove: handleApprove, onDeny: handleDeny, colors: colors, denyReasonMode: denyReasonMode, denyReason: denyReason })), _jsx(MemoizedInputArea, { onSubmit: handleSubmit, disabled: isProcessing || !!pendingApproval, borderColor: pendingApproval ? '#FFD700' : '#87CEEB', textColor: "#98FB98", cursorColor: "#87CEEB", onCtrlC: handleCtrlC, onCtrlL: handleCtrlL, onCtrlP: handleCtrlP, onEscape: handleEscape, onToggleToolExpand: handleToggleToolExpand, onToggleThinking: handleToggleThinking, commandPaletteOpen: commandPaletteOpen, onCommandPaletteInput: handleCommandPaletteInput, approvalDialogOpen: !!pendingApproval, approvalDenyReasonMode: denyReasonMode, onApprovalApprove: handleApprove, onApprovalDeny: handleDeny, onApprovalDenyWithReason: handleDenyWithReason, onApprovalCancelDenyReason: handleCancelDenyReason, onApprovalDenyReasonInput: handleApprovalDenyReasonInput }), commandPaletteOpen && (_jsx(ControlledCommandPalette, { theme: selectedTheme, items: filteredCommandItems, visible: commandPaletteOpen, query: commandPaletteQuery, selectedIndex: commandPaletteIndex, onQueryChange: setCommandPaletteQuery, onSelectItem: (item) => {
|
|
1259
|
+
}, onApprove: handleApprove, onDeny: handleDeny, colors: colors, denyReasonMode: denyReasonMode, denyReason: denyReason })), _jsx(MemoizedInputArea, { onSubmit: handleSubmit, disabled: isProcessing || !!pendingApproval, borderColor: pendingApproval ? '#FFD700' : '#87CEEB', textColor: "#98FB98", cursorColor: "#87CEEB", onCtrlC: handleCtrlC, onCtrlL: handleCtrlL, onCtrlP: handleCtrlP, onEscape: handleEscape, onToggleToolExpand: handleToggleToolExpand, onToggleThinking: handleToggleThinking, onToggleTransparency: handleToggleTransparency, commandPaletteOpen: commandPaletteOpen, onCommandPaletteInput: handleCommandPaletteInput, approvalDialogOpen: !!pendingApproval, approvalDenyReasonMode: denyReasonMode, onApprovalApprove: handleApprove, onApprovalAlwaysAllow: handleAlwaysAllow, onApprovalDeny: handleDeny, onApprovalDenyWithReason: handleDenyWithReason, onApprovalCancelDenyReason: handleCancelDenyReason, onApprovalDenyReasonInput: handleApprovalDenyReasonInput }), commandPaletteOpen && (_jsx(ControlledCommandPalette, { theme: selectedTheme, items: filteredCommandItems, visible: commandPaletteOpen, query: commandPaletteQuery, selectedIndex: commandPaletteIndex, onQueryChange: setCommandPaletteQuery, onSelectItem: (item) => {
|
|
1065
1260
|
setCommandPaletteOpen(false);
|
|
1066
1261
|
setCommandPaletteQuery('');
|
|
1067
1262
|
setCommandPaletteIndex(0);
|
|
@@ -1070,7 +1265,9 @@ export function TUIApp({ agent, sessionStore, mcpClient, compactor, lspManager,
|
|
|
1070
1265
|
setCommandPaletteOpen(false);
|
|
1071
1266
|
setCommandPaletteQuery('');
|
|
1072
1267
|
setCommandPaletteIndex(0);
|
|
1073
|
-
} })), _jsxs(Box, { borderStyle: "single", borderColor: isProcessing ? colors.info : colors.textMuted, paddingX: 1, justifyContent: "space-between", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: isProcessing ? colors.info : '#98FB98', bold: isProcessing, children: isProcessing ? '[~]' : '[*]' }), _jsx(Text, { color: isProcessing ? colors.info : colors.text, bold: isProcessing, children: status.mode.length > 40 ? status.mode.slice(0, 37) + '...' : status.mode }), isProcessing && elapsedTime > 0 && _jsxs(Text, { color: colors.textMuted, dimColor: true, children: ["| ", elapsedTime, "s"] }), status.iter > 0 && _jsxs(Text, { color: colors.textMuted, dimColor: true, children: ["| iter ", status.iter] })] }), _jsxs(Box, { gap: 2, children: [_jsx(Text, { color: "#DDA0DD", dimColor: true, children: modelShort }), _jsx(Text, { color: contextPct > 70 ? '#FFD700' : colors.textMuted, dimColor: true, children:
|
|
1268
|
+
} })), _jsxs(Box, { borderStyle: "single", borderColor: isProcessing ? colors.info : colors.textMuted, paddingX: 1, justifyContent: "space-between", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: isProcessing ? colors.info : '#98FB98', bold: isProcessing, children: isProcessing ? '[~]' : '[*]' }), _jsx(Text, { color: isProcessing ? colors.info : colors.text, bold: isProcessing, children: status.mode.length > 40 ? status.mode.slice(0, 37) + '...' : status.mode }), isProcessing && elapsedTime > 0 && _jsxs(Text, { color: colors.textMuted, dimColor: true, children: ["| ", elapsedTime, "s"] }), status.iter > 0 && _jsxs(Text, { color: colors.textMuted, dimColor: true, children: ["| iter ", status.iter] })] }), _jsxs(Box, { gap: 2, children: [_jsx(Text, { color: "#DDA0DD", dimColor: true, children: modelShort }), _jsx(Text, { color: contextPct > 70 ? '#FFD700' : colors.textMuted, dimColor: true, children: '[' + '='.repeat(Math.min(8, Math.round((contextPct / 100) * 8))) +
|
|
1269
|
+
'-'.repeat(Math.max(0, 8 - Math.round((contextPct / 100) * 8))) + '] ' +
|
|
1270
|
+
contextPct + '%' }), _jsx(Text, { color: "#98FB98", dimColor: true, children: costStr }), gitBranch && _jsx(Text, { color: "#87CEEB", dimColor: true, children: gitBranch }), transparencyState?.activeLearnings && transparencyState.activeLearnings.length > 0 && (_jsxs(Text, { color: "#87CEEB", dimColor: true, children: ["L:", transparencyState.activeLearnings.length] })), _jsx(Text, { color: colors.textMuted, dimColor: true, children: "^P:help" })] })] })] })] }));
|
|
1074
1271
|
}
|
|
1075
1272
|
export default TUIApp;
|
|
1076
1273
|
//# sourceMappingURL=app.js.map
|