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.
Files changed (103) hide show
  1. package/CHANGELOG.md +64 -1
  2. package/README.md +138 -10
  3. package/dist/src/agent.d.ts +75 -1
  4. package/dist/src/agent.d.ts.map +1 -1
  5. package/dist/src/agent.js +700 -25
  6. package/dist/src/agent.js.map +1 -1
  7. package/dist/src/commands/agents-commands.d.ts +24 -0
  8. package/dist/src/commands/agents-commands.d.ts.map +1 -0
  9. package/dist/src/commands/agents-commands.js +284 -0
  10. package/dist/src/commands/agents-commands.js.map +1 -0
  11. package/dist/src/commands/handler.d.ts.map +1 -1
  12. package/dist/src/commands/handler.js +135 -19
  13. package/dist/src/commands/handler.js.map +1 -1
  14. package/dist/src/commands/init-commands.d.ts +35 -0
  15. package/dist/src/commands/init-commands.d.ts.map +1 -0
  16. package/dist/src/commands/init-commands.js +187 -0
  17. package/dist/src/commands/init-commands.js.map +1 -0
  18. package/dist/src/commands/skills-commands.d.ts +26 -0
  19. package/dist/src/commands/skills-commands.d.ts.map +1 -0
  20. package/dist/src/commands/skills-commands.js +309 -0
  21. package/dist/src/commands/skills-commands.js.map +1 -0
  22. package/dist/src/commands/types.d.ts +13 -2
  23. package/dist/src/commands/types.d.ts.map +1 -1
  24. package/dist/src/defaults.d.ts +29 -1
  25. package/dist/src/defaults.d.ts.map +1 -1
  26. package/dist/src/defaults.js +66 -0
  27. package/dist/src/defaults.js.map +1 -1
  28. package/dist/src/integrations/agent-registry.d.ts +68 -2
  29. package/dist/src/integrations/agent-registry.d.ts.map +1 -1
  30. package/dist/src/integrations/agent-registry.js +230 -23
  31. package/dist/src/integrations/agent-registry.js.map +1 -1
  32. package/dist/src/integrations/cancellation.d.ts +5 -0
  33. package/dist/src/integrations/cancellation.d.ts.map +1 -1
  34. package/dist/src/integrations/cancellation.js +7 -0
  35. package/dist/src/integrations/cancellation.js.map +1 -1
  36. package/dist/src/integrations/capabilities.d.ts +160 -0
  37. package/dist/src/integrations/capabilities.d.ts.map +1 -0
  38. package/dist/src/integrations/capabilities.js +426 -0
  39. package/dist/src/integrations/capabilities.js.map +1 -0
  40. package/dist/src/integrations/context-engineering.d.ts +6 -1
  41. package/dist/src/integrations/context-engineering.d.ts.map +1 -1
  42. package/dist/src/integrations/context-engineering.js +7 -0
  43. package/dist/src/integrations/context-engineering.js.map +1 -1
  44. package/dist/src/integrations/index.d.ts +12 -2
  45. package/dist/src/integrations/index.d.ts.map +1 -1
  46. package/dist/src/integrations/index.js +22 -2
  47. package/dist/src/integrations/index.js.map +1 -1
  48. package/dist/src/integrations/interactive-planning.d.ts +322 -0
  49. package/dist/src/integrations/interactive-planning.d.ts.map +1 -0
  50. package/dist/src/integrations/interactive-planning.js +655 -0
  51. package/dist/src/integrations/interactive-planning.js.map +1 -0
  52. package/dist/src/integrations/learning-store.d.ts +291 -0
  53. package/dist/src/integrations/learning-store.d.ts.map +1 -0
  54. package/dist/src/integrations/learning-store.js +640 -0
  55. package/dist/src/integrations/learning-store.js.map +1 -0
  56. package/dist/src/integrations/pending-plan.d.ts.map +1 -1
  57. package/dist/src/integrations/pending-plan.js +69 -10
  58. package/dist/src/integrations/pending-plan.js.map +1 -1
  59. package/dist/src/integrations/skill-executor.d.ts +113 -0
  60. package/dist/src/integrations/skill-executor.d.ts.map +1 -0
  61. package/dist/src/integrations/skill-executor.js +270 -0
  62. package/dist/src/integrations/skill-executor.js.map +1 -0
  63. package/dist/src/integrations/skills.d.ts +98 -7
  64. package/dist/src/integrations/skills.d.ts.map +1 -1
  65. package/dist/src/integrations/skills.js +210 -11
  66. package/dist/src/integrations/skills.js.map +1 -1
  67. package/dist/src/providers/circuit-breaker.d.ts +180 -0
  68. package/dist/src/providers/circuit-breaker.d.ts.map +1 -0
  69. package/dist/src/providers/circuit-breaker.js +349 -0
  70. package/dist/src/providers/circuit-breaker.js.map +1 -0
  71. package/dist/src/providers/fallback-chain.d.ts +194 -0
  72. package/dist/src/providers/fallback-chain.d.ts.map +1 -0
  73. package/dist/src/providers/fallback-chain.js +363 -0
  74. package/dist/src/providers/fallback-chain.js.map +1 -0
  75. package/dist/src/providers/llm-resilience.d.ts +126 -0
  76. package/dist/src/providers/llm-resilience.d.ts.map +1 -0
  77. package/dist/src/providers/llm-resilience.js +261 -0
  78. package/dist/src/providers/llm-resilience.js.map +1 -0
  79. package/dist/src/providers/resilient-provider.d.ts +124 -0
  80. package/dist/src/providers/resilient-provider.d.ts.map +1 -0
  81. package/dist/src/providers/resilient-provider.js +242 -0
  82. package/dist/src/providers/resilient-provider.js.map +1 -0
  83. package/dist/src/tricks/recursive-context.d.ts +296 -0
  84. package/dist/src/tricks/recursive-context.d.ts.map +1 -0
  85. package/dist/src/tricks/recursive-context.js +518 -0
  86. package/dist/src/tricks/recursive-context.js.map +1 -0
  87. package/dist/src/tui/app.d.ts.map +1 -1
  88. package/dist/src/tui/app.js +226 -29
  89. package/dist/src/tui/app.js.map +1 -1
  90. package/dist/src/tui/components/ApprovalDialog.d.ts.map +1 -1
  91. package/dist/src/tui/components/ApprovalDialog.js +1 -1
  92. package/dist/src/tui/components/ApprovalDialog.js.map +1 -1
  93. package/dist/src/tui/index.d.ts +1 -0
  94. package/dist/src/tui/index.d.ts.map +1 -1
  95. package/dist/src/tui/index.js +2 -0
  96. package/dist/src/tui/index.js.map +1 -1
  97. package/dist/src/tui/transparency-aggregator.d.ts +100 -0
  98. package/dist/src/tui/transparency-aggregator.d.ts.map +1 -0
  99. package/dist/src/tui/transparency-aggregator.js +234 -0
  100. package/dist/src/tui/transparency-aggregator.js.map +1 -0
  101. package/dist/src/types.d.ts +155 -0
  102. package/dist/src/types.d.ts.map +1 -1
  103. package/package.json +1 -1
