@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.
Files changed (102) hide show
  1. package/AGENTS.md +15 -0
  2. package/CHANGELOG.md +9 -0
  3. package/dist/server/index.js +11 -24
  4. package/dist/server/routes/dev-server.js +2 -1
  5. package/dist/server/routes/pull-requests.js +5 -4
  6. package/dist/server/routes/workspaces.js +122 -91
  7. package/dist/server/services/agent/engines/claude-code/engine.js +26 -0
  8. package/dist/server/services/agent/engines/claude-code/event-mapper.js +10 -10
  9. package/dist/server/services/agent/engines/codex/client.js +1 -0
  10. package/dist/server/services/agent/engines/codex/engine.js +47 -10
  11. package/dist/server/services/agent/engines/codex/event-mapper.js +5 -1
  12. package/dist/server/services/agent/engines/codex/jsonrpc/peer.js +21 -8
  13. package/dist/server/services/agent/engines/codex/jsonrpc/transport.js +30 -3
  14. package/dist/server/services/agent/orchestrator.js +175 -68
  15. package/dist/server/services/agent/session-controller.js +12 -2
  16. package/dist/server/services/auto-loop-service.js +71 -6
  17. package/dist/server/services/change-source-branch-service.js +36 -4
  18. package/dist/server/services/cron-service.js +19 -0
  19. package/dist/server/services/db-backup-service.js +30 -0
  20. package/dist/server/services/dev-server-service.js +167 -38
  21. package/dist/server/services/image-service.js +25 -28
  22. package/dist/server/services/pr-checkout-service.js +34 -5
  23. package/dist/server/services/quota-backoff-service.js +25 -8
  24. package/dist/server/services/wakeup-service.js +37 -3
  25. package/dist/server/services/worktree-purge-service.js +3 -7
  26. package/dist/server/utils/agent-stop-result.js +12 -0
  27. package/dist/server/utils/git-ops.js +8 -1
  28. package/dist/server/utils/git-worktree-fingerprint.js +86 -0
  29. package/dist/server/utils/safe-path.js +21 -0
  30. package/dist/server/utils/script-runner.js +32 -9
  31. package/dist/server/utils/workspace-lifecycle-guard.js +27 -0
  32. package/package.json +1 -1
  33. package/src/client/dist/pwa/assets/{ActivityFeed-Bakgvay6.css → ActivityFeed-BDyFBoxd.css} +1 -1
  34. package/src/client/dist/pwa/assets/ActivityFeed-C_-hxIo8.js +8 -0
  35. package/src/client/dist/pwa/assets/{ChangelogPage-DVLWWvvz.js → ChangelogPage-CBlGJ4zT.js} +1 -1
  36. package/src/client/dist/pwa/assets/{ComparisonPanel-LZrvk6a6.js → ComparisonPanel-DZAE2X9j.js} +1 -1
  37. package/src/client/dist/pwa/assets/CreatePage-A3pVradx.css +1 -0
  38. package/src/client/dist/pwa/assets/CreatePage-ArmtcF4d.js +2 -0
  39. package/src/client/dist/pwa/assets/{DashboardPage-0odIhseb.js → DashboardPage-eQTCXcdz.js} +1 -1
  40. package/src/client/dist/pwa/assets/{DiffViewer-NWB2uQkz.js → DiffViewer-CaT7vuSr.js} +3 -3
  41. package/src/client/dist/pwa/assets/{DocumentsPanel-3yApHgbl.js → DocumentsPanel-A0isCTaI.js} +1 -1
  42. package/src/client/dist/pwa/assets/{DrawerToggleButton-C_bNtZsE.js → DrawerToggleButton-T70fh8dp.js} +1 -1
  43. package/src/client/dist/pwa/assets/{GitPanel-BMjWwiAX.js → GitPanel-CAvk0G7h.js} +3 -3
  44. package/src/client/dist/pwa/assets/{HealthPage-Vyr1YG2o.js → HealthPage-D8Mr0V6w.js} +1 -1
  45. package/src/client/dist/pwa/assets/{MainLayout-s8_9FPE2.js → MainLayout-x6Es4Da-.js} +2 -2
  46. package/src/client/dist/pwa/assets/{QBtnDropdown-BxuXEBp5.js → QBtnDropdown-CQqzzlFI.js} +1 -1
  47. package/src/client/dist/pwa/assets/{QDrawer-BdlDiU6a.js → QDrawer-Cwj2qh0r.js} +1 -1
  48. package/src/client/dist/pwa/assets/{QSelect-BrSemS3o.js → QSelect-BSPtuXJg.js} +1 -1
  49. package/src/client/dist/pwa/assets/{QTooltip-CLCFHW2D.js → QTooltip-DC_kDKku.js} +1 -1
  50. package/src/client/dist/pwa/assets/{SchedulePanel-F9bdAq6K.js → SchedulePanel-9aJSoz6Y.js} +1 -1
  51. package/src/client/dist/pwa/assets/{SearchPage-CBCefYqc.js → SearchPage-BjNvtE_U.js} +1 -1
  52. package/src/client/dist/pwa/assets/{SettingsPage-BPgTHmKj.js → SettingsPage-CvLnT73V.js} +1 -1
  53. package/src/client/dist/pwa/assets/{SplitWorkspacePage-DDs2nuoT.js → SplitWorkspacePage-DTao0DHr.js} +1 -1
  54. package/src/client/dist/pwa/assets/{TerminalPanel-CulE0wTU.js → TerminalPanel-BrKIW-bJ.js} +1 -1
  55. package/src/client/dist/pwa/assets/{TouchPan-DUgqs2cW.js → TouchPan-_n1AQgoo.js} +1 -1
  56. package/src/client/dist/pwa/assets/{TourReplayButton-CdHpGBdr.js → TourReplayButton-CJF3nVvn.js} +1 -1
  57. package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-W5SvqzXO.js → WorkspaceAttentionLabels-CxesUiIm.js} +1 -1
  58. package/src/client/dist/pwa/assets/{WorkspacePage-DYQrfeYw.css → WorkspacePage-DoXtwpjV.css} +1 -1
  59. package/src/client/dist/pwa/assets/WorkspacePage-MxBE0mr3.js +4 -0
  60. package/src/client/dist/pwa/assets/{browser-CnuqDTyc.js → browser-DGWpkvN3.js} +1 -1
  61. package/src/client/dist/pwa/assets/{build-path-tree-JodIO7-I.js → build-path-tree-Ba1dSLgi.js} +1 -1
  62. package/src/client/dist/pwa/assets/{de-Dt_m-H83.js → de-DVK21RB6.js} +1 -1
  63. package/src/client/dist/pwa/assets/{documents-3ZBK1pXE.js → documents-BMHzjW_g.js} +1 -1
  64. package/src/client/dist/pwa/assets/{editor.api-DZ9WO_J7.js → editor.api-CYTuNwqu.js} +1 -1
  65. package/src/client/dist/pwa/assets/{efforts-LrtJPx-j.js → efforts-BPlRSHXp.js} +1 -1
  66. package/src/client/dist/pwa/assets/{engineFeatures-DmnHj_uD.js → engineFeatures-DVB2rklO.js} +1 -1
  67. package/src/client/dist/pwa/assets/{es-DFg25DCS.js → es-C8cQwDZM.js} +1 -1
  68. package/src/client/dist/pwa/assets/{expand-template-Dv4JC2zU.js → expand-template-Cj82YE-O.js} +1 -1
  69. package/src/client/dist/pwa/assets/{format-CHjfjuhr.js → format-Dk7O3LhA.js} +1 -1
  70. package/src/client/dist/pwa/assets/{formatters-BsgH8CoH.js → formatters-TGjvs7Ca.js} +1 -1
  71. package/src/client/dist/pwa/assets/{fr-CQ1slpol.js → fr-BVOMZ2Bl.js} +1 -1
  72. package/src/client/dist/pwa/assets/{freemarker2-Dla55SIR.js → freemarker2-39lO_dWH.js} +1 -1
  73. package/src/client/dist/pwa/assets/{handlebars-DtuuZy7D.js → handlebars-M_0V82cD.js} +1 -1
  74. package/src/client/dist/pwa/assets/{html-CKYOCR8W.js → html-yILqYDsl.js} +1 -1
  75. package/src/client/dist/pwa/assets/i18n-2UeMfd5m.js +1 -0
  76. package/src/client/dist/pwa/assets/{index-CIP08MpU.js → index-BJU5dnkF.js} +2 -2
  77. package/src/client/dist/pwa/assets/{it-w09rfaP7.js → it-CxkAoOj4.js} +1 -1
  78. package/src/client/dist/pwa/assets/{javascript-BxzTnsIQ.js → javascript-DrW-amDc.js} +1 -1
  79. package/src/client/dist/pwa/assets/{liquid-C8BfPFUy.js → liquid-D_mXmUoL.js} +1 -1
  80. package/src/client/dist/pwa/assets/{mdx-DZm_YXkC.js → mdx-E8O3lvR5.js} +1 -1
  81. package/src/client/dist/pwa/assets/{monaco-contributions-HDbRHiNR.js → monaco-contributions-C2K3sl_V.js} +1 -1
  82. package/src/client/dist/pwa/assets/{monaco.contribution-DRW-851R.js → monaco.contribution-DLhHQIJn.js} +2 -2
  83. package/src/client/dist/pwa/assets/{network-auth-Ce9hsryo.js → network-auth-BUwWtnui.js} +1 -1
  84. package/src/client/dist/pwa/assets/{notify-theme-OBpMEBvP.js → notify-theme-1mOpY2vN.js} +1 -1
  85. package/src/client/dist/pwa/assets/{pending-diff-open-CVQxWyzP.js → pending-diff-open-BL7dKRqF.js} +1 -1
  86. package/src/client/dist/pwa/assets/{permissionModes-CVFHS2ec.js → permissionModes-BtC3_P-P.js} +1 -1
  87. package/src/client/dist/pwa/assets/{project-color-mcpQp2o6.js → project-color-BFBFZWeG.js} +1 -1
  88. package/src/client/dist/pwa/assets/{python-CO5iQN74.js → python-DoZAYim6.js} +1 -1
  89. package/src/client/dist/pwa/assets/{razor-BC7D8LMf.js → razor-9bZ30g2y.js} +1 -1
  90. package/src/client/dist/pwa/assets/{render-chat-markdown-Cb15kvox.js → render-chat-markdown-7EtWNjG0.js} +1 -1
  91. package/src/client/dist/pwa/assets/{typescript-BCHCSNpD.js → typescript-C1CBfL3L.js} +1 -1
  92. package/src/client/dist/pwa/assets/{use-tours-DhLhglwr.js → use-tours-BJ81clv6.js} +1 -1
  93. package/src/client/dist/pwa/assets/{workspace-templates-CoWrOEac.js → workspace-templates-r1-bipjM.js} +1 -1
  94. package/src/client/dist/pwa/assets/{xml-y9M3NAIZ.js → xml-COuu5isK.js} +1 -1
  95. package/src/client/dist/pwa/assets/{yaml-CthlToPo.js → yaml-ChZ7WqaJ.js} +1 -1
  96. package/src/client/dist/pwa/index.html +1 -1
  97. package/src/client/dist/pwa/sw.js +1 -1
  98. package/src/client/dist/pwa/assets/ActivityFeed-YRbIRutK.js +0 -8
  99. package/src/client/dist/pwa/assets/CreatePage-DxOyY65u.css +0 -1
  100. package/src/client/dist/pwa/assets/CreatePage-gFrn4xl9.js +0 -2
  101. package/src/client/dist/pwa/assets/WorkspacePage-B6O7j57u.js +0 -4
  102. 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 existing = db
