@lucashca/claudecontrol 0.3.25 → 0.3.27

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.
@@ -118,6 +118,14 @@ export function pushLog(session: AgentSession, entry: LogEntry) {
118
118
  persistLogEntry(session.workspaceId, session.id, entry);
119
119
  }
120
120
 
121
+ export function hideLogEntry(session: AgentSession, predicate: (e: LogEntry) => boolean) {
122
+ for (const entry of session.log) {
123
+ if (predicate(entry)) entry.hidden = true;
124
+ }
125
+ const logPath = agentLogPath(session.workspaceId, session.id);
126
+ try { writeFileSync(logPath, session.log.map(e => JSON.stringify(e)).join('\n') + (session.log.length ? '\n' : '')); } catch { /* ignore */ }
127
+ }
128
+
121
129
  export function genId(): string {
122
130
  return Math.random().toString(36).slice(2, 10);
123
131
  }
@@ -5,7 +5,7 @@ import { broadcast } from '../ws.js';
5
5
  import { getWorkspaceById } from '../workspaces.js';
6
6
  import {
7
7
  genId, agents, agentAbortControllers, agentConversations,
8
- persistAgents, persistLogEntry, addAgentToIndex, pushLog,
8
+ persistAgents, persistLogEntry, addAgentToIndex, pushLog, hideLogEntry,
9
9
  delegations, agentQueues,
10
10
  } from '../agents/store.js';
11
11
  import { sendToAgent, runAgentQuery, delegateTask, compactAgentContext } from '../agents/lifecycle.js';
@@ -81,6 +81,19 @@ export async function handleAgentRoutes(
81
81
  return true;
82
82
  }
83
83
 
84
+ // ── PATCH /api/agents/:id/log?delegationId=xxx (hide log entry) ──
85
+ const hideLogParams = matchRoute(path, '/api/agents/:id/log');
86
+ if (hideLogParams && method === 'PATCH') {
87
+ const agent = agents.get(hideLogParams.id);
88
+ if (!agent) { jsonError(res, 'Agent not found', 404); return true; }
89
+ const delegationId = _urlObj.searchParams.get('delegationId');
90
+ if (!delegationId) { jsonError(res, 'delegationId is required'); return true; }
91
+ hideLogEntry(agent, e => e.delegationId === delegationId);
92
+ broadcast({ type: 'agent_log_updated', agentId: agent.id, workspaceId: agent.workspaceId, log: agent.log });
93
+ json(res, { ok: true });
94
+ return true;
95
+ }
96
+
84
97
  // ── DELETE /api/agents/:id (close — move to history) ──
85
98
  const deleteAgentParams = matchRoute(path, '/api/agents/:id');
