repoburg 1.3.196 → 1.3.197
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/agent-pane/backend/data.db-shm +0 -0
- package/agent-pane/backend/data.db-wal +0 -0
- package/agent-pane/backend/dist/index.js +13399 -10390
- package/agent-pane/backend/package-lock.json +162 -92
- package/agent-pane/backend/scripts/backfill-from-jsonl.ts +206 -0
- package/agent-pane/backend/scripts/import-session-from-jsonl.ts +264 -0
- package/agent-pane/backend/src/claude/claude-message-mapper.test.ts +116 -8
- package/agent-pane/backend/src/claude/claude-message-mapper.ts +114 -9
- package/agent-pane/backend/src/claude/claude-provider.ts +918 -84
- package/agent-pane/backend/src/claude/claude-types.ts +16 -4
- package/agent-pane/backend/src/codex/app-server-client.ts +22 -0
- package/agent-pane/backend/src/db/index.ts +102 -0
- package/agent-pane/backend/src/db/schema.ts +4 -0
- package/agent-pane/backend/src/index.ts +3 -1
- package/agent-pane/backend/src/integration/helpers/test-app.ts +2 -0
- package/agent-pane/backend/src/integration/session-operations.test.ts +18 -11
- package/agent-pane/backend/src/integration/threads.test.ts +309 -0
- package/agent-pane/backend/src/providers/runtime-provider.ts +19 -1
- package/agent-pane/backend/src/routes/providers.ts +11 -0
- package/agent-pane/backend/src/routes/sessions.test.ts +143 -9
- package/agent-pane/backend/src/routes/sessions.ts +133 -28
- package/agent-pane/backend/src/routes/threads.ts +308 -0
- package/agent-pane/backend/src/routes/turns.ts +32 -9
- package/agent-pane/frontend/dist/assets/index-BYo-lnxm.js +362 -0
- package/agent-pane/frontend/dist/assets/index-Dwwhx7YN.css +1 -0
- package/agent-pane/frontend/dist/index.html +2 -2
- package/agent-pane/frontend/package-lock.json +51 -9
- package/agent-pane/frontend/src/__tests__/ThreadsPanel.test.tsx +192 -0
- package/agent-pane/frontend/src/api/providers.ts +6 -1
- package/agent-pane/frontend/src/api/sessions.test.ts +4 -0
- package/agent-pane/frontend/src/api/sessions.ts +13 -5
- package/agent-pane/frontend/src/api/websocket.ts +65 -1
- package/agent-pane/frontend/src/components/CollapsibleToolCalls.test.tsx +4 -4
- package/agent-pane/frontend/src/components/CollapsibleToolCalls.tsx +13 -4
- package/agent-pane/frontend/src/components/CommandInputBar.test.tsx +2 -2
- package/agent-pane/frontend/src/components/CommandInputBar.tsx +292 -29
- package/agent-pane/frontend/src/components/CommandPalette.test.tsx +0 -4
- package/agent-pane/frontend/src/components/CommandPalette.tsx +1 -17
- package/agent-pane/frontend/src/components/CommentableText.tsx +291 -0
- package/agent-pane/frontend/src/components/RenderArea.tsx +9 -2
- package/agent-pane/frontend/src/components/RightPanel.test.tsx +5 -5
- package/agent-pane/frontend/src/components/RightPanel.tsx +20 -14
- package/agent-pane/frontend/src/components/SessionTabs.tsx +7 -2
- package/agent-pane/frontend/src/components/StatusBar.tsx +68 -1
- package/agent-pane/frontend/src/components/TurnContent.test.tsx +29 -18
- package/agent-pane/frontend/src/components/TurnContent.tsx +199 -28
- package/agent-pane/frontend/src/components/input/SlashPopup.tsx +24 -5
- package/agent-pane/frontend/src/components/panels/TasksPanel.test.tsx +97 -0
- package/agent-pane/frontend/src/components/panels/TasksPanel.tsx +208 -0
- package/agent-pane/frontend/src/components/panels/ThreadsPanel.tsx +350 -0
- package/agent-pane/frontend/src/components/panels/TurnsPanel.tsx +47 -25
- package/agent-pane/frontend/src/hooks/useSessionStatus.test.ts +2 -0
- package/agent-pane/frontend/src/store/sessionSlice.ts +29 -6
- package/agent-pane/frontend/src/store/store.test.ts +5 -5
- package/agent-pane/frontend/src/store/store.ts +4 -1
- package/agent-pane/frontend/src/store/threadSlice.ts +136 -0
- package/agent-pane/frontend/src/store/wsSlice.ts +72 -3
- package/agent-pane/frontend/src/styles/app.css +150 -19
- package/agent-pane/frontend/src/test/fixtures.ts +4 -0
- package/agent-pane/frontend/src/types/index.ts +5 -2
- package/agent-pane/frontend/src/utils/duration.test.ts +4 -0
- package/agent-pane/frontend/vite.config.ts +2 -2
- package/agent-pane/shared/provider-types.ts +14 -0
- package/agent-pane/shared/types.ts +60 -0
- package/package.json +1 -1
- package/agent-pane/frontend/dist/assets/index-Bg0PF2aV.css +0 -1
- package/agent-pane/frontend/dist/assets/index-BlybeX5e.js +0 -281
- package/agent-pane/frontend/src/components/panels/LogsPanel.test.tsx +0 -58
- package/agent-pane/frontend/src/components/panels/LogsPanel.tsx +0 -25
- package/agent-pane/frontend/src/components/panels/MetricsPanel.test.tsx +0 -71
- package/agent-pane/frontend/src/components/panels/MetricsPanel.tsx +0 -114
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { render, screen, cleanup } from '@testing-library/react';
|
|
3
|
-
import LogsPanel from './LogsPanel.tsx';
|
|
4
|
-
import { useStore } from '../../store/store.ts';
|
|
5
|
-
import { resetStore } from '../../test/component-helpers.ts';
|
|
6
|
-
|
|
7
|
-
describe('LogsPanel', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
resetStore();
|
|
10
|
-
});
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
cleanup();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('shows empty state when no events', () => {
|
|
16
|
-
render(<LogsPanel />);
|
|
17
|
-
expect(screen.getByText('No events logged yet')).toBeInTheDocument();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('renders log entries', () => {
|
|
21
|
-
useStore.setState({
|
|
22
|
-
eventLog: [
|
|
23
|
-
{ time: '10:00:00', level: 'INFO', message: 'First event' },
|
|
24
|
-
{ time: '10:01:00', level: 'ERROR', message: 'Second event' },
|
|
25
|
-
],
|
|
26
|
-
});
|
|
27
|
-
render(<LogsPanel />);
|
|
28
|
-
expect(screen.getByText('First event')).toBeInTheDocument();
|
|
29
|
-
expect(screen.getByText('Second event')).toBeInTheDocument();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('shows entries in reverse chronological order', () => {
|
|
33
|
-
useStore.setState({
|
|
34
|
-
eventLog: [
|
|
35
|
-
{ time: '10:00:00', level: 'INFO', message: 'First' },
|
|
36
|
-
{ time: '10:01:00', level: 'INFO', message: 'Second' },
|
|
37
|
-
],
|
|
38
|
-
});
|
|
39
|
-
render(<LogsPanel />);
|
|
40
|
-
const entries = document.querySelectorAll('.log-entry');
|
|
41
|
-
expect(entries[0].textContent).toContain('Second');
|
|
42
|
-
expect(entries[1].textContent).toContain('First');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('renders log level with correct CSS class', () => {
|
|
46
|
-
useStore.setState({
|
|
47
|
-
eventLog: [
|
|
48
|
-
{ time: '10:00:00', level: 'INFO', message: 'OK' },
|
|
49
|
-
{ time: '10:01:00', level: 'ERROR', message: 'Failed' },
|
|
50
|
-
],
|
|
51
|
-
});
|
|
52
|
-
render(<LogsPanel />);
|
|
53
|
-
// Entries are reversed (newest first): ERROR then INFO
|
|
54
|
-
const levels = document.querySelectorAll('.log-level');
|
|
55
|
-
expect(levels[0]).toHaveClass('log-error');
|
|
56
|
-
expect(levels[1]).toHaveClass('log-info');
|
|
57
|
-
});
|
|
58
|
-
});
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { useStore } from '../../store/store.ts';
|
|
2
|
-
|
|
3
|
-
export default function LogsPanel() {
|
|
4
|
-
const eventLog = useStore((s) => s.eventLog);
|
|
5
|
-
|
|
6
|
-
if (eventLog.length === 0) {
|
|
7
|
-
return <div className="empty-state">No events logged yet</div>;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Show in reverse chronological order (newest first)
|
|
11
|
-
const logs = [...eventLog].reverse();
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<div className="right-section">
|
|
15
|
-
<div className="right-section-title">Event Log</div>
|
|
16
|
-
{logs.map((entry, i) => (
|
|
17
|
-
<div key={i} className="log-entry">
|
|
18
|
-
<span className="log-time">{entry.time}</span>
|
|
19
|
-
<span className={`log-level log-${entry.level.toLowerCase()}`}>{entry.level}</span>
|
|
20
|
-
<span className="log-msg">{entry.message}</span>
|
|
21
|
-
</div>
|
|
22
|
-
))}
|
|
23
|
-
</div>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { render, screen, cleanup } from '@testing-library/react';
|
|
3
|
-
import MetricsPanel from './MetricsPanel.tsx';
|
|
4
|
-
import { resetStore } from '../../test/component-helpers.ts';
|
|
5
|
-
import { makeSession, makeDetail, makeTurn, makeUsage } from '../../test/fixtures.ts';
|
|
6
|
-
|
|
7
|
-
describe('MetricsPanel', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
resetStore();
|
|
10
|
-
});
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
cleanup();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('shows empty state when no session', () => {
|
|
16
|
-
render(<MetricsPanel />);
|
|
17
|
-
expect(screen.getByText('No session selected')).toBeInTheDocument();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('shows session model or "default" when null', () => {
|
|
21
|
-
const session = makeSession('s1', { model: null });
|
|
22
|
-
resetStore({ activeSessionId: 's1', sessions: [session] });
|
|
23
|
-
render(<MetricsPanel />);
|
|
24
|
-
expect(screen.getByText('default')).toBeInTheDocument();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('shows repo name from cwd', () => {
|
|
28
|
-
const session = makeSession('s1', { cwd: '/home/user/my-project' });
|
|
29
|
-
resetStore({ activeSessionId: 's1', sessions: [session] });
|
|
30
|
-
render(<MetricsPanel />);
|
|
31
|
-
expect(screen.getByText('my-project')).toBeInTheDocument();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('shows "—" when cwd is null', () => {
|
|
35
|
-
const session = makeSession('s1', { cwd: null });
|
|
36
|
-
resetStore({ activeSessionId: 's1', sessions: [session] });
|
|
37
|
-
render(<MetricsPanel />);
|
|
38
|
-
// "—" appears in both Repo and Cost rows — check the Repo label row
|
|
39
|
-
const repoLabel = screen.getByText('Repo');
|
|
40
|
-
const repoVal = repoLabel.closest('.metric-row')!.querySelector('.metric-val')!;
|
|
41
|
-
expect(repoVal.textContent).toBe('—');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('shows turn count', () => {
|
|
45
|
-
const session = makeSession('s1');
|
|
46
|
-
const detail = makeDetail('s1', [makeTurn('t1', 's1'), makeTurn('t2', 's1')]);
|
|
47
|
-
resetStore({ activeSessionId: 's1', sessions: [session], sessionDetails: { s1: detail } });
|
|
48
|
-
render(<MetricsPanel />);
|
|
49
|
-
expect(screen.getByText('2')).toBeInTheDocument();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('shows token totals from all turns', () => {
|
|
53
|
-
const session = makeSession('s1');
|
|
54
|
-
const t1 = makeTurn('t1', 's1', { token_usage: makeUsage({ input_tokens: 100, output_tokens: 200 }) });
|
|
55
|
-
const t2 = makeTurn('t2', 's1', { token_usage: makeUsage({ input_tokens: 300, output_tokens: 400 }) });
|
|
56
|
-
const detail = makeDetail('s1', [t1, t2]);
|
|
57
|
-
resetStore({ activeSessionId: 's1', sessions: [session], sessionDetails: { s1: detail } });
|
|
58
|
-
render(<MetricsPanel />);
|
|
59
|
-
expect(screen.getByText('400')).toBeInTheDocument(); // total input
|
|
60
|
-
expect(screen.getByText('600')).toBeInTheDocument(); // total output
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('shows reasoning tokens only when > 0', () => {
|
|
64
|
-
const session = makeSession('s1');
|
|
65
|
-
const t1 = makeTurn('t1', 's1', { token_usage: makeUsage({ reasoning_output_tokens: 500 }) });
|
|
66
|
-
const detail = makeDetail('s1', [t1]);
|
|
67
|
-
resetStore({ activeSessionId: 's1', sessions: [session], sessionDetails: { s1: detail } });
|
|
68
|
-
render(<MetricsPanel />);
|
|
69
|
-
expect(screen.getByText('500')).toBeInTheDocument();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { useStore } from '../../store/store.ts';
|
|
2
|
-
import StatusDot from '../StatusDot.tsx';
|
|
3
|
-
import { deriveSessionStatus } from '../../hooks/useSessionStatus.ts';
|
|
4
|
-
import { calcSessionDuration } from '../../utils/duration.ts';
|
|
5
|
-
|
|
6
|
-
export default function MetricsPanel() {
|
|
7
|
-
const activeSessionId = useStore((s) => s.activeSessionId);
|
|
8
|
-
const session = useStore((s) =>
|
|
9
|
-
activeSessionId ? (s.sessions.find((x) => x.id === activeSessionId) ?? null) : null,
|
|
10
|
-
);
|
|
11
|
-
const detail = useStore((s) => (activeSessionId ? (s.sessionDetails[activeSessionId] ?? null) : null));
|
|
12
|
-
|
|
13
|
-
if (!session) {
|
|
14
|
-
return <div className="empty-state">No session selected</div>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const status = deriveSessionStatus(session);
|
|
18
|
-
const duration = calcSessionDuration(detail, status.isRunning);
|
|
19
|
-
|
|
20
|
-
// Aggregate token usage from all completed turns
|
|
21
|
-
let totalIn = 0;
|
|
22
|
-
let totalOut = 0;
|
|
23
|
-
let totalReasoning = 0;
|
|
24
|
-
if (detail) {
|
|
25
|
-
for (const turn of detail.turns) {
|
|
26
|
-
if (turn.token_usage) {
|
|
27
|
-
totalIn += turn.token_usage.input_tokens;
|
|
28
|
-
totalOut += turn.token_usage.output_tokens;
|
|
29
|
-
totalReasoning += turn.token_usage.reasoning_output_tokens;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const turnCount = detail?.turns.length ?? 0;
|
|
35
|
-
const repoName = session.cwd ? session.cwd.split('/').pop() || session.cwd : '—';
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<>
|
|
39
|
-
<div className="right-section">
|
|
40
|
-
<div className="right-section-title">Session</div>
|
|
41
|
-
<div className="metric-row">
|
|
42
|
-
<span className="metric-label">Runtime</span>
|
|
43
|
-
<span className="metric-val">Codex</span>
|
|
44
|
-
</div>
|
|
45
|
-
<div className="metric-row">
|
|
46
|
-
<span className="metric-label">Model</span>
|
|
47
|
-
<span className="metric-val">{session.model || 'default'}</span>
|
|
48
|
-
</div>
|
|
49
|
-
<div className="metric-row">
|
|
50
|
-
<span className="metric-label">Repo</span>
|
|
51
|
-
<span className="metric-val" title={session.cwd || undefined}>
|
|
52
|
-
{repoName}
|
|
53
|
-
</span>
|
|
54
|
-
</div>
|
|
55
|
-
<div className="metric-row">
|
|
56
|
-
<span className="metric-label">Turns</span>
|
|
57
|
-
<span className="metric-val">{turnCount}</span>
|
|
58
|
-
</div>
|
|
59
|
-
<div className="metric-row">
|
|
60
|
-
<span className="metric-label">Duration</span>
|
|
61
|
-
<span className="metric-val">{duration}</span>
|
|
62
|
-
</div>
|
|
63
|
-
<div className="metric-row">
|
|
64
|
-
<span className="metric-label">Status</span>
|
|
65
|
-
<span className="metric-val">
|
|
66
|
-
<span className="badge badge-running">
|
|
67
|
-
<StatusDot status={status.dot} size={5} /> {status.label}
|
|
68
|
-
</span>
|
|
69
|
-
</span>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
|
|
73
|
-
<div className="right-section">
|
|
74
|
-
<div className="right-section-title">Resources</div>
|
|
75
|
-
<div className="metric-row">
|
|
76
|
-
<span className="metric-label">Tokens In</span>
|
|
77
|
-
<span className="metric-val">{totalIn.toLocaleString()}</span>
|
|
78
|
-
</div>
|
|
79
|
-
<div className="metric-row">
|
|
80
|
-
<span className="metric-label">Tokens Out</span>
|
|
81
|
-
<span className="metric-val">{totalOut.toLocaleString()}</span>
|
|
82
|
-
</div>
|
|
83
|
-
{totalReasoning > 0 && (
|
|
84
|
-
<div className="metric-row">
|
|
85
|
-
<span className="metric-label">Reasoning</span>
|
|
86
|
-
<span className="metric-val">{totalReasoning.toLocaleString()}</span>
|
|
87
|
-
</div>
|
|
88
|
-
)}
|
|
89
|
-
<div className="metric-row">
|
|
90
|
-
<span className="metric-label">Cost</span>
|
|
91
|
-
<span className="metric-val" style={{ color: 'var(--amber)' }}>
|
|
92
|
-
—
|
|
93
|
-
</span>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
<div className="right-section">
|
|
98
|
-
<div className="right-section-title">Metadata</div>
|
|
99
|
-
<div className="metric-row">
|
|
100
|
-
<span className="metric-label">Created</span>
|
|
101
|
-
<span className="metric-val" style={{ fontSize: '11px' }}>
|
|
102
|
-
{new Date(session.created_at).toLocaleString()}
|
|
103
|
-
</span>
|
|
104
|
-
</div>
|
|
105
|
-
<div className="metric-row">
|
|
106
|
-
<span className="metric-label">Thread ID</span>
|
|
107
|
-
<span className="metric-val" style={{ fontSize: '10px', color: 'var(--text-muted)' }}>
|
|
108
|
-
{session.external_thread_id ? session.external_thread_id.slice(0, 16) + '…' : '—'}
|
|
109
|
-
</span>
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
</>
|
|
113
|
-
);
|
|
114
|
-
}
|