30
- .prepare('SELECT retry_count FROM pending_quota_backoffs WHERE workspace_id = ?')
31
- .get(workspaceId);
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
- const timer = setTimeout(() => fireOrSkip(workspaceId), Math.max(0, delayMs));
46
- timer.unref?.();
47
- timers.set(workspaceId, timer);
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
- const timeout = setTimeout(() => fire(workspaceId), clampedSeconds * 1000);
58
- timeout.unref?.();
59
- timers.set(workspaceId, timeout);
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
- try {
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
- git(repoPath, ['checkout', '-q', workingBranch]);
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 scriptPath = path.join(worktreePath, '.ai', tmpFileName);
18
- fs.mkdirSync(path.dirname(scriptPath), { recursive: true });
19
- fs.writeFileSync(scriptPath, script, { mode: 0o755 });
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
- try {
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.35",
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)?`![${r}](${`/api/workspaces/${encodeURIComponent(t)}/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-CIP08MpU.js";import{a as v,i as y,n as b}from"./use-tours-DhLhglwr.js";import{n as x}from"./render-chat-markdown-Cb15kvox.js";import{t as S}from"./QChip-owciRuZu.js";import{t as C}from"./QPage-DaX9sMQC.js";import{t as w}from"./DrawerToggleButton-C_bNtZsE.js";import{t as T}from"./TourReplayButton-CdHpGBdr.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};
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};
@@ -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-CIP08MpU.js";import{t as se}from"./QMarkupTable-B9QuKtK_.js";import{a as m,i as h}from"./use-tours-DhLhglwr.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
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};