@loicngr/kobo 1.11.35 → 1.11.37
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 +15 -0
- package/CHANGELOG.md +9 -0
- package/dist/server/index.js +11 -24
- package/dist/server/routes/dev-server.js +2 -1
- package/dist/server/routes/pull-requests.js +5 -4
- package/dist/server/routes/workspaces.js +122 -91
- 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/engines/codex/client.js +1 -0
- package/dist/server/services/agent/engines/codex/engine.js +47 -10
- package/dist/server/services/agent/engines/codex/event-mapper.js +5 -1
- package/dist/server/services/agent/engines/codex/jsonrpc/peer.js +21 -8
- package/dist/server/services/agent/engines/codex/jsonrpc/transport.js +30 -3
- package/dist/server/services/agent/orchestrator.js +175 -68
- package/dist/server/services/agent/session-controller.js +12 -2
- package/dist/server/services/auto-loop-service.js +71 -6
- package/dist/server/services/change-source-branch-service.js +36 -4
- package/dist/server/services/cron-service.js +19 -0
- package/dist/server/services/db-backup-service.js +30 -0
- package/dist/server/services/dev-server-service.js +167 -38
- package/dist/server/services/image-service.js +25 -28
- package/dist/server/services/pr-checkout-service.js +34 -5
- package/dist/server/services/quota-backoff-service.js +25 -8
- package/dist/server/services/wakeup-service.js +37 -3
- package/dist/server/services/worktree-purge-service.js +3 -7
- package/dist/server/utils/agent-stop-result.js +12 -0
- package/dist/server/utils/git-ops.js +8 -1
- package/dist/server/utils/git-worktree-fingerprint.js +86 -0
- package/dist/server/utils/safe-path.js +21 -0
- package/dist/server/utils/script-runner.js +32 -9
- package/dist/server/utils/workspace-lifecycle-guard.js +27 -0
- package/package.json +1 -1
- package/src/client/dist/pwa/assets/{ActivityFeed-Bakgvay6.css → ActivityFeed-BDyFBoxd.css} +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-C_-hxIo8.js +8 -0
- package/src/client/dist/pwa/assets/{ChangelogPage-DVLWWvvz.js → ChangelogPage-CBlGJ4zT.js} +1 -1
- package/src/client/dist/pwa/assets/{ComparisonPanel-LZrvk6a6.js → ComparisonPanel-DZAE2X9j.js} +1 -1
- package/src/client/dist/pwa/assets/CreatePage-A3pVradx.css +1 -0
- package/src/client/dist/pwa/assets/CreatePage-ArmtcF4d.js +2 -0
- package/src/client/dist/pwa/assets/{DashboardPage-0odIhseb.js → DashboardPage-eQTCXcdz.js} +1 -1
- package/src/client/dist/pwa/assets/{DiffViewer-NWB2uQkz.js → DiffViewer-CaT7vuSr.js} +3 -3
- package/src/client/dist/pwa/assets/{DocumentsPanel-3yApHgbl.js → DocumentsPanel-A0isCTaI.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-C_bNtZsE.js → DrawerToggleButton-T70fh8dp.js} +1 -1
- package/src/client/dist/pwa/assets/{GitPanel-BMjWwiAX.js → GitPanel-CAvk0G7h.js} +3 -3
- package/src/client/dist/pwa/assets/{HealthPage-Vyr1YG2o.js → HealthPage-D8Mr0V6w.js} +1 -1
- package/src/client/dist/pwa/assets/{MainLayout-s8_9FPE2.js → MainLayout-x6Es4Da-.js} +2 -2
- package/src/client/dist/pwa/assets/{QBtnDropdown-BxuXEBp5.js → QBtnDropdown-CQqzzlFI.js} +1 -1
- package/src/client/dist/pwa/assets/{QDrawer-BdlDiU6a.js → QDrawer-Cwj2qh0r.js} +1 -1
- package/src/client/dist/pwa/assets/{QSelect-BrSemS3o.js → QSelect-BSPtuXJg.js} +1 -1
- package/src/client/dist/pwa/assets/{QTooltip-CLCFHW2D.js → QTooltip-DC_kDKku.js} +1 -1
- package/src/client/dist/pwa/assets/{SchedulePanel-F9bdAq6K.js → SchedulePanel-9aJSoz6Y.js} +1 -1
- package/src/client/dist/pwa/assets/{SearchPage-CBCefYqc.js → SearchPage-BjNvtE_U.js} +1 -1
- package/src/client/dist/pwa/assets/{SettingsPage-BPgTHmKj.js → SettingsPage-CvLnT73V.js} +1 -1
- package/src/client/dist/pwa/assets/{SplitWorkspacePage-DDs2nuoT.js → SplitWorkspacePage-DTao0DHr.js} +1 -1
- package/src/client/dist/pwa/assets/{TerminalPanel-CulE0wTU.js → TerminalPanel-BrKIW-bJ.js} +1 -1
- package/src/client/dist/pwa/assets/{TouchPan-DUgqs2cW.js → TouchPan-_n1AQgoo.js} +1 -1
- package/src/client/dist/pwa/assets/{TourReplayButton-CdHpGBdr.js → TourReplayButton-CJF3nVvn.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-W5SvqzXO.js → WorkspaceAttentionLabels-CxesUiIm.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspacePage-DYQrfeYw.css → WorkspacePage-DoXtwpjV.css} +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-MxBE0mr3.js +4 -0
- package/src/client/dist/pwa/assets/{browser-CnuqDTyc.js → browser-DGWpkvN3.js} +1 -1
- package/src/client/dist/pwa/assets/{build-path-tree-JodIO7-I.js → build-path-tree-Ba1dSLgi.js} +1 -1
- package/src/client/dist/pwa/assets/{de-Dt_m-H83.js → de-DVK21RB6.js} +1 -1
- package/src/client/dist/pwa/assets/{documents-3ZBK1pXE.js → documents-BMHzjW_g.js} +1 -1
- package/src/client/dist/pwa/assets/{editor.api-DZ9WO_J7.js → editor.api-CYTuNwqu.js} +1 -1
- package/src/client/dist/pwa/assets/{efforts-LrtJPx-j.js → efforts-BPlRSHXp.js} +1 -1
- package/src/client/dist/pwa/assets/{engineFeatures-DmnHj_uD.js → engineFeatures-DVB2rklO.js} +1 -1
- package/src/client/dist/pwa/assets/{es-DFg25DCS.js → es-C8cQwDZM.js} +1 -1
- package/src/client/dist/pwa/assets/{expand-template-Dv4JC2zU.js → expand-template-Cj82YE-O.js} +1 -1
- package/src/client/dist/pwa/assets/{format-CHjfjuhr.js → format-Dk7O3LhA.js} +1 -1
- package/src/client/dist/pwa/assets/{formatters-BsgH8CoH.js → formatters-TGjvs7Ca.js} +1 -1
- package/src/client/dist/pwa/assets/{fr-CQ1slpol.js → fr-BVOMZ2Bl.js} +1 -1
- package/src/client/dist/pwa/assets/{freemarker2-Dla55SIR.js → freemarker2-39lO_dWH.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-DtuuZy7D.js → handlebars-M_0V82cD.js} +1 -1
- package/src/client/dist/pwa/assets/{html-CKYOCR8W.js → html-yILqYDsl.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-2UeMfd5m.js +1 -0
- package/src/client/dist/pwa/assets/{index-CIP08MpU.js → index-BJU5dnkF.js} +2 -2
- package/src/client/dist/pwa/assets/{it-w09rfaP7.js → it-CxkAoOj4.js} +1 -1
- package/src/client/dist/pwa/assets/{javascript-BxzTnsIQ.js → javascript-DrW-amDc.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-C8BfPFUy.js → liquid-D_mXmUoL.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-DZm_YXkC.js → mdx-E8O3lvR5.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-HDbRHiNR.js → monaco-contributions-C2K3sl_V.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco.contribution-DRW-851R.js → monaco.contribution-DLhHQIJn.js} +2 -2
- package/src/client/dist/pwa/assets/{network-auth-Ce9hsryo.js → network-auth-BUwWtnui.js} +1 -1
- package/src/client/dist/pwa/assets/{notify-theme-OBpMEBvP.js → notify-theme-1mOpY2vN.js} +1 -1
- package/src/client/dist/pwa/assets/{pending-diff-open-CVQxWyzP.js → pending-diff-open-BL7dKRqF.js} +1 -1
- package/src/client/dist/pwa/assets/{permissionModes-CVFHS2ec.js → permissionModes-BtC3_P-P.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-mcpQp2o6.js → project-color-BFBFZWeG.js} +1 -1
- package/src/client/dist/pwa/assets/{python-CO5iQN74.js → python-DoZAYim6.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-BC7D8LMf.js → razor-9bZ30g2y.js} +1 -1
- package/src/client/dist/pwa/assets/{render-chat-markdown-Cb15kvox.js → render-chat-markdown-7EtWNjG0.js} +1 -1
- package/src/client/dist/pwa/assets/{typescript-BCHCSNpD.js → typescript-C1CBfL3L.js} +1 -1
- package/src/client/dist/pwa/assets/{use-tours-DhLhglwr.js → use-tours-BJ81clv6.js} +1 -1
- package/src/client/dist/pwa/assets/{workspace-templates-CoWrOEac.js → workspace-templates-r1-bipjM.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-y9M3NAIZ.js → xml-COuu5isK.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-CthlToPo.js → yaml-ChZ7WqaJ.js} +1 -1
- package/src/client/dist/pwa/index.html +1 -1
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-YRbIRutK.js +0 -8
- package/src/client/dist/pwa/assets/CreatePage-DxOyY65u.css +0 -1
- package/src/client/dist/pwa/assets/CreatePage-gFrn4xl9.js +0 -2
- package/src/client/dist/pwa/assets/WorkspacePage-B6O7j57u.js +0 -4
- package/src/client/dist/pwa/assets/i18n-DTfKQUMf.js +0 -1
|
@@ -2,7 +2,15 @@ import { getDb } from '../db/index.js';
|
|
|
2
2
|
import { emitEphemeral } from './websocket-service.js';
|
|
3
3
|
import { getWorkspace } from './workspace-service.js';
|
|
4
4
|
const timers = new Map();
|
|
5
|
+
let suspended = false;
|
|
5
6
|
let onFireCallback = null;
|
|
7
|
+
/** Stop in-memory delivery without consuming the schedules needed by the next boot. */
|
|
8
|
+
export function suspendForShutdown() {
|
|
9
|
+
suspended = true;
|
|
10
|
+
for (const timer of timers.values())
|
|
11
|
+
clearTimeout(timer);
|
|
12
|
+
timers.clear();
|
|
13
|
+
}
|
|
6
14
|
function rowToPending(row) {
|
|
7
15
|
return {
|
|
8
16
|
workspaceId: row.workspace_id,
|
|
@@ -26,10 +34,9 @@ export function arm(workspaceId, delayMs, meta) {
|
|
|
26
34
|
const db = getDb();
|
|
27
35
|
const now = new Date();
|
|
28
36
|
const targetAt = new Date(now.getTime() + delayMs).toISOString();
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const retryCount = (existing?.retry_count ?? 0) + 1;
|
|
37
|
+
const retryCount = meta.retryCount;
|
|
38
|
+
if (!Number.isSafeInteger(retryCount) || retryCount < 1)
|
|
39
|
+
throw new Error('retryCount must be a positive integer');
|
|
33
40
|
db.prepare(`INSERT INTO pending_quota_backoffs (workspace_id, target_at, resets_at, source, reason, retry_count, created_at)
|
|
34
41
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
35
42
|
ON CONFLICT(workspace_id) DO UPDATE SET
|
|
@@ -42,9 +49,12 @@ export function arm(workspaceId, delayMs, meta) {
|
|
|
42
49
|
const previous = timers.get(workspaceId);
|
|
43
50
|
if (previous)
|
|
44
51
|
clearTimeout(previous);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
timers.delete(workspaceId);
|
|
53
|
+
if (!suspended) {
|
|
54
|
+
const timer = setTimeout(() => fireOrSkip(workspaceId), Math.max(0, delayMs));
|
|
55
|
+
timer.unref?.();
|
|
56
|
+
timers.set(workspaceId, timer);
|
|
57
|
+
}
|
|
48
58
|
emitEphemeral(workspaceId, 'agent:quota-backoff', {
|
|
49
59
|
targetAt,
|
|
50
60
|
resetsAt: meta.resetsAt,
|
|
@@ -90,6 +100,8 @@ export function setOnFireCallback(fn) {
|
|
|
90
100
|
* archived or missing workspaces are deleted without firing.
|
|
91
101
|
*/
|
|
92
102
|
export function restoreOnBoot(onFire) {
|
|
103
|
+
suspendForShutdown();
|
|
104
|
+
suspended = false;
|
|
93
105
|
setOnFireCallback(onFire);
|
|
94
106
|
const db = getDb();
|
|
95
107
|
const rows = db.prepare('SELECT * FROM pending_quota_backoffs').all();
|
|
@@ -107,7 +119,12 @@ export function restoreOnBoot(onFire) {
|
|
|
107
119
|
}
|
|
108
120
|
/** Internal — invoked when a timer fires. */
|
|
109
121
|
function fireOrSkip(workspaceId) {
|
|
122
|
+
if (suspended)
|
|
123
|
+
return;
|
|
110
124
|
timers.delete(workspaceId);
|
|
125
|
+
const pending = getPending(workspaceId);
|
|
126
|
+
if (!pending)
|
|
127
|
+
return;
|
|
111
128
|
// Final archive check before firing — workspace might have been archived
|
|
112
129
|
// between the timer being armed and now.
|
|
113
130
|
const ws = getWorkspace(workspaceId);
|
|
@@ -124,7 +141,7 @@ function fireOrSkip(workspaceId) {
|
|
|
124
141
|
const cb = onFireCallback;
|
|
125
142
|
if (!cb)
|
|
126
143
|
return;
|
|
127
|
-
cb(workspaceId);
|
|
144
|
+
cb(workspaceId, pending);
|
|
128
145
|
}
|
|
129
146
|
/** @internal test-only */
|
|
130
147
|
export const _timers = timers;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getDb } from '../db/index.js';
|
|
2
2
|
import { slugifyProjectName } from '../utils/project-slug.js';
|
|
3
|
+
import { isWorkspaceLifecycleBusy } from '../utils/workspace-lifecycle-guard.js';
|
|
3
4
|
import { resolveWorkspaceWorktreePath } from '../utils/worktree-paths.js';
|
|
4
5
|
import * as orchestrator from './agent/orchestrator.js';
|
|
5
6
|
import * as settingsService from './settings-service.js';
|
|
@@ -12,6 +13,14 @@ const AUTONOMOUS_LOOP_SENTINEL = '<<autonomous-loop-dynamic>>';
|
|
|
12
13
|
const AUTONOMOUS_LOOP_FALLBACK_PROMPT = 'Continue where you left off.';
|
|
13
14
|
/** In-memory timers — cleared on cancel/fire; rebuilt on boot via rehydrate. */
|
|
14
15
|
const timers = new Map();
|
|
16
|
+
let suspended = false;
|
|
17
|
+
/** Preserve persisted wakeups while preventing delivery during teardown. */
|
|
18
|
+
export function suspendForShutdown() {
|
|
19
|
+
suspended = true;
|
|
20
|
+
for (const timer of timers.values())
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
timers.clear();
|
|
23
|
+
}
|
|
15
24
|
function clamp(n, lo, hi) {
|
|
16
25
|
return Math.max(lo, Math.min(hi, n));
|
|
17
26
|
}
|
|
@@ -54,9 +63,12 @@ export function schedule(workspaceId, delaySeconds, prompt, reason, agentSession
|
|
|
54
63
|
db.prepare(`INSERT OR REPLACE INTO pending_wakeups
|
|
55
64
|
(workspace_id, target_at, prompt, reason, created_at, agent_session_id)
|
|
56
65
|
VALUES (?, ?, ?, ?, ?, ?)`).run(workspaceId, targetAtIso, effectivePrompt, reason ?? null, new Date().toISOString(), agentSessionId ?? null);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
timers.delete(workspaceId);
|
|
67
|
+
if (!suspended) {
|
|
68
|
+
const timeout = setTimeout(() => fire(workspaceId), clampedSeconds * 1000);
|
|
69
|
+
timeout.unref?.();
|
|
70
|
+
timers.set(workspaceId, timeout);
|
|
71
|
+
}
|
|
60
72
|
emitEphemeral(workspaceId, 'wakeup:scheduled', { targetAt: targetAtIso, reason });
|
|
61
73
|
}
|
|
62
74
|
catch (err) {
|
|
@@ -105,6 +117,8 @@ export function isWakeupScheduled(workspaceId) {
|
|
|
105
117
|
}
|
|
106
118
|
/** Re-register timers for rows persisted across restart. Skips stale entries. */
|
|
107
119
|
export function rehydrate() {
|
|
120
|
+
suspendForShutdown();
|
|
121
|
+
suspended = false;
|
|
108
122
|
try {
|
|
109
123
|
const db = getDb();
|
|
110
124
|
const rows = db.prepare('SELECT * FROM pending_wakeups').all();
|
|
@@ -139,13 +153,33 @@ export function rehydrate() {
|
|
|
139
153
|
}
|
|
140
154
|
/** Internal — invoked by setTimeout. */
|
|
141
155
|
function fire(workspaceId) {
|
|
156
|
+
if (suspended)
|
|
157
|
+
return;
|
|
142
158
|
try {
|
|
143
159
|
const db = getDb();
|
|
144
160
|
const row = db.prepare('SELECT * FROM pending_wakeups WHERE workspace_id = ?').get(workspaceId);
|
|
145
161
|
timers.delete(workspaceId);
|
|
146
162
|
if (!row)
|
|
147
163
|
return;
|
|
164
|
+
if (isWorkspaceLifecycleBusy(workspaceId)) {
|
|
165
|
+
defer(workspaceId, row);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
148
168
|
if (orchestrator.hasController(workspaceId)) {
|
|
169
|
+
// Claude can keep the stream open after a result while a background
|
|
170
|
+
// task runs. Waiting for that controller to disappear deadlocks a
|
|
171
|
+
// scheduled check on the very work it is meant to inspect.
|
|
172
|
+
try {
|
|
173
|
+
if (orchestrator.sendWakeupIfWaiting(workspaceId, row.prompt, row.agent_session_id ?? undefined)) {
|
|
174
|
+
failedRetries.delete(workspaceId);
|
|
175
|
+
db.prepare('DELETE FROM pending_wakeups WHERE workspace_id = ?').run(workspaceId);
|
|
176
|
+
emitEphemeral(workspaceId, 'wakeup:fired', {});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
console.error(`[wakeup-service] delivery to active session failed for '${workspaceId}':`, err);
|
|
182
|
+
}
|
|
149
183
|
defer(workspaceId, row);
|
|
150
184
|
return;
|
|
151
185
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
|
+
import { assertAgentStopped } from '../utils/agent-stop-result.js';
|
|
4
5
|
import { logError, logWarn } from '../utils/logger.js';
|
|
5
6
|
import { withWorkspaceLifecycleGuard } from '../utils/workspace-lifecycle-guard.js';
|
|
6
7
|
import * as agentManager from './agent/orchestrator.js';
|
|
@@ -29,19 +30,14 @@ async function purgeWorktreeGuarded(workspaceId, expectedArchivedAt) {
|
|
|
29
30
|
const warnings = [];
|
|
30
31
|
// `removeWorktree` runs later in this same function — the agent must be dead
|
|
31
32
|
// by then, not merely asked to stop.
|
|
32
|
-
|
|
33
|
-
await agentManager.stopAgentAndWait(workspaceId, undefined, 'purge');
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
37
|
-
logError('purge', `stopAgent failed for '${workspace.name}'`, { workspaceId: workspace.id, error: msg });
|
|
38
|
-
}
|
|
33
|
+
assertAgentStopped(await agentManager.stopAgentAndWait(workspaceId, undefined, 'purge'));
|
|
39
34
|
try {
|
|
40
35
|
await devServerService.stopDevServer(workspaceId);
|
|
41
36
|
}
|
|
42
37
|
catch (err) {
|
|
43
38
|
const msg = err instanceof Error ? err.message : String(err);
|
|
44
39
|
logError('purge', `stopDevServer failed for '${workspace.name}'`, { workspaceId: workspace.id, error: msg });
|
|
40
|
+
throw err;
|
|
45
41
|
}
|
|
46
42
|
try {
|
|
47
43
|
destroyTerminal(workspaceId);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class AgentStopError extends Error {
|
|
2
|
+
outcome;
|
|
3
|
+
code = 'agent-stop-incomplete';
|
|
4
|
+
constructor(outcome) {
|
|
5
|
+
super(`Agent stop is not confirmed (${outcome}). Wait for it to stop and retry.`);
|
|
6
|
+
this.outcome = outcome;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function assertAgentStopped(outcome) {
|
|
10
|
+
if (outcome !== 'stopped' && outcome !== 'not-running')
|
|
11
|
+
throw new AgentStopError(outcome);
|
|
12
|
+
}
|
|
@@ -669,7 +669,13 @@ export function listProperCommits(repoPath, workingBranch, newBase, oldBase) {
|
|
|
669
669
|
* `repoPath`. The caller (and the Kōbō worktree orchestrator) must ensure
|
|
670
670
|
* `workingBranch` is the active branch — do NOT add a `git checkout` here.
|
|
671
671
|
*/
|
|
672
|
+
export function assertCurrentBranch(repoPath, expected) {
|
|
673
|
+
const actual = getCurrentBranch(repoPath);
|
|
674
|
+
if (actual !== expected)
|
|
675
|
+
throw new Error(`Expected checkout '${expected}', found '${actual || 'detached HEAD'}'`);
|
|
676
|
+
}
|
|
672
677
|
export function reconstructBranchOnto(repoPath, workingBranch, newBase, commits) {
|
|
678
|
+
assertCurrentBranch(repoPath, workingBranch);
|
|
673
679
|
const baseRef = resolveBase(repoPath, newBase);
|
|
674
680
|
const backupBranch = `kobo-backup/${workingBranch}-${Date.now()}`;
|
|
675
681
|
git(repoPath, ['branch', backupBranch, workingBranch]);
|
|
@@ -749,8 +755,9 @@ export function pruneBackupBranches(repoPath, workingBranch, keep = 3) {
|
|
|
749
755
|
}
|
|
750
756
|
/** Abort any in-progress operation, then hard-reset `workingBranch` to a backup branch. */
|
|
751
757
|
export function restoreBranchFromBackup(repoPath, workingBranch, backupBranch) {
|
|
758
|
+
assertCurrentBranch(repoPath, workingBranch);
|
|
752
759
|
abortOngoingGitOperation(repoPath);
|
|
753
|
-
|
|
760
|
+
assertCurrentBranch(repoPath, workingBranch);
|
|
754
761
|
git(repoPath, ['reset', '--hard', backupBranch]);
|
|
755
762
|
}
|
|
756
763
|
/** Return structured diff shortstat between two refs (three-dot merge base). */
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { constants } from 'node:fs';
|
|
4
|
+
import { lstat, open, readlink, realpath } from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
/** Hash diffs and untracked contents without loading large files into memory. */
|
|
7
|
+
export async function fingerprintWorktree(cwd) {
|
|
8
|
+
return fingerprintRepository(await realpath(cwd), new Set());
|
|
9
|
+
}
|
|
10
|
+
/** Nested repositories contribute HEAD and their own Git-filtered working state, never a .git traversal. */
|
|
11
|
+
async function fingerprintRepository(cwd, visited) {
|
|
12
|
+
if (visited.has(cwd))
|
|
13
|
+
throw new Error('Cannot fingerprint a recursive repository path');
|
|
14
|
+
visited.add(cwd);
|
|
15
|
+
const gitOptions = { cwd, encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024 };
|
|
16
|
+
const topLevel = execFileSync('git', ['rev-parse', '--show-toplevel'], gitOptions).trim();
|
|
17
|
+
if ((await realpath(topLevel)) !== cwd)
|
|
18
|
+
throw new Error('Working tree changed during fingerprinting; retry diagnosis');
|
|
19
|
+
const hash = createHash('sha256');
|
|
20
|
+
let head;
|
|
21
|
+
try {
|
|
22
|
+
head = execFileSync('git', ['rev-parse', '--verify', '--quiet', 'HEAD'], gitOptions).trim();
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
// A freshly initialized nested repository has no commit yet. Other Git failures remain fatal.
|
|
26
|
+
if (err.status !== 1)
|
|
27
|
+
throw err;
|
|
28
|
+
head = 'unborn';
|
|
29
|
+
}
|
|
30
|
+
hash.update(`HEAD\0${head}\0`);
|
|
31
|
+
for (const staged of [false, true]) {
|
|
32
|
+
hash.update(staged ? 'index\0' : 'working-tree\0');
|
|
33
|
+
await new Promise((resolve, reject) => {
|
|
34
|
+
const child = spawn('git', ['diff', '--no-ext-diff', '--no-textconv', '--binary', '--no-color', ...(staged ? ['--cached'] : []), '--'], { cwd, stdio: ['ignore', 'pipe', 'pipe'], timeout: 30_000 });
|
|
35
|
+
child.stdout.on('data', (chunk) => hash.update(chunk));
|
|
36
|
+
child.stderr.resume();
|
|
37
|
+
child.on('error', reject);
|
|
38
|
+
child.on('close', (code) => (code === 0 ? resolve() : reject(new Error('Cannot fingerprint working tree diff'))));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const names = execFileSync('git', ['ls-files', '--others', '--exclude-standard', '-z'], {
|
|
42
|
+
cwd,
|
|
43
|
+
encoding: 'utf8',
|
|
44
|
+
timeout: 30_000,
|
|
45
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
46
|
+
})
|
|
47
|
+
.split('\0')
|
|
48
|
+
.filter(Boolean)
|
|
49
|
+
.sort();
|
|
50
|
+
for (const name of names) {
|
|
51
|
+
hash.update(`\0untracked\0${name}\0`);
|
|
52
|
+
// Remove Git's trailing slash for nested repos before lstat, so a link is never dereferenced.
|
|
53
|
+
const filePath = path.resolve(cwd, name);
|
|
54
|
+
const stat = await lstat(filePath);
|
|
55
|
+
hash.update(`${stat.mode}\0`);
|
|
56
|
+
if (stat.isSymbolicLink()) {
|
|
57
|
+
hash.update(await readlink(filePath));
|
|
58
|
+
}
|
|
59
|
+
else if (stat.isFile()) {
|
|
60
|
+
const file = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
61
|
+
try {
|
|
62
|
+
const before = await file.stat();
|
|
63
|
+
for await (const chunk of file.createReadStream({ autoClose: false }))
|
|
64
|
+
hash.update(chunk);
|
|
65
|
+
const after = await file.stat();
|
|
66
|
+
if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs)
|
|
67
|
+
throw new Error('Working tree changed during fingerprinting; retry diagnosis');
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
await file.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (stat.isDirectory()) {
|
|
74
|
+
// `ls-files --others` reports an embedded repository as a directory instead of its files.
|
|
75
|
+
const nestedPath = await realpath(filePath);
|
|
76
|
+
const relative = path.relative(cwd, nestedPath);
|
|
77
|
+
if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
|
|
78
|
+
throw new Error('Nested repository escapes the working tree');
|
|
79
|
+
hash.update(`\0repository\0${await fingerprintRepository(nestedPath, visited)}\0`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
throw new Error('Cannot fingerprint a non-regular untracked file');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return hash.digest('hex');
|
|
86
|
+
}
|
|
@@ -66,3 +66,24 @@ function assertLeafIsNotSymlink(candidate) {
|
|
|
66
66
|
throw err;
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
/** Create storage directories without accepting symlinked path components. */
|
|
70
|
+
export function ensureDirectoryInside(rootPath, relativePath) {
|
|
71
|
+
assertPathInside(rootPath, relativePath);
|
|
72
|
+
const root = fs.realpathSync(rootPath);
|
|
73
|
+
let current = root;
|
|
74
|
+
for (const segment of relativePath.split(path.sep).filter(Boolean)) {
|
|
75
|
+
current = path.join(current, segment);
|
|
76
|
+
try {
|
|
77
|
+
fs.mkdirSync(current);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (err.code !== 'EEXIST')
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
const stat = fs.lstatSync(current);
|
|
84
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
85
|
+
throw new Error('Symbolic links are not allowed in storage directories');
|
|
86
|
+
current = resolveExistingPathInside(root, current);
|
|
87
|
+
}
|
|
88
|
+
return current;
|
|
89
|
+
}
|
|
@@ -3,6 +3,7 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import * as wsService from '../services/websocket-service.js';
|
|
5
5
|
import { getIndexLockPath } from './git-ops.js';
|
|
6
|
+
import { ensureDirectoryInside } from './safe-path.js';
|
|
6
7
|
/** Default wall-clock budget for a user script before it is force-killed. */
|
|
7
8
|
export const SCRIPT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
8
9
|
/**
|
|
@@ -13,10 +14,31 @@ export const SCRIPT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
|
13
14
|
export function runScript(opts) {
|
|
14
15
|
const { workspaceId, worktreePath, script, eventPrefix, tmpFileName, env, extraEnv } = opts;
|
|
15
16
|
const timeoutMs = opts.timeoutMs ?? SCRIPT_TIMEOUT_MS;
|
|
17
|
+
let scriptPath;
|
|
18
|
+
let scriptDirectory;
|
|
19
|
+
const cleanup = () => {
|
|
20
|
+
if (scriptPath) {
|
|
21
|
+
try {
|
|
22
|
+
fs.unlinkSync(scriptPath);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
/* best effort */
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (scriptDirectory) {
|
|
29
|
+
try {
|
|
30
|
+
fs.rmdirSync(scriptDirectory);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* best effort */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
16
37
|
return new Promise((resolve) => {
|
|
17
|
-
const
|
|
18
|
-
fs.
|
|
19
|
-
|
|
38
|
+
const storage = ensureDirectoryInside(worktreePath, '.ai');
|
|
39
|
+
scriptDirectory = fs.mkdtempSync(path.join(storage, '.script-'));
|
|
40
|
+
scriptPath = path.join(scriptDirectory, path.basename(tmpFileName));
|
|
41
|
+
fs.writeFileSync(scriptPath, script, { mode: 0o700, flag: 'wx' });
|
|
20
42
|
const proc = spawn('bash', [scriptPath], {
|
|
21
43
|
cwd: worktreePath,
|
|
22
44
|
env: {
|
|
@@ -93,12 +115,7 @@ export function runScript(opts) {
|
|
|
93
115
|
return;
|
|
94
116
|
settled = true;
|
|
95
117
|
clearTimeout(timeout);
|
|
96
|
-
|
|
97
|
-
fs.unlinkSync(scriptPath);
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
/* best-effort */
|
|
101
|
-
}
|
|
118
|
+
cleanup();
|
|
102
119
|
if (exitCode === 0) {
|
|
103
120
|
wsService.emitEphemeral(workspaceId, `${eventPrefix}:complete`, { hadOutput: outputEmitted });
|
|
104
121
|
}
|
|
@@ -119,5 +136,11 @@ export function runScript(opts) {
|
|
|
119
136
|
proc.on('close', (code) => {
|
|
120
137
|
finish(code ?? 1);
|
|
121
138
|
});
|
|
139
|
+
}).catch((err) => {
|
|
140
|
+
cleanup();
|
|
141
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
142
|
+
wsService.emit(workspaceId, `${eventPrefix}:output`, { text: `[kobo] Script failed to start: ${message}` });
|
|
143
|
+
wsService.emitEphemeral(workspaceId, `${eventPrefix}:error`, { exitCode: 1, message });
|
|
144
|
+
return { exitCode: 1 };
|
|
122
145
|
});
|
|
123
146
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const busyWorkspaces = new Set();
|
|
2
|
+
const deferred = new Map();
|
|
2
3
|
export class WorkspaceLifecycleBusyError extends Error {
|
|
3
4
|
code = 'workspace-busy';
|
|
4
5
|
constructor() {
|
|
@@ -8,6 +9,19 @@ export class WorkspaceLifecycleBusyError extends Error {
|
|
|
8
9
|
export function isWorkspaceLifecycleBusy(id) {
|
|
9
10
|
return busyWorkspaces.has(id);
|
|
10
11
|
}
|
|
12
|
+
export function assertWorkspaceLifecycleAvailable(id) {
|
|
13
|
+
if (busyWorkspaces.has(id))
|
|
14
|
+
throw new WorkspaceLifecycleBusyError();
|
|
15
|
+
}
|
|
16
|
+
/** Keep automatic work pending without polling or acquiring the guard recursively. */
|
|
17
|
+
export function deferUntilWorkspaceAvailable(id, retry) {
|
|
18
|
+
if (!busyWorkspaces.has(id))
|
|
19
|
+
return false;
|
|
20
|
+
const callbacks = deferred.get(id) ?? new Set();
|
|
21
|
+
callbacks.add(retry);
|
|
22
|
+
deferred.set(id, callbacks);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
11
25
|
/** Exclude competing restore/purge/delete operations, including their awaits. */
|
|
12
26
|
export async function withWorkspaceLifecycleGuard(id, action) {
|
|
13
27
|
if (busyWorkspaces.has(id))
|
|
@@ -18,5 +32,18 @@ export async function withWorkspaceLifecycleGuard(id, action) {
|
|
|
18
32
|
}
|
|
19
33
|
finally {
|
|
20
34
|
busyWorkspaces.delete(id);
|
|
35
|
+
const callbacks = deferred.get(id);
|
|
36
|
+
deferred.delete(id);
|
|
37
|
+
if (callbacks)
|
|
38
|
+
queueMicrotask(() => {
|
|
39
|
+
for (const retry of callbacks) {
|
|
40
|
+
try {
|
|
41
|
+
retry();
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.error('[workspace-lifecycle] deferred operation failed:', err);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
21
48
|
}
|
|
22
49
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.37",
|
|
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",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.agent-error-item[data-v-e98c3c02]{gap:var(--kobo-space-sm);padding:var(--kobo-space-sm) var(--kobo-space-md);border-left:2px solid var(--kobo-danger);background:var(--kobo-surface-2);border-radius:var(--kobo-radius-sm);align-items:flex-start;display:flex}.agent-error-item__icon[data-v-e98c3c02]{color:var(--kobo-danger);flex:none;margin-top:2px}.agent-error-item__body[data-v-e98c3c02]{min-width:0}.agent-error-item__title[data-v-e98c3c02]{color:var(--kobo-text);font-weight:600}.agent-error-item__message[data-v-e98c3c02]{color:var(--kobo-text-2);font-family:var(--kobo-font-mono);white-space:pre-wrap;word-break:break-word;font-size:12px}.markdown-message[data-v-99371035]{color:var(--kobo-text-2);word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-99371035] *{max-width:100%}.markdown-message[data-v-99371035] p{margin:0 0 .5em}.markdown-message[data-v-99371035] p:last-child{margin-bottom:0}.markdown-message[data-v-99371035] pre{background:#00000059;border-radius:4px;margin:.5em 0;padding:.5em .75em;overflow-x:auto}.markdown-message[data-v-99371035] code{word-break:break-all;background:#0000004d;border-radius:3px;padding:.1em .3em;font-size:.9em}.markdown-message[data-v-99371035] pre code{background:0 0;padding:0}.markdown-message[data-v-99371035] ul,.markdown-message[data-v-99371035] ol{margin:.25em 0 .5em;padding-left:1.5em}.markdown-message[data-v-99371035] li{margin:.15em 0}.markdown-message[data-v-99371035] a{color:var(--kobo-text-2);text-decoration:underline}.markdown-message[data-v-99371035] .document-link{color:var(--kobo-text-2);cursor:pointer;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-message[data-v-99371035] .document-link:hover{color:var(--kobo-text);-webkit-text-decoration:underline;text-decoration:underline}.markdown-message[data-v-99371035] h1,.markdown-message[data-v-99371035] h2,.markdown-message[data-v-99371035] h3,.markdown-message[data-v-99371035] h4,.markdown-message[data-v-99371035] h5,.markdown-message[data-v-99371035] h6{margin:.5em 0 .3em;font-weight:600;line-height:1.3}.markdown-message[data-v-99371035] h1{font-size:1.25em}.markdown-message[data-v-99371035] h2{font-size:1.15em}.markdown-message[data-v-99371035] h3{font-size:1.08em}.markdown-message[data-v-99371035] h4,.markdown-message[data-v-99371035] h5,.markdown-message[data-v-99371035] h6{font-size:1em}.markdown-message[data-v-99371035] blockquote{color:#ffffffb3;border-left:3px solid #fff3;margin:.5em 0;padding-left:.75em}.markdown-message[data-v-99371035] table{border-collapse:collapse;margin:.5em 0}.markdown-message[data-v-99371035] th,.markdown-message[data-v-99371035] td{border:1px solid #ffffff26;padding:.25em .5em}.tool-row[data-v-2482adfe]{border-radius:4px;margin:0;font-size:12px}.tool-header[data-v-2482adfe]{color:var(--kobo-text-2);cursor:default;align-items:center;gap:10px;min-width:0;padding:5px 10px;display:flex}.tool-row:not(.tool-row-generic) .tool-header[data-v-2482adfe],.tool-row--toggleable .tool-header[data-v-2482adfe]{cursor:pointer}.tool-row:has(.tool-diff) .tool-header[data-v-2482adfe]{cursor:pointer}.tool-row:not(.tool-row-generic) .tool-header[data-v-2482adfe]:hover,.tool-row--toggleable .tool-header[data-v-2482adfe]:hover{background:#ffffff08}.tool-icon[data-v-2482adfe]{color:var(--kobo-text-2);flex-shrink:0}.tool-name[data-v-2482adfe]{color:var(--kobo-text-2);flex-shrink:0;font-weight:600}.tool-arg[data-v-2482adfe],.tool-path[data-v-2482adfe]{color:var(--kobo-text-3);text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%;font-family:var(--kobo-font-mono);font-size:11.5px;overflow:hidden}.tool-open-diff[data-v-2482adfe]{flex-shrink:0}.tool-path[data-v-2482adfe],.tool-arg[data-v-2482adfe]{flex:1}.tool-stat-add[data-v-2482adfe]{color:var(--kobo-success);flex-shrink:0;font-size:11px;font-weight:600}.tool-stat-del[data-v-2482adfe]{color:var(--kobo-danger);flex-shrink:0;font-size:11px;font-weight:600}.tool-diff[data-v-2482adfe]{max-height:400px;font-family:var(--kobo-font-mono);background:#0003;border-radius:4px;margin-top:4px;padding:8px 0;font-size:11px;line-height:1.5;overflow:auto}.diff-line[data-v-2482adfe]{white-space:pre;color:var(--kobo-text-2);padding:0 12px}.diff-sign[data-v-2482adfe]{width:14px;color:var(--kobo-text-disabled);-webkit-user-select:none;user-select:none;display:inline-block}.diff-add[data-v-2482adfe]{color:var(--kobo-success);background:#66bb6a1a}.diff-add .diff-sign[data-v-2482adfe]{color:var(--kobo-success)}.diff-del[data-v-2482adfe]{color:var(--kobo-danger);background:#ef53501a}.diff-del .diff-sign[data-v-2482adfe]{color:var(--kobo-danger)}.tool-output[data-v-2482adfe]{color:var(--kobo-text-2);font-family:var(--kobo-font-mono);white-space:pre-wrap;background:#00000026;border-radius:4px;max-height:8em;margin-top:4px;padding:6px 10px;font-size:11px;overflow:auto}.markdown-message[data-v-7687eb7e]{color:var(--kobo-text-2);word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-7687eb7e] *{max-width:100%}.markdown-message[data-v-7687eb7e] img{-o-object-fit:contain;object-fit:contain;cursor:zoom-in;background:#0003;border-radius:4px;max-width:180px;max-height:100px;margin:.3em 0;display:block}.markdown-message[data-v-7687eb7e] code{word-break:break-all}.markdown-message[data-v-7687eb7e] p{margin:0 0 .4em}.markdown-message[data-v-7687eb7e] p:last-child{margin-bottom:0}.markdown-message[data-v-7687eb7e] code{background:#00000040;border-radius:3px;padding:.1em .3em}.markdown-message[data-v-7687eb7e] h1,.markdown-message[data-v-7687eb7e] h2,.markdown-message[data-v-7687eb7e] h3,.markdown-message[data-v-7687eb7e] h4,.markdown-message[data-v-7687eb7e] h5,.markdown-message[data-v-7687eb7e] h6{margin:.4em 0 .25em;font-weight:600;line-height:1.3}.markdown-message[data-v-7687eb7e] h1{font-size:1.25em}.markdown-message[data-v-7687eb7e] h2{font-size:1.15em}.markdown-message[data-v-7687eb7e] h3{font-size:1.08em}.markdown-message[data-v-7687eb7e] h4,.markdown-message[data-v-7687eb7e] h5,.markdown-message[data-v-7687eb7e] h6{font-size:1em}.markdown-user-prompt[data-v-7687eb7e]{color:var(--kobo-text-2);font-size:12px;font-style:italic}.markdown-user-prompt[data-v-7687eb7e] p{margin:0 0 .4em}.markdown-user-prompt[data-v-7687eb7e] code{background:#ffffff14;border-radius:3px;padding:.1em .3em;font-style:normal}.image-lightbox-img{-o-object-fit:contain;object-fit:contain;cursor:zoom-out;background:#0000004d;border-radius:4px;max-width:92vw;max-height:92vh;display:block}.turn-card[data-v-6e61835e]{border:1px solid #ffffff14;border-left:3px solid var(--turn-accent);background:#ffffff05;border-radius:6px;min-width:0;max-width:100%;margin:14px 0;overflow:hidden}.turn-card--highlighted[data-v-6e61835e]{animation:1.8s ease-out search-highlight-6e61835e}@keyframes search-highlight-6e61835e{0%,45%{box-shadow:0 0 0 2px #818cf8f2,0 0 22px #818cf873}to{box-shadow:none}}.turn-header[data-v-6e61835e]{color:var(--kobo-text-3);background:#ffffff08;border-bottom:1px solid #ffffff0d;align-items:center;gap:8px;padding:8px 14px;font-size:11px;display:flex}.turn-badge[data-v-6e61835e]{letter-spacing:.3px;border-radius:3px;padding:2px 8px;font-size:11px;font-weight:700}.turn-badge-user[data-v-6e61835e]{color:var(--kobo-turn-user);background:#ce93d826}.turn-badge-agent[data-v-6e61835e]{color:var(--kobo-turn-agent);background:#7986cb26}.turn-badge-system[data-v-6e61835e]{color:var(--kobo-text-2);background:#75757533;font-style:italic}.turn-badge-session[data-v-6e61835e]{color:var(--kobo-text-3);background:#61616133}.turn-badge-script[data-v-6e61835e]{color:var(--kobo-success);background:#4db6ac26}.turn-time[data-v-6e61835e]{color:var(--kobo-text-disabled);font-family:var(--kobo-font-mono);font-size:11px}.turn-time-arrow[data-v-6e61835e]{opacity:.7;margin:0 -2px}.turn-time-updated[data-v-6e61835e]{color:var(--kobo-text-3)}.turn-actions[data-v-6e61835e]{color:var(--kobo-text-3);font-size:11px}.turn-body[data-v-6e61835e]{flex-direction:column;gap:12px;min-width:0;padding:14px 18px;display:flex}.turn-body[data-v-6e61835e]>*{min-width:0;max-width:100%}.turn-body[data-v-6e61835e] .tool-row+.tool-row{margin-top:-8px}.turn-scroll-top[data-v-6e61835e]{justify-content:flex-start;padding:0 8px 6px;display:flex}.turn-scroll-top-btn[data-v-6e61835e]{opacity:.5;transition:opacity .15s}.turn-scroll-top-btn[data-v-6e61835e]:hover{opacity:1}.activity-feed-wrap[data-v-01cd9f2a]{width:100%;height:100%;position:relative}.activity-feed-scroll[data-v-01cd9f2a]{width:100%;height:100%}.activity-feed-nav-cluster[data-v-01cd9f2a]{z-index:2;align-items:center;gap:8px;display:flex;position:absolute;bottom:14px;right:14px}.activity-feed-compacting[data-v-01cd9f2a]{z-index:3;background:var(--kobo-surface);border:1px solid var(--kobo-border);border-radius:6px;align-items:center;gap:8px;padding:6px 14px;display:flex;position:absolute;top:12px;left:50%;transform:translate(-50%);box-shadow:0 4px 16px #00000059}.fade-enter-active[data-v-01cd9f2a],.fade-leave-active[data-v-01cd9f2a]{transition:opacity .16s}.fade-enter-from[data-v-01cd9f2a],.fade-leave-to[data-v-01cd9f2a]{opacity:0}.activity-feed-nav-btn[data-v-01cd9f2a]{opacity:.8;transition:opacity .12s}.activity-feed-nav-btn[data-v-01cd9f2a]:hover{opacity:1}.activity-feed-scroll[data-v-01cd9f2a] .q-scrollarea__content{max-width:100%;overflow-x:hidden}.activity-feed-switching[data-v-01cd9f2a]{justify-content:center;align-items:center;width:100%;height:100%;display:flex}.activity-feed-empty[data-v-01cd9f2a]{align-items:center;gap:var(--kobo-space-sm);padding:var(--kobo-space-4xl) var(--kobo-space-xl);color:var(--kobo-text-3);text-align:center;flex-direction:column;display:flex}.activity-feed-empty__title[data-v-01cd9f2a]{font-family:var(--kobo-font-sans);color:var(--kobo-text-2);font-size:14px;font-weight:500}.activity-feed-empty__hint[data-v-01cd9f2a]{font-family:var(--kobo-font-sans);color:var(--kobo-text-3);max-width:380px;font-size:13px}
|
|
1
|
+
.agent-error-item[data-v-e98c3c02]{gap:var(--kobo-space-sm);padding:var(--kobo-space-sm) var(--kobo-space-md);border-left:2px solid var(--kobo-danger);background:var(--kobo-surface-2);border-radius:var(--kobo-radius-sm);align-items:flex-start;display:flex}.agent-error-item__icon[data-v-e98c3c02]{color:var(--kobo-danger);flex:none;margin-top:2px}.agent-error-item__body[data-v-e98c3c02]{min-width:0}.agent-error-item__title[data-v-e98c3c02]{color:var(--kobo-text);font-weight:600}.agent-error-item__message[data-v-e98c3c02]{color:var(--kobo-text-2);font-family:var(--kobo-font-mono);white-space:pre-wrap;word-break:break-word;font-size:12px}.markdown-message[data-v-99371035]{color:var(--kobo-text-2);word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-99371035] *{max-width:100%}.markdown-message[data-v-99371035] p{margin:0 0 .5em}.markdown-message[data-v-99371035] p:last-child{margin-bottom:0}.markdown-message[data-v-99371035] pre{background:#00000059;border-radius:4px;margin:.5em 0;padding:.5em .75em;overflow-x:auto}.markdown-message[data-v-99371035] code{word-break:break-all;background:#0000004d;border-radius:3px;padding:.1em .3em;font-size:.9em}.markdown-message[data-v-99371035] pre code{background:0 0;padding:0}.markdown-message[data-v-99371035] ul,.markdown-message[data-v-99371035] ol{margin:.25em 0 .5em;padding-left:1.5em}.markdown-message[data-v-99371035] li{margin:.15em 0}.markdown-message[data-v-99371035] a{color:var(--kobo-text-2);text-decoration:underline}.markdown-message[data-v-99371035] .document-link{color:var(--kobo-text-2);cursor:pointer;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-message[data-v-99371035] .document-link:hover{color:var(--kobo-text);-webkit-text-decoration:underline;text-decoration:underline}.markdown-message[data-v-99371035] h1,.markdown-message[data-v-99371035] h2,.markdown-message[data-v-99371035] h3,.markdown-message[data-v-99371035] h4,.markdown-message[data-v-99371035] h5,.markdown-message[data-v-99371035] h6{margin:.5em 0 .3em;font-weight:600;line-height:1.3}.markdown-message[data-v-99371035] h1{font-size:1.25em}.markdown-message[data-v-99371035] h2{font-size:1.15em}.markdown-message[data-v-99371035] h3{font-size:1.08em}.markdown-message[data-v-99371035] h4,.markdown-message[data-v-99371035] h5,.markdown-message[data-v-99371035] h6{font-size:1em}.markdown-message[data-v-99371035] blockquote{color:#ffffffb3;border-left:3px solid #fff3;margin:.5em 0;padding-left:.75em}.markdown-message[data-v-99371035] table{border-collapse:collapse;margin:.5em 0}.markdown-message[data-v-99371035] th,.markdown-message[data-v-99371035] td{border:1px solid #ffffff26;padding:.25em .5em}.tool-row[data-v-2482adfe]{border-radius:4px;margin:0;font-size:12px}.tool-header[data-v-2482adfe]{color:var(--kobo-text-2);cursor:default;align-items:center;gap:10px;min-width:0;padding:5px 10px;display:flex}.tool-row:not(.tool-row-generic) .tool-header[data-v-2482adfe],.tool-row--toggleable .tool-header[data-v-2482adfe]{cursor:pointer}.tool-row:has(.tool-diff) .tool-header[data-v-2482adfe]{cursor:pointer}.tool-row:not(.tool-row-generic) .tool-header[data-v-2482adfe]:hover,.tool-row--toggleable .tool-header[data-v-2482adfe]:hover{background:#ffffff08}.tool-icon[data-v-2482adfe]{color:var(--kobo-text-2);flex-shrink:0}.tool-name[data-v-2482adfe]{color:var(--kobo-text-2);flex-shrink:0;font-weight:600}.tool-arg[data-v-2482adfe],.tool-path[data-v-2482adfe]{color:var(--kobo-text-3);text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%;font-family:var(--kobo-font-mono);font-size:11.5px;overflow:hidden}.tool-open-diff[data-v-2482adfe]{flex-shrink:0}.tool-path[data-v-2482adfe],.tool-arg[data-v-2482adfe]{flex:1}.tool-stat-add[data-v-2482adfe]{color:var(--kobo-success);flex-shrink:0;font-size:11px;font-weight:600}.tool-stat-del[data-v-2482adfe]{color:var(--kobo-danger);flex-shrink:0;font-size:11px;font-weight:600}.tool-diff[data-v-2482adfe]{max-height:400px;font-family:var(--kobo-font-mono);background:#0003;border-radius:4px;margin-top:4px;padding:8px 0;font-size:11px;line-height:1.5;overflow:auto}.diff-line[data-v-2482adfe]{white-space:pre;color:var(--kobo-text-2);padding:0 12px}.diff-sign[data-v-2482adfe]{width:14px;color:var(--kobo-text-disabled);-webkit-user-select:none;user-select:none;display:inline-block}.diff-add[data-v-2482adfe]{color:var(--kobo-success);background:#66bb6a1a}.diff-add .diff-sign[data-v-2482adfe]{color:var(--kobo-success)}.diff-del[data-v-2482adfe]{color:var(--kobo-danger);background:#ef53501a}.diff-del .diff-sign[data-v-2482adfe]{color:var(--kobo-danger)}.tool-output[data-v-2482adfe]{color:var(--kobo-text-2);font-family:var(--kobo-font-mono);white-space:pre-wrap;background:#00000026;border-radius:4px;max-height:8em;margin-top:4px;padding:6px 10px;font-size:11px;overflow:auto}.markdown-message[data-v-7687eb7e]{color:var(--kobo-text-2);word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-7687eb7e] *{max-width:100%}.markdown-message[data-v-7687eb7e] img{-o-object-fit:contain;object-fit:contain;cursor:zoom-in;background:#0003;border-radius:4px;max-width:180px;max-height:100px;margin:.3em 0;display:block}.markdown-message[data-v-7687eb7e] code{word-break:break-all}.markdown-message[data-v-7687eb7e] p{margin:0 0 .4em}.markdown-message[data-v-7687eb7e] p:last-child{margin-bottom:0}.markdown-message[data-v-7687eb7e] code{background:#00000040;border-radius:3px;padding:.1em .3em}.markdown-message[data-v-7687eb7e] h1,.markdown-message[data-v-7687eb7e] h2,.markdown-message[data-v-7687eb7e] h3,.markdown-message[data-v-7687eb7e] h4,.markdown-message[data-v-7687eb7e] h5,.markdown-message[data-v-7687eb7e] h6{margin:.4em 0 .25em;font-weight:600;line-height:1.3}.markdown-message[data-v-7687eb7e] h1{font-size:1.25em}.markdown-message[data-v-7687eb7e] h2{font-size:1.15em}.markdown-message[data-v-7687eb7e] h3{font-size:1.08em}.markdown-message[data-v-7687eb7e] h4,.markdown-message[data-v-7687eb7e] h5,.markdown-message[data-v-7687eb7e] h6{font-size:1em}.markdown-user-prompt[data-v-7687eb7e]{color:var(--kobo-text-2);font-size:12px;font-style:italic}.markdown-user-prompt[data-v-7687eb7e] p{margin:0 0 .4em}.markdown-user-prompt[data-v-7687eb7e] code{background:#ffffff14;border-radius:3px;padding:.1em .3em;font-style:normal}.image-lightbox-img{-o-object-fit:contain;object-fit:contain;cursor:zoom-out;background:#0000004d;border-radius:4px;max-width:92vw;max-height:92vh;display:block}.turn-card[data-v-6e61835e]{border:1px solid #ffffff14;border-left:3px solid var(--turn-accent);background:#ffffff05;border-radius:6px;min-width:0;max-width:100%;margin:14px 0;overflow:hidden}.turn-card--highlighted[data-v-6e61835e]{animation:1.8s ease-out search-highlight-6e61835e}@keyframes search-highlight-6e61835e{0%,45%{box-shadow:0 0 0 2px #818cf8f2,0 0 22px #818cf873}to{box-shadow:none}}.turn-header[data-v-6e61835e]{color:var(--kobo-text-3);background:#ffffff08;border-bottom:1px solid #ffffff0d;align-items:center;gap:8px;padding:8px 14px;font-size:11px;display:flex}.turn-badge[data-v-6e61835e]{letter-spacing:.3px;border-radius:3px;padding:2px 8px;font-size:11px;font-weight:700}.turn-badge-user[data-v-6e61835e]{color:var(--kobo-turn-user);background:#ce93d826}.turn-badge-agent[data-v-6e61835e]{color:var(--kobo-turn-agent);background:#7986cb26}.turn-badge-system[data-v-6e61835e]{color:var(--kobo-text-2);background:#75757533;font-style:italic}.turn-badge-session[data-v-6e61835e]{color:var(--kobo-text-3);background:#61616133}.turn-badge-script[data-v-6e61835e]{color:var(--kobo-success);background:#4db6ac26}.turn-time[data-v-6e61835e]{color:var(--kobo-text-disabled);font-family:var(--kobo-font-mono);font-size:11px}.turn-time-arrow[data-v-6e61835e]{opacity:.7;margin:0 -2px}.turn-time-updated[data-v-6e61835e]{color:var(--kobo-text-3)}.turn-actions[data-v-6e61835e]{color:var(--kobo-text-3);font-size:11px}.turn-body[data-v-6e61835e]{flex-direction:column;gap:12px;min-width:0;padding:14px 18px;display:flex}.turn-body[data-v-6e61835e]>*{min-width:0;max-width:100%}.turn-body[data-v-6e61835e] .tool-row+.tool-row{margin-top:-8px}.turn-scroll-top[data-v-6e61835e]{justify-content:flex-start;padding:0 8px 6px;display:flex}.turn-scroll-top-btn[data-v-6e61835e]{opacity:.5;transition:opacity .15s}.turn-scroll-top-btn[data-v-6e61835e]:hover{opacity:1}.activity-feed-wrap[data-v-d83f0940]{width:100%;height:100%;position:relative}.activity-feed-scroll[data-v-d83f0940]{width:100%;height:100%}.activity-feed-nav-cluster[data-v-d83f0940]{z-index:2;align-items:center;gap:8px;display:flex;position:absolute;bottom:14px;right:14px}.activity-feed-compacting[data-v-d83f0940]{z-index:3;background:var(--kobo-surface);border:1px solid var(--kobo-border);border-radius:6px;align-items:center;gap:8px;padding:6px 14px;display:flex;position:absolute;top:12px;left:50%;transform:translate(-50%);box-shadow:0 4px 16px #00000059}.fade-enter-active[data-v-d83f0940],.fade-leave-active[data-v-d83f0940]{transition:opacity .16s}.fade-enter-from[data-v-d83f0940],.fade-leave-to[data-v-d83f0940]{opacity:0}.activity-feed-nav-btn[data-v-d83f0940]{opacity:.8;transition:opacity .12s}.activity-feed-nav-btn[data-v-d83f0940]:hover{opacity:1}.activity-feed-scroll[data-v-d83f0940] .q-scrollarea__content{max-width:100%;overflow-x:hidden}.activity-feed-switching[data-v-d83f0940]{justify-content:center;align-items:center;width:100%;height:100%;display:flex}.activity-feed-empty[data-v-d83f0940]{align-items:center;gap:var(--kobo-space-sm);padding:var(--kobo-space-4xl) var(--kobo-space-xl);color:var(--kobo-text-3);text-align:center;flex-direction:column;display:flex}.activity-feed-empty__title[data-v-d83f0940]{font-family:var(--kobo-font-sans);color:var(--kobo-text-2);font-size:14px;font-weight:500}.activity-feed-empty__hint[data-v-d83f0940]{font-family:var(--kobo-font-sans);color:var(--kobo-text-3);max-width:380px;font-size:13px}
|
|
@@ -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 N}from"./QBtn-CZaBclNw.js";import{A as te,At as ne,Ct as re,Nt as P,O as F,d as ie,k as ae,m as oe,p as se,q as ce,xt as I}from"./index-BJU5dnkF.js";import{t as le}from"./QSpinnerDots-DnCV3VzI.js";import{t as ue}from"./QList-qupzuEqO.js";import{t as de}from"./QMarkupTable-B9QuKtK_.js";import{c as L,l as R}from"./QSelect-BSPtuXJg.js";import{d as fe}from"./permissionModes-BtC3_P-P.js";import{r as pe,t as z}from"./pending-diff-open-BL7dKRqF.js";import{t as B}from"./documents-BMHzjW_g.js";import{a as V}from"./use-tours-BJ81clv6.js";import{t as H}from"./QTooltip-DC_kDKku.js";import{i as U,n as me,o as he,r as W,t as ge}from"./render-chat-markdown-7EtWNjG0.js";import{a as _e,i as ve,n as ye,o as be,r as xe,s as Se}from"./WorkspacePage-MxBE0mr3.js";function G(e,t){return o(`div`,e,[o(`table`,{class:`q-table`},t)])}var Ce={list:ue,table:de},we=[`list`,`table`,`__qtable`],Te=j({name:`QVirtualScroll`,props:{...R,type:{type:String,default:`list`,validator:e=>we.includes(e)},items:{type:Array,default:()=>[]},itemsFn:Function,itemsSize:Number,scrollTarget:P},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}=L({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`?G({ref:a,class:`q-table__middle `+g.value,tabindex:0},T()):o(Ce[t.type],{...r,ref:a,class:[r.class,g.value],..._.value},T)}}}),K=new Set([`setup`,`cleanup`,`archive`]);function q(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:K.has(e.sender)||F(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function Ee(e){let t=[],n=null,r=null;for(let i of e){let e=q(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 De(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 Oe(e){let t=e.items[0];return`${e.speaker}:${t?De(t):`empty`}`}function ke(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 Ae(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 je={class:`agent-error-item`},Me={class:`agent-error-item__body`},Ne={class:`agent-error-item__title`},J={class:`agent-error-item__message`},Pe=V(w({__name:`AgentErrorItem`,props:{item:{}},setup(e){let t=e,{te:n}=I(),r=b(()=>{let e=`agent.error.${t.item.category}`;return n(e)?e:`agent.error.other`});return(t,n)=>(l(),x(`div`,je,[m(k,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),C(`div`,Me,[C(`div`,Ne,i(t.$t(r.value)),1),C(`div`,J,i(e.item.message),1)])]))}}),[[`__scopeId`,`data-v-e98c3c02`]]),Fe={class:`text-caption text-kobo-3`},Y=w({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,n=b(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return Se(t.item.detail);case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(l(),x(`span`,Fe,i(e.$t(n.value)),1))}});function Ie(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){X(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 X(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=Le(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 Le(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 Z=[`innerHTML`],Q=V(w({__name:`TextMessageItem`,props:{item:{}},setup(e){let t=e,n=B(),r=se(),i=N(),{t:a}=I(),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 ge(e.text);let n=o.value;return W(`${e.messageId}|${n.join(`,`)}`,e.text,e=>{let t=Ie(he.parse(e,{async:!1,breaks:!0,gfm:!0}),n);return U(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,Z),e.item.streaming?(l(),h(M,{key:0,size:`xs`,class:`q-ml-xs`})):g(``,!0)]))}}),[[`__scopeId`,`data-v-99371035`]]);function Re(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,...$(l,u),...c]}function $(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 ze(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 Be(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=ze(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 Ve(e,t){if(!e||!t?.projectPath)return e;let n=t.worktreePath;if(n){let t=He(e,n);if(t!==e)return t}let r=He(e,`${t.projectPath}/${te}/${t.workingBranch}`);return r===e?He(e,t.projectPath):r}function He(e,t){if(!t)return e;let n=Ue(t);return n?e.replace(RegExp(`${n}[\\\\/]+`,`g`),``).replace(RegExp(`${n}(?=\\s|$|["'\`])`,`g`),`.`):e}function Ue(e){let t=e.replace(/[\\/]+$/,``);return t?`${/^[\\/]+/.test(t)?`[\\\\/]+`:``}${t.split(/[\\/]+/).filter(Boolean).map(We).join(`[\\\\/]+`)}`:``}function We(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}var Ge={class:`tool-name`},Ke=[`title`],qe={key:0,class:`tool-stat-add`},Je={key:1,class:`tool-stat-del`},Ye={class:`diff-sign`},Xe={class:`tool-name`},Ze=[`title`],Qe=V(w({__name:`ToolCallItem`,props:{item:{}},setup(e){let t=e,o=a(!1),s=se(),{t:u}=I(),d=b(()=>Be(t.item.name,t.item.input)),f=b(()=>d.value?Ve(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?Ve(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?Re(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 N(){o.value=!o.value}function te(){let e=d.value?.filePath,t=s.selectedWorkspace,n=t?.id,r=e?Ve(e,t):``;r&&!/^(?:[A-Za-z]:)?[\\/]/.test(r)&&n&&(z(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:N},[m(k,{name:y.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),C(`span`,Ge,i(d.value.toolName===`Bash:rm`?`Bash`:d.value.toolName),1),C(`span`,{class:`tool-path`,title:d.value.filePath},i(f.value),9,Ke),m(ee,{flat:``,dense:``,round:``,size:`xs`,icon:`open_in_new`,class:`tool-open-diff`,onClick:A(te,[`stop`])},{default:p(()=>[m(H,null,{default:p(()=>[_(i(n(u)(`chat.openInGitDiff`)),1)]),_:1})]),_:1}),d.value.additions>0?(l(),x(`span`,qe,`+`+i(d.value.additions),1)):g(``,!0),d.value.deletions>0?(l(),x(`span`,Je,`-`+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`,Ye,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&&N()},[m(k,{name:y.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),C(`span`,Xe,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,Ze)):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 $e(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 et=[`innerHTML`],tt=[`innerHTML`],nt=[`src`],rt=V(w({__name:`UserMessageItem`,props:{item:{}},setup(e){let t=e,n=se(),r=b(()=>t.item.sender===`system-prompt`),i=b(()=>{let e=$e(t.item.content,n.selectedWorkspaceId??``);return me(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(fe,{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,et)]),_:1},8,[`label`])):(l(),x(`div`,{key:1,class:`markdown-message`,onClick:c},[C(`div`,{innerHTML:i.value},null,8,tt)])),m(re,{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,nt)):g(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-7687eb7e`]]),it=[`data-event-ids`],at={class:`turn-header`},ot={key:0,class:`turn-time`},st={class:`turn-time turn-time-updated`},ct={key:2,class:`turn-actions`},lt={class:`turn-body`},ut={key:0,class:`turn-scroll-top`},dt=V(w({__name:`TurnCard`,props:{turn:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(e,{emit:o}){let s=e,c=o,{t:u}=I(),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:F(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`,at,[C(`span`,{class:r([`turn-badge`,v.value.badgeClass])},i(v.value.label),3),w.value?(l(),x(`span`,ot,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`,st,[_(i(O.value)+` `,1),m(H,null,{default:p(()=>[_(i(n(u)(`chat.lastUpdatedAt`,{time:O.value})),1)]),_:1})])],64)):g(``,!0),j.value>0?(l(),x(`span`,ct,` · `+i(n(u)(`chat.nActions`,{n:j.value})),1)):g(``,!0)]),C(`div`,lt,[(l(!0),x(S,null,T(e.turn.items,e=>(l(),x(S,{key:n(De)(e)},[e.type===`text`?(l(),h(Q,{key:0,item:e},null,8,[`item`])):e.type===`thinking`?(l(),h(ye,{key:1,item:e},null,8,[`item`])):e.type===`tool`?(l(),h(Qe,{key:2,item:e},null,8,[`item`])):e.type===`user`?(l(),h(rt,{key:3,item:e},null,8,[`item`])):e.type===`session`?(l(),h(Y,{key:4,item:e},null,8,[`item`])):e.type===`error`?(l(),h(Pe,{key:5,item:e},null,8,[`item`])):g(``,!0)],64))),128))]),e.turn.items.length>4?(l(),x(`div`,ut,[m(ee,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`kobo-3`,class:`turn-scroll-top-btn`,onClick:f},{default:p(()=>[m(H,null,{default:p(()=>[_(i(n(u)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):g(``,!0)],14,it))}}),[[`__scopeId`,`data-v-6e61835e`]]),ft={key:0,class:`activity-feed-switching`},pt={key:1,class:`activity-feed-wrap`,"data-tour":`ws-chat`},mt=[`aria-label`],ht={key:0,class:`activity-feed-compacting`},gt={class:`text-caption text-kobo-2`},_t={key:0,class:`text-center q-py-sm text-caption text-kobo-3`},vt={key:1,class:`activity-feed-empty q-pa-md`},yt={class:`activity-feed-empty__title`},bt={class:`activity-feed-empty__hint`},xt={key:2,class:`q-px-md q-pb-md`},St={class:`activity-feed-nav-cluster`},Ct=300,wt=60,Tt=200,Et=180,Dt=V(w({__name:`ActivityFeed`,props:{workspaceId:{}},setup(e){let{t}=I(),r=e,o=ce(),u=ae(),v=se(),y=ie(),w=b(()=>o.isCompacting(r.workspaceId)),E=b(()=>v.selectedSessionId),D=b(()=>v.sessions.find(e=>e.id===E.value)?.engineSessionId??null),A=b(()=>{let e=v.sessions;return e.length!==0&&E.value===e[e.length-1].id});function j(e){return E.value?e?e===E.value||e===D.value:A.value:!0}let N=b(()=>(v.activityFeeds[r.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&j(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 oe(e?.status)}),ne=xe(),re=``,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++)j(n[r])&&(a.push(e[r]),s.push(t[r]),c.push(i[r]??null));let l=`${r.workspaceId}::${E.value??`*`}`;l!==re&&(ne=xe(),re=l);let d=ve(ne,a,s,te.value,c);return Ee(be(d,N.value).filter(e=>e.type===`thinking`?!1:e.type===`session`?e.kind===`ended`?u.showVerboseSystemMessages||!_e(e.detail):u.showVerboseSystemMessages:!0))}),F=b(()=>u.showVerboseSystemMessages?o.eventsFor(r.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),ue=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}),de=a(``);c(ue,(e,n)=>{if(!e||e.key===n?.key)return;let r=e.key.startsWith(`text:`)?t(`activity.a11y.agentSaid`,{text:e.text.slice(0,Ct)}):e.text;de.value=r});let L=a(null),R=a(!0),z=a(!1),B=a(null),V=!1,H=a(new Set),U=b(()=>{if(P.value.length>0||F.value.length>0)return!1;let e=r.workspaceId,t=E.value;return!!v.loadingSessions[e]||y.isSyncPending(e)||t!==null&&H.value.has(W(e,t))}),me=a(new Map);function he(e){let t=e.verticalSize-e.verticalPosition-e.verticalContainerSize;R.value=t<=wt,V&&e.verticalPosition<=Tt&&!z.value&&ge()&&Ce()}function W(e,t){return`${e}:${t}`}function ge(){let e=E.value;return e?me.value.get(W(r.workspaceId,e))??!0:o.hasMoreOlderFor(r.workspaceId)}function ye(e,t,n){me.value.set(W(e,t),n)}function Se(e){if(!E.value)return o.oldestIdFor(e);let t=o.eventIdsFor(e),n=o.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!j(n[e]))continue;let r=t[e];if(r)return r}}let G=null;function Ce(){return G||(G=we().finally(()=>{G=null}),G)}async function we(){let e=r.workspaceId,t=E.value,n=Se(e);if(n){z.value=!0;try{let r=L.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=await fetch(`/api/workspaces/${e}/events?${s.toString()}`);if(!c.ok){t?ye(e,t,!1):o.prepend(e,[],[],{oldestId:n,hasMoreOlder:!1});return}let l=await c.json(),u=l.events??[],f=u.filter(t=>t.type===`agent:event`&&t.workspaceId===e),p=u.filter(t=>t.type===`user:message`&&t.workspaceId===e),m=f.map(e=>e.payload),h=f.map(e=>e.createdAt),g=f.map(e=>e.sessionId??null),_=f.map(e=>e.id),y=u.length>0?u[0].id:n;t&&ye(e,t,l.hasMore),o.prepend(e,m,h,{oldestId:y,hasMoreOlder:t?o.hasMoreOlderFor(e):l.hasMore,sessionIds:g,eventIds:_});for(let t of p){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{z.value=!1}}}async function K(e=0){await d();let t=L.value;if(!t)return;let n=t.getScroll();t.setScrollPosition(`vertical`,n.verticalSize,e)}let q=null,De=0;function je(){if(q!=null)return;let e=performance.now()-De<Et;q=requestAnimationFrame(()=>{q=null,De=performance.now(),K(e?0:180)})}function Me(e){let t=L.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function Ne(){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 J(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 Ae(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;B.value=t.eventId,Pe.value?.scrollTo(l,`center`),Y.value=l,window.setTimeout(()=>{B.value===t.eventId&&(B.value=null)},1800)}catch(e){console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Pe=a(null),Fe=a(null),Y=a(0);function Ie(e){Y.value=e.index}let X=a(!1);async function Le(){if(X.value)return;X.value=!0;let e=r.workspaceId;try{let t=ke(P.value,Y.value);if(t<0)for(let n=0;n<15;n++){if(e!==r.workspaceId)return;if(!ge())break;let n=P.value.length;if(await Ce(),await d(),e!==r.workspaceId)return;if(Y.value+=P.value.length-n,t=ke(P.value,Y.value),t>=0)break}t>=0&&e===r.workspaceId&&(Pe.value?.scrollTo(t,`start`),Y.value=t)}finally{X.value=!1}}async function Z(){V=!1,await d(),await K(0),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e()))),await K(0),V=!0}let Q=b(()=>{let e=o.sessionIdsFor(r.workspaceId);if(!E.value)return e.length;let t=0;for(let n of e)j(n)&&t++;return t});c(U,async e=>{!e&&Q.value>0&&await Z(),e||d(()=>{Fe.value=L.value?.getScrollTarget()??null})}),f(()=>{window.addEventListener(`kobo:focus-history-event`,J),d(()=>{Fe.value=L.value?.getScrollTarget()??null}),Q.value>0&&Z(),E.value&&ze();let e=new URLSearchParams(window.location.hash.split(`?`)[1]??``).get(`eventId`);e&&(J(new CustomEvent(`kobo:focus-history-event`,{detail:{workspaceId:r.workspaceId,sessionId:E.value,eventId:e}})),Ne())});let Re=Q.value>0;c(Q,async(e,t)=>{if(!z.value){if(!Re&&e>0){Re=!0,await Z();return}e>t&&R.value&&!z.value&&je()}}),s(()=>{window.removeEventListener(`kobo:focus-history-event`,J),q!=null&&(cancelAnimationFrame(q),q=null)}),c(()=>r.workspaceId,()=>{R.value=!0,Re=Q.value>0,V=!1,ze(),Q.value>0&&Z()}),c(()=>v.selectedSessionId,async()=>{R.value=!0,V=!1,ze(),await Z()});let $=new Set;async function ze(){let e=r.workspaceId,t=E.value;if(!t||Q.value>0)return;let n=W(e,t);if(!$.has(n)){$.add(n),H.value.add(n);try{let i=await fetch(`/api/workspaces/${e}/events?session=${encodeURIComponent(t)}&limit=500`);if(!i.ok){$.delete(n);return}let a=await i.json(),s=a.events??[];if(s.length===0)return;let c=s.filter(t=>t.type===`agent:event`&&t.workspaceId===e),l=s.filter(t=>t.type===`user:message`&&t.workspaceId===e),u=c.map(e=>e.payload),f=c.map(e=>e.createdAt),p=c.map(e=>e.sessionId??null),m=c.map(e=>e.id);ye(e,t,a.hasMore),u.length>0&&o.prepend(e,u,f,{oldestId:s[0].id,hasMoreOlder:o.hasMoreOlderFor(e),sessionIds:p,eventIds:m});for(let t of l){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`}})}await d(),r.workspaceId===e&&E.value===t&&await K(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),$.delete(n)}finally{H.value.delete(n)}}}let Be=b(()=>N.value.filter(e=>e.sender!==`system-prompt`).length);c(Be,async(e,t)=>{e>t&&(R.value=!0,await K(180))});async function Ve(){R.value=!0,await K(250)}return(e,t)=>U.value?(l(),x(`div`,ft,[m(le,{size:`40px`,color:`primary`})])):(l(),x(`div`,pt,[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(de.value),9,mt),m(O,{name:`fade`},{default:p(()=>[w.value?(l(),x(`div`,ht,[m(le,{size:`18px`,color:`primary`}),C(`span`,gt,i(e.$t(`activity.compacting`)),1)])):g(``,!0)]),_:1}),m(pe,{ref_key:`scrollRef`,ref:L,class:`activity-feed-scroll`,onScroll:he},{default:p(()=>[z.value?(l(),x(`div`,_t,[m(M,{size:`sm`}),_(` `+i(e.$t(`activity.loading_older`)),1)])):g(``,!0),m(Te,{ref_key:`virtualScrollRef`,ref:Pe,class:`q-pa-md`,items:P.value,"scroll-target":Fe.value??void 0,"virtual-scroll-item-size":160,"virtual-scroll-slice-size":20,onVirtualScroll:Ie},{default:p(({item:e,index:t})=>[(l(),h(dt,{key:n(Oe)(e),turn:e,"data-turn-index":t,highlighted:e.items.some(e=>e.eventIds?.includes(B.value??``)??!1),onScrollTo:Me},null,8,[`turn`,`data-turn-index`,`highlighted`]))]),_:1},8,[`items`,`scroll-target`]),P.value.length===0&&F.value.length===0&&!z.value?(l(),x(`div`,vt,[m(k,{name:`forum`,size:`28px`}),C(`div`,yt,i(e.$t(`activity.empty`)),1),C(`div`,bt,i(e.$t(`activity.emptyHint`)),1)])):g(``,!0),F.value.length?(l(),x(`div`,xt,[m(fe,{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`,St,[R.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:Ve},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:X.value,disable:X.value,onClick:Le},null,8,[`title`,`aria-label`,`loading`,`disable`])])]))}}),[[`__scopeId`,`data-v-d83f0940`]]);export{Dt as default};
|
|
@@ -1 +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-
|
|
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-BJU5dnkF.js";import{a as v,i as y,n as b}from"./use-tours-BJ81clv6.js";import{n as x}from"./render-chat-markdown-7EtWNjG0.js";import{t as S}from"./QChip-owciRuZu.js";import{t as C}from"./QPage-DaX9sMQC.js";import{t as w}from"./DrawerToggleButton-T70fh8dp.js";import{t as T}from"./TourReplayButton-CJF3nVvn.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};
|
package/src/client/dist/pwa/assets/{ComparisonPanel-LZrvk6a6.js → ComparisonPanel-DZAE2X9j.js}
RENAMED
|
@@ -1,2 +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-
|
|
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-BJU5dnkF.js";import{t as se}from"./QMarkupTable-B9QuKtK_.js";import{a as m,i as h}from"./use-tours-BJ81clv6.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
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};
|