@loicngr/kobo 1.11.34 → 1.11.36
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/AGENTS.md +8 -0
- package/CHANGELOG.md +9 -0
- package/dist/server/db/migrations.js +14 -0
- package/dist/server/db/schema.js +9 -0
- package/dist/server/index.js +2 -0
- package/dist/server/routes/activity.js +17 -0
- package/dist/server/services/activity-service.js +62 -0
- package/dist/server/services/agent/engines/claude-code/engine.js +26 -0
- package/dist/server/services/agent/engines/claude-code/event-mapper.js +10 -10
- package/dist/server/services/agent/orchestrator.js +29 -0
- package/dist/server/services/auto-loop-service.js +14 -3
- package/dist/server/services/wakeup-service.js +14 -0
- package/dist/server/services/websocket-service.js +3 -0
- package/package.json +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-BPx-xhFY.js +8 -0
- package/src/client/dist/pwa/assets/ChangelogPage-BFGYQ6eY.js +1 -0
- package/src/client/dist/pwa/assets/ClosePopup-BqW5dy-O.js +1 -0
- package/src/client/dist/pwa/assets/ComparisonPanel-C6DfjpZZ.js +2 -0
- package/src/client/dist/pwa/assets/CreatePage-ypEzkAKv.js +2 -0
- package/src/client/dist/pwa/assets/DashboardPage-DsVRjzzy.js +1 -0
- package/src/client/dist/pwa/assets/DiffViewer-DSlSAIiW.js +8 -0
- package/src/client/dist/pwa/assets/DocumentsPanel-C1eXTw_Q.js +1 -0
- package/src/client/dist/pwa/assets/DrawerToggleButton-BxYpUb4y.js +1 -0
- package/src/client/dist/pwa/assets/{GitPanel-De5_E4NA.css → GitPanel-BFCnyhE2.css} +1 -1
- package/src/client/dist/pwa/assets/GitPanel-DnBWSdVp.js +3 -0
- package/src/client/dist/pwa/assets/HealthPage-rX1TiaP9.js +1 -0
- package/src/client/dist/pwa/assets/MainLayout-Ceuh5uJj.js +2 -0
- package/src/client/dist/pwa/assets/MainLayout-DxMVVA6f.css +1 -0
- package/src/client/dist/pwa/assets/{QBadge-CWEaKfKi.js → QBadge-DbkxGtht.js} +1 -1
- package/src/client/dist/pwa/assets/{QBanner-DRiFK0GP.js → QBanner-DhqH95GW.js} +1 -1
- package/src/client/dist/pwa/assets/QBtn-CZaBclNw.js +1 -0
- package/src/client/dist/pwa/assets/QBtnDropdown-K7syiyyH.js +1 -0
- package/src/client/dist/pwa/assets/{QBtnGroup-BY6XqeTJ.js → QBtnGroup-DELKYo3u.js} +1 -1
- package/src/client/dist/pwa/assets/{QBtnToggle-CCgwqSgg.js → QBtnToggle-BI3k3Ch2.js} +1 -1
- package/src/client/dist/pwa/assets/{QChip-CJ6WmvGE.js → QChip-owciRuZu.js} +1 -1
- package/src/client/dist/pwa/assets/QDrawer-DapzE8DH.js +1 -0
- package/src/client/dist/pwa/assets/QItemSection-D_5_0UrX.js +1 -0
- package/src/client/dist/pwa/assets/QList-qupzuEqO.js +1 -0
- package/src/client/dist/pwa/assets/{QMarkupTable-MxPL53nz.js → QMarkupTable-B9QuKtK_.js} +1 -1
- package/src/client/dist/pwa/assets/QPage-DaX9sMQC.js +1 -0
- package/src/client/dist/pwa/assets/QSelect-Bodml89k.js +1 -0
- package/src/client/dist/pwa/assets/{QSpinnerDots-6v3Yl0T6.js → QSpinnerDots-DnCV3VzI.js} +1 -1
- package/src/client/dist/pwa/assets/QTooltip-K-9OdDfq.js +1 -0
- package/src/client/dist/pwa/assets/SchedulePanel-CfumTzAa.js +1 -0
- package/src/client/dist/pwa/assets/SearchPage-AhZYS-dS.js +1 -0
- package/src/client/dist/pwa/assets/SettingsPage-BoHXWvKn.js +9 -0
- package/src/client/dist/pwa/assets/{SettingsPage-CDA54yPx.css → SettingsPage-Do9d0Pov.css} +1 -1
- package/src/client/dist/pwa/assets/SplitWorkspacePage-3wK11mcu.js +1 -0
- package/src/client/dist/pwa/assets/SplitWorkspacePage-lWQO8xro.css +1 -0
- package/src/client/dist/pwa/assets/{TerminalPanel-BzUFE4eS.js → TerminalPanel-CKg0BeNK.js} +9 -9
- package/src/client/dist/pwa/assets/TouchPan-D6ctyPYf.js +1 -0
- package/src/client/dist/pwa/assets/TourReplayButton-Dn4Dy_kH.js +1 -0
- package/src/client/dist/pwa/assets/WorkspaceAttentionLabels-DnoabsfK.js +1 -0
- package/src/client/dist/pwa/assets/WorkspacePage-BI7CW2yT.js +4 -0
- package/src/client/dist/pwa/assets/{WorkspacePage-ygEb8olx.css → WorkspacePage-DYQrfeYw.css} +1 -1
- package/src/client/dist/pwa/assets/{browser-pf20JpKz.js → browser-DHpSUK8-.js} +1 -1
- package/src/client/dist/pwa/assets/build-path-tree-DtEjsZlh.js +1 -0
- package/src/client/dist/pwa/assets/de-Dt_m-H83.js +17 -0
- package/src/client/dist/pwa/assets/{documents-C1u794ea.js → documents-B2dg1wRf.js} +1 -1
- package/src/client/dist/pwa/assets/{editor.api-BaRghNyI.js → editor.api-Cd1D35oj.js} +1 -1
- package/src/client/dist/pwa/assets/{efforts-EHbgZlde.js → efforts-IB1vPjJQ.js} +2 -2
- package/src/client/dist/pwa/assets/engineFeatures-DYSyDEJe.js +1 -0
- package/src/client/dist/pwa/assets/es-DFg25DCS.js +17 -0
- package/src/client/dist/pwa/assets/{expand-template-D7OUa8G7.js → expand-template-CNAHiesL.js} +1 -1
- package/src/client/dist/pwa/assets/{format-D3dPFA-T.js → format-DidwgbbX.js} +7 -7
- package/src/client/dist/pwa/assets/{formatters-DBdoGhB7.js → formatters-BjzyUHlu.js} +1 -1
- package/src/client/dist/pwa/assets/fr-CQ1slpol.js +17 -0
- package/src/client/dist/pwa/assets/{freemarker2-DI7u6MdV.js → freemarker2-jNGWawVw.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-BgkIQIEY.js → handlebars-DfSqClrW.js} +1 -1
- package/src/client/dist/pwa/assets/{html-BOPtZli3.js → html-ByLX-n8T.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-CnjXQjxy.js +1 -0
- package/src/client/dist/pwa/assets/index-DcwcFDrZ.js +20 -0
- package/src/client/dist/pwa/assets/it-w09rfaP7.js +17 -0
- package/src/client/dist/pwa/assets/{javascript-Cf6-AeMO.js → javascript-Dja1fHfv.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-Bd9X87K-.js → liquid-D315hW6C.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-C_ObhhDT.js → mdx-B5yUwpzv.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-rHxRMwAP.js → monaco-contributions-NfxrU0r3.js} +1 -1
- package/src/client/dist/pwa/assets/monaco.contribution-D1hXPkDZ.js +2 -0
- package/src/client/dist/pwa/assets/network-auth-CARVtbNe.js +1 -0
- package/src/client/dist/pwa/assets/notify-theme-CKC3-EAZ.js +1 -0
- package/src/client/dist/pwa/assets/pending-diff-open-BlLrQaW7.js +1 -0
- package/src/client/dist/pwa/assets/permissionModes-DMzFEQhr.js +36 -0
- package/src/client/dist/pwa/assets/{pinia-jcytgarH.js → pinia-BKqmoVru.js} +1 -1
- package/src/client/dist/pwa/assets/{private.use-form-DVqJLg-9.js → private.use-form-C7umbGO-.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-CoEJs17L.js → project-color-CFjdIj6W.js} +1 -1
- package/src/client/dist/pwa/assets/{python-BIG7FEi1.js → python-Dc6mnEip.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-CNJSV12m.js → razor-DArVFBGO.js} +1 -1
- package/src/client/dist/pwa/assets/{render-chat-markdown-Ddq-hxhM.js → render-chat-markdown-BTPAjU2g.js} +1 -1
- package/src/client/dist/pwa/assets/runtime-core.esm-bundler-CDnVUfeZ.js +1 -0
- package/src/client/dist/pwa/assets/{typescript-Blrg4f-0.js → typescript-COE1vIyB.js} +1 -1
- package/src/client/dist/pwa/assets/{use-checkbox-DDRAnhSJ.js → use-checkbox-FnauAgTp.js} +1 -1
- package/src/client/dist/pwa/assets/use-id-BIuaWKkB.js +1 -0
- package/src/client/dist/pwa/assets/use-is-mobile-CWjqe8gC.js +1 -0
- package/src/client/dist/pwa/assets/use-tours-C8x2V1w5.js +2 -0
- package/src/client/dist/pwa/assets/workspace-page-layout-Dw9NhZsL.js +1 -0
- package/src/client/dist/pwa/assets/{workspace-templates-BS34DvEp.js → workspace-templates-DHyA_GEW.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-Bv6_W6i5.js → xml-eYjr4cGR.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-DH7PMAq0.js → yaml-UooOcssF.js} +1 -1
- package/src/client/dist/pwa/index.html +7 -6
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-CGb-YHB0.js +0 -8
- package/src/client/dist/pwa/assets/ChangelogPage-BDVpqtWz.js +0 -1
- package/src/client/dist/pwa/assets/ClosePopup-ClH5KG_w.js +0 -1
- package/src/client/dist/pwa/assets/ComparisonPanel-D6kz5Q-o.js +0 -2
- package/src/client/dist/pwa/assets/CreatePage-f-C_R4o9.js +0 -2
- package/src/client/dist/pwa/assets/DashboardPage-C_9_IXdR.js +0 -1
- package/src/client/dist/pwa/assets/DiffViewer-CMjfVpAG.js +0 -8
- package/src/client/dist/pwa/assets/DocumentsPanel-Yim06L6k.js +0 -1
- package/src/client/dist/pwa/assets/DrawerToggleButton-aD_LMlna.js +0 -1
- package/src/client/dist/pwa/assets/GitPanel-x-whHRdM.js +0 -3
- package/src/client/dist/pwa/assets/HealthPage-BNZP_1l-.js +0 -1
- package/src/client/dist/pwa/assets/MainLayout-BMxAagOe.js +0 -2
- package/src/client/dist/pwa/assets/MainLayout-DfddEd0b.css +0 -1
- package/src/client/dist/pwa/assets/QBtn-DqZryKOh.js +0 -1
- package/src/client/dist/pwa/assets/QBtnDropdown-BLNdi1w5.js +0 -1
- package/src/client/dist/pwa/assets/QDrawer-C6TAn5dv.js +0 -1
- package/src/client/dist/pwa/assets/QItemSection-CjrpcdN_.js +0 -1
- package/src/client/dist/pwa/assets/SchedulePanel-D2HyVnU8.js +0 -1
- package/src/client/dist/pwa/assets/SearchPage-BG_zXo6V.js +0 -1
- package/src/client/dist/pwa/assets/SettingsPage-BNBeaT9t.js +0 -9
- package/src/client/dist/pwa/assets/TouchPan-CK2rmJkA.js +0 -1
- package/src/client/dist/pwa/assets/TourReplayButton-DHFo5_Kk.js +0 -1
- package/src/client/dist/pwa/assets/WorkspaceAttentionLabels-BbtPN7pb.js +0 -1
- package/src/client/dist/pwa/assets/WorkspacePage-Aay3kkPT.js +0 -4
- package/src/client/dist/pwa/assets/build-path-tree-BRLZv-pU.js +0 -1
- package/src/client/dist/pwa/assets/de-CG7RYRLI.js +0 -17
- package/src/client/dist/pwa/assets/engineFeatures-DyKNpIKw.js +0 -1
- package/src/client/dist/pwa/assets/es-DfpfEjEu.js +0 -17
- package/src/client/dist/pwa/assets/fr-DgzZRmjM.js +0 -17
- package/src/client/dist/pwa/assets/i18n-Butermk0.js +0 -1
- package/src/client/dist/pwa/assets/index-BATViFoR.js +0 -20
- package/src/client/dist/pwa/assets/it-DKzqhjlT.js +0 -17
- package/src/client/dist/pwa/assets/monaco.contribution-DkNJdReE.js +0 -2
- package/src/client/dist/pwa/assets/network-auth-CyD51r9F.js +0 -1
- package/src/client/dist/pwa/assets/notify-theme-Bnw3Xbxg.js +0 -1
- package/src/client/dist/pwa/assets/pending-diff-open-DsW49A6Z.js +0 -1
- package/src/client/dist/pwa/assets/permissionModes-mF8Kofv1.js +0 -36
- package/src/client/dist/pwa/assets/runtime-core.esm-bundler-CS0xkoG_.js +0 -1
- package/src/client/dist/pwa/assets/use-is-mobile-ClPMtqZc.js +0 -1
- package/src/client/dist/pwa/assets/use-tours-Cz5Zz63W.js +0 -2
package/AGENTS.md
CHANGED
|
@@ -265,6 +265,14 @@ Sequence: `captureRestoreData` (best-effort forge lookup for PR number / URL / m
|
|
|
265
265
|
|
|
266
266
|
**Restore**: `POST /api/workspaces/:id/restore-worktree` calls `worktree-restore-service.ts` to recreate the exact checkout and only then clear purge/archive metadata via `restoreWorktreeFromDisk`. Source order: surviving local branch → optional `headCommitSha` in the existing restore JSON → exact branch fetched from `origin`. No SQL migration is needed for the optional JSON key; older records remain supported. No reset, overwrite, agent/dev-server start or setup script. `workspace-lifecycle-guard.ts` excludes overlapping restore/purge/delete operations, while the existing common-Git-directory lock serializes Git mutations. The watcher shares `isMatchingWorkspaceWorktree` validation and invalidates in-flight PR checks on restoration; delayed auto-purges carry their expected archive timestamp to avoid purging a workspace restored in the meantime. The context menu and purged banner expose the action; HTTP and the existing `workspace:worktree-restored` event reconcile client lists. Discarded uncommitted/ignored files and dependencies are not recoverable.
|
|
267
267
|
|
|
268
|
+
### Workspace navigation and activity digest
|
|
269
|
+
|
|
270
|
+
`utils/workspace-sort.ts` applies the browser-persisted sort within each drawer group; fuzzy search relevance remains primary. `ActionAvailability.vue` pairs disabled controls with visible, focusable explanations derived from `utils/action-blocker.ts`.
|
|
271
|
+
|
|
272
|
+
`SplitWorkspacePage.vue` hosts two same-origin embedded clients (`?pane=1`), isolating routers, stores and terminal registries. `utils/split-workspace.ts` and the router bridge synchronize the host URL without treating that bookkeeping as a departure. Actual departures consult both panes for dirty edits, unsent drafts and all in-memory queued messages. Embedded clients suppress automatic tours and browser/audio notifications; the host owns these.
|
|
273
|
+
|
|
274
|
+
`activity-service.ts` records significant metadata from `emit` and `emitEphemeral` in `workspace_activity` (migration v41, 30-day retention). It excludes streaming data and superseded session endings. `/api/activity` exposes a paginated monotonic cursor; `stores/activity.ts` keeps browser-local visit checkpoints and protects against late responses after visibility changes. `ActivityDigest.vue` links events to workspace/session or Git context. Never advance an unread checkpoint to the head of unloaded pages.
|
|
275
|
+
|
|
268
276
|
### Workspace attention indicators
|
|
269
277
|
|
|
270
278
|
`src/client/src/utils/workspace-attention.ts` derives a small set of badges (CI failure, changes-requested) from the PR snapshot + git stats stored on each workspace. `WorkspaceAttentionLabels.vue` renders them inline on the workspace cards in the left drawer. The derivation is a pure function, easy to unit-test and free of IO. Drawer cards therefore stay reactive to whatever the pr-watcher / bulk-info refresh writes back into the store.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ All notable changes to Kōbō are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/). Each release is an `## <version>`
|
|
5
5
|
section — the in-app "What's new" dialog reads this file.
|
|
6
6
|
|
|
7
|
+
## 1.11.36
|
|
8
|
+
|
|
9
|
+
- fix(agent): resume quota waits and scheduled background checks
|
|
10
|
+
- ✨ feat(quota): Améliorer la détection et récupération des erreurs de limite de quota
|
|
11
|
+
|
|
12
|
+
## 1.11.35
|
|
13
|
+
|
|
14
|
+
- feat(workspaces): add split view, activity digest and compact toolbar
|
|
15
|
+
|
|
7
16
|
## 1.11.34
|
|
8
17
|
|
|
9
18
|
- feat(workspaces): restore purged workspaces and improve startup display
|
|
@@ -726,6 +726,20 @@ export const migrations = [
|
|
|
726
726
|
}
|
|
727
727
|
},
|
|
728
728
|
},
|
|
729
|
+
{
|
|
730
|
+
version: 41,
|
|
731
|
+
name: 'add-workspace-activity',
|
|
732
|
+
migrate: (db) => {
|
|
733
|
+
db.exec(`CREATE TABLE IF NOT EXISTS workspace_activity (
|
|
734
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
735
|
+
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
736
|
+
kind TEXT NOT NULL,
|
|
737
|
+
session_id TEXT,
|
|
738
|
+
created_at TEXT NOT NULL
|
|
739
|
+
);
|
|
740
|
+
CREATE INDEX IF NOT EXISTS idx_workspace_activity_created ON workspace_activity(created_at);`);
|
|
741
|
+
},
|
|
742
|
+
},
|
|
729
743
|
];
|
|
730
744
|
/** Current schema version — always equals the highest migration version. */
|
|
731
745
|
export const SCHEMA_VERSION = migrations.length > 0 ? migrations[migrations.length - 1].version : 1;
|
package/dist/server/db/schema.js
CHANGED
|
@@ -192,6 +192,15 @@ export function initSchema(db) {
|
|
|
192
192
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
193
193
|
);
|
|
194
194
|
|
|
195
|
+
CREATE TABLE IF NOT EXISTS workspace_activity (
|
|
196
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
197
|
+
workspace_id TEXT NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
198
|
+
kind TEXT NOT NULL,
|
|
199
|
+
session_id TEXT,
|
|
200
|
+
created_at TEXT NOT NULL
|
|
201
|
+
);
|
|
202
|
+
CREATE INDEX IF NOT EXISTS idx_workspace_activity_created ON workspace_activity(created_at);
|
|
203
|
+
|
|
195
204
|
CREATE INDEX IF NOT EXISTS idx_tasks_workspace_id ON tasks(workspace_id);
|
|
196
205
|
CREATE INDEX IF NOT EXISTS idx_agent_sessions_workspace_id ON agent_sessions(workspace_id);
|
|
197
206
|
CREATE INDEX IF NOT EXISTS idx_ws_events_workspace_id ON ws_events(workspace_id);
|
package/dist/server/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { closeDb, getDb } from './db/index.js';
|
|
|
8
8
|
import { getPendingMigrations, runMigrations } from './db/migrations.js';
|
|
9
9
|
import { hostCheckMiddleware } from './middleware/host-check-middleware.js';
|
|
10
10
|
import { networkAuthMiddleware } from './middleware/network-auth-middleware.js';
|
|
11
|
+
import activityRouter from './routes/activity.js';
|
|
11
12
|
import changelogRouter from './routes/changelog.js';
|
|
12
13
|
import devServerRouter from './routes/dev-server.js';
|
|
13
14
|
import documentsRouter from './routes/documents.js';
|
|
@@ -163,6 +164,7 @@ app.route('/api/sentry', sentryRouter);
|
|
|
163
164
|
app.route('/api/git', gitRouter);
|
|
164
165
|
app.route('/api/fs', fsRouter);
|
|
165
166
|
app.route('/api/changelog', changelogRouter);
|
|
167
|
+
app.route('/api/activity', activityRouter);
|
|
166
168
|
app.route('/api/settings', settingsRouter);
|
|
167
169
|
app.route('/api/dev-server', devServerRouter);
|
|
168
170
|
app.route('/api/templates', templatesRouter);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { activityCursor, listActivity } from '../services/activity-service.js';
|
|
3
|
+
const app = new Hono();
|
|
4
|
+
app.get('/', (c) => {
|
|
5
|
+
try {
|
|
6
|
+
if (c.req.query('head') === '1')
|
|
7
|
+
return c.json({ cursor: activityCursor() });
|
|
8
|
+
const raw = c.req.query('after') ?? '0';
|
|
9
|
+
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(Number(raw)))
|
|
10
|
+
return c.json({ error: 'after must be a non-negative integer' }, 400);
|
|
11
|
+
return c.json(listActivity(Number(raw)));
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
export default app;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getDb } from '../db/index.js';
|
|
2
|
+
export function classifyActivity(type, payload) {
|
|
3
|
+
const event = payload && typeof payload === 'object' ? payload : {};
|
|
4
|
+
if (type === 'agent:event') {
|
|
5
|
+
if (event.kind === 'session:ended' && event.superseded)
|
|
6
|
+
return null;
|
|
7
|
+
if (event.kind === 'session:user-input-requested')
|
|
8
|
+
return event.requestKind === 'permission' ? 'permission' : 'question';
|
|
9
|
+
if (event.kind === 'error')
|
|
10
|
+
return 'error';
|
|
11
|
+
if (event.kind === 'session:ended' && event.reason === 'completed')
|
|
12
|
+
return 'completed';
|
|
13
|
+
if (event.kind === 'session:ended' && (event.reason === 'error' || event.reason === 'watchdog'))
|
|
14
|
+
return 'error';
|
|
15
|
+
}
|
|
16
|
+
const kinds = {
|
|
17
|
+
'pr:merged': 'pr-merged',
|
|
18
|
+
'pr:approved': 'pr-approved',
|
|
19
|
+
'pr:changes-requested': 'pr-changes-requested',
|
|
20
|
+
'pr:ci-failed': 'pr-ci-failed',
|
|
21
|
+
'pr:ci-recovered': 'pr-ci-recovered',
|
|
22
|
+
'pr:merge-conflict': 'pr-merge-conflict',
|
|
23
|
+
'pr:ready-to-merge': 'pr-ready-to-merge',
|
|
24
|
+
'workspace:archived': 'archived',
|
|
25
|
+
'workspace:worktree-purged': 'purged',
|
|
26
|
+
'workspace:worktree-restored': 'restored',
|
|
27
|
+
};
|
|
28
|
+
return kinds[type] ?? null;
|
|
29
|
+
}
|
|
30
|
+
/** Compact metadata only; failure must never interrupt agent/event delivery. */
|
|
31
|
+
export function recordActivity(workspaceId, type, payload, sessionId, db) {
|
|
32
|
+
const kind = classifyActivity(type, payload);
|
|
33
|
+
if (!kind)
|
|
34
|
+
return;
|
|
35
|
+
try {
|
|
36
|
+
const database = db ?? getDb();
|
|
37
|
+
database
|
|
38
|
+
.prepare('INSERT INTO workspace_activity (workspace_id, kind, session_id, created_at) VALUES (?, ?, ?, ?)')
|
|
39
|
+
.run(workspaceId, kind, sessionId ?? null, new Date().toISOString());
|
|
40
|
+
// Bounded even when no browser is connected. Only significant events reach here.
|
|
41
|
+
database
|
|
42
|
+
.prepare('DELETE FROM workspace_activity WHERE created_at < ?')
|
|
43
|
+
.run(new Date(Date.now() - 30 * 86400_000).toISOString());
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('[activity] Could not record workspace activity:', error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function activityCursor(db = getDb()) {
|
|
50
|
+
return (db.prepare("SELECT seq FROM sqlite_sequence WHERE name = 'workspace_activity'").get()?.seq ?? 0);
|
|
51
|
+
}
|
|
52
|
+
export function listActivity(after, limit = 200, db = getDb()) {
|
|
53
|
+
limit = Math.min(200, Math.max(1, limit));
|
|
54
|
+
const rows = db
|
|
55
|
+
.prepare(`SELECT a.id, a.workspace_id AS workspaceId, w.name AS workspaceName,
|
|
56
|
+
a.kind, a.session_id AS sessionId, a.created_at AS createdAt
|
|
57
|
+
FROM workspace_activity a JOIN workspaces w ON w.id = a.workspace_id
|
|
58
|
+
WHERE a.id > ? AND a.created_at >= ? ORDER BY a.id ASC LIMIT ?`)
|
|
59
|
+
.all(after, new Date(Date.now() - 30 * 86400_000).toISOString(), limit + 1);
|
|
60
|
+
const items = rows.slice(0, limit);
|
|
61
|
+
return { items, nextCursor: items.at(-1)?.id ?? after, cursor: activityCursor(db), hasMore: rows.length > limit };
|
|
62
|
+
}
|
|
@@ -255,6 +255,7 @@ export function createClaudeCodeEngine() {
|
|
|
255
255
|
let iteratorRunning = false;
|
|
256
256
|
let userInterrupted = false;
|
|
257
257
|
let completedResponses = 0;
|
|
258
|
+
let waitingForBackground = false;
|
|
258
259
|
let turnCompletedEmittedForResponse = 0;
|
|
259
260
|
// `result` is the Claude Agent SDK's per-turn completion signal. Keep
|
|
260
261
|
// it distinct from iterator completion: the SDK may still drain
|
|
@@ -433,6 +434,11 @@ export function createClaudeCodeEngine() {
|
|
|
433
434
|
turnLiveness.start();
|
|
434
435
|
try {
|
|
435
436
|
for await (const msg of q) {
|
|
437
|
+
// A parent continuation owns the foreground again. Background
|
|
438
|
+
// progress notifications alone do not end the between-turn wait.
|
|
439
|
+
if (msg.type === 'assistant' || msg.type === 'user' || msg.type === 'stream_event') {
|
|
440
|
+
waitingForBackground = false;
|
|
441
|
+
}
|
|
436
442
|
// This SDK message proves that any drain condition armed by a
|
|
437
443
|
// *previous* message is no longer current. A drain armed while
|
|
438
444
|
// processing this message remains active if the generator then
|
|
@@ -495,6 +501,7 @@ export function createClaudeCodeEngine() {
|
|
|
495
501
|
turnLiveness.setTimeoutMs(pendingToolCallIds.size > 0 ? CLAUDE_TOOL_IDLE_TIMEOUT_MS : CLAUDE_STREAM_IDLE_TIMEOUT_MS);
|
|
496
502
|
reevaluateLivenessPause();
|
|
497
503
|
if (msg.type === 'result') {
|
|
504
|
+
waitingForBackground = false;
|
|
498
505
|
pendingToolCallIds.clear();
|
|
499
506
|
completedResponses++;
|
|
500
507
|
emitTurnCompletedIfSettled();
|
|
@@ -506,6 +513,7 @@ export function createClaudeCodeEngine() {
|
|
|
506
513
|
armResultDrainWatchdog();
|
|
507
514
|
}
|
|
508
515
|
else {
|
|
516
|
+
waitingForBackground = true;
|
|
509
517
|
armSubagentStallWatchdog();
|
|
510
518
|
}
|
|
511
519
|
}
|
|
@@ -583,6 +591,24 @@ export function createClaudeCodeEngine() {
|
|
|
583
591
|
throw new Error(`Claude ${AGENT_NO_LONGER_RUNNING_TEXT}`);
|
|
584
592
|
inputStream.send(text);
|
|
585
593
|
},
|
|
594
|
+
sendWakeupIfWaiting(text) {
|
|
595
|
+
if (!iteratorRunning ||
|
|
596
|
+
abortController.signal.aborted ||
|
|
597
|
+
mapperState.sawErrorResult ||
|
|
598
|
+
!waitingForBackground ||
|
|
599
|
+
activeSubagentTaskIds.size === 0 ||
|
|
600
|
+
pendingToolCallIds.size > 0 ||
|
|
601
|
+
pendingResolvers.size > 0 ||
|
|
602
|
+
isCompacting ||
|
|
603
|
+
inputStream.hasUnansweredInput(completedResponses))
|
|
604
|
+
return false;
|
|
605
|
+
inputStream.send(text);
|
|
606
|
+
waitingForBackground = false;
|
|
607
|
+
// The wakeup starts another turn on this stream; its result will
|
|
608
|
+
// re-arm the stall deadline if background work is still outstanding.
|
|
609
|
+
clearSubagentStallWatchdog();
|
|
610
|
+
return true;
|
|
611
|
+
},
|
|
586
612
|
interrupt() {
|
|
587
613
|
userInterrupted = true;
|
|
588
614
|
// The SDK ends an interrupted run by emitting a `result` with
|
|
@@ -63,20 +63,20 @@ function normalizeRateLimitInfo(info) {
|
|
|
63
63
|
}
|
|
64
64
|
// ── Public API ────────────────────────────────────────────────────────────────
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* - assistant `message:text` blocks (the SDK occasionally streams the user-
|
|
70
|
-
* visible quota notice as plain assistant text instead of a structured
|
|
71
|
-
* error result; see workspace `-GyiAYM7X4xTWyZbcHGiR` session #25 for the
|
|
72
|
-
* repro that motivated this path)
|
|
73
|
-
* - CLI stderr in `engine.ts`
|
|
66
|
+
* Quota keywords for error results and CLI stderr. Assistant prose uses
|
|
67
|
+
* the narrower ASSISTANT_QUOTA_NOTICE below, because summaries may mention
|
|
68
|
+
* a previous quota failure while the current run is working normally.
|
|
74
69
|
*
|
|
75
70
|
* Patterns are kept loose on purpose to absorb minor wording drift between
|
|
76
71
|
* Anthropic's surfaces (`rate_limit_exceeded`, `Claude AI usage limit
|
|
77
72
|
* reached`, `You're out of extra usage`, `quota exceeded`).
|
|
78
73
|
*/
|
|
79
74
|
export const QUOTA_PATTERN = /\b429\b|out of extra usage|rate[_ ]limit|usage limit|quota exceeded/i;
|
|
75
|
+
/** Only canonical, standalone notices qualify on the assistant prose surface.
|
|
76
|
+
* Broad quota keywords remain appropriate for structured errors, not summaries
|
|
77
|
+
* of a previous attempt, a sub-agent failure, or application HTTP handling.
|
|
78
|
+
*/
|
|
79
|
+
const ASSISTANT_QUOTA_NOTICE = /^(?:you(?:'|’)re out of extra usage|you(?:'|’)ve hit your (?:session|weekly|usage) limit|claude ai usage limit reached|usage limit reached|quota exceeded|rate limit exceeded)(?:[.!]?(?:[ \t]*[·—-]?[ \t]*resets?\b[^\r\n]*)?)?[.!]?$/i;
|
|
80
80
|
export function createMapperState() {
|
|
81
81
|
return {
|
|
82
82
|
sessionStartedEmitted: false,
|
|
@@ -274,8 +274,8 @@ export function mapSdkMessage(msg, state) {
|
|
|
274
274
|
// Last-resort fallback: some SDK runs surface the quota notice as
|
|
275
275
|
// plain assistant text without setting `assistant.error` or a
|
|
276
276
|
// `result.error`. The structured signals above cover modern SDK
|
|
277
|
-
// versions;
|
|
278
|
-
if (
|
|
277
|
+
// versions; only a standalone notice qualifies on this prose surface.
|
|
278
|
+
if (ASSISTANT_QUOTA_NOTICE.test(text.trim())) {
|
|
279
279
|
tryEmitQuotaError(state, events, text);
|
|
280
280
|
}
|
|
281
281
|
}
|
|
@@ -695,6 +695,15 @@ function handleEvent(workspaceId, agentSessionId, sourceController, ev) {
|
|
|
695
695
|
const sourceControllerIsStopping = sourceController?.status === 'stopping';
|
|
696
696
|
const routedEvent = ev.kind === 'session:ended' && (hasReplacement || sourceIsSuperseded) ? { ...ev, superseded: true } : ev;
|
|
697
697
|
routeEvent(workspaceId, agentSessionId, routedEvent);
|
|
698
|
+
// A fresh tool invocation proves this controller resumed working (for
|
|
699
|
+
// example after an account change). Tool results can still drain after a
|
|
700
|
+
// rejection, so they must never count as recovery.
|
|
701
|
+
if (ev.kind === 'tool:call' && !sourceControllerIsStopping && getWs(workspaceId)?.status === 'quota') {
|
|
702
|
+
updateWorkspaceStatus(workspaceId, 'executing');
|
|
703
|
+
quotaBackoffService.cancel(workspaceId, 'completed');
|
|
704
|
+
retryCounts.delete(workspaceId);
|
|
705
|
+
emitEphemeral(workspaceId, 'agent:quota-recovered', {});
|
|
706
|
+
}
|
|
698
707
|
if (ev.kind === 'rate_limit') {
|
|
699
708
|
latestRateLimitInfo.set(workspaceId, ev.info);
|
|
700
709
|
}
|
|
@@ -1345,6 +1354,23 @@ export async function stopAllAgents(timeoutMs = 3_000) {
|
|
|
1345
1354
|
// dead code pretending to guard against a failure mode that can't occur.
|
|
1346
1355
|
await Promise.all([...controllers.keys()].map((workspaceId) => stopAgentAndWait(workspaceId, timeoutMs)));
|
|
1347
1356
|
}
|
|
1357
|
+
/** Deliver a scheduled check without stopping background work or cancelling the wakeup as user input. */
|
|
1358
|
+
export function sendWakeupIfWaiting(workspaceId, content, expectedSessionId) {
|
|
1359
|
+
const ctrl = controllers.get(workspaceId);
|
|
1360
|
+
if (ctrl?.status !== 'running')
|
|
1361
|
+
return false;
|
|
1362
|
+
if (expectedSessionId && ctrl.agentSessionId !== expectedSessionId)
|
|
1363
|
+
return false;
|
|
1364
|
+
const workspace = getWs(workspaceId);
|
|
1365
|
+
if (!workspace || workspace.archivedAt || workspace.worktreePurgedAt)
|
|
1366
|
+
return false;
|
|
1367
|
+
if (workspace.status === 'awaiting-user' || workspace.status === 'quota')
|
|
1368
|
+
return false;
|
|
1369
|
+
if (!ctrl.engineProcess?.sendWakeupIfWaiting?.(content))
|
|
1370
|
+
return false;
|
|
1371
|
+
emit(workspaceId, 'user:message', { content, sender: 'system-prompt' }, ctrl.agentSessionId);
|
|
1372
|
+
return true;
|
|
1373
|
+
}
|
|
1348
1374
|
/** Write a user message to the running agent. */
|
|
1349
1375
|
export async function sendMessage(workspaceId, content, expectedSessionId) {
|
|
1350
1376
|
const ctrl = controllers.get(workspaceId);
|
|
@@ -1864,6 +1890,9 @@ async function handleQuota(workspaceId, _agentSessionId) {
|
|
|
1864
1890
|
return;
|
|
1865
1891
|
}
|
|
1866
1892
|
const { delayMs, resetsAt, source } = await computeQuotaBackoffMs(workspaceId, retryCount);
|
|
1893
|
+
// Usage lookup can finish after the agent has already resumed.
|
|
1894
|
+
if (getWs(workspaceId)?.status !== 'quota')
|
|
1895
|
+
return;
|
|
1867
1896
|
retryCounts.set(workspaceId, retryCount + 1);
|
|
1868
1897
|
// The quotaBackoffService owns the timer + the persistent row + the
|
|
1869
1898
|
// 'agent:quota-backoff' WS emit. Hand off everything to it.
|
|
@@ -9,7 +9,7 @@ import * as lifecycleHookService from './lifecycle-hook-service.js';
|
|
|
9
9
|
import * as settingsService from './settings-service.js';
|
|
10
10
|
import { getSuitePrompts } from './skill-suite-prompts.js';
|
|
11
11
|
import { emit, emitEphemeral } from './websocket-service.js';
|
|
12
|
-
import { listTasks } from './workspace-service.js';
|
|
12
|
+
import { listTasks, updateWorkspaceStatus } from './workspace-service.js';
|
|
13
13
|
const NO_PROGRESS_STALL_THRESHOLD = 3;
|
|
14
14
|
function getRow(workspaceId) {
|
|
15
15
|
const db = getDb();
|
|
@@ -186,6 +186,10 @@ export function rehydrate() {
|
|
|
186
186
|
const row = getRow(id);
|
|
187
187
|
if (row?.auto_loop_ready !== 1)
|
|
188
188
|
continue;
|
|
189
|
+
// Persisted quota timers own the restart of these workspaces. Boot
|
|
190
|
+
// restores them after this pass; starting here bypasses their delay.
|
|
191
|
+
if (row.status === 'quota')
|
|
192
|
+
continue;
|
|
189
193
|
if (countPendingTasks(id) === 0) {
|
|
190
194
|
disable(id, 'completed');
|
|
191
195
|
continue;
|
|
@@ -427,9 +431,9 @@ function spawnNextIteration(workspaceId, opts = {}) {
|
|
|
427
431
|
* Spawns the next auto-loop iteration if the workspace is still in quota status
|
|
428
432
|
* with auto_loop active; no-ops otherwise (race-safe).
|
|
429
433
|
*
|
|
430
|
-
* No-op cases —
|
|
431
|
-
* manual user action:
|
|
434
|
+
* No-op cases — leave the workspace unchanged:
|
|
432
435
|
* - workspace was deleted between arm and fire
|
|
436
|
+
* - workspace was archived during the backoff window
|
|
433
437
|
* - `auto_loop !== 1` (workspace was never an auto-loop target, OR the user
|
|
434
438
|
* toggled the loop off during the backoff window)
|
|
435
439
|
* - `status !== 'quota'` (user already manually resumed, or another path
|
|
@@ -439,10 +443,17 @@ export function onQuotaBackoffExpired(workspaceId) {
|
|
|
439
443
|
const row = getRow(workspaceId);
|
|
440
444
|
if (!row)
|
|
441
445
|
return;
|
|
446
|
+
if (row.archived_at !== null)
|
|
447
|
+
return;
|
|
442
448
|
if (row.auto_loop !== 1)
|
|
443
449
|
return;
|
|
444
450
|
if (row.status !== 'quota')
|
|
445
451
|
return;
|
|
452
|
+
// The timer has consumed its persisted row. Release quota ownership before
|
|
453
|
+
// checking capacity so resumeWaitingWorkspaces can pick this workspace up
|
|
454
|
+
// when a slot frees, even if no agent can start at this instant.
|
|
455
|
+
updateWorkspaceStatus(workspaceId, 'idle');
|
|
456
|
+
emitEphemeral(workspaceId, 'agent:quota-backoff-cancelled', { reason: 'completed' });
|
|
446
457
|
spawnNextIteration(workspaceId);
|
|
447
458
|
}
|
|
448
459
|
/**
|
|
@@ -146,6 +146,20 @@ function fire(workspaceId) {
|
|
|
146
146
|
if (!row)
|
|
147
147
|
return;
|
|
148
148
|
if (orchestrator.hasController(workspaceId)) {
|
|
149
|
+
// Claude can keep the stream open after a result while a background
|
|
150
|
+
// task runs. Waiting for that controller to disappear deadlocks a
|
|
151
|
+
// scheduled check on the very work it is meant to inspect.
|
|
152
|
+
try {
|
|
153
|
+
if (orchestrator.sendWakeupIfWaiting(workspaceId, row.prompt, row.agent_session_id ?? undefined)) {
|
|
154
|
+
failedRetries.delete(workspaceId);
|
|
155
|
+
db.prepare('DELETE FROM pending_wakeups WHERE workspace_id = ?').run(workspaceId);
|
|
156
|
+
emitEphemeral(workspaceId, 'wakeup:fired', {});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
console.error(`[wakeup-service] delivery to active session failed for '${workspaceId}':`, err);
|
|
162
|
+
}
|
|
149
163
|
defer(workspaceId, row);
|
|
150
164
|
return;
|
|
151
165
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
2
|
import { getDb } from '../db/index.js';
|
|
3
|
+
import { recordActivity } from './activity-service.js';
|
|
3
4
|
import { getAllPersistedSnapshots } from './usage/db.js';
|
|
4
5
|
// ── State ──────────────────────────────────────────────────────────────────────
|
|
5
6
|
/** Maps each WS client to the set of workspaceIds they are subscribed to */
|
|
@@ -154,6 +155,7 @@ function insertEventStatement() {
|
|
|
154
155
|
return cachedInsert.statement;
|
|
155
156
|
}
|
|
156
157
|
export function emit(workspaceId, type, payload, sessionId) {
|
|
158
|
+
recordActivity(workspaceId, type, payload, sessionId);
|
|
157
159
|
const id = nanoid();
|
|
158
160
|
const createdAt = new Date().toISOString();
|
|
159
161
|
let replayable = false;
|
|
@@ -198,6 +200,7 @@ export function emit(workspaceId, type, payload, sessionId) {
|
|
|
198
200
|
* Used for ephemeral status updates (e.g., dev-server status) that don't need replay.
|
|
199
201
|
*/
|
|
200
202
|
export function emitEphemeral(workspaceId, type, payload) {
|
|
203
|
+
recordActivity(workspaceId, type, payload);
|
|
201
204
|
const id = nanoid();
|
|
202
205
|
const createdAt = new Date().toISOString();
|
|
203
206
|
const event = { id, workspaceId, type, payload, createdAt, replayable: false };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.36",
|
|
4
4
|
"description": "Kōbō — multi-workspace agent manager for Claude Code. Orchestrates isolated git worktrees with dev servers, Notion integration, and MCP tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{S as e,ct as t,j as n,ot as r,ut as i,v as a}from"./reactivity.esm-bundler-DEJj9cFv.js";import{C as o,F as s,J as c,L as l,N as u,O as d,P as f,Y as p,_ as m,d as h,f as g,g as _,j as v,k as y,l as b,p as x,r as S,u as C,y as w,z as T}from"./runtime-core.esm-bundler-CDnVUfeZ.js";import{J as E,b as D,ct as O,l as k,pt as A,t as ee,tt as j,w as M,y as te}from"./QBtn-CZaBclNw.js";import{A as N,At as ne,Ct as P,Nt as F,O as re,k as ie,m as ae,p as I,q as oe,xt as L}from"./index-DcwcFDrZ.js";import{t as se}from"./QSpinnerDots-DnCV3VzI.js";import{t as ce}from"./QList-qupzuEqO.js";import{t as R}from"./QMarkupTable-B9QuKtK_.js";import{c as z,l as B}from"./QSelect-Bodml89k.js";import{d as le}from"./permissionModes-DMzFEQhr.js";import{r as ue,t as V}from"./pending-diff-open-BlLrQaW7.js";import{t as H}from"./documents-B2dg1wRf.js";import{a as U}from"./use-tours-C8x2V1w5.js";import{t as W}from"./QTooltip-K-9OdDfq.js";import{i as de,n as G,o as fe,r as K,t as pe}from"./render-chat-markdown-BTPAjU2g.js";import{a as me,i as he,n as q,o as ge,r as _e,s as ve}from"./WorkspacePage-BI7CW2yT.js";function ye(e,t){return o(`div`,e,[o(`table`,{class:`q-table`},t)])}var J={list:ce,table:R},Y=[`list`,`table`,`__qtable`],be=j({name:`QVirtualScroll`,props:{...B,type:{type:String,default:`list`,validator:e=>Y.includes(e)},items:{type:Array,default:()=>[]},itemsFn:Function,itemsSize:Number,scrollTarget:F},setup(t,{slots:n,attrs:r}){let i,a=e(null),s=b(()=>t.itemsSize>=0&&t.itemsFn!==void 0?Number.parseInt(t.itemsSize,10):Array.isArray(t.items)?t.items.length:0),{virtualScrollSliceRange:l,localResetVirtualScroll:d,padVirtualScroll:p,onVirtualScrollEvt:m}=z({virtualScrollLength:s,getVirtualScrollTarget:S,getVirtualScrollEl:x}),h=b(()=>{if(s.value===0)return[];let e=(e,t)=>({index:l.value.from+t,item:e});return t.itemsFn===void 0?t.items.slice(l.value.from,l.value.to).map(e):t.itemsFn(l.value.from,l.value.to-l.value.from).map(e)}),g=b(()=>`q-virtual-scroll q-virtual-scroll`+(t.virtualScrollHorizontal?`--horizontal`:`--vertical`)+(t.scrollTarget===void 0?` scroll`:``)),_=b(()=>t.scrollTarget===void 0?{tabindex:0}:{});c(s,()=>{d()}),c(()=>t.scrollTarget,()=>{w(),C()});function x(){let e=a.value;return e===null?null:e.$el||e}function S(){return i}function C(){let e=x();e!==null&&(i=ne(e,t.scrollTarget),i.addEventListener(`scroll`,m,E.passive))}function w(){i!==void 0&&(i.removeEventListener(`scroll`,m,E.passive),i=void 0)}function T(){let e=p(t.type===`list`?`div`:`tbody`,h.value.map(n.default));return n.before!==void 0&&(e=n.before().concat(e)),D(n.after,e)}return d(),f(()=>{C()}),y(()=>{C()}),u(()=>{w()}),v(()=>{w()}),()=>{if(n.default===void 0){console.error(`QVirtualScroll: default scoped slot is required for rendering`);return}return t.type===`__qtable`?ye({ref:a,class:`q-table__middle `+g.value,tabindex:0},T()):o(J[t.type],{...r,ref:a,class:[r.class,g.value],..._.value},T)}}}),xe=new Set([`setup`,`cleanup`,`archive`]);function Se(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:xe.has(e.sender)||re(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function Ce(e){let t=[],n=null,r=null;for(let i of e){let e=Se(i),a=e===`session`||e===`system-prompt`,o=e===`script`&&i.type===`user`?`script:${i.sender}`:e;!n||r!==o||a?(n={speaker:e,ts:i.ts,items:[i]},r=o,t.push(n),a&&(n=null)):n.items.push(i)}return t}function we(e){switch(e.type){case`text`:return`text:${e.messageId}`;case`thinking`:return`thinking:${e.messageId}:${e.eventIds?.[0]??e.ts??``}`;case`tool`:return`tool:${e.toolCallId}`;case`session`:return`session:${e.kind}:${e.eventIds?.[0]??e.ts??``}`;case`user`:return`user:${e.eventIds?.[0]??`${e.sender}:${e.ts??``}`}`;case`error`:return`error:${e.eventIds?.[0]??`${e.category}:${e.ts??``}`}`}}function Te(e){let t=e.items[0];return`${e.speaker}:${t?we(t):`empty`}`}function Ee(e,t){let n=Math.min(t,e.length)-1;for(let t=n;t>=0;t--)if(e[t]?.speaker===`user`)return t;return-1}function De(e,t,n){return new Promise(r=>{let i=!1,a=null,o=null,s=e=>{i||(i=!0,a!==null&&clearTimeout(a),a=null,o?.(),r(e))};if(o=c(e,e=>{t(e)&&s(!0)},{immediate:!0}),i){o();return}a=setTimeout(()=>s(!1),n)})}var Oe={class:`agent-error-item`},ke={class:`agent-error-item__body`},Ae={class:`agent-error-item__title`},je={class:`agent-error-item__message`},X=U(w({__name:`AgentErrorItem`,props:{item:{}},setup(e){let t=e,{te:n}=L(),r=b(()=>{let e=`agent.error.${t.item.category}`;return n(e)?e:`agent.error.other`});return(t,n)=>(l(),x(`div`,Oe,[m(k,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),C(`div`,ke,[C(`div`,Ae,i(t.$t(r.value)),1),C(`div`,je,i(e.item.message),1)])]))}}),[[`__scopeId`,`data-v-e98c3c02`]]),Me={class:`text-caption text-kobo-3`},Z=w({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,n=b(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return ve(t.item.detail);case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(l(),x(`span`,Me,i(e.$t(n.value)),1))}});function Ne(e,t){if(t.length===0||e.length===0)return e;let n=[...t].sort((e,t)=>t.length-e.length),r=new DOMParser().parseFromString(`<div>${e}</div>`,`text/html`),i=r.body.firstChild;if(!i)return e;function a(e){if(e.nodeType===Node.TEXT_NODE){Q(e,n,r);return}if(e.nodeName===`A`)return;let t=Array.from(e.childNodes);for(let e of t)a(e)}return a(i),i.innerHTML}function Q(e,t,n){let r=e.textContent??``;if(!t.some(e=>r.includes(e)))return;let i=n.createDocumentFragment(),a=0;for(;a<r.length;){let e=$(r,a,t);if(!e){i.appendChild(n.createTextNode(r.slice(a)));break}e.index>a&&i.appendChild(n.createTextNode(r.slice(a,e.index)));let o=n.createElement(`a`);o.className=`document-link`,o.setAttribute(`data-document-path`,e.path),o.setAttribute(`href`,`#`),o.textContent=e.path,i.appendChild(o),a=e.index+e.path.length}e.parentNode?.replaceChild(i,e)}function $(e,t,n){let r=null;for(let i of n){let n=e.indexOf(i,t);n<0||(!r||n<r.index||n===r.index&&i.length>r.path.length)&&(r={index:n,path:i})}return r}var Pe=[`innerHTML`],Fe=U(w({__name:`TextMessageItem`,props:{item:{}},setup(e){let t=e,n=H(),r=I(),i=te(),{t:a}=L(),o=b(()=>{let e=r.selectedWorkspaceId;return e?n.documentsFor(e).map(e=>e.path):[]}),s=b(()=>{let e=t.item;if(e.streaming)return pe(e.text);let n=o.value;return K(`${e.messageId}|${n.join(`,`)}`,e.text,e=>{let t=Ne(fe.parse(e,{async:!1,breaks:!0,gfm:!0}),n);return de(t,{addAttr:[`data-document-path`]})})});function c(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let o=t.getAttribute(`data-document-path`),s=r.selectedWorkspaceId;o&&s&&n.openDocumentByPath(s,o).catch(e=>{console.error(`[TextMessageItem] openDocumentByPath failed:`,e),i.notify({type:`negative`,message:a(`documents.loadFailed`),position:`top`})})}return(t,n)=>(l(),x(`div`,{class:`markdown-message`,onClick:c},[C(`div`,{innerHTML:s.value},null,8,Pe),e.item.streaming?(l(),h(M,{key:0,size:`xs`,class:`q-ml-xs`})):g(``,!0)]))}}),[[`__scopeId`,`data-v-99371035`]]);function Ie(e,t){let n=e.split(`
|
|
2
|
+
`),r=t.split(`
|
|
3
|
+
`),i=0;for(;i<n.length&&i<r.length&&n[i]===r[i];)i++;let a=n.length,o=r.length;for(;a>i&&o>i&&n[a-1]===r[o-1];)a--,o--;let s=n.slice(0,i).map(e=>({type:`context`,content:e})),c=n.slice(a).map(e=>({type:`context`,content:e})),l=n.slice(i,a),u=r.slice(i,o);return l.length>800||u.length>800?[...s,...l.map(e=>({type:`del`,content:e})),...u.map(e=>({type:`add`,content:e})),...c]:[...s,...Le(l,u),...c]}function Le(e,t){let n=e.length,r=t.length;if(n===0&&r===0)return[];if(n===0)return t.map(e=>({type:`add`,content:e}));if(r===0)return e.map(e=>({type:`del`,content:e}));let i=r+1,a=new Int32Array((n+1)*i);for(let o=n-1;o>=0;o--)for(let n=r-1;n>=0;n--)a[o*i+n]=e[o]===t[n]?a[(o+1)*i+(n+1)]+1:Math.max(a[(o+1)*i+n],a[o*i+(n+1)]);let o=[],s=0,c=0;for(;s<n&&c<r;)e[s]===t[c]?(o.push({type:`context`,content:e[s]}),s++,c++):a[(s+1)*i+c]>=a[s*i+(c+1)]?(o.push({type:`del`,content:e[s]}),s++):(o.push({type:`add`,content:t[c]}),c++);for(;s<n;)o.push({type:`del`,content:e[s++]});for(;c<r;)o.push({type:`add`,content:t[c++]});return o}function Re(e){let t=e.split(`
|
|
4
|
+
`),n=[],r=!1;for(let e of t){if(e.startsWith(`@@`)){r=!0;continue}(r||!e.startsWith(`+++`)&&!e.startsWith(`---`))&&(e.startsWith(`+`)?n.push({type:`add`,content:e.slice(1)}):e.startsWith(`-`)?n.push({type:`del`,content:e.slice(1)}):e.startsWith(` `)?n.push({type:`context`,content:e.slice(1)}):e.length>0&&n.push({type:`context`,content:e}))}return n}function ze(e,t){if(!t||typeof t!=`object`)return null;let n=t;if(e===`Edit`){let e=n.file_path;if(!e)return null;let t=n.old_string??``,r=n.new_string??``,i=typeof n.diff==`string`?n.diff:``;if(!t&&!r&&i.length>0){let t=Re(i);return{toolName:`Edit`,filePath:e,additions:t.filter(e=>e.type===`add`).length,deletions:t.filter(e=>e.type===`del`).length,diffLines:t}}return{toolName:`Edit`,filePath:e,oldString:t,newString:r,replaceAll:n.replace_all??!1,additions:r?r.split(`
|
|
5
|
+
`).length:0,deletions:t?t.split(`
|
|
6
|
+
`).length:0}}if(e===`Write`){let e=n.file_path;if(!e)return null;let t=n.content??``;return{toolName:`Write`,filePath:e,content:t,additions:t?t.split(`
|
|
7
|
+
`).length:0,deletions:0}}if(e===`Bash`){let e=(n.command??``).match(/^\s*rm\s+(?:-[a-zA-Z]*\s+)*(.+)/);if(e)return{toolName:`Bash:rm`,filePath:e[1].trim().replace(/["']/g,``),additions:0,deletions:1}}return null}function Be(e,t){if(!e||!t?.projectPath)return e;let n=t.worktreePath;if(n){let t=Ve(e,n);if(t!==e)return t}let r=Ve(e,`${t.projectPath}/${N}/${t.workingBranch}`);return r===e?Ve(e,t.projectPath):r}function Ve(e,t){if(!t)return e;let n=He(t);return n?e.replace(RegExp(`${n}[\\\\/]+`,`g`),``).replace(RegExp(`${n}(?=\\s|$|["'\`])`,`g`),`.`):e}function He(e){let t=e.replace(/[\\/]+$/,``);return t?`${/^[\\/]+/.test(t)?`[\\\\/]+`:``}${t.split(/[\\/]+/).filter(Boolean).map(Ue).join(`[\\\\/]+`)}`:``}function Ue(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}var We={class:`tool-name`},Ge=[`title`],Ke={key:0,class:`tool-stat-add`},qe={key:1,class:`tool-stat-del`},Je={class:`diff-sign`},Ye={class:`tool-name`},Xe=[`title`],Ze=U(w({__name:`ToolCallItem`,props:{item:{}},setup(e){let t=e,o=a(!1),s=I(),{t:u}=L(),d=b(()=>ze(t.item.name,t.item.input)),f=b(()=>d.value?Be(d.value.filePath,s.selectedWorkspace):``),v={Bash:`terminal`,Read:`description`,Edit:`edit`,Write:`edit_note`,MultiEdit:`edit`,Glob:`folder_open`,Grep:`manage_search`,LS:`list`,Skill:`auto_awesome`,Task:`hub`,Agent:`hub`,TodoWrite:`checklist`,TodoRead:`checklist`,TaskCreate:`add_task`,TaskUpdate:`checklist`,TaskGet:`checklist`,TaskList:`checklist`,ToolSearch:`search`,WebFetch:`public`,WebSearch:`travel_explore`,NotebookRead:`book`,NotebookEdit:`edit_note`,SendMessage:`send`,ExitPlanMode:`check_circle_outline`,KillShell:`stop_circle`,BashOutput:`terminal`},y=b(()=>v[t.item.name]??`build`),w=b(()=>{if(d.value)return``;let e=t.item.input,n=E(e);return n?Be(n,s.selectedWorkspace):``});function E(e){if(!e||typeof e!=`object`)return typeof e==`string`?e:``;let t=e;for(let e of[`file_path`,`path`,`command`,`pattern`,`query`,`url`,`skill`,`description`,`subject`,`prompt`]){let n=t[e];if(typeof n==`string`&&n.length>0)return n}for(let e of Object.values(t))if(typeof e==`string`&&e.length>0)return e;return``}let D=b(()=>{let e=d.value;return e?e.diffLines?e.diffLines:e.toolName===`Edit`&&e.oldString!==void 0&&e.newString!==void 0?Ie(e.oldString,e.newString):e.toolName===`Write`&&e.content!==void 0?e.content.split(`
|
|
8
|
+
`).map(e=>({type:`add`,content:e})):e.toolName===`Bash:rm`?[{type:`del`,content:`File deleted`}]:null:null}),O=b(()=>{let e=t.item.result;if(!e)return``;if(typeof e.output==`string`)return e.output;try{return JSON.stringify(e.output)}catch{return String(e.output)}}),j=new Set([`Read`]),M=b(()=>!!(t.item.result&&O.value)&&(!j.has(t.item.name)||t.item.result?.isError===!0));function te(){o.value=!o.value}function N(){let e=d.value?.filePath,t=s.selectedWorkspace,n=t?.id,r=e?Be(e,t):``;r&&!/^(?:[A-Za-z]:)?[\\/]/.test(r)&&n&&(V(n,r),window.dispatchEvent(new CustomEvent(`kobo:open-diff`,{detail:{workspaceId:n,path:r}})))}return c(()=>t.item.result?.isError===!0,e=>{e&&(o.value=!0)},{immediate:!0}),(t,a)=>d.value?(l(),x(`div`,{key:0,class:r([`tool-row`,{"tool-row-expanded":o.value}])},[C(`div`,{class:`tool-header`,onClick:te},[m(k,{name:y.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),C(`span`,We,i(d.value.toolName===`Bash:rm`?`Bash`:d.value.toolName),1),C(`span`,{class:`tool-path`,title:d.value.filePath},i(f.value),9,Ge),m(ee,{flat:``,dense:``,round:``,size:`xs`,icon:`open_in_new`,class:`tool-open-diff`,onClick:A(N,[`stop`])},{default:p(()=>[m(W,null,{default:p(()=>[_(i(n(u)(`chat.openInGitDiff`)),1)]),_:1})]),_:1}),d.value.additions>0?(l(),x(`span`,Ke,`+`+i(d.value.additions),1)):g(``,!0),d.value.deletions>0?(l(),x(`span`,qe,`-`+i(d.value.deletions),1)):g(``,!0),e.item.result?.isError?(l(),h(k,{key:2,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-xs`})):e.item.result?(l(),h(k,{key:3,name:`check`,color:`positive`,size:`xs`,class:`q-ml-xs`})):g(``,!0),m(k,{name:o.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-auto text-kobo-3`},null,8,[`name`])]),o.value&&D.value?(l(),x(`div`,{key:0,class:`tool-diff`,onClick:a[0]||=A(()=>{},[`stop`])},[(l(!0),x(S,null,T(D.value,(e,t)=>(l(),x(`div`,{key:t,class:r([`diff-line`,{"diff-del":e.type===`del`,"diff-add":e.type===`add`,"diff-context":e.type===`context`}])},[C(`span`,Je,i(e.type===`del`?`-`:e.type===`add`?`+`:` `),1),_(i(e.content),1)],2))),128))])):g(``,!0)],2)):(l(),x(`div`,{key:1,class:r([`tool-row tool-row-generic`,{"tool-row-expanded":o.value,"tool-row--toggleable":M.value}])},[C(`div`,{class:`tool-header`,onClick:a[1]||=e=>M.value&&te()},[m(k,{name:y.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),C(`span`,Ye,i(e.item.name),1),w.value?(l(),x(`span`,{key:0,class:`tool-arg`,title:E(e.item.input)||w.value},i(w.value),9,Xe)):g(``,!0),e.item.result?.isError?(l(),h(k,{key:1,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-auto`})):e.item.result?(l(),h(k,{key:2,name:`check`,color:`positive`,size:`xs`,class:`q-ml-auto`})):g(``,!0),M.value?(l(),h(k,{key:3,name:o.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-xs text-kobo-3`},null,8,[`name`])):g(``,!0)]),o.value&&M.value?(l(),x(`div`,{key:0,class:`tool-output`,onClick:a[2]||=A(()=>{},[`stop`])},i(O.value),1)):g(``,!0)],2))}}),[[`__scopeId`,`data-v-2482adfe`]]);function Qe(e,t){return t?e.replace(/\[image:\s+([^\]]+)\]/g,(e,n)=>{let r=String(n).trim();return/^(\.ai\/images\/|images\/)/.test(r)?`}/images/file?path=${encodeURIComponent(r)}`})`:e}):e}var $e=[`innerHTML`],et=[`innerHTML`],tt=[`src`],nt=U(w({__name:`UserMessageItem`,props:{item:{}},setup(e){let t=e,n=I(),r=b(()=>t.item.sender===`system-prompt`),i=b(()=>{let e=Qe(t.item.content,n.selectedWorkspaceId??``);return G(e)}),o=a(null),s=a(!1);function c(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(o.value=n.src,s.value=!0)}return(e,t)=>(l(),x(S,null,[r.value?(l(),h(le,{key:0,dense:``,"dense-toggle":``,label:e.$t(`chat.systemPrompt`),"header-class":`text-kobo-2 text-caption`},{default:p(()=>[C(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:i.value},null,8,$e)]),_:1},8,[`label`])):(l(),x(`div`,{key:1,class:`markdown-message`,onClick:c},[C(`div`,{innerHTML:i.value},null,8,et)])),m(P,{modelValue:s.value,"onUpdate:modelValue":t[1]||=e=>s.value=e},{default:p(()=>[o.value?(l(),x(`img`,{key:0,src:o.value,alt:``,class:`image-lightbox-img`,onClick:t[0]||=e=>s.value=!1},null,8,tt)):g(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-7687eb7e`]]),rt=[`data-event-ids`],it={class:`turn-header`},at={key:0,class:`turn-time`},ot={class:`turn-time turn-time-updated`},st={key:2,class:`turn-actions`},ct={class:`turn-body`},lt={key:0,class:`turn-scroll-top`},ut=U(w({__name:`TurnCard`,props:{turn:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(e,{emit:o}){let s=e,c=o,{t:u}=L(),d=a(null);function f(){let e=d.value;if(!e)return;let t=e.closest(`.q-scrollarea`)?.querySelector(`.q-scrollarea__content`);if(!t){e.scrollIntoView({behavior:`smooth`,block:`start`});return}let n=e.getBoundingClientRect().top-t.getBoundingClientRect().top;c(`scrollTo`,Math.max(0,n-8))}let v=b(()=>{switch(s.turn.speaker){case`user`:return{label:u(`chat.you`),accent:`var(--kobo-turn-user)`,badgeClass:`turn-badge-user`};case`agent`:return{label:u(`chat.agent`),accent:`var(--kobo-turn-agent)`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:u(`chat.systemPrompt`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-system`};case`session`:return{label:u(`chat.session`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-session`};case`script`:{let e=s.turn.items[0],t=e?.type===`user`?e.sender:``;return{label:re(t)?u(`chat.hookScript`,{event:t.slice(5)}):u(t===`archive`?`chat.archiveScript`:t===`setup`?`chat.setupScript`:`chat.cleanupScript`),accent:`var(--kobo-success)`,badgeClass:`turn-badge-script`}}}});function y(e,t=!1){if(!e)return``;let n=new Date(e);return Number.isNaN(n.getTime())?``:n.toLocaleTimeString(void 0,t?{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}:{hour:`2-digit`,minute:`2-digit`})}let w=b(()=>y(s.turn.ts)),E=b(()=>s.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),D=b(()=>{let e=s.turn.items;if(e.length===0)return null;for(let t=e.length-1;t>=0;t--){let n=e[t].ts;if(n)return n}return null}),O=b(()=>{let e=s.turn.ts,t=D.value;if(!t||!e||t===e)return``;let n=new Date(e).getTime(),r=new Date(t).getTime();return Number.isNaN(n)||Number.isNaN(r)||r<=n?``:y(t,r-n<6e4)}),A=b(()=>O.value!==``),j=b(()=>s.turn.items.filter(e=>e.type===`tool`).length);return(a,o)=>(l(),x(`div`,{ref_key:`cardEl`,ref:d,class:r([`turn-card`,{"turn-card--user":e.turn.speaker===`user`,"turn-card--highlighted":e.highlighted}]),"data-event-ids":E.value,style:t({"--turn-accent":v.value.accent})},[C(`div`,it,[C(`span`,{class:r([`turn-badge`,v.value.badgeClass])},i(v.value.label),3),w.value?(l(),x(`span`,at,i(w.value),1)):g(``,!0),A.value?(l(),x(S,{key:1},[m(k,{name:`arrow_forward`,size:`10px`,color:`kobo-3`,class:`turn-time-arrow`}),C(`span`,ot,[_(i(O.value)+` `,1),m(W,null,{default:p(()=>[_(i(n(u)(`chat.lastUpdatedAt`,{time:O.value})),1)]),_:1})])],64)):g(``,!0),j.value>0?(l(),x(`span`,st,` · `+i(n(u)(`chat.nActions`,{n:j.value})),1)):g(``,!0)]),C(`div`,ct,[(l(!0),x(S,null,T(e.turn.items,e=>(l(),x(S,{key:n(we)(e)},[e.type===`text`?(l(),h(Fe,{key:0,item:e},null,8,[`item`])):e.type===`thinking`?(l(),h(q,{key:1,item:e},null,8,[`item`])):e.type===`tool`?(l(),h(Ze,{key:2,item:e},null,8,[`item`])):e.type===`user`?(l(),h(nt,{key:3,item:e},null,8,[`item`])):e.type===`session`?(l(),h(Z,{key:4,item:e},null,8,[`item`])):e.type===`error`?(l(),h(X,{key:5,item:e},null,8,[`item`])):g(``,!0)],64))),128))]),e.turn.items.length>4?(l(),x(`div`,lt,[m(ee,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`kobo-3`,class:`turn-scroll-top-btn`,onClick:f},{default:p(()=>[m(W,null,{default:p(()=>[_(i(n(u)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):g(``,!0)],14,rt))}}),[[`__scopeId`,`data-v-6e61835e`]]),dt={key:0,class:`activity-feed-switching`},ft={key:1,class:`activity-feed-wrap`,"data-tour":`ws-chat`},pt=[`aria-label`],mt={key:0,class:`activity-feed-compacting`},ht={class:`text-caption text-kobo-2`},gt={key:0,class:`text-center q-py-sm text-caption text-kobo-3`},_t={key:1,class:`activity-feed-empty q-pa-md`},vt={class:`activity-feed-empty__title`},yt={class:`activity-feed-empty__hint`},bt={key:2,class:`q-px-md q-pb-md`},xt={class:`activity-feed-nav-cluster`},St=300,Ct=60,wt=200,Tt=200,Et=400,Dt=200,Ot=180,kt=1200,At=U(w({__name:`ActivityFeed`,props:{workspaceId:{}},setup(e){let{t}=L(),r=e,o=oe(),u=ie(),v=I(),y=b(()=>o.isCompacting(r.workspaceId)),w=b(()=>v.selectedSessionId),E=b(()=>v.sessions.find(e=>e.id===w.value)?.engineSessionId??null),D=b(()=>{let e=v.sessions;return e.length!==0&&w.value===e[e.length-1].id});function A(e){return w.value?e?e===w.value||e===E.value:D.value:!0}let j=b(()=>(v.activityFeeds[r.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&A(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),te=b(()=>{let e=v.workspaces.find(e=>e.id===r.workspaceId);return ae(e?.status)}),N=_e(),ne=``,P=b(()=>{let e=o.eventsFor(r.workspaceId),t=o.timestampsFor(r.workspaceId),n=o.sessionIdsFor(r.workspaceId),i=o.eventIdsFor(r.workspaceId),a=[],s=[],c=[];for(let r=0;r<e.length;r++)A(n[r])&&(a.push(e[r]),s.push(t[r]),c.push(i[r]??null));let l=`${r.workspaceId}::${w.value??`*`}`;l!==ne&&(N=_e(),ne=l);let d=he(N,a,s,te.value,c);return Ce(ge(d,j.value).filter(e=>e.type===`thinking`?!1:e.type===`session`?e.kind===`ended`?u.showVerboseSystemMessages||!me(e.detail):u.showVerboseSystemMessages:!0))}),F=b(()=>u.showVerboseSystemMessages?o.eventsFor(r.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),re=b(()=>{for(let e=P.value.length-1;e>=0;e--){let n=P.value[e]?.items??[];for(let e=n.length-1;e>=0;e--){let r=n[e];if(r){if(r.type===`text`&&!r.streaming)return{key:`text:${r.messageId}`,text:r.text};if(r.type===`tool`)return r.result?{key:`tool:${r.toolCallId}:end`,text:r.result.isError?t(`activity.a11y.toolFailed`,{name:r.name}):t(`activity.a11y.toolFinished`,{name:r.name})}:{key:`tool:${r.toolCallId}:start`,text:t(`activity.a11y.toolStarted`,{name:r.name})}}}}return null}),ce=a(``);c(re,(e,n)=>{if(!e||e.key===n?.key)return;let r=e.key.startsWith(`text:`)?t(`activity.a11y.agentSaid`,{text:e.text.slice(0,St)}):e.text;ce.value=r});let R=a(null),z=a(!0),B=a(!1),V=a(null),H=!1,U=a(!0),W=a(new Map);function de(e){let t=e.verticalSize-e.verticalPosition-e.verticalContainerSize;z.value=t<=Ct,H&&e.verticalPosition<=wt&&!B.value&&fe()&&ve()}function G(e,t){return`${e}:${t}`}function fe(){let e=w.value;return e?W.value.get(G(r.workspaceId,e))??!0:o.hasMoreOlderFor(r.workspaceId)}function K(e,t,n){W.value.set(G(e,t),n)}function pe(e){if(!w.value)return o.oldestIdFor(e);let t=o.eventIdsFor(e),n=o.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!A(n[e]))continue;let r=t[e];if(r)return r}}let q=null;function ve(){return q||(q=ye().finally(()=>{q=null}),q)}async function ye(){let e=r.workspaceId,t=w.value,n=pe(e);if(!n)return;B.value=!0;let i=Date.now();try{let r=R.value;await d();let i=r?.getScroll().verticalSize??0,a=r?.getScroll().verticalPosition??0,s=new URLSearchParams({before:n,limit:`200`});t&&s.set(`session`,t);let c=fetch(`/api/workspaces/${e}/events?${s.toString()}`),l=new Promise(e=>setTimeout(e,Tt)),[u]=await Promise.all([c,l]);if(!u.ok){t?K(e,t,!1):o.prepend(e,[],[],{oldestId:n,hasMoreOlder:!1});return}let f=await u.json(),p=f.events??[],m=p.filter(t=>t.type===`agent:event`&&t.workspaceId===e),h=p.filter(t=>t.type===`user:message`&&t.workspaceId===e),g=m.map(e=>e.payload),_=m.map(e=>e.createdAt),y=m.map(e=>e.sessionId??null),b=m.map(e=>e.id),x=p.length>0?p[0].id:n;t&&K(e,t,f.hasMore),o.prepend(e,g,_,{oldestId:x,hasMoreOlder:t?o.hasMoreOlderFor(e):f.hasMore,sessionIds:y,eventIds:b});for(let t of h){let n=t.payload;typeof n.content==`string`&&v.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`}})}if(await d(),r){let e=r.getScroll().verticalSize-i;if(e>0){let t=Math.max(a+e,250);r.setScrollPosition(`vertical`,t,0)}}}catch(e){console.error(`[ActivityFeed] failed to load older events:`,e)}finally{let e=Date.now()-i,t=Math.max(0,Tt-e);await new Promise(e=>setTimeout(e,t+Et)),B.value=!1}}async function J(e=0){await d();let t=R.value;if(!t)return;let n=t.getScroll();t.setScrollPosition(`vertical`,n.verticalSize,e)}let Y=null,xe=0;function Se(){if(Y!=null)return;let e=performance.now()-xe<Ot;Y=requestAnimationFrame(()=>{Y=null,xe=performance.now(),J(e?0:180)})}function we(e){let t=R.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function Oe(){let e=window.location.hash,t=e.indexOf(`?`);if(t===-1||!new URLSearchParams(e.slice(t+1)).has(`eventId`))return;let n=e.slice(0,t);window.history.replaceState(window.history.state,``,`${window.location.pathname}${window.location.search}${n}`)}async function ke(e){let t=e.detail;if(t?.workspaceId===r.workspaceId&&t.eventId)try{let e=new URLSearchParams({around:t.eventId,limit:`200`});t.sessionId&&e.set(`session`,t.sessionId);let n=await fetch(`/api/workspaces/${r.workspaceId}/events?${e}`);if(!n.ok)return;let i=await n.json(),a=i.events??[],s=a.filter(e=>e.type===`agent:event`&&e.workspaceId===r.workspaceId),c=a.filter(e=>e.type===`user:message`&&e.workspaceId===r.workspaceId);o.reset(r.workspaceId,s.map(e=>e.payload),s.map(e=>e.createdAt),{oldestId:a[0]?.id,hasMoreOlder:i.hasMore??!1,sessionIds:s.map(e=>e.sessionId),eventIds:s.map(e=>e.id)});for(let e of c){let t=e.payload;typeof t.content==`string`&&v.addActivityItem(r.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await De(U,e=>!e,5e3),await d(),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e())));let l=P.value.findIndex(e=>e.items.some(e=>e.eventIds?.includes(t.eventId)??!1));if(l<0)return;V.value=t.eventId,Ae.value?.scrollTo(l,`center`),X.value=l,window.setTimeout(()=>{V.value===t.eventId&&(V.value=null)},1800)}catch(e){console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Ae=a(null),je=a(null),X=a(0);function Me(e){X.value=e.index}let Z=a(!1);async function Ne(){if(Z.value)return;Z.value=!0;let e=r.workspaceId;try{let t=Ee(P.value,X.value);if(t<0)for(let n=0;n<15;n++){if(e!==r.workspaceId)return;if(!fe())break;let n=P.value.length;if(await ve(),await d(),e!==r.workspaceId)return;if(X.value+=P.value.length-n,t=Ee(P.value,X.value),t>=0)break}t>=0&&e===r.workspaceId&&(Ae.value?.scrollTo(t,`start`),X.value=t)}finally{Z.value=!1}}async function Q(){H=!1,await d(),await J(0),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e()))),await J(0),H=!0}let $=b(()=>{let e=o.sessionIdsFor(r.workspaceId);if(!w.value)return e.length;let t=0;for(let n of e)A(n)&&t++;return t}),Pe=b(()=>o.eventsFor(r.workspaceId).length);async function Fe(){U.value=!0;let e=Date.now();await new Promise(e=>setTimeout(e,Dt));let t=Math.max(0,e+kt-Date.now());await De(Pe,e=>e>0,t),U.value=!1}c(U,async e=>{!e&&$.value>0&&await Q(),!e&&$.value===0&&w.value&&Re(),e||d(()=>{je.value=R.value?.getScrollTarget()??null})}),f(()=>{window.addEventListener(`kobo:focus-history-event`,ke),d(()=>{je.value=R.value?.getScrollTarget()??null}),Fe(),$.value>0&&Q(),w.value&&Re();let e=new URLSearchParams(window.location.hash.split(`?`)[1]??``).get(`eventId`);e&&(ke(new CustomEvent(`kobo:focus-history-event`,{detail:{workspaceId:r.workspaceId,sessionId:w.value,eventId:e}})),Oe())});let Ie=$.value>0;c($,async(e,t)=>{if(!B.value){if(!Ie&&e>0){Ie=!0,await Q();return}e>t&&z.value&&!B.value&&Se()}}),s(()=>{window.removeEventListener(`kobo:focus-history-event`,ke),Y!=null&&(cancelAnimationFrame(Y),Y=null)}),c(()=>r.workspaceId,()=>{z.value=!0,Ie=$.value>0,H=!1,Fe(),$.value>0&&Q()}),c(()=>v.selectedSessionId,async()=>{z.value=!0,H=!1,await Q(),Re()});let Le=new Set;async function Re(){let e=w.value;if(!e||$.value>0)return;let t=G(r.workspaceId,e);if(!Le.has(t)){Le.add(t);try{let t=await fetch(`/api/workspaces/${r.workspaceId}/events?session=${encodeURIComponent(e)}&limit=500`);if(!t.ok)return;let n=await t.json(),i=n.events??[];if(i.length===0)return;let a=i.filter(e=>e.type===`agent:event`&&e.workspaceId===r.workspaceId),s=i.filter(e=>e.type===`user:message`&&e.workspaceId===r.workspaceId),c=a.map(e=>e.payload),l=a.map(e=>e.createdAt),u=a.map(e=>e.sessionId??null),f=a.map(e=>e.id);K(r.workspaceId,e,n.hasMore),c.length>0&&o.prepend(r.workspaceId,c,l,{oldestId:i[0].id,hasMoreOlder:o.hasMoreOlderFor(r.workspaceId),sessionIds:u,eventIds:f});for(let e of s){let t=e.payload;typeof t.content==`string`&&v.addActivityItem(r.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await d(),await J(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Le.delete(t)}}}let ze=b(()=>j.value.filter(e=>e.sender!==`system-prompt`).length);c(ze,async(e,t)=>{e>t&&(z.value=!0,await J(180))});async function Be(){z.value=!0,await J(250)}return(e,t)=>U.value?(l(),x(`div`,dt,[m(se,{size:`40px`,color:`primary`})])):(l(),x(`div`,ft,[C(`div`,{"data-testid":`activity-live-region`,class:`sr-only`,role:`log`,"aria-live":`polite`,"aria-atomic":`true`,"aria-label":e.$t(`activity.a11y.region`)},i(ce.value),9,pt),m(O,{name:`fade`},{default:p(()=>[y.value?(l(),x(`div`,mt,[m(se,{size:`18px`,color:`primary`}),C(`span`,ht,i(e.$t(`activity.compacting`)),1)])):g(``,!0)]),_:1}),m(ue,{ref_key:`scrollRef`,ref:R,class:`activity-feed-scroll`,onScroll:de},{default:p(()=>[B.value?(l(),x(`div`,gt,[m(M,{size:`sm`}),_(` `+i(e.$t(`activity.loading_older`)),1)])):g(``,!0),m(be,{ref_key:`virtualScrollRef`,ref:Ae,class:`q-pa-md`,items:P.value,"scroll-target":je.value??void 0,"virtual-scroll-item-size":160,"virtual-scroll-slice-size":20,onVirtualScroll:Me},{default:p(({item:e,index:t})=>[(l(),h(ut,{key:n(Te)(e),turn:e,"data-turn-index":t,highlighted:e.items.some(e=>e.eventIds?.includes(V.value??``)??!1),onScrollTo:we},null,8,[`turn`,`data-turn-index`,`highlighted`]))]),_:1},8,[`items`,`scroll-target`]),P.value.length===0&&F.value.length===0&&!B.value?(l(),x(`div`,_t,[m(k,{name:`forum`,size:`28px`}),C(`div`,vt,i(e.$t(`activity.empty`)),1),C(`div`,yt,i(e.$t(`activity.emptyHint`)),1)])):g(``,!0),F.value.length?(l(),x(`div`,bt,[m(le,{label:e.$t(`activity.raw_lines`,{n:F.value.length}),dense:``},{default:p(()=>[(l(!0),x(S,null,T(F.value,(e,t)=>(l(),x(`div`,{key:t,class:`text-caption text-kobo-3 q-pa-xs`},i(e),1))),128))]),_:1},8,[`label`])])):g(``,!0)]),_:1},512),C(`div`,xt,[z.value?g(``,!0):(l(),h(ee,{key:0,round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.scroll_to_bottom`),"aria-label":e.$t(`activity.scroll_to_bottom`),onClick:Be},null,8,[`title`,`aria-label`])),m(ee,{round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.prev_user_message`),"aria-label":e.$t(`activity.prev_user_message`),loading:Z.value,disable:Z.value,onClick:Ne},null,8,[`title`,`aria-label`,`loading`,`disable`])])]))}}),[[`__scopeId`,`data-v-01cd9f2a`]]);export{At as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as e,ut as t,v as n}from"./reactivity.esm-bundler-DEJj9cFv.js";import{L as r,P as i,Y as a,_ as o,d as s,f as c,p as l,r as u,u as d,y as f,z as p}from"./runtime-core.esm-bundler-CDnVUfeZ.js";import{t as m}from"./QBtn-CZaBclNw.js";import{Lt as h,n as g,zt as _}from"./index-DcwcFDrZ.js";import{a as v,i as y,n as b}from"./use-tours-C8x2V1w5.js";import{n as x}from"./render-chat-markdown-BTPAjU2g.js";import{t as S}from"./QChip-owciRuZu.js";import{t as C}from"./QPage-DaX9sMQC.js";import{t as w}from"./DrawerToggleButton-BxYpUb4y.js";import{t as T}from"./TourReplayButton-Dn4Dy_kH.js";var E={class:`row items-center q-mb-md`},D={class:`text-h6 q-ml-sm`},O={key:0,class:`text-kobo-3 text-center q-pa-lg`},k={key:1,class:`text-negative text-center q-pa-lg`},A={key:2,class:`text-kobo-3 text-center q-pa-lg`},j={key:3,class:`column q-gutter-md`,"data-tour":`changelog-list`},M={key:0,class:`text-caption text-kobo-3`},N={class:`row items-center q-mb-sm`},P={class:`text-subtitle1 text-kobo-2`,style:{"font-family":`var(--kobo-font-mono, monospace)`}},F=[`innerHTML`],I=v(f({__name:`ChangelogPage`,setup(f){let v=g(),{scheduleAutoRun:I}=b(),L=n([]),R=n(``),z=n(!1),B=n(null);function V(e){return x(e)}async function H(){z.value=!0,B.value=null;try{let e=await fetch(`/api/changelog`);if(!e.ok)throw Error(`HTTP ${e.status}`);let t=await e.json();R.value=t.currentVersion??``,L.value=t.versions??[]}catch(e){B.value=e instanceof Error?e.message:String(e)}finally{z.value=!1}}return i(()=>{H(),I(`changelog`)}),(n,i)=>(r(),s(C,{class:`q-pa-md`,style:{"max-width":`900px`,margin:`0 auto`}},{default:a(()=>[d(`div`,E,[o(w,{class:`q-mr-xs`}),o(m,{flat:``,dense:``,round:``,icon:`arrow_back`,onClick:i[0]||=t=>e(v).back()}),d(`div`,D,t(n.$t(`changelog.title`)),1),o(y),o(m,{flat:``,dense:``,icon:`refresh`,loading:z.value,label:n.$t(`common.refresh`),onClick:H},null,8,[`loading`,`label`]),o(T,{"tour-id":`changelog`,class:`q-ml-xs`})]),z.value&&L.value.length===0?(r(),l(`div`,O,t(n.$t(`common.loading`)),1)):B.value?(r(),l(`div`,k,t(B.value),1)):L.value.length===0?(r(),l(`div`,A,t(n.$t(`changelog.empty`)),1)):(r(),l(`div`,j,[R.value?(r(),l(`div`,M,t(n.$t(`changelog.currentVersion`,{version:R.value})),1)):c(``,!0),(r(!0),l(u,null,p(L.value,e=>(r(),s(h,{key:e.version,dark:``,flat:``,bordered:``},{default:a(()=>[o(_,null,{default:a(()=>[d(`div`,N,[d(`div`,P,` v`+t(e.version),1),e.version===R.value?(r(),s(S,{key:0,dense:``,size:`sm`,color:`primary`,"text-color":`kobo-1`,label:n.$t(`changelog.current`),class:`q-ml-sm`},null,8,[`label`])):c(``,!0)]),d(`div`,{class:`changelog-notes`,innerHTML:V(e.notes)},null,8,F)]),_:2},1024)]),_:2},1024))),128))]))]),_:1}))}}),[[`__scopeId`,`data-v-6137cb7c`]]);export{I as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{V as e,nt as t}from"./QBtn-CZaBclNw.js";import{m as n,p as r}from"./use-id-BIuaWKkB.js";function i(e){return e===!1?0:e===!0||e===void 0?1:Number.parseInt(e,10)||0}function a(e){let t=e.currentTarget,i=t.__qclosepopup;i!==0&&setTimeout(()=>{let a=n(t);a!==void 0&&r(a,e,i)},0)}function o(t){e(t,13)&&a(t)}var s=t({name:`close-popup`,beforeMount(e,{value:t}){e.__qclosepopup=i(t),e.addEventListener(`click`,a),e.addEventListener(`keyup`,o)},updated(e,{value:t,oldValue:n}){t!==n&&e.__qclosepopup!==void 0&&(e.__qclosepopup=i(t))},beforeUnmount(e){e.__qclosepopup!==void 0&&(e.removeEventListener(`click`,a),e.removeEventListener(`keyup`,o),e.__qclosepopup=void 0)}});export{s as t};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{ot as e,ut as t,v as n}from"./reactivity.esm-bundler-DEJj9cFv.js";import{J as r,L as i,P as a,Y as ee,_ as o,d as te,f as s,g as ne,l as c,p as l,r as u,u as d,y as f,z as re}from"./runtime-core.esm-bundler-CDnVUfeZ.js";import{t as p}from"./QBtn-CZaBclNw.js";import{n as ie,p as ae,xt as oe}from"./index-DcwcFDrZ.js";import{t as se}from"./QMarkupTable-B9QuKtK_.js";import{a as m,i as h}from"./use-tours-C8x2V1w5.js";var g=[`workspace`,`engine`,`model`,`status`,`branch`,`tasks_done`,`tasks_total`,`commits`,`files_changed`,`insertions`,`deletions`,`agent_messages`,`user_messages`,`injected_prompts`,`questions`,`tool_calls`,`errors`,`input_tokens`,`output_tokens`,`sessions`,`duration_seconds`];function _(e){if(e==null)return``;let t=String(e);return/[",\r\n]/.test(t)?`"${t.replace(/"/g,`""`)}"`:t}function v(e){let t=e.map(e=>{let t=e.gitStats,n=e.activity;return[e.workspace.name,e.workspace.engine,e.workspace.model,e.workspace.status,e.workspace.workingBranch,e.tasks.done,e.tasks.total,t?.commitCount??null,t?.filesChanged??null,t?.insertions??null,t?.deletions??null,n.agentMessages,n.userMessages,n.injectedPrompts,n.questions,n.toolCalls,n.errors,n.inputTokens,n.outputTokens,n.sessions,Math.round(n.durationMs/1e3)].map(_).join(`,`)});return`${[g.join(`,`),...t].join(`\r
|
|
2
|
+
`)}`}var y={key:0,class:`comparison-panel q-pa-md`},b={class:`row items-center q-mb-sm`},x={class:`text-subtitle2`},S={class:`text-caption text-kobo-3 q-mb-sm`},C={class:`text-left`},w={class:`text-left`},T={class:`text-right`},E={class:`text-right`},D={class:`text-right`},O={class:`text-right`},k={class:`text-right`},A={class:`text-right`},j={class:`text-right`},M={class:`text-right`},N={class:`text-right`},P={class:`text-right`},F={class:`text-right`},I={class:`text-caption text-kobo-3 comparison-mono`},L={key:0,class:`text-caption text-kobo-3`},R={class:`text-right comparison-mono`},z={class:`text-right`},B={class:`text-right`},V={class:`text-right`},ce={key:0,class:`comparison-mono`},H={class:`text-kobo-success`},U={class:`text-kobo-danger q-ml-xs`},W={key:1,class:`text-kobo-3`},G={class:`text-right comparison-mono`},le=[`title`],ue={class:`text-right`},de={class:`text-right`},fe={class:`text-right comparison-mono`},pe=[`title`],me={class:`text-right`},he={class:`text-right comparison-mono`},ge={class:`text-right`},_e={key:1,class:`text-caption text-kobo-3`},ve={key:0,class:`text-caption text-kobo-warning q-mt-sm`},K=m(f({__name:`ComparisonPanel`,props:{workspaceId:{}},setup(f){let m=f;function g(e){return e<1e3?String(e):e<1e6?`${(e/1e3).toFixed(+(e<1e4))}k`:`${(e/1e6).toFixed(1)}M`}function _(e){if(e<=0)return`-`;let t=Math.round(e/1e3);if(t<60)return q(`reliability.durationSeconds`,{s:t});let n=Math.floor(t/60);return n<60?q(`reliability.durationMinutes`,{m:n,s:t%60}):q(`reliability.durationHours`,{h:Math.floor(n/60),m:n%60})}let K=ie(),ye=ae(),{t:q}=oe(),J=n([]),Y=n(!1),be={created:`workspaceStatus.created`,extracting:`workspaceStatus.extracting`,brainstorming:`workspaceStatus.brainstorming`,executing:`workspaceStatus.executing`,"awaiting-user":`workspaceStatus.awaitingUser`,completed:`workspaceStatus.completed`,idle:`workspaceStatus.idle`,error:`workspaceStatus.error`,quota:`workspaceStatus.quota`,retrying:`workspaceStatus.retrying`},X=n({});function xe(e){return X.value[e]??e}async function Se(){try{let e=await fetch(`/api/engines`);if(!e.ok)return;let t=await e.json();X.value=Object.fromEntries(t.map(e=>[e.id,e.displayName]))}catch{}}function Z(e){let t=be[e];return t?q(t):e}let Ce=c(()=>m.workspaceId);function Q(e){return e.workspace.id===Ce.value}function we(e){ye.selectWorkspace(e),K.push({name:`workspace`,params:{id:e}})}function Te(){let e=v(J.value),t=new Blob([e],{type:`text/csv;charset=utf-8`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`kobo-comparison-${J.value[0]?.workspace.comparisonId??`comparison`}.csv`,r.click(),URL.revokeObjectURL(n)}async function $(){Y.value=!0;try{let e=await fetch(`/api/workspaces/${m.workspaceId}/comparison`);if(!e.ok){J.value=[];return}let t=await e.json();J.value=t.members??[]}catch{J.value=[]}finally{Y.value=!1}}return a(()=>{Se(),$()}),r(()=>m.workspaceId,$),(n,r)=>J.value.length>0?(i(),l(`div`,y,[d(`div`,b,[d(`div`,x,t(n.$t(`comparison.title`)),1),o(h),o(p,{flat:``,dense:``,round:``,size:`sm`,icon:`download`,title:n.$t(`comparison.exportCsv`),onClick:Te},null,8,[`title`]),o(p,{flat:``,dense:``,round:``,size:`sm`,icon:`refresh`,loading:Y.value,onClick:$},null,8,[`loading`])]),d(`div`,S,t(n.$t(`comparison.hint`)),1),o(se,{flat:``,dense:``,dark:``,class:`comparison-table`},{default:ee(()=>[d(`thead`,null,[d(`tr`,null,[d(`th`,C,t(n.$t(`comparison.engine`)),1),d(`th`,w,t(n.$t(`comparison.status`)),1),d(`th`,T,t(n.$t(`comparison.tasks`)),1),d(`th`,E,t(n.$t(`comparison.commits`)),1),d(`th`,D,t(n.$t(`comparison.files`)),1),d(`th`,O,t(n.$t(`comparison.diff`)),1),d(`th`,k,t(n.$t(`comparison.messages`)),1),d(`th`,A,t(n.$t(`comparison.questions`)),1),d(`th`,j,t(n.$t(`comparison.tools`)),1),d(`th`,M,t(n.$t(`comparison.errors`)),1),d(`th`,N,t(n.$t(`comparison.tokens`)),1),d(`th`,P,t(n.$t(`comparison.sessions`)),1),d(`th`,F,t(n.$t(`comparison.duration`)),1),r[0]||=d(`th`,null,null,-1)])]),d(`tbody`,null,[(i(!0),l(u,null,re(J.value,r=>(i(),l(`tr`,{key:r.workspace.id,class:e({"comparison-current":Q(r)})},[d(`td`,null,[d(`div`,null,t(xe(r.workspace.engine)),1),d(`div`,I,t(r.workspace.model),1)]),d(`td`,null,[ne(t(Z(r.workspace.status))+` `,1),r.workspace.archivedAt?(i(),l(`span`,L,` · `+t(n.$t(`comparison.archived`)),1)):s(``,!0)]),d(`td`,R,t(r.tasks.done)+`/`+t(r.tasks.total),1),d(`td`,z,t(r.gitStats?.commitCount??`-`),1),d(`td`,B,t(r.gitStats?.filesChanged??`-`),1),d(`td`,V,[r.gitStats?(i(),l(`span`,ce,[d(`span`,H,`+`+t(r.gitStats.insertions),1),d(`span`,U,`-`+t(r.gitStats.deletions),1)])):(i(),l(`span`,W,`-`))]),d(`td`,G,[d(`span`,{title:n.$t(`comparison.messagesTitle`)},t(r.activity.agentMessages)+` / `+t(r.activity.userMessages),9,le)]),d(`td`,ue,t(r.activity.questions),1),d(`td`,de,t(r.activity.toolCalls),1),d(`td`,{class:e([`text-right`,r.activity.errors>0?`text-kobo-danger`:``])},t(r.activity.errors),3),d(`td`,fe,[d(`span`,{title:n.$t(`comparison.tokensTitle`)},t(g(r.activity.inputTokens))+` / `+t(g(r.activity.outputTokens)),9,pe)]),d(`td`,me,t(r.activity.sessions),1),d(`td`,he,t(_(r.activity.durationMs)),1),d(`td`,ge,[Q(r)?(i(),l(`span`,_e,t(n.$t(`comparison.current`)),1)):(i(),te(p,{key:0,flat:``,dense:``,"no-caps":``,size:`sm`,label:n.$t(`comparison.open`),onClick:e=>we(r.workspace.id)},null,8,[`label`,`onClick`]))])],2))),128))])]),_:1}),J.value.length<2?(i(),l(`div`,ve,t(n.$t(`comparison.incomplete`)),1)):s(``,!0)])):s(``,!0)}}),[[`__scopeId`,`data-v-95e8c6b5`]]);export{K as default};
|