repoburg 1.3.201 → 1.3.204
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/dist/index.js +2714 -32395
- package/agent-pane/backend/dist/server.js +45304 -0
- package/askuser-after-step5-postgres-selected.png +0 -0
- package/askuser-after-step7-answer-sent.png +0 -0
- package/askuser-card-visible.png +0 -0
- package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.d.ts +16 -0
- package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js +87 -0
- package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js.map +1 -1
- package/backend/dist/src/agent-runtime/agent-runtime.types.d.ts +16 -1
- package/backend/dist/src/app.controller.d.ts +1 -0
- package/backend/dist/src/app.service.d.ts +1 -0
- package/backend/dist/src/app.service.js +4 -1
- package/backend/dist/src/app.service.js.map +1 -1
- package/backend/dist/src/interactive-chat/chat.service.d.ts +4 -0
- package/backend/dist/src/interactive-chat/chat.service.js +21 -1
- package/backend/dist/src/interactive-chat/chat.service.js.map +1 -1
- package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js +12 -1
- package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js.map +1 -1
- package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.d.ts +1 -0
- package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js +1 -0
- package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js.map +1 -1
- package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js +2 -0
- package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js.map +1 -1
- package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
- package/daemon/dist/agentPane.js +128 -0
- package/daemon/dist/agentPane.spec.js +183 -0
- package/daemon/dist/api/services.js +4 -0
- package/daemon/dist/index.js +28 -8
- package/daemon/dist/serviceManager.js +30 -11
- package/daemon/dist/serviceManager.spec.js +26 -0
- package/daemon/dist/workspace-validation.js +63 -0
- package/daemon/dist/workspace-validation.spec.js +63 -0
- package/daemon/package.json +1 -1
- package/package.json +15 -2
- package/r2-step4-apple-banana.png +0 -0
- package/r2-step5-after-rewind-apple-last.png +0 -0
- package/r2-step7-apple-cherry.png +0 -0
- package/r2-step8-cherry-menu-no-restore.png +0 -0
- package/r3-step3-live-task-card-running.png +0 -0
- package/r3-step4-after-completion.png +0 -0
- package/r3-step5-persistence-after-reload.png +0 -0
- package/r3-step6-tasks-tab-card.png +0 -0
- package/reverify-answered-state-answer-sent.png +0 -0
- package/reverify-card-visible-after-completion.png +0 -0
- package/rewind-step3-apple-banana-turns.png +0 -0
- package/rewind-step5-ui-unchanged-after-confirmed-rewind.png +0 -0
- package/rewind-step6-apple-menu-no-restore.png +0 -0
- package/rewind-step7-final-turnlist-unchanged.png +0 -0
- package/rewind-step7-negative-error-toast.png +0 -0
- package/rewind-step7-negative-error-toast2.png +0 -0
- package/subagent-r2-step5-turn-after-reload-no-card.png +0 -0
- package/subagent-r2-step6-tasks-panel-card.png +0 -0
- package/subagent-step3-tasks-tab-done-card.png +0 -0
- package/subagent-step4-turn-stuck-no-cards.png +0 -0
- package/agent-pane/.prettierignore +0 -5
- package/agent-pane/.prettierrc +0 -8
- package/agent-pane/backend/coverage/base.css +0 -224
- package/agent-pane/backend/coverage/block-navigation.js +0 -87
- package/agent-pane/backend/coverage/clover.xml +0 -887
- package/agent-pane/backend/coverage/coverage-final.json +0 -10
- package/agent-pane/backend/coverage/favicon.png +0 -0
- package/agent-pane/backend/coverage/index.html +0 -176
- package/agent-pane/backend/coverage/prettify.css +0 -1
- package/agent-pane/backend/coverage/prettify.js +0 -2
- package/agent-pane/backend/coverage/sort-arrow-sprite.png +0 -0
- package/agent-pane/backend/coverage/sorter.js +0 -210
- package/agent-pane/backend/coverage/src/codex/codex-provider.ts.html +0 -1402
- package/agent-pane/backend/coverage/src/codex/index.html +0 -131
- package/agent-pane/backend/coverage/src/codex/item-mapper.ts.html +0 -331
- package/agent-pane/backend/coverage/src/db/index.html +0 -131
- package/agent-pane/backend/coverage/src/db/index.ts.html +0 -436
- package/agent-pane/backend/coverage/src/db/schema.ts.html +0 -175
- package/agent-pane/backend/coverage/src/index.html +0 -116
- package/agent-pane/backend/coverage/src/index.ts.html +0 -262
- package/agent-pane/backend/coverage/src/routes/index.html +0 -146
- package/agent-pane/backend/coverage/src/routes/sessions.ts.html +0 -991
- package/agent-pane/backend/coverage/src/routes/turns.ts.html +0 -256
- package/agent-pane/backend/coverage/src/routes/workspaces.ts.html +0 -406
- package/agent-pane/backend/coverage/src/ws/index.html +0 -116
- package/agent-pane/backend/coverage/src/ws/index.ts.html +0 -238
- package/agent-pane/backend/data.db +0 -0
- package/agent-pane/backend/data.db-shm +0 -0
- package/agent-pane/backend/data.db-wal +0 -0
- package/agent-pane/backend/package-lock.json +0 -5945
- package/agent-pane/backend/package.json +0 -33
- package/agent-pane/backend/scripts/backfill-from-jsonl.ts +0 -206
- package/agent-pane/backend/scripts/import-session-from-jsonl.ts +0 -264
- package/agent-pane/backend/src/claude/claude-message-mapper.test.ts +0 -271
- package/agent-pane/backend/src/claude/claude-message-mapper.ts +0 -259
- package/agent-pane/backend/src/claude/claude-provider.test.ts +0 -455
- package/agent-pane/backend/src/claude/claude-provider.ts +0 -1319
- package/agent-pane/backend/src/claude/claude-types.ts +0 -45
- package/agent-pane/backend/src/codex/app-server-client.test.ts +0 -183
- package/agent-pane/backend/src/codex/app-server-client.ts +0 -247
- package/agent-pane/backend/src/codex/app-server-types.ts +0 -259
- package/agent-pane/backend/src/codex/codex-provider-runtime-config.test.ts +0 -56
- package/agent-pane/backend/src/codex/codex-provider.test.ts +0 -60
- package/agent-pane/backend/src/codex/codex-provider.ts +0 -619
- package/agent-pane/backend/src/codex/item-mapper.test.ts +0 -221
- package/agent-pane/backend/src/codex/item-mapper.ts +0 -86
- package/agent-pane/backend/src/codex/shared-app-server.ts +0 -25
- package/agent-pane/backend/src/db/index.test.ts +0 -125
- package/agent-pane/backend/src/db/index.ts +0 -230
- package/agent-pane/backend/src/db/schema.ts +0 -34
- package/agent-pane/backend/src/index.ts +0 -89
- package/agent-pane/backend/src/integration/__fixtures__/cancel-turn.json +0 -29
- package/agent-pane/backend/src/integration/__fixtures__/delta-streaming.json +0 -40
- package/agent-pane/backend/src/integration/__fixtures__/failed-turn.json +0 -16
- package/agent-pane/backend/src/integration/__fixtures__/mcp-tool-call.json +0 -60
- package/agent-pane/backend/src/integration/__fixtures__/missing-item-started.json +0 -34
- package/agent-pane/backend/src/integration/__fixtures__/multi-item-turn.json +0 -85
- package/agent-pane/backend/src/integration/__fixtures__/multi-turn.json +0 -48
- package/agent-pane/backend/src/integration/__fixtures__/simple-turn.json +0 -35
- package/agent-pane/backend/src/integration/delta-streaming.test.ts +0 -130
- package/agent-pane/backend/src/integration/helpers/app-server-mock.ts +0 -272
- package/agent-pane/backend/src/integration/helpers/test-app.ts +0 -110
- package/agent-pane/backend/src/integration/helpers/ws-capture.ts +0 -64
- package/agent-pane/backend/src/integration/item-lifecycle.test.ts +0 -133
- package/agent-pane/backend/src/integration/mcp-tool-call.test.ts +0 -102
- package/agent-pane/backend/src/integration/missing-item-started.test.ts +0 -91
- package/agent-pane/backend/src/integration/multi-turn.test.ts +0 -121
- package/agent-pane/backend/src/integration/session-operations.test.ts +0 -175
- package/agent-pane/backend/src/integration/threads-claude-fork.test.ts +0 -241
- package/agent-pane/backend/src/integration/threads.test.ts +0 -309
- package/agent-pane/backend/src/integration/title-generation.test.ts +0 -109
- package/agent-pane/backend/src/integration/turn-cancel.test.ts +0 -111
- package/agent-pane/backend/src/integration/turn-failure.test.ts +0 -97
- package/agent-pane/backend/src/integration/turn-lifecycle.test.ts +0 -141
- package/agent-pane/backend/src/providers/index.ts +0 -16
- package/agent-pane/backend/src/providers/registry.test.ts +0 -92
- package/agent-pane/backend/src/providers/registry.ts +0 -43
- package/agent-pane/backend/src/providers/runtime-provider.ts +0 -43
- package/agent-pane/backend/src/repoburg/action-mapping.ts +0 -216
- package/agent-pane/backend/src/repoburg/agent-runtime-client.test.ts +0 -271
- package/agent-pane/backend/src/repoburg/agent-runtime-client.ts +0 -262
- package/agent-pane/backend/src/repoburg/backend-resolver.test.ts +0 -271
- package/agent-pane/backend/src/repoburg/backend-resolver.ts +0 -246
- package/agent-pane/backend/src/repoburg/repoburg-client.test.ts +0 -159
- package/agent-pane/backend/src/repoburg/repoburg-client.ts +0 -181
- package/agent-pane/backend/src/repoburg/repoburg-provider.test.ts +0 -723
- package/agent-pane/backend/src/repoburg/repoburg-provider.ts +0 -422
- package/agent-pane/backend/src/repoburg/turn-projector.test.ts +0 -475
- package/agent-pane/backend/src/repoburg/turn-projector.ts +0 -537
- package/agent-pane/backend/src/routes/files.ts +0 -31
- package/agent-pane/backend/src/routes/mcp.ts +0 -33
- package/agent-pane/backend/src/routes/models.test.ts +0 -92
- package/agent-pane/backend/src/routes/models.ts +0 -16
- package/agent-pane/backend/src/routes/plugins.ts +0 -40
- package/agent-pane/backend/src/routes/providers.test.ts +0 -86
- package/agent-pane/backend/src/routes/providers.ts +0 -86
- package/agent-pane/backend/src/routes/sessions.test.ts +0 -558
- package/agent-pane/backend/src/routes/sessions.ts +0 -504
- package/agent-pane/backend/src/routes/skills.test.ts +0 -114
- package/agent-pane/backend/src/routes/skills.ts +0 -70
- package/agent-pane/backend/src/routes/threads.ts +0 -323
- package/agent-pane/backend/src/routes/turns.test.ts +0 -196
- package/agent-pane/backend/src/routes/turns.ts +0 -103
- package/agent-pane/backend/src/routes/workspaces.test.ts +0 -197
- package/agent-pane/backend/src/routes/workspaces.ts +0 -128
- package/agent-pane/backend/src/ws/index.test.ts +0 -107
- package/agent-pane/backend/src/ws/index.ts +0 -51
- package/agent-pane/backend/tsconfig.json +0 -25
- package/agent-pane/backend/vitest.config.ts +0 -20
- package/agent-pane/eslint.config.js +0 -33
- package/agent-pane/frontend/coverage/base.css +0 -224
- package/agent-pane/frontend/coverage/block-navigation.js +0 -87
- package/agent-pane/frontend/coverage/clover.xml +0 -2962
- package/agent-pane/frontend/coverage/coverage-final.json +0 -37
- package/agent-pane/frontend/coverage/favicon.png +0 -0
- package/agent-pane/frontend/coverage/index.html +0 -251
- package/agent-pane/frontend/coverage/prettify.css +0 -1
- package/agent-pane/frontend/coverage/prettify.js +0 -2
- package/agent-pane/frontend/coverage/sort-arrow-sprite.png +0 -0
- package/agent-pane/frontend/coverage/sorter.js +0 -210
- package/agent-pane/frontend/coverage/src/App.tsx.html +0 -217
- package/agent-pane/frontend/coverage/src/AppInitializer.tsx.html +0 -223
- package/agent-pane/frontend/coverage/src/api/client.ts.html +0 -187
- package/agent-pane/frontend/coverage/src/api/index.html +0 -161
- package/agent-pane/frontend/coverage/src/api/sessions.ts.html +0 -316
- package/agent-pane/frontend/coverage/src/api/websocket.ts.html +0 -544
- package/agent-pane/frontend/coverage/src/api/workspaces.ts.html +0 -199
- package/agent-pane/frontend/coverage/src/components/CollapsibleToolCalls.tsx.html +0 -226
- package/agent-pane/frontend/coverage/src/components/CommandInputBar.tsx.html +0 -382
- package/agent-pane/frontend/coverage/src/components/CommandPalette.tsx.html +0 -715
- package/agent-pane/frontend/coverage/src/components/IdeWorkspace.tsx.html +0 -160
- package/agent-pane/frontend/coverage/src/components/ModelSelector.tsx.html +0 -478
- package/agent-pane/frontend/coverage/src/components/NewSessionDialog.tsx.html +0 -472
- package/agent-pane/frontend/coverage/src/components/RenderArea.tsx.html +0 -574
- package/agent-pane/frontend/coverage/src/components/RightPanel.tsx.html +0 -205
- package/agent-pane/frontend/coverage/src/components/RuntimeIcon.tsx.html +0 -133
- package/agent-pane/frontend/coverage/src/components/SessionTabs.tsx.html +0 -217
- package/agent-pane/frontend/coverage/src/components/StatusBar.tsx.html +0 -271
- package/agent-pane/frontend/coverage/src/components/StatusDot.tsx.html +0 -109
- package/agent-pane/frontend/coverage/src/components/Toast.tsx.html +0 -313
- package/agent-pane/frontend/coverage/src/components/TurnContent.tsx.html +0 -1216
- package/agent-pane/frontend/coverage/src/components/WorkspaceSidebar.tsx.html +0 -1138
- package/agent-pane/frontend/coverage/src/components/index.html +0 -326
- package/agent-pane/frontend/coverage/src/components/panels/ChangesPanel.tsx.html +0 -247
- package/agent-pane/frontend/coverage/src/components/panels/LogsPanel.tsx.html +0 -160
- package/agent-pane/frontend/coverage/src/components/panels/MetricsPanel.tsx.html +0 -427
- package/agent-pane/frontend/coverage/src/components/panels/TurnsPanel.tsx.html +0 -397
- package/agent-pane/frontend/coverage/src/components/panels/index.html +0 -161
- package/agent-pane/frontend/coverage/src/constants/index.html +0 -116
- package/agent-pane/frontend/coverage/src/constants/models.ts.html +0 -406
- package/agent-pane/frontend/coverage/src/hooks/index.html +0 -146
- package/agent-pane/frontend/coverage/src/hooks/useEscapeKey.ts.html +0 -124
- package/agent-pane/frontend/coverage/src/hooks/useOutsideClick.ts.html +0 -142
- package/agent-pane/frontend/coverage/src/hooks/useSessionStatus.ts.html +0 -187
- package/agent-pane/frontend/coverage/src/index.html +0 -146
- package/agent-pane/frontend/coverage/src/main.tsx.html +0 -115
- package/agent-pane/frontend/coverage/src/store/index.html +0 -116
- package/agent-pane/frontend/coverage/src/store/store.ts.html +0 -2404
- package/agent-pane/frontend/coverage/src/test/component-helpers.ts.html +0 -190
- package/agent-pane/frontend/coverage/src/test/fixtures.ts.html +0 -367
- package/agent-pane/frontend/coverage/src/test/index.html +0 -131
- package/agent-pane/frontend/coverage/src/types/index.html +0 -116
- package/agent-pane/frontend/coverage/src/types/index.ts.html +0 -466
- package/agent-pane/frontend/coverage/src/utils/changes.ts.html +0 -202
- package/agent-pane/frontend/coverage/src/utils/duration.ts.html +0 -127
- package/agent-pane/frontend/coverage/src/utils/index.html +0 -131
- package/agent-pane/frontend/dist/assets/index-34WaZYsW.css +0 -1
- package/agent-pane/frontend/dist/assets/index-y7xlT86e.js +0 -362
- package/agent-pane/frontend/dist/index.html +0 -19
- package/agent-pane/frontend/index.html +0 -18
- package/agent-pane/frontend/package-lock.json +0 -4211
- package/agent-pane/frontend/package.json +0 -35
- package/agent-pane/frontend/src/App.test.tsx +0 -74
- package/agent-pane/frontend/src/App.tsx +0 -44
- package/agent-pane/frontend/src/AppInitializer.test.tsx +0 -142
- package/agent-pane/frontend/src/AppInitializer.tsx +0 -46
- package/agent-pane/frontend/src/__tests__/ThreadsPanel.test.tsx +0 -192
- package/agent-pane/frontend/src/api/client.test.ts +0 -126
- package/agent-pane/frontend/src/api/client.ts +0 -34
- package/agent-pane/frontend/src/api/models.ts +0 -7
- package/agent-pane/frontend/src/api/providers.ts +0 -17
- package/agent-pane/frontend/src/api/sessions.test.ts +0 -245
- package/agent-pane/frontend/src/api/sessions.ts +0 -97
- package/agent-pane/frontend/src/api/skills.ts +0 -12
- package/agent-pane/frontend/src/api/websocket.test.ts +0 -288
- package/agent-pane/frontend/src/api/websocket.ts +0 -238
- package/agent-pane/frontend/src/api/workspaces.test.ts +0 -112
- package/agent-pane/frontend/src/api/workspaces.ts +0 -38
- package/agent-pane/frontend/src/components/CollapsibleToolCalls.test.tsx +0 -49
- package/agent-pane/frontend/src/components/CollapsibleToolCalls.tsx +0 -56
- package/agent-pane/frontend/src/components/CommandInputBar.test.tsx +0 -133
- package/agent-pane/frontend/src/components/CommandInputBar.tsx +0 -584
- package/agent-pane/frontend/src/components/CommandPalette.test.tsx +0 -226
- package/agent-pane/frontend/src/components/CommandPalette.tsx +0 -194
- package/agent-pane/frontend/src/components/CommentableText.test.tsx +0 -196
- package/agent-pane/frontend/src/components/CommentableText.tsx +0 -379
- package/agent-pane/frontend/src/components/IdeWorkspace.test.tsx +0 -39
- package/agent-pane/frontend/src/components/IdeWorkspace.tsx +0 -51
- package/agent-pane/frontend/src/components/ModelSelector.test.tsx +0 -129
- package/agent-pane/frontend/src/components/ModelSelector.tsx +0 -191
- package/agent-pane/frontend/src/components/NewSessionDialog.test.tsx +0 -95
- package/agent-pane/frontend/src/components/NewSessionDialog.tsx +0 -129
- package/agent-pane/frontend/src/components/RenderArea.test.tsx +0 -160
- package/agent-pane/frontend/src/components/RenderArea.tsx +0 -170
- package/agent-pane/frontend/src/components/ResizeHandle.tsx +0 -29
- package/agent-pane/frontend/src/components/RightPanel.test.tsx +0 -43
- package/agent-pane/frontend/src/components/RightPanel.tsx +0 -46
- package/agent-pane/frontend/src/components/RuntimeIcon.test.tsx +0 -32
- package/agent-pane/frontend/src/components/RuntimeIcon.tsx +0 -16
- package/agent-pane/frontend/src/components/SessionTabs.test.tsx +0 -72
- package/agent-pane/frontend/src/components/SessionTabs.tsx +0 -49
- package/agent-pane/frontend/src/components/StatusBar.test.tsx +0 -56
- package/agent-pane/frontend/src/components/StatusBar.tsx +0 -129
- package/agent-pane/frontend/src/components/StatusDot.test.tsx +0 -25
- package/agent-pane/frontend/src/components/StatusDot.tsx +0 -8
- package/agent-pane/frontend/src/components/Toast.test.tsx +0 -79
- package/agent-pane/frontend/src/components/Toast.tsx +0 -76
- package/agent-pane/frontend/src/components/TurnContent.test.tsx +0 -298
- package/agent-pane/frontend/src/components/TurnContent.tsx +0 -548
- package/agent-pane/frontend/src/components/WorkspaceSidebar.test.tsx +0 -297
- package/agent-pane/frontend/src/components/WorkspaceSidebar.tsx +0 -351
- package/agent-pane/frontend/src/components/input/OptionSubPopup.tsx +0 -32
- package/agent-pane/frontend/src/components/input/SkillMentionPopup.test.tsx +0 -63
- package/agent-pane/frontend/src/components/input/SkillMentionPopup.tsx +0 -78
- package/agent-pane/frontend/src/components/input/SlashPopup.test.tsx +0 -96
- package/agent-pane/frontend/src/components/input/SlashPopup.tsx +0 -82
- package/agent-pane/frontend/src/components/panels/ChangesPanel.test.tsx +0 -61
- package/agent-pane/frontend/src/components/panels/ChangesPanel.tsx +0 -54
- package/agent-pane/frontend/src/components/panels/TasksPanel.test.tsx +0 -97
- package/agent-pane/frontend/src/components/panels/TasksPanel.tsx +0 -208
- package/agent-pane/frontend/src/components/panels/ThreadsPanel.tsx +0 -382
- package/agent-pane/frontend/src/components/panels/TurnsPanel.test.tsx +0 -83
- package/agent-pane/frontend/src/components/panels/TurnsPanel.tsx +0 -126
- package/agent-pane/frontend/src/constants/models.test.ts +0 -97
- package/agent-pane/frontend/src/constants/models.ts +0 -166
- package/agent-pane/frontend/src/hooks/useEscapeKey.test.ts +0 -42
- package/agent-pane/frontend/src/hooks/useEscapeKey.ts +0 -13
- package/agent-pane/frontend/src/hooks/useOutsideClick.test.ts +0 -92
- package/agent-pane/frontend/src/hooks/useOutsideClick.ts +0 -19
- package/agent-pane/frontend/src/hooks/useProvider.ts +0 -44
- package/agent-pane/frontend/src/hooks/useResizable.test.ts +0 -97
- package/agent-pane/frontend/src/hooks/useResizable.ts +0 -75
- package/agent-pane/frontend/src/hooks/useSessionStatus.test.ts +0 -116
- package/agent-pane/frontend/src/hooks/useSessionStatus.ts +0 -34
- package/agent-pane/frontend/src/main.test.tsx +0 -37
- package/agent-pane/frontend/src/main.tsx +0 -10
- package/agent-pane/frontend/src/store/sessionSlice.ts +0 -440
- package/agent-pane/frontend/src/store/store.test.ts +0 -1319
- package/agent-pane/frontend/src/store/store.ts +0 -37
- package/agent-pane/frontend/src/store/threadSlice.ts +0 -136
- package/agent-pane/frontend/src/store/uiSlice.ts +0 -133
- package/agent-pane/frontend/src/store/workspaceSlice.ts +0 -77
- package/agent-pane/frontend/src/store/wsSlice.ts +0 -361
- package/agent-pane/frontend/src/styles/app.css +0 -611
- package/agent-pane/frontend/src/styles/tokens.css +0 -62
- package/agent-pane/frontend/src/test/component-helpers.ts +0 -38
- package/agent-pane/frontend/src/test/fixtures.ts +0 -100
- package/agent-pane/frontend/src/test-setup.ts +0 -5
- package/agent-pane/frontend/src/types/index.test.ts +0 -128
- package/agent-pane/frontend/src/types/index.ts +0 -132
- package/agent-pane/frontend/src/utils/changes.test.ts +0 -81
- package/agent-pane/frontend/src/utils/changes.ts +0 -39
- package/agent-pane/frontend/src/utils/duration.test.ts +0 -75
- package/agent-pane/frontend/src/utils/duration.ts +0 -14
- package/agent-pane/frontend/tsconfig.json +0 -25
- package/agent-pane/frontend/vite.config.ts +0 -22
- package/agent-pane/frontend/vitest.config.ts +0 -29
- package/agent-pane/package-lock.json +0 -1527
- package/agent-pane/package.json +0 -19
- package/agent-pane/shared/provider-types.ts +0 -67
- package/agent-pane/shared/types.ts +0 -167
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "agent-pane-frontend",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "vite",
|
|
8
|
-
"build": "tsc --noEmit && vite build",
|
|
9
|
-
"preview": "vite preview",
|
|
10
|
-
"typecheck": "tsc --noEmit",
|
|
11
|
-
"test": "vitest run",
|
|
12
|
-
"test:watch": "vitest"
|
|
13
|
-
},
|
|
14
|
-
"dependencies": {
|
|
15
|
-
"dompurify": "^3.2.0",
|
|
16
|
-
"lucide-react": "^0.469.0",
|
|
17
|
-
"marked": "^15.0.0",
|
|
18
|
-
"react": "^18.3.1",
|
|
19
|
-
"react-dom": "^18.3.1",
|
|
20
|
-
"zustand": "^5.0.0"
|
|
21
|
-
},
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"@testing-library/jest-dom": "^6.4.0",
|
|
24
|
-
"@testing-library/react": "^16.1.0",
|
|
25
|
-
"@types/dompurify": "^3.0.5",
|
|
26
|
-
"@types/react": "^18.3.0",
|
|
27
|
-
"@types/react-dom": "^18.3.0",
|
|
28
|
-
"@vitejs/plugin-react": "^4.3.0",
|
|
29
|
-
"@vitest/coverage-v8": "^3.2.7",
|
|
30
|
-
"jsdom": "^25.0.0",
|
|
31
|
-
"typescript": "^5.7.0",
|
|
32
|
-
"vite": "^6.0.0",
|
|
33
|
-
"vitest": "^3.2.4"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import { render, screen, fireEvent, cleanup } from '@testing-library/react';
|
|
3
|
-
import App from './App.tsx';
|
|
4
|
-
import { resetStore } from './test/component-helpers.ts';
|
|
5
|
-
|
|
6
|
-
// Mock child components that have side effects (WebSocket, init)
|
|
7
|
-
vi.mock('./api/websocket.ts', () => ({
|
|
8
|
-
connectWebSocket: vi.fn(),
|
|
9
|
-
disconnectWebSocket: vi.fn(),
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
// Mock API modules to prevent real network calls
|
|
13
|
-
vi.mock('./api/sessions.ts', () => ({
|
|
14
|
-
fetchSessions: vi.fn(),
|
|
15
|
-
fetchSessionDetail: vi.fn(),
|
|
16
|
-
createSession: vi.fn(),
|
|
17
|
-
deleteSession: vi.fn(),
|
|
18
|
-
submitPrompt: vi.fn(),
|
|
19
|
-
cancelTurn: vi.fn(),
|
|
20
|
-
renameSession: vi.fn(),
|
|
21
|
-
markFinished: vi.fn(),
|
|
22
|
-
reopenSession: vi.fn(),
|
|
23
|
-
clearUnreviewed: vi.fn(),
|
|
24
|
-
rewindSession: vi.fn(),
|
|
25
|
-
branchSession: vi.fn(),
|
|
26
|
-
updateModelConfig: vi.fn(),
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
|
-
vi.mock('./api/workspaces.ts', () => ({
|
|
30
|
-
fetchWorkspaces: vi.fn(),
|
|
31
|
-
createWorkspace: vi.fn(),
|
|
32
|
-
hideWorkspace: vi.fn(),
|
|
33
|
-
deleteWorkspace: vi.fn(),
|
|
34
|
-
pickDirectory: vi.fn(),
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
describe('App', () => {
|
|
38
|
-
beforeEach(() => {
|
|
39
|
-
// Mock init as no-op to prevent real store init from running
|
|
40
|
-
resetStore({ init: vi.fn().mockResolvedValue(undefined) });
|
|
41
|
-
});
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
cleanup();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('shows "Loading sessions…" when loading and no sessions', () => {
|
|
47
|
-
resetStore({ loading: true, sessions: [] });
|
|
48
|
-
render(<App />);
|
|
49
|
-
expect(screen.getByText('Loading sessions…')).toBeInTheDocument();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('shows error message when error and no sessions', () => {
|
|
53
|
-
const init = vi.fn().mockResolvedValue(undefined); // no-op init
|
|
54
|
-
resetStore({ loading: false, error: 'Connection refused', sessions: [], init });
|
|
55
|
-
render(<App />);
|
|
56
|
-
expect(screen.getByText('Error: Connection refused')).toBeInTheDocument();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('renders IdeWorkspace when sessions loaded', () => {
|
|
60
|
-
const session = { id: 's1', title: 'Test', runtime_type: 'codex', model: null, model_reasoning_effort: null, cwd: null, external_thread_id: null, status: 'active' as const, is_running: 0, lifecycle: 'working', has_unreviewed: 0, created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-01T00:00:00Z' };
|
|
61
|
-
resetStore({ loading: false, sessions: [session], openTabs: ['s1'], activeSessionId: 's1' });
|
|
62
|
-
render(<App />);
|
|
63
|
-
// IdeWorkspace renders the workspace sidebar
|
|
64
|
-
expect(document.querySelector('.ide-root')).toBeInTheDocument();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('Cmd+K opens command palette', () => {
|
|
68
|
-
const setShowCommandPalette = vi.fn();
|
|
69
|
-
resetStore({ loading: false, sessions: [], setShowCommandPalette });
|
|
70
|
-
render(<App />);
|
|
71
|
-
fireEvent.keyDown(window, { key: 'k', metaKey: true });
|
|
72
|
-
expect(setShowCommandPalette).toHaveBeenCalledWith(true);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import { useStore } from './store/store.ts';
|
|
3
|
-
import IdeWorkspace from './components/IdeWorkspace.tsx';
|
|
4
|
-
import NewSessionDialog from './components/NewSessionDialog.tsx';
|
|
5
|
-
import CommandPalette from './components/CommandPalette.tsx';
|
|
6
|
-
import Toast from './components/Toast.tsx';
|
|
7
|
-
import AppInitializer from './AppInitializer.tsx';
|
|
8
|
-
|
|
9
|
-
export default function App() {
|
|
10
|
-
const loading = useStore((s) => s.loading);
|
|
11
|
-
const error = useStore((s) => s.error);
|
|
12
|
-
const sessions = useStore((s) => s.sessions);
|
|
13
|
-
const setShowCommandPalette = useStore((s) => s.setShowCommandPalette);
|
|
14
|
-
|
|
15
|
-
// Cmd+K / Ctrl+K listener
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
function handleKeyDown(e: KeyboardEvent) {
|
|
18
|
-
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
19
|
-
e.preventDefault();
|
|
20
|
-
setShowCommandPalette(true);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
window.addEventListener('keydown', handleKeyDown);
|
|
24
|
-
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
25
|
-
}, [setShowCommandPalette]);
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<>
|
|
29
|
-
<AppInitializer />
|
|
30
|
-
{loading && sessions.length === 0 ? (
|
|
31
|
-
<div className="empty-state">Loading sessions…</div>
|
|
32
|
-
) : error && sessions.length === 0 ? (
|
|
33
|
-
<div className="empty-state">Error: {error}</div>
|
|
34
|
-
) : (
|
|
35
|
-
<>
|
|
36
|
-
<IdeWorkspace />
|
|
37
|
-
<NewSessionDialog />
|
|
38
|
-
<CommandPalette />
|
|
39
|
-
<Toast />
|
|
40
|
-
</>
|
|
41
|
-
)}
|
|
42
|
-
</>
|
|
43
|
-
);
|
|
44
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import { render, cleanup } from '@testing-library/react';
|
|
3
|
-
import AppInitializer from './AppInitializer.tsx';
|
|
4
|
-
import { useStore } from './store/store.ts';
|
|
5
|
-
import { resetStore } from './test/component-helpers.ts';
|
|
6
|
-
|
|
7
|
-
// Mock WebSocket module
|
|
8
|
-
vi.mock('./api/websocket.ts', () => ({
|
|
9
|
-
connectWebSocket: vi.fn(),
|
|
10
|
-
disconnectWebSocket: vi.fn(),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
// Mock API modules
|
|
14
|
-
vi.mock('./api/sessions.ts', () => ({
|
|
15
|
-
fetchSessions: vi.fn(),
|
|
16
|
-
fetchSessionDetail: vi.fn(),
|
|
17
|
-
createSession: vi.fn(),
|
|
18
|
-
deleteSession: vi.fn(),
|
|
19
|
-
submitPrompt: vi.fn(),
|
|
20
|
-
cancelTurn: vi.fn(),
|
|
21
|
-
renameSession: vi.fn(),
|
|
22
|
-
markFinished: vi.fn(),
|
|
23
|
-
reopenSession: vi.fn(),
|
|
24
|
-
clearUnreviewed: vi.fn(),
|
|
25
|
-
rewindSession: vi.fn(),
|
|
26
|
-
branchSession: vi.fn(),
|
|
27
|
-
updateModelConfig: vi.fn(),
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
vi.mock('./api/workspaces.ts', () => ({
|
|
31
|
-
fetchWorkspaces: vi.fn(),
|
|
32
|
-
createWorkspace: vi.fn(),
|
|
33
|
-
hideWorkspace: vi.fn(),
|
|
34
|
-
deleteWorkspace: vi.fn(),
|
|
35
|
-
pickDirectory: vi.fn(),
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
/** Helper: set document.visibilityState then dispatch visibilitychange */
|
|
39
|
-
function dispatchVisibility(state: string) {
|
|
40
|
-
Object.defineProperty(document, 'visibilityState', {
|
|
41
|
-
value: state,
|
|
42
|
-
configurable: true,
|
|
43
|
-
});
|
|
44
|
-
document.dispatchEvent(new Event('visibilitychange'));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
describe('AppInitializer', () => {
|
|
48
|
-
beforeEach(() => {
|
|
49
|
-
vi.clearAllMocks();
|
|
50
|
-
resetStore();
|
|
51
|
-
// Restore default visibility
|
|
52
|
-
Object.defineProperty(document, 'visibilityState', {
|
|
53
|
-
value: 'visible',
|
|
54
|
-
configurable: true,
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
afterEach(() => {
|
|
58
|
-
cleanup();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('calls init on mount', () => {
|
|
62
|
-
const init = vi.fn().mockResolvedValue(undefined);
|
|
63
|
-
useStore.setState({ init });
|
|
64
|
-
render(<AppInitializer />);
|
|
65
|
-
expect(init).toHaveBeenCalled();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('calls connectWebSocket on mount', async () => {
|
|
69
|
-
const { connectWebSocket } = await import('./api/websocket.ts');
|
|
70
|
-
render(<AppInitializer />);
|
|
71
|
-
expect(connectWebSocket).toHaveBeenCalled();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('cleanup calls disconnectWebSocket', async () => {
|
|
75
|
-
const { disconnectWebSocket } = await import('./api/websocket.ts');
|
|
76
|
-
const init = vi.fn().mockResolvedValue(undefined);
|
|
77
|
-
useStore.setState({ init });
|
|
78
|
-
const { unmount } = render(<AppInitializer />);
|
|
79
|
-
unmount();
|
|
80
|
-
expect(disconnectWebSocket).toHaveBeenCalled();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('visibilitychange hidden: does not reconnect or refresh', async () => {
|
|
84
|
-
const { connectWebSocket } = await import('./api/websocket.ts');
|
|
85
|
-
const refreshActiveSession = vi.fn();
|
|
86
|
-
const addLogEntry = vi.fn();
|
|
87
|
-
useStore.setState({ refreshActiveSession, addLogEntry });
|
|
88
|
-
render(<AppInitializer />);
|
|
89
|
-
vi.clearAllMocks();
|
|
90
|
-
dispatchVisibility('hidden');
|
|
91
|
-
expect(connectWebSocket).not.toHaveBeenCalled();
|
|
92
|
-
expect(refreshActiveSession).not.toHaveBeenCalled();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('visibilitychange visible with no active session: no reconnect, no refresh', async () => {
|
|
96
|
-
const { connectWebSocket } = await import('./api/websocket.ts');
|
|
97
|
-
const refreshActiveSession = vi.fn();
|
|
98
|
-
useStore.setState({ activeSessionId: null, refreshActiveSession });
|
|
99
|
-
render(<AppInitializer />);
|
|
100
|
-
vi.clearAllMocks();
|
|
101
|
-
dispatchVisibility('visible');
|
|
102
|
-
expect(connectWebSocket).not.toHaveBeenCalled();
|
|
103
|
-
expect(refreshActiveSession).not.toHaveBeenCalled();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('visibilitychange visible with WS down: reconnects, no refresh', async () => {
|
|
107
|
-
const { connectWebSocket } = await import('./api/websocket.ts');
|
|
108
|
-
const refreshActiveSession = vi.fn();
|
|
109
|
-
useStore.setState({
|
|
110
|
-
activeSessionId: 's1',
|
|
111
|
-
wsConnected: false,
|
|
112
|
-
sessionDetails: {},
|
|
113
|
-
refreshActiveSession,
|
|
114
|
-
});
|
|
115
|
-
render(<AppInitializer />);
|
|
116
|
-
vi.clearAllMocks();
|
|
117
|
-
dispatchVisibility('visible');
|
|
118
|
-
expect(connectWebSocket).toHaveBeenCalledTimes(1);
|
|
119
|
-
expect(refreshActiveSession).not.toHaveBeenCalled();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('visibilitychange visible with running session: refreshes and logs', async () => {
|
|
123
|
-
const { connectWebSocket } = await import('./api/websocket.ts');
|
|
124
|
-
const refreshActiveSession = vi.fn();
|
|
125
|
-
const addLogEntry = vi.fn();
|
|
126
|
-
useStore.setState({
|
|
127
|
-
activeSessionId: 's1',
|
|
128
|
-
wsConnected: true,
|
|
129
|
-
sessionDetails: {
|
|
130
|
-
s1: { id: 's1', is_running: 1 } as never,
|
|
131
|
-
},
|
|
132
|
-
refreshActiveSession,
|
|
133
|
-
addLogEntry,
|
|
134
|
-
});
|
|
135
|
-
render(<AppInitializer />);
|
|
136
|
-
vi.clearAllMocks();
|
|
137
|
-
dispatchVisibility('visible');
|
|
138
|
-
expect(refreshActiveSession).toHaveBeenCalledTimes(1);
|
|
139
|
-
expect(addLogEntry).toHaveBeenCalledWith('INFO', 'Tab returned — reconciling session state');
|
|
140
|
-
expect(connectWebSocket).not.toHaveBeenCalled();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import { useStore } from './store/store.ts';
|
|
3
|
-
import { connectWebSocket, disconnectWebSocket } from './api/websocket.ts';
|
|
4
|
-
|
|
5
|
-
export default function AppInitializer() {
|
|
6
|
-
const init = useStore((s) => s.init);
|
|
7
|
-
|
|
8
|
-
useEffect(() => {
|
|
9
|
-
// Fetch initial sessions from REST API
|
|
10
|
-
init();
|
|
11
|
-
|
|
12
|
-
// Connect WebSocket for live streaming
|
|
13
|
-
connectWebSocket();
|
|
14
|
-
|
|
15
|
-
// visibilitychange — reconcile when tab returns to foreground
|
|
16
|
-
function handleVisibilityChange() {
|
|
17
|
-
if (document.visibilityState !== 'visible') return;
|
|
18
|
-
|
|
19
|
-
const state = useStore.getState();
|
|
20
|
-
const activeId = state.activeSessionId;
|
|
21
|
-
if (!activeId) return;
|
|
22
|
-
|
|
23
|
-
const detail = state.sessionDetails[activeId];
|
|
24
|
-
|
|
25
|
-
// If WS is not connected, reconnect
|
|
26
|
-
if (!state.wsConnected) {
|
|
27
|
-
connectWebSocket();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// If session was running, re-fetch to catch missed events
|
|
31
|
-
if (detail && detail.is_running === 1) {
|
|
32
|
-
state.refreshActiveSession();
|
|
33
|
-
state.addLogEntry('INFO', 'Tab returned — reconciling session state');
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
38
|
-
|
|
39
|
-
return () => {
|
|
40
|
-
disconnectWebSocket();
|
|
41
|
-
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
42
|
-
};
|
|
43
|
-
}, [init]);
|
|
44
|
-
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { render, screen, fireEvent, cleanup } from '@testing-library/react';
|
|
3
|
-
import ThreadsPanel from '../components/panels/ThreadsPanel.tsx';
|
|
4
|
-
import { useStore } from '../store/store.ts';
|
|
5
|
-
import { makeSession, makeDetail, makeTurn } from '../test/fixtures.ts';
|
|
6
|
-
import type { DiscussionThread } from '../types/index.ts';
|
|
7
|
-
|
|
8
|
-
const NOW = '2025-01-01T00:00:00Z';
|
|
9
|
-
|
|
10
|
-
function makeThread(id: string, sessionId: string, overrides: Partial<DiscussionThread> = {}): DiscussionThread {
|
|
11
|
-
return {
|
|
12
|
-
id,
|
|
13
|
-
session_id: sessionId,
|
|
14
|
-
item_id: 'i1',
|
|
15
|
-
turn_id: 't1',
|
|
16
|
-
start_offset: 0,
|
|
17
|
-
end_offset: 10,
|
|
18
|
-
selected_text: 'Some selected text',
|
|
19
|
-
status: 'open',
|
|
20
|
-
resolved_at: null,
|
|
21
|
-
created_at: NOW,
|
|
22
|
-
updated_at: NOW,
|
|
23
|
-
external_thread_id: null,
|
|
24
|
-
...overrides,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
describe('ThreadsPanel', () => {
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
// Reset store to a clean state with a session
|
|
31
|
-
useStore.setState({
|
|
32
|
-
sessions: [],
|
|
33
|
-
sessionDetails: {},
|
|
34
|
-
activeSessionId: null,
|
|
35
|
-
openTabs: [],
|
|
36
|
-
activeTurnIndex: 0,
|
|
37
|
-
submittingPrompt: null,
|
|
38
|
-
restoredPrompt: null,
|
|
39
|
-
activeRightTab: 'threads',
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
afterEach(() => {
|
|
44
|
-
cleanup();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('shows empty state when no session selected', () => {
|
|
48
|
-
render(<ThreadsPanel />);
|
|
49
|
-
expect(screen.getByText('No session selected')).toBeInTheDocument();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('shows empty state with instructions when no threads exist', () => {
|
|
53
|
-
const session = makeSession('s1');
|
|
54
|
-
const detail = makeDetail('s1', []);
|
|
55
|
-
useStore.setState({
|
|
56
|
-
sessions: [session],
|
|
57
|
-
sessionDetails: { s1: detail },
|
|
58
|
-
activeSessionId: 's1',
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
render(<ThreadsPanel />);
|
|
62
|
-
expect(screen.getByText('No threads yet.')).toBeInTheDocument();
|
|
63
|
-
expect(screen.getByText(/Select text in an agent response/)).toBeInTheDocument();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('renders an open thread card with quoted text', () => {
|
|
67
|
-
const thread = makeThread('th1', 's1');
|
|
68
|
-
const detail = makeDetail('s1', [], { threads: [thread] });
|
|
69
|
-
|
|
70
|
-
useStore.setState({
|
|
71
|
-
sessions: [makeSession('s1')],
|
|
72
|
-
sessionDetails: { s1: detail },
|
|
73
|
-
activeSessionId: 's1',
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
render(<ThreadsPanel />);
|
|
77
|
-
expect(screen.getByText(/Some selected text/)).toBeInTheDocument();
|
|
78
|
-
expect(screen.getByText('Open')).toBeInTheDocument();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('shows filter tabs with counts', () => {
|
|
82
|
-
const openThread = makeThread('th1', 's1', { status: 'open' });
|
|
83
|
-
const resolvedThread = makeThread('th2', 's1', { status: 'resolved' });
|
|
84
|
-
const detail = makeDetail('s1', [], { threads: [openThread, resolvedThread] });
|
|
85
|
-
|
|
86
|
-
useStore.setState({
|
|
87
|
-
sessions: [makeSession('s1')],
|
|
88
|
-
sessionDetails: { s1: detail },
|
|
89
|
-
activeSessionId: 's1',
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
render(<ThreadsPanel />);
|
|
93
|
-
expect(screen.getByText(/Open \(1\)/)).toBeInTheDocument();
|
|
94
|
-
expect(screen.getByText(/Resolved \(1\)/)).toBeInTheDocument();
|
|
95
|
-
expect(screen.getByText(/All \(2\)/)).toBeInTheDocument();
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('filters threads by resolved status', () => {
|
|
99
|
-
const openThread = makeThread('th1', 's1', { status: 'open', selected_text: 'Open thread text' });
|
|
100
|
-
const resolvedThread = makeThread('th2', 's1', { status: 'resolved', selected_text: 'Resolved thread text' });
|
|
101
|
-
const detail = makeDetail('s1', [], { threads: [openThread, resolvedThread] });
|
|
102
|
-
|
|
103
|
-
useStore.setState({
|
|
104
|
-
sessions: [makeSession('s1')],
|
|
105
|
-
sessionDetails: { s1: detail },
|
|
106
|
-
activeSessionId: 's1',
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
render(<ThreadsPanel />);
|
|
110
|
-
// Click "Resolved" filter
|
|
111
|
-
fireEvent.click(screen.getByText(/Resolved/));
|
|
112
|
-
expect(screen.getByText(/Resolved thread text/)).toBeInTheDocument();
|
|
113
|
-
expect(screen.queryByText(/Open thread text/)).not.toBeInTheDocument();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('shows thread conversation history from turns', () => {
|
|
117
|
-
const thread = makeThread('th1', 's1');
|
|
118
|
-
const threadTurn = makeTurn('tt1', 's1', {
|
|
119
|
-
thread_id: 'th1',
|
|
120
|
-
user_prompt: '[Quoting from your previous response]:\n> Some text\n\nWhat is this?',
|
|
121
|
-
status: 'completed',
|
|
122
|
-
items: [{
|
|
123
|
-
id: 'ti1',
|
|
124
|
-
turn_id: 'tt1',
|
|
125
|
-
external_item_id: 'ext-ti1',
|
|
126
|
-
type: 'agent_message' as const,
|
|
127
|
-
data: { text: 'This is an explanation.' },
|
|
128
|
-
status: 'completed' as const,
|
|
129
|
-
created_at: NOW,
|
|
130
|
-
updated_at: NOW,
|
|
131
|
-
}],
|
|
132
|
-
});
|
|
133
|
-
const detail = makeDetail('s1', [threadTurn], { threads: [thread] });
|
|
134
|
-
|
|
135
|
-
useStore.setState({
|
|
136
|
-
sessions: [makeSession('s1')],
|
|
137
|
-
sessionDetails: { s1: detail },
|
|
138
|
-
activeSessionId: 's1',
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
render(<ThreadsPanel />);
|
|
142
|
-
// User message (with quote prefix stripped)
|
|
143
|
-
expect(screen.getByText('What is this?')).toBeInTheDocument();
|
|
144
|
-
// AI response
|
|
145
|
-
expect(screen.getByText('This is an explanation.')).toBeInTheDocument();
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('shows follow-up input for open threads', () => {
|
|
149
|
-
const thread = makeThread('th1', 's1', { status: 'open' });
|
|
150
|
-
const detail = makeDetail('s1', [], { threads: [thread] });
|
|
151
|
-
|
|
152
|
-
useStore.setState({
|
|
153
|
-
sessions: [makeSession('s1')],
|
|
154
|
-
sessionDetails: { s1: detail },
|
|
155
|
-
activeSessionId: 's1',
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
render(<ThreadsPanel />);
|
|
159
|
-
expect(screen.getByPlaceholderText('Type a follow-up…')).toBeInTheDocument();
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('shows resolve and delete actions for open threads', () => {
|
|
163
|
-
const thread = makeThread('th1', 's1', { status: 'open' });
|
|
164
|
-
const detail = makeDetail('s1', [], { threads: [thread] });
|
|
165
|
-
|
|
166
|
-
useStore.setState({
|
|
167
|
-
sessions: [makeSession('s1')],
|
|
168
|
-
sessionDetails: { s1: detail },
|
|
169
|
-
activeSessionId: 's1',
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
render(<ThreadsPanel />);
|
|
173
|
-
expect(screen.getByText('Resolve')).toBeInTheDocument();
|
|
174
|
-
expect(screen.getByText('Delete')).toBeInTheDocument();
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('shows reopen action for resolved threads', () => {
|
|
178
|
-
const thread = makeThread('th1', 's1', { status: 'resolved' });
|
|
179
|
-
const detail = makeDetail('s1', [], { threads: [thread] });
|
|
180
|
-
|
|
181
|
-
useStore.setState({
|
|
182
|
-
sessions: [makeSession('s1')],
|
|
183
|
-
sessionDetails: { s1: detail },
|
|
184
|
-
activeSessionId: 's1',
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
render(<ThreadsPanel />);
|
|
188
|
-
// Switch to "All" filter to see resolved threads
|
|
189
|
-
fireEvent.click(screen.getByText(/All/));
|
|
190
|
-
expect(screen.getByText('Reopen')).toBeInTheDocument();
|
|
191
|
-
});
|
|
192
|
-
});
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import { apiGet, apiPost, apiDelete, apiPatch } from './client.ts';
|
|
3
|
-
|
|
4
|
-
describe('apiGet', () => {
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
global.fetch = vi.fn();
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it('sends GET request and returns parsed JSON', async () => {
|
|
10
|
-
(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
11
|
-
ok: true,
|
|
12
|
-
json: () => Promise.resolve({ id: '123' }),
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const result = await apiGet('/sessions');
|
|
16
|
-
expect(result).toEqual({ id: '123' });
|
|
17
|
-
expect(global.fetch).toHaveBeenCalledWith('/api/sessions', {
|
|
18
|
-
method: 'GET',
|
|
19
|
-
headers: undefined,
|
|
20
|
-
body: undefined,
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('apiPost', () => {
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
global.fetch = vi.fn();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('sends POST with JSON body and Content-Type header', async () => {
|
|
31
|
-
(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
32
|
-
ok: true,
|
|
33
|
-
json: () => Promise.resolve({ success: true }),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const result = await apiPost('/sessions', { title: 'Test' });
|
|
37
|
-
expect(result).toEqual({ success: true });
|
|
38
|
-
expect(global.fetch).toHaveBeenCalledWith('/api/sessions', {
|
|
39
|
-
method: 'POST',
|
|
40
|
-
headers: { 'Content-Type': 'application/json' },
|
|
41
|
-
body: JSON.stringify({ title: 'Test' }),
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('sends POST without body when body is undefined', async () => {
|
|
46
|
-
(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
47
|
-
ok: true,
|
|
48
|
-
json: () => Promise.resolve({ ok: true }),
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
await apiPost('/sessions/1/cancel');
|
|
52
|
-
expect(global.fetch).toHaveBeenCalledWith('/api/sessions/1/cancel', {
|
|
53
|
-
method: 'POST',
|
|
54
|
-
headers: undefined,
|
|
55
|
-
body: undefined,
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('apiPatch', () => {
|
|
61
|
-
beforeEach(() => {
|
|
62
|
-
global.fetch = vi.fn();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('sends PATCH with JSON body', async () => {
|
|
66
|
-
(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
67
|
-
ok: true,
|
|
68
|
-
json: () => Promise.resolve({ id: '1', title: 'Updated' }),
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const result = await apiPatch('/sessions/1', { title: 'Updated' });
|
|
72
|
-
expect(result).toEqual({ id: '1', title: 'Updated' });
|
|
73
|
-
expect(global.fetch).toHaveBeenCalledWith('/api/sessions/1', {
|
|
74
|
-
method: 'PATCH',
|
|
75
|
-
headers: { 'Content-Type': 'application/json' },
|
|
76
|
-
body: JSON.stringify({ title: 'Updated' }),
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe('apiDelete', () => {
|
|
82
|
-
beforeEach(() => {
|
|
83
|
-
global.fetch = vi.fn();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('sends DELETE request', async () => {
|
|
87
|
-
(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
88
|
-
ok: true,
|
|
89
|
-
json: () => Promise.resolve({ success: true }),
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const result = await apiDelete('/sessions/1');
|
|
93
|
-
expect(result).toEqual({ success: true });
|
|
94
|
-
expect(global.fetch).toHaveBeenCalledWith('/api/sessions/1', {
|
|
95
|
-
method: 'DELETE',
|
|
96
|
-
headers: undefined,
|
|
97
|
-
body: undefined,
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
describe('error handling', () => {
|
|
103
|
-
beforeEach(() => {
|
|
104
|
-
global.fetch = vi.fn();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('throws with method, path, and status on non-ok response', async () => {
|
|
108
|
-
(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
109
|
-
ok: false,
|
|
110
|
-
status: 404,
|
|
111
|
-
statusText: 'Not Found',
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
await expect(apiGet('/sessions/missing')).rejects.toThrow('GET /sessions/missing failed: 404 Not Found');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('throws for POST error', async () => {
|
|
118
|
-
(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
119
|
-
ok: false,
|
|
120
|
-
status: 500,
|
|
121
|
-
statusText: 'Internal Server Error',
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
await expect(apiPost('/sessions', {})).rejects.toThrow('POST /sessions failed: 500 Internal Server Error');
|
|
125
|
-
});
|
|
126
|
-
});
|