@@ -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
- '> SUBAGENTS',
404
- ' /agents List available agents',
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
- try {
770
- const agentList = agent.formatAgentList();
771
- addMessage('system', `Available Agents:\n${agentList}`);
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
- catch (e) {
774
- addMessage('error', e.message);
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 (event.type === 'tool.start') {
817
- setStatus(s => ({ ...s, mode: `calling ${event.tool}` }));
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: `${event.tool}-${Date.now()}`,
820
- name: event.tool,
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
- setStatus(s => ({ ...s, mode: 'thinking' }));
828
- setToolCalls(prev => prev.map(t => t.name === event.tool ? {
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
- setToolCalls(prev => prev.map(t => t.name === event.tool ? {
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', `* ${e.inputTokens.toLocaleString()} in, ${e.outputTokens.toLocaleString()} out${e.cost ? ` $${e.cost.toFixed(6)}` : ''}`);
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
- const metricsLine = `\n---\n${metrics.inputTokens.toLocaleString()} in | ${metrics.outputTokens.toLocaleString()} out | ${metrics.toolCalls} tools | ${durationSec}s`;
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
- const planSummary = `\n[PLAN] ${plan.proposedChanges.length} change(s) queued\n/show-plan | /approve | /reject`;
864
- addMessage('assistant', (result.response || 'Changes queued.') + planSummary + metricsLine);
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}`)))] })), pendingApproval && (_jsx(ApprovalDialog, { visible: true, request: {
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: `${(contextTokens / 1000).toFixed(1)}k` }), _jsx(Text, { color: "#98FB98", dimColor: true, children: costStr }), gitBranch && _jsx(Text, { color: "#87CEEB", dimColor: true, children: gitBranch }), _jsx(Text, { color: colors.textMuted, dimColor: true, children: "ESC:cancel ^P:help" })] })] })] })] }));
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