86
99
  if (deleteAgentParams && method === 'DELETE') {
package/backend/types.ts CHANGED
@@ -10,6 +10,7 @@ export interface AttachmentData {
10
10
 
11
11
  export interface LogEntry {
12
12
  type: 'agent' | 'user' | 'tool' | 'system' | 'delegation' | 'spawn_request';
13
+ hidden?: boolean;
13
14
  text: string;
14
15
  timestamp: string;
15
16
  tool?: string;
@@ -86,6 +86,7 @@ export const api = {
86
86
  post(`/workspaces/${wsId}/agents`, data),
87
87
 
88
88
  deleteAgent: (id: string) => del(`/agents/${id}`),
89
+ hideLogEntry: (agentId: string, delegationId: string) => patch(`/agents/${agentId}/log?delegationId=${encodeURIComponent(delegationId)}`, {}),
89
90
 
90
91
  sendMessage: (agentId: string, message: string, attachments?: import('./types').Attachment[], effort?: string, model?: string) =>
91
92
  post(`/agents/${agentId}/send`, { message, attachments, effort, model }),
@@ -1,6 +1,7 @@
1
1
  import { useState } from 'react';
2
2
  import { useAtomValue } from 'jotai';
3
3
  import { agentsMapAtom } from '@/atoms';
4
+ import { api } from '@/api';
4
5
  import { ChevronDown, ChevronUp, Loader2, CheckCircle2, XCircle, Clock, Wrench, MessageSquare, X } from 'lucide-react';
5
6
  import type { LogEntry } from '@/types';
6
7
 
@@ -47,16 +48,24 @@ const STATUS_CONFIG = {
47
48
  },
48
49
  };
49
50
 
51
+ function loadDismissed(): Record<string, boolean> {
52
+ try { return JSON.parse(localStorage.getItem('cc-dismissed-delegations') || '{}'); } catch { return {}; }
53
+ }
54
+
55
+ function saveDismissed(d: Record<string, boolean>) {
56
+ localStorage.setItem('cc-dismissed-delegations', JSON.stringify(d));
57
+ }
58
+
50
59
  export function DelegationStatusBar({ agentId }: { agentId: string }) {
51
60
  const agentsMap = useAtomValue(agentsMapAtom);
52
61
  const [expanded, setExpanded] = useState<Record<string, boolean>>({});
53
- const [dismissed, setDismissed] = useState<Record<string, boolean>>({});
62
+ const [dismissed, setDismissed] = useState<Record<string, boolean>>(loadDismissed);
54
63
 
55
64
  const agent = agentsMap.get(agentId);
56
65
  if (!agent) return null;
57
66
 
58
67
  const allDelegations = agent.log.filter(
59
- e => e.type === 'delegation' && e.delegationTo && e.delegationStatus,
68
+ e => e.type === 'delegation' && e.delegationTo && e.delegationStatus && !e.hidden,
60
69
  );
61
70
 
62
71
  const delegations = allDelegations.filter(e => !dismissed[e.delegationId!]);
@@ -93,7 +102,10 @@ export function DelegationStatusBar({ agentId }: { agentId: string }) {
93
102
  <div key={delegation.delegationId} className="border-b border-cc-border/30 last:border-b-0">
94
103
  <div className={`flex items-center transition-colors ${cfg.bg}`}>
95
104
  <button
96
- onClick={() => setDismissed(prev => ({ ...prev, [delegation.delegationId!]: true }))}
105
+ onClick={() => {
106
+ setDismissed(prev => { const next = { ...prev, [delegation.delegationId!]: true }; saveDismissed(next); return next; });
107
+ api.hideLogEntry(agentId, delegation.delegationId!).catch(() => {});
108
+ }}
97
109
  className="px-2 py-1.5 text-cc-muted hover:text-cc-text-secondary transition-colors shrink-0"
98
110
  >
99
111
  <X size={10} />
@@ -223,6 +223,16 @@ function handleMessage(msg: any) {
223
223
  store.set(agentsMapAtom, next);
224
224
  break;
225
225
  }
226
+ case 'agent_log_updated': {
227
+ if (!isActive) break;
228
+ const prev = store.get(agentsMapAtom);
229
+ const existing = prev.get(msg.agentId);
230
+ if (!existing) break;
231
+ const next = new Map(prev);
232
+ next.set(msg.agentId, { ...existing, log: msg.log });
233
+ store.set(agentsMapAtom, next);
234
+ break;
235
+ }
226
236
  case 'agent_removed':
227
237
  if (isActive) removeAgent(msg.agentId);
228
238
  break;
@@ -31,6 +31,7 @@ export interface Attachment {
31
31
 
32
32
  export interface LogEntry {
33
33
  type: 'agent' | 'user' | 'tool' | 'system' | 'delegation' | 'spawn_request';
34
+ hidden?: boolean;
34
35
  text: string;
35
36
  timestamp: string;
36
37
  tool?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucashca/claudecontrol",
3
- "version": "0.3.25",
3
+ "version": "0.3.27",
4
4
  "description": "AI Agent Dashboard — manage multiple Claude Code agents across projects",
5
5
  "type": "module",
6
6
  "bin": {
package/version.json CHANGED
@@ -1 +1 @@
1
- {"version":"0.3.25","build":"2026-04-03T23:11:55.577Z"}
1
+ {"version":"0.3.27","build":"2026-04-03T23:24:27.772Z"}