@loicngr/kobo 1.7.2 → 1.7.4

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 (75) hide show
  1. package/dist/mcp-server/kobo-tasks-handlers.js +8 -1
  2. package/dist/server/routes/health.js +12 -6
  3. package/dist/server/routes/settings.js +16 -0
  4. package/dist/server/routes/workspaces.js +207 -4
  5. package/dist/server/services/agent/orchestrator.js +3 -0
  6. package/dist/server/services/auto-loop-service.js +7 -1
  7. package/dist/server/services/initial-prompt-template-service.js +48 -0
  8. package/dist/server/services/review-template-service.js +58 -0
  9. package/dist/server/services/settings-service.js +70 -3
  10. package/dist/server/services/wakeup-service.js +9 -1
  11. package/dist/server/services/worktree-service.js +2 -2
  12. package/dist/server/utils/git-ops.js +130 -9
  13. package/dist/server/utils/project-slug.js +52 -0
  14. package/dist/server/utils/worktree-paths.js +12 -10
  15. package/package.json +1 -1
  16. package/src/client/dist/spa/assets/ActivityFeed-ClJLeAXJ.js +7 -0
  17. package/src/client/dist/spa/assets/{ActivityFeed-LXnbg3ff.css → ActivityFeed-DVBfmJWJ.css} +1 -1
  18. package/src/client/dist/spa/assets/{ClosePopup-BP025_cK.js → ClosePopup-D_UAdwkA.js} +1 -1
  19. package/src/client/dist/spa/assets/CreatePage-BOkt0Psl.js +2 -0
  20. package/src/client/dist/spa/assets/CreatePage-DssmsAsV.css +1 -0
  21. package/src/client/dist/spa/assets/DiffViewer-Dls1jFCN.js +7 -0
  22. package/src/client/dist/spa/assets/{DiffViewer-D1Sdu307.css → DiffViewer-wFfQ9tcY.css} +1 -1
  23. package/src/client/dist/spa/assets/{HealthPage-CkHv5qMK.js → HealthPage-CMxH3SBS.js} +1 -1
  24. package/src/client/dist/spa/assets/{MainLayout-l91ohFQA.js → MainLayout-DHNIerYJ.js} +17 -17
  25. package/src/client/dist/spa/assets/MainLayout-DKurmqtk.css +1 -0
  26. package/src/client/dist/spa/assets/{QExpansionItem-BaQJkGb-.js → QExpansionItem-CH1ipL9n.js} +1 -1
  27. package/src/client/dist/spa/assets/{QMenu-DgWZe7Uh.js → QMenu-B4xMxMGd.js} +1 -1
  28. package/src/client/dist/spa/assets/{QTabPanels-CjpZTIJg.js → QTabPanels-D2ks0UIA.js} +1 -1
  29. package/src/client/dist/spa/assets/{QTooltip-D_hSPb7r.js → QTooltip-fDNzBEfN.js} +1 -1
  30. package/src/client/dist/spa/assets/{SearchPage-B1WhFCUf.js → SearchPage-BEnZ-CLq.js} +1 -1
  31. package/src/client/dist/spa/assets/SettingsPage-CMyeQ9_u.css +1 -0
  32. package/src/client/dist/spa/assets/SettingsPage-DeCbWvPb.js +1 -0
  33. package/src/client/dist/spa/assets/{TouchPan-1PETKHN0.js → TouchPan-DoE24Io3.js} +1 -1
  34. package/src/client/dist/spa/assets/{WorkspacePage-D3MBshNH.js → WorkspacePage-DFAFT5OW.js} +3 -3
  35. package/src/client/dist/spa/assets/{WorkspacePage-d_B0-LNG.css → WorkspacePage-eymEd4kx.css} +1 -1
  36. package/src/client/dist/spa/assets/{build-path-tree-w3SEPAbh.js → build-path-tree-B1Lvvqto.js} +1 -1
  37. package/src/client/dist/spa/assets/{cssMode-B6CD4qMI.js → cssMode-AlflsawW.js} +1 -1
  38. package/src/client/dist/spa/assets/{editor.api-wizjkvCK.js → editor.api-DtvjQlUm.js} +1 -1
  39. package/src/client/dist/spa/assets/{editor.main-Bn6fpPLF.js → editor.main-Ccy_gjVD.js} +3 -3
  40. package/src/client/dist/spa/assets/{expand-template-Cu5GSLCM.js → expand-template-AQsvbQ8_.js} +1 -1
  41. package/src/client/dist/spa/assets/{freemarker2-DW-DFUis.js → freemarker2-DdQktlXK.js} +1 -1
  42. package/src/client/dist/spa/assets/{handlebars-CSSQFRHS.js → handlebars-CE3ee2NH.js} +1 -1
  43. package/src/client/dist/spa/assets/{html-Ba5lfQna.js → html-CCKX8Xv9.js} +1 -1
  44. package/src/client/dist/spa/assets/{htmlMode-ocrlHn5h.js → htmlMode-Dh8jDJum.js} +1 -1
  45. package/src/client/dist/spa/assets/i18n-BOsrrRj4.js +1 -0
  46. package/src/client/dist/spa/assets/index-_ZaIBxd6.js +2 -0
  47. package/src/client/dist/spa/assets/{javascript-DL3j24x3.js → javascript-DhmZNdUp.js} +1 -1
  48. package/src/client/dist/spa/assets/{jsonMode-CtFp2BJe.js → jsonMode-B0xAtnNK.js} +1 -1
  49. package/src/client/dist/spa/assets/{liquid-B_GGNnlJ.js → liquid-ByL0HpZ0.js} +1 -1
  50. package/src/client/dist/spa/assets/{mdx-BXe8MrIz.js → mdx-DX4pehAZ.js} +1 -1
  51. package/src/client/dist/spa/assets/{models-BMOYJtwv.js → models-ClWoqWeC.js} +1 -1
  52. package/src/client/dist/spa/assets/{monaco.contribution-DSSRKV2r.js → monaco.contribution-Fegh8Y1Y.js} +2 -2
  53. package/src/client/dist/spa/assets/{notifications-CG-oL2m2.js → notifications-OnPq4FrH.js} +1 -1
  54. package/src/client/dist/spa/assets/purify.es-BWZjBa9F.js +60 -0
  55. package/src/client/dist/spa/assets/{python-DPtBXcrE.js → python-COS2MM8n.js} +1 -1
  56. package/src/client/dist/spa/assets/{razor-y1p5VjhT.js → razor-Cc3xCJU7.js} +1 -1
  57. package/src/client/dist/spa/assets/render-chat-markdown-DcGIpMoe.js +1 -0
  58. package/src/client/dist/spa/assets/{tsMode-CV2CQlAd.js → tsMode-eQIJjERk.js} +1 -1
  59. package/src/client/dist/spa/assets/{typescript-DsjWQLAN.js → typescript-DwIlacVU.js} +1 -1
  60. package/src/client/dist/spa/assets/{use-panel-D2MjPZiL.js → use-panel-D-8nAQns.js} +1 -1
  61. package/src/client/dist/spa/assets/{xml-AQhpP8em.js → xml-DP-09Aih.js} +1 -1
  62. package/src/client/dist/spa/assets/{yaml-zZFlU7RD.js → yaml-BhrtimeA.js} +1 -1
  63. package/src/client/dist/spa/index.html +2 -2
  64. package/src/mcp-server/kobo-tasks-handlers.ts +10 -1
  65. package/src/client/dist/spa/assets/ActivityFeed-B85xav_e.js +0 -7
  66. package/src/client/dist/spa/assets/CreatePage-BE3xfQsC.css +0 -1
  67. package/src/client/dist/spa/assets/CreatePage-DyR33jFM.js +0 -2
  68. package/src/client/dist/spa/assets/DiffViewer-CqhpTkym.js +0 -7
  69. package/src/client/dist/spa/assets/MainLayout-B07zv82Z.css +0 -1
  70. package/src/client/dist/spa/assets/QScrollArea-usfgatuS.js +0 -1
  71. package/src/client/dist/spa/assets/SettingsPage-B7S5fXGG.js +0 -1
  72. package/src/client/dist/spa/assets/SettingsPage-kHd651y8.css +0 -1
  73. package/src/client/dist/spa/assets/i18n-CqK8B0Nz.js +0 -1
  74. package/src/client/dist/spa/assets/index-DE3PxEjy.js +0 -2
  75. package/src/client/dist/spa/assets/marked.esm-D4t0_2pc.js +0 -60
@@ -4,7 +4,9 @@ import { WORKTREES_PATH } from '../../shared/consts.js';
4
4
  import { listClaudeMcpEntries } from '../utils/mcp-client.js';
5
5
  import { getSettingsPath } from '../utils/paths.js';
6
6
  import { InvalidWorktreesPathError, resolveGlobalWorktreesRoot, sanitizeWorktreesPath, validateWorktreesPath, } from '../utils/worktree-paths.js';
7
- const DEFAULT_GIT_CONVENTIONS = `# Git conventions
7
+ import { DEFAULT_NOTION_INITIAL_PROMPT, DEFAULT_SENTRY_INITIAL_PROMPT } from './initial-prompt-template-service.js';
8
+ import { DEFAULT_REVIEW_PROMPT_TEMPLATE } from './review-template-service.js';
9
+ export const DEFAULT_GIT_CONVENTIONS = `# Git conventions
8
10
 
9
11
  ## Commits
10
12
  - Use Conventional Commits: \`type(scope): subject\`
@@ -29,7 +31,7 @@ const DEFAULT_GIT_CONVENTIONS = `# Git conventions
29
31
  - Never skip hooks (--no-verify) unless the user explicitly asks
30
32
  - Always inspect \`git status\` and \`git diff\` before staging
31
33
  `;
32
- const DEFAULT_PR_PROMPT_TEMPLATE = `A pull request has been opened: {{pr_url}} (#{{pr_number}})
34
+ export const DEFAULT_PR_PROMPT_TEMPLATE = `A pull request has been opened: {{pr_url}} (#{{pr_number}})
33
35
 
34
36
  Context:
35
37
  - Workspace: {{workspace_name}}
@@ -60,7 +62,9 @@ export const DEFAULT_FINALIZATION_PROMPT = `Run final quality checks before clos
60
62
  1. Verify all other tasks are marked \`done\`. If any remain \`pending\`, stop and report.
61
63
  2. Run the project's linters, type-checkers, and tests (see CLAUDE.md or package.json scripts).
62
64
  3. If any check fails, create a new regular task at the end of the list with a title like \`Fix lint failure in X\` (NO \`[FINAL]\` or \`[E2E]\` prefix — it must use the default iteration prompt) and mark this \`[FINAL]\` task as \`done\`. The auto-loop will pick up the fix on the next iteration. The finalization mechanism is single-shot per grooming pass; if you want quality checks to re-run after the fix, mark the fix task \`done\` and re-trigger grooming manually.
63
- 4. If everything passes, mark this task as \`done\`.`;
65
+ 4. If everything passes, mark this task as \`done\`.
66
+
67
+ HARD RULE: Do NOT open a pull request, do NOT run \`gh pr create\` or any equivalent command. The finalization step never opens a PR — that is a separate, explicit user action via the "Open PR" button.`;
64
68
  /** Default workspace tags seeded on fresh install and on settings upgrade. */
65
69
  export const DEFAULT_WORKSPACE_TAGS = [
66
70
  'bug',
@@ -219,6 +223,49 @@ const settingsMigrations = [
219
223
  }
220
224
  },
221
225
  },
226
+ {
227
+ version: 14,
228
+ name: 'add-worktrees-prefix-by-project',
229
+ migrate({ global }) {
230
+ if (typeof global.worktreesPrefixByProject !== 'boolean') {
231
+ global.worktreesPrefixByProject = false;
232
+ }
233
+ },
234
+ },
235
+ {
236
+ version: 15,
237
+ name: 'add-review-prompt-template',
238
+ migrate({ global, projects }) {
239
+ if (typeof global.reviewPromptTemplate !== 'string') {
240
+ global.reviewPromptTemplate = DEFAULT_REVIEW_PROMPT_TEMPLATE;
241
+ }
242
+ for (const p of projects) {
243
+ if (typeof p.reviewPromptTemplate !== 'string') {
244
+ p.reviewPromptTemplate = '';
245
+ }
246
+ }
247
+ },
248
+ },
249
+ {
250
+ version: 16,
251
+ name: 'add-notion-sentry-initial-prompts',
252
+ migrate({ global, projects }) {
253
+ if (typeof global.notionInitialPromptTemplate !== 'string') {
254
+ global.notionInitialPromptTemplate = DEFAULT_NOTION_INITIAL_PROMPT;
255
+ }
256
+ if (typeof global.sentryInitialPromptTemplate !== 'string') {
257
+ global.sentryInitialPromptTemplate = DEFAULT_SENTRY_INITIAL_PROMPT;
258
+ }
259
+ for (const p of projects) {
260
+ if (typeof p.notionInitialPromptTemplate !== 'string') {
261
+ p.notionInitialPromptTemplate = '';
262
+ }
263
+ if (typeof p.sentryInitialPromptTemplate !== 'string') {
264
+ p.sentryInitialPromptTemplate = '';
265
+ }
266
+ }
267
+ },
268
+ },
222
269
  ];
223
270
  /** Current settings schema version — always equals the highest migration version. */
224
271
  export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
@@ -249,6 +296,9 @@ function defaultSettings() {
249
296
  defaultModel: 'claude-opus-4-7',
250
297
  dangerouslySkipPermissions: true,
251
298
  prPromptTemplate: DEFAULT_PR_PROMPT_TEMPLATE,
299
+ reviewPromptTemplate: DEFAULT_REVIEW_PROMPT_TEMPLATE,
300
+ notionInitialPromptTemplate: DEFAULT_NOTION_INITIAL_PROMPT,
301
+ sentryInitialPromptTemplate: DEFAULT_SENTRY_INITIAL_PROMPT,
252
302
  gitConventions: DEFAULT_GIT_CONVENTIONS,
253
303
  editorCommand: '',
254
304
  browserNotifications: true,
@@ -262,6 +312,7 @@ function defaultSettings() {
262
312
  sentryMcpKey: '',
263
313
  tags: [...DEFAULT_WORKSPACE_TAGS],
264
314
  worktreesPath: WORKTREES_PATH,
315
+ worktreesPrefixByProject: false,
265
316
  },
266
317
  projects: [],
267
318
  };
@@ -274,6 +325,9 @@ function defaultProjectSettings(projectPath) {
274
325
  defaultModel: '',
275
326
  dangerouslySkipPermissions: true,
276
327
  prPromptTemplate: '',
328
+ reviewPromptTemplate: '',
329
+ notionInitialPromptTemplate: '',
330
+ sentryInitialPromptTemplate: '',
277
331
  gitConventions: '',
278
332
  setupScript: '',
279
333
  devServer: {
@@ -447,6 +501,9 @@ export function getEffectiveSettings(projectPath) {
447
501
  model: settings.global.defaultModel,
448
502
  dangerouslySkipPermissions: settings.global.dangerouslySkipPermissions,
449
503
  prPromptTemplate: settings.global.prPromptTemplate,
504
+ reviewPromptTemplate: settings.global.reviewPromptTemplate,
505
+ notionInitialPromptTemplate: settings.global.notionInitialPromptTemplate,
506
+ sentryInitialPromptTemplate: settings.global.sentryInitialPromptTemplate,
450
507
  gitConventions: settings.global.gitConventions,
451
508
  sourceBranch: '',
452
509
  devServer: null,
@@ -459,6 +516,9 @@ export function getEffectiveSettings(projectPath) {
459
516
  model: project.defaultModel || settings.global.defaultModel,
460
517
  dangerouslySkipPermissions: project.dangerouslySkipPermissions ?? settings.global.dangerouslySkipPermissions,
461
518
  prPromptTemplate: project.prPromptTemplate || settings.global.prPromptTemplate,
519
+ reviewPromptTemplate: project.reviewPromptTemplate || settings.global.reviewPromptTemplate,
520
+ notionInitialPromptTemplate: project.notionInitialPromptTemplate || settings.global.notionInitialPromptTemplate,
521
+ sentryInitialPromptTemplate: project.sentryInitialPromptTemplate || settings.global.sentryInitialPromptTemplate,
462
522
  gitConventions: project.gitConventions || settings.global.gitConventions,
463
523
  sourceBranch: project.defaultSourceBranch,
464
524
  devServer: project.devServer,
@@ -474,6 +534,9 @@ export function updateGlobalSettings(data) {
474
534
  'defaultModel',
475
535
  'dangerouslySkipPermissions',
476
536
  'prPromptTemplate',
537
+ 'reviewPromptTemplate',
538
+ 'notionInitialPromptTemplate',
539
+ 'sentryInitialPromptTemplate',
477
540
  'gitConventions',
478
541
  'editorCommand',
479
542
  'browserNotifications',
@@ -487,6 +550,7 @@ export function updateGlobalSettings(data) {
487
550
  'sentryMcpKey',
488
551
  'tags',
489
552
  'worktreesPath',
553
+ 'worktreesPrefixByProject',
490
554
  ];
491
555
  const filtered = pickKnownKeys(data, allowedGlobalKeys);
492
556
  if (filtered.tags !== undefined) {
@@ -532,6 +596,9 @@ export function upsertProject(projectPath, data) {
532
596
  'defaultModel',
533
597
  'dangerouslySkipPermissions',
534
598
  'prPromptTemplate',
599
+ 'reviewPromptTemplate',
600
+ 'notionInitialPromptTemplate',
601
+ 'sentryInitialPromptTemplate',
535
602
  'gitConventions',
536
603
  'setupScript',
537
604
  'devServer',
@@ -1,6 +1,8 @@
1
1
  import { getDb } from '../db/index.js';
2
+ import { slugifyProjectName } from '../utils/project-slug.js';
2
3
  import { resolveWorkspaceWorktreePath } from '../utils/worktree-paths.js';
3
4
  import * as orchestrator from './agent/orchestrator.js';
5
+ import * as settingsService from './settings-service.js';
4
6
  import { emitEphemeral } from './websocket-service.js';
5
7
  const MIN_DELAY_SECONDS = 60;
6
8
  const MAX_DELAY_SECONDS = 3600;
@@ -132,7 +134,13 @@ function fire(workspaceId) {
132
134
  emitEphemeral(workspaceId, 'wakeup:skipped', { reason: 'fire-failed' });
133
135
  return;
134
136
  }
135
- const worktreePath = wsRow.worktree_path ?? resolveWorkspaceWorktreePath(wsRow.project_path, wsRow.working_branch);
137
+ const globalSettings = settingsService.getGlobalSettings();
138
+ const projectSettings = settingsService.getProjectSettings(wsRow.project_path);
139
+ const projectSlug = globalSettings.worktreesPrefixByProject
140
+ ? slugifyProjectName(projectSettings?.displayName ?? '', wsRow.project_path)
141
+ : undefined;
142
+ const worktreePath = wsRow.worktree_path ??
143
+ resolveWorkspaceWorktreePath(wsRow.project_path, wsRow.working_branch, globalSettings.worktreesPath, projectSlug);
136
144
  // Narrow against the four known values; unknowns → 'bypass'.
137
145
  const stored = wsRow.agent_permission_mode;
138
146
  const agentPermissionMode = stored === 'plan' || stored === 'strict' || stored === 'interactive' ? stored : 'bypass';
@@ -49,12 +49,12 @@ function removeFromExclude(projectPath, worktreePath) {
49
49
  fs.writeFileSync(excludeFile, trimmed ? `${trimmed}\n` : '', 'utf-8');
50
50
  }
51
51
  /** Create a git worktree for the given branch. Returns the worktree path. */
52
- export function createWorktree(projectPath, branchName, sourceBranch, worktreesPath) {
52
+ export function createWorktree(projectPath, branchName, sourceBranch, worktreesPath, projectSlug) {
53
53
  const worktreesDir = resolveWorktreesRoot(projectPath, worktreesPath);
54
54
  if (!fs.existsSync(worktreesDir)) {
55
55
  fs.mkdirSync(worktreesDir, { recursive: true });
56
56
  }
57
- const worktreePath = resolveWorkspaceWorktreePath(projectPath, branchName, worktreesPath);
57
+ const worktreePath = resolveWorkspaceWorktreePath(projectPath, branchName, worktreesPath, projectSlug);
58
58
  try {
59
59
  // Use origin/<sourceBranch> as the base so the worktree starts from the
60
60
  // freshly-fetched remote ref (fetchSourceBranch is always called first).
@@ -4,7 +4,14 @@ import { join } from 'node:path';
4
4
  import { promisify } from 'node:util';
5
5
  const execFileAsync = promisify(execFileCb);
6
6
  function git(repoPath, args) {
7
- return execFileSync('git', args, { cwd: repoPath, encoding: 'utf-8' }).trim();
7
+ // `trimEnd` (not `trim`): some git outputs are column-aligned and the LEADING
8
+ // space carries information. The classic case is `git status --porcelain`,
9
+ // where each line is `XY filename` and X is " " when the index has no
10
+ // change. Stripping that leading space silently shifts every column by one
11
+ // and makes `line.substring(3)` chop the first character of the filename
12
+ // (e.g. `front/foo` → `ront/foo`). Trailing whitespace (the final `\n` git
13
+ // always appends) still goes — that's what every caller expects.
14
+ return execFileSync('git', args, { cwd: repoPath, encoding: 'utf-8' }).trimEnd();
8
15
  }
9
16
  /** Return the name of the currently checked-out branch. */
10
17
  export function getCurrentBranch(repoPath) {
@@ -267,6 +274,22 @@ export function getCommitCount(repoPath, base, head) {
267
274
  return 0;
268
275
  }
269
276
  }
277
+ /**
278
+ * Count commits in `base` that are not in `head` — i.e. how far `head` lags
279
+ * behind `base`. Mirrors `getCommitCount` but in reverse direction.
280
+ * Returns 0 on failure.
281
+ */
282
+ export function getCommitsBehind(repoPath, base, head) {
283
+ try {
284
+ const ref = resolveBase(repoPath, base);
285
+ const output = git(repoPath, ['rev-list', '--count', `${head}..${ref}`]);
286
+ const n = parseInt(output.trim(), 10);
287
+ return Number.isFinite(n) ? n : 0;
288
+ }
289
+ catch {
290
+ return 0;
291
+ }
292
+ }
270
293
  /** Return structured diff shortstat between two refs (three-dot merge base). */
271
294
  export function getStructuredDiffStatsBetween(repoPath, base, head) {
272
295
  try {
@@ -340,6 +363,40 @@ export function listBranchCommits(repoPath, sourceBranch, workingBranch, limit =
340
363
  }
341
364
  return commits;
342
365
  }
366
+ /**
367
+ * List commits on `sourceBranch` that are NOT yet on `workingBranch` —
368
+ * i.e. commits the working branch is "behind" by. Mirror of `listBranchCommits`
369
+ * in the opposite direction. Up to `limit` commits, most recent first.
370
+ */
371
+ export function listCommitsBehind(repoPath, sourceBranch, workingBranch, limit = 50) {
372
+ const sourceRef = resolveBase(repoPath, sourceBranch);
373
+ const FORMAT = '--pretty=format:%H%x00%h%x00%s%x00%an%x00%aI';
374
+ let raw;
375
+ try {
376
+ raw = git(repoPath, ['log', `${workingBranch}..${sourceRef}`, `--max-count=${limit}`, FORMAT]);
377
+ }
378
+ catch {
379
+ return [];
380
+ }
381
+ if (!raw)
382
+ return [];
383
+ const commits = [];
384
+ for (const line of raw.split('\n')) {
385
+ if (!line)
386
+ continue;
387
+ const [sha, shortSha, subject, author, date] = line.split('\x00');
388
+ if (!sha)
389
+ continue;
390
+ commits.push({
391
+ sha,
392
+ shortSha: shortSha ?? '',
393
+ subject: subject ?? '',
394
+ author: author ?? '',
395
+ date: date ?? '',
396
+ });
397
+ }
398
+ return commits;
399
+ }
343
400
  /** Get the GitHub PR URL for a branch using `gh pr view`. Returns null if no PR exists. */
344
401
  export function getPrUrl(repoPath, branchName) {
345
402
  try {
@@ -626,17 +683,38 @@ export function getWorkingTreeStatus(repoPath) {
626
683
  return { staged: 0, modified: 0, untracked: 0 };
627
684
  }
628
685
  }
629
- /** Count commits ahead of upstream. Returns -1 if no upstream is set. */
630
- export function getUnpushedCount(repoPath) {
686
+ /**
687
+ * Count commits ahead of `origin/<workingBranch>`. Returns `-1` when the remote
688
+ * ref does not exist (i.e. the branch has never been pushed).
689
+ *
690
+ * We deliberately use `origin/<workingBranch>` instead of the local `@{u}`
691
+ * upstream pointer: Kōbō creates worktrees with `git worktree add -b <new>
692
+ * <path> origin/<sourceBranch>`, so `@{u}` points at `origin/<sourceBranch>`,
693
+ * NOT at the working branch's remote sibling. Comparing HEAD with that wrong
694
+ * upstream silently reported "0 unpushed" for never-pushed branches that
695
+ * happened to be aligned with their source — surfacing as a false "Pushé"
696
+ * label in the GitPanel.
697
+ */
698
+ export function getUnpushedCount(repoPath, workingBranch) {
699
+ const remoteRef = `origin/${workingBranch}`;
700
+ try {
701
+ execFileSync('git', ['rev-parse', '--verify', remoteRef], {
702
+ cwd: repoPath,
703
+ stdio: ['pipe', 'pipe', 'pipe'],
704
+ });
705
+ }
706
+ catch {
707
+ return -1; // branch never pushed (no remote ref)
708
+ }
631
709
  try {
632
- const output = execFileSync('git', ['rev-list', '@{u}..HEAD', '--count'], {
710
+ const output = execFileSync('git', ['rev-list', `${remoteRef}..HEAD`, '--count'], {
633
711
  cwd: repoPath,
634
712
  encoding: 'utf-8',
635
713
  }).trim();
636
714
  return parseInt(output, 10) || 0;
637
715
  }
638
716
  catch {
639
- return -1; // no upstream
717
+ return -1;
640
718
  }
641
719
  }
642
720
  /** Return raw `git diff --shortstat` output between two refs (three-dot). */
@@ -648,6 +726,21 @@ export function getDiffStatsBetween(repoPath, base, head) {
648
726
  return '';
649
727
  }
650
728
  }
729
+ /**
730
+ * Return `git diff --stat HEAD` output (working tree vs HEAD) as a single string.
731
+ * Empty string if the working tree is clean or the command fails. Best-effort: never throws.
732
+ */
733
+ export function getWorkingTreeDiffStats(repoPath) {
734
+ try {
735
+ return execFileSync('git', ['diff', '--stat', 'HEAD'], {
736
+ cwd: repoPath,
737
+ encoding: 'utf-8',
738
+ });
739
+ }
740
+ catch {
741
+ return '';
742
+ }
743
+ }
651
744
  // ── Async versions ───────────────────────────────────────────────────────────
652
745
  // Non-blocking alternatives for hot paths (pr-watcher, route handlers).
653
746
  /** Async version of getPrUrl. Returns null if no PR exists. */
@@ -684,16 +777,44 @@ export async function getPrStatusAsync(repoPath, branchName) {
684
777
  return null;
685
778
  }
686
779
  }
687
- /** Async version of getUnpushedCount. Returns -1 if no upstream is set. */
688
- export async function getUnpushedCountAsync(repoPath) {
780
+ /**
781
+ * Async version of `getUnpushedCount`. Same `origin/<workingBranch>` semantic:
782
+ * returns `-1` when the remote ref does not exist (never pushed), `0` when
783
+ * pushed and aligned, `>0` when pushed but ahead.
784
+ */
785
+ export async function getUnpushedCountAsync(repoPath, workingBranch) {
786
+ const remoteRef = `origin/${workingBranch}`;
787
+ try {
788
+ await execFileAsync('git', ['rev-parse', '--verify', remoteRef], { cwd: repoPath });
789
+ }
790
+ catch {
791
+ return -1; // branch never pushed (no remote ref)
792
+ }
689
793
  try {
690
- const { stdout } = await execFileAsync('git', ['rev-list', '@{u}..HEAD', '--count'], {
794
+ const { stdout } = await execFileAsync('git', ['rev-list', `${remoteRef}..HEAD`, '--count'], {
691
795
  cwd: repoPath,
692
796
  encoding: 'utf-8',
693
797
  });
694
798
  return parseInt(stdout.trim(), 10) || 0;
695
799
  }
696
800
  catch {
697
- return -1; // no upstream
801
+ return -1;
802
+ }
803
+ }
804
+ /**
805
+ * Best-effort async `git fetch <remote> <branch>`. Never throws — by contract,
806
+ * suitable for both fire-and-forget and `await` use without try/catch at the
807
+ * call site. Logs a warning on failure but resolves cleanly.
808
+ *
809
+ * Mirrors the sync `fetchSourceBranch` sibling, including the optional `remote`
810
+ * parameter (defaults to `'origin'`).
811
+ */
812
+ export async function fetchSourceBranchAsync(repoPath, branch, remote = 'origin') {
813
+ try {
814
+ await execFileAsync('git', ['fetch', remote, branch], { cwd: repoPath });
815
+ }
816
+ catch (err) {
817
+ const msg = err instanceof Error ? err.message : String(err);
818
+ console.warn(`[git-ops] fetchSourceBranchAsync(${remote}/${branch}) failed: ${msg}`);
698
819
  }
699
820
  }
@@ -0,0 +1,52 @@
1
+ import path from 'node:path';
2
+ const WINDOWS_RESERVED = new Set([
3
+ 'con',
4
+ 'prn',
5
+ 'aux',
6
+ 'nul',
7
+ 'com1',
8
+ 'com2',
9
+ 'com3',
10
+ 'com4',
11
+ 'com5',
12
+ 'com6',
13
+ 'com7',
14
+ 'com8',
15
+ 'com9',
16
+ 'lpt1',
17
+ 'lpt2',
18
+ 'lpt3',
19
+ 'lpt4',
20
+ 'lpt5',
21
+ 'lpt6',
22
+ 'lpt7',
23
+ 'lpt8',
24
+ 'lpt9',
25
+ ]);
26
+ function slugifyOne(value) {
27
+ return value
28
+ .normalize('NFD')
29
+ .replace(/[̀-ͯ]/g, '')
30
+ .toLowerCase()
31
+ .replace(/[^a-z0-9]+/g, '-')
32
+ .replace(/^-+|-+$/g, '');
33
+ }
34
+ /**
35
+ * Produce a cross-OS-safe directory name (Linux + macOS + Windows) from a
36
+ * project's display name, falling back to the path basename and finally to
37
+ * the literal `'project'`. Output is guaranteed to be non-empty and to avoid
38
+ * Windows reserved names (CON, PRN, COM1..9, LPT1..9, AUX, NUL).
39
+ */
40
+ export function slugifyProjectName(displayName, projectPath) {
41
+ const fromDisplay = slugifyOne((displayName ?? '').trim());
42
+ if (fromDisplay)
43
+ return guard(fromDisplay);
44
+ const basename = path.basename(projectPath ?? '');
45
+ const fromBasename = slugifyOne(basename);
46
+ if (fromBasename)
47
+ return guard(fromBasename);
48
+ return 'project';
49
+ }
50
+ function guard(slug) {
51
+ return WINDOWS_RESERVED.has(slug) ? `${slug}-project` : slug;
52
+ }
@@ -115,20 +115,22 @@ export function resolveGlobalWorktreesRoot(configuredPath) {
115
115
  return flavor.isAbsolute(expanded) ? flavor.normalize(expanded) : null;
116
116
  }
117
117
  /** Resolve the full on-disk path for a workspace worktree. */
118
- export function resolveWorkspaceWorktreePath(projectPath, workingBranch, configuredPath) {
118
+ export function resolveWorkspaceWorktreePath(projectPath, workingBranch, configuredPath, projectSlug) {
119
119
  const root = resolveWorktreesRoot(projectPath, configuredPath);
120
- return pathFlavor(projectPath, root).join(root, ...branchPathSegments(workingBranch));
120
+ const flavor = pathFlavor(projectPath, root);
121
+ const slugSegment = projectSlug && projectSlug.length > 0 ? [projectSlug] : [];
122
+ return flavor.join(root, ...slugSegment, ...branchPathSegments(workingBranch));
121
123
  }
122
124
  /** Resolve a renamed worktree next to its current path when the current path still matches its branch. */
123
- export function resolveSiblingWorkspaceWorktreePath(projectPath, worktreePath, currentBranch, nextBranch) {
125
+ export function resolveSiblingWorkspaceWorktreePath(projectPath, worktreePath, currentBranch, nextBranch, projectSlug) {
124
126
  const flavor = pathFlavor(projectPath, worktreePath);
125
127
  const normalizedWorktreePath = flavor.normalize(worktreePath);
126
- const currentBranchPath = flavor.join(...branchPathSegments(currentBranch));
127
- const currentBranchSuffix = `${flavor.sep}${currentBranchPath}`;
128
- const comparableWorktreePath = flavor === path.win32 ? normalizedWorktreePath.toLowerCase() : normalizedWorktreePath;
129
- const comparableSuffix = flavor === path.win32 ? currentBranchSuffix.toLowerCase() : currentBranchSuffix;
130
- const root = comparableWorktreePath.endsWith(comparableSuffix)
131
- ? normalizedWorktreePath.slice(0, -currentBranchSuffix.length)
128
+ const slugSegment = projectSlug && projectSlug.length > 0 ? [projectSlug] : [];
129
+ const currentSuffix = `${flavor.sep}${flavor.join(...slugSegment, ...branchPathSegments(currentBranch))}`;
130
+ const comparablePath = flavor === path.win32 ? normalizedWorktreePath.toLowerCase() : normalizedWorktreePath;
131
+ const comparableSuffix = flavor === path.win32 ? currentSuffix.toLowerCase() : currentSuffix;
132
+ const root = comparablePath.endsWith(comparableSuffix)
133
+ ? normalizedWorktreePath.slice(0, -currentSuffix.length)
132
134
  : resolveWorktreesRoot(projectPath);
133
- return flavor.join(root, ...branchPathSegments(nextBranch));
135
+ return flavor.join(root, ...slugSegment, ...branchPathSegments(nextBranch));
134
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loicngr/kobo",
3
- "version": "1.7.2",
3
+ "version": "1.7.4",
4
4
  "description": "Kōbō — multi-workspace agent manager for Claude Code. Orchestrates isolated git worktrees with dev servers, Notion integration, and MCP tools.",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-or-later",
@@ -0,0 +1,7 @@
1
+ import{E as e,F as t,H as n,L as r,M as i,Q as a,U as o,_t as s,bt as c,d as l,f as u,g as d,h as f,l as p,p as m,r as h,rt as g,u as _,v,yt as y}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{U as b,l as x,t as S}from"./QIcon-BJuyqdsT.js";import{c as C,s as w}from"./notifications-OnPq4FrH.js";import{t as T}from"./QBtn-a6jxWjmW.js";import{n as E}from"./vue-i18n-BcfTCFFS.js";import{d as D,g as O,l as k,p as A}from"./index-_ZaIBxd6.js";import{t as j}from"./QSpinnerDots-CszPQQ9J.js";import{t as M}from"./QTooltip-fDNzBEfN.js";import{t as N}from"./QExpansionItem-CH1ipL9n.js";import{n as ee,r as te,t as P}from"./render-chat-markdown-DcGIpMoe.js";import{n as F}from"./purify.es-BWZjBa9F.js";import{t as I}from"./documents-kx0vLfSG.js";import{t as L}from"./_plugin-vue_export-helper-Cj6tcsj6.js";function ne(e,t,n=!0){let r=[],i=new Map,a=new Map;for(let n=0;n<e.length;n++){let o=e[n],s=t?.[n];switch(o.kind){case`message:text`:{let e=i.get(o.messageId);if(e)e.text+=o.text,e.streaming=o.streaming;else{let e={type:`text`,messageId:o.messageId,text:o.text,streaming:o.streaming,ts:s};i.set(o.messageId,e),r.push(e)}break}case`message:end`:{let e=i.get(o.messageId);e&&(e.streaming=!1);break}case`message:thinking`:r.push({type:`thinking`,messageId:o.messageId,text:o.text,ts:s});break;case`tool:call`:{let e={type:`tool`,toolCallId:o.toolCallId,name:o.name,input:o.input,ts:s};a.set(o.toolCallId,e),r.push(e);break}case`tool:result`:{let e=a.get(o.toolCallId);e&&(e.result={output:o.output,isError:o.isError});break}case`session:started`:r.push({type:`session`,kind:`started`,detail:{engineSessionId:o.engineSessionId,model:o.model},ts:s});break;case`session:ended`:r.push({type:`session`,kind:`ended`,detail:{reason:o.reason,exitCode:o.exitCode},ts:s});break;case`session:compacted`:r.push({type:`session`,kind:`compacted`,ts:s});break;case`session:brainstorm-complete`:case`session:user-input-requested`:case`message:raw`:case`skills:discovered`:case`usage`:case`rate_limit`:case`subagent:progress`:case`error`:break;default:}}let o=null;for(let e of r)e.type===`text`&&e.streaming&&(o&&(o.streaming=!1),o=e);return o&&!n&&(o.streaming=!1),r}function re(e,t){if(t.length===0)return e;let n=t.map(e=>({type:`user`,content:e.content,sender:e.sender,ts:e.ts})),r=[...e,...n];r.sort((e,t)=>{let n=e.ts??``,r=t.ts??``;return n===r?0:n?r?n<r?-1:1:-1:1});let i;for(let e of r)e.type===`user`&&e.sender!==`system-prompt`&&e.ts&&(!i||e.ts>i)&&(i=e.ts);if(i)for(let e of r)e.type===`text`&&e.streaming&&(!e.ts||e.ts<i)&&(e.streaming=!1);return r}function R(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:`user`;case`session`:return`session`;default:return`agent`}}function ie(e){let t=[],n=null;for(let r of e){let e=R(r),i=e===`session`||e===`system-prompt`;!n||n.speaker!==e||i?(n={speaker:e,ts:r.ts,items:[r]},t.push(n),i&&(n=null)):n.items.push(r)}return t}var z={class:`text-caption text-grey-6`},B=v({__name:`SessionEventItem`,props:{item:{}},setup(e){let n=e,r=p(()=>{switch(n.item.kind){case`started`:return`session.started`;case`ended`:return`session.ended`;case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,n)=>(t(),m(`span`,z,c(e.$t(r.value)),1))}});function V(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){ae(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 ae(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=H(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 H(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 U=[`innerHTML`],W=L(v({__name:`TextMessageItem`,props:{item:{}},setup(e){let n=e,r=I(),i=k(),a=p(()=>{let e=i.selectedWorkspaceId;return e?r.documentsFor(e).map(e=>e.path):[]}),o=p(()=>ee(V(F.parse(n.item.text,{async:!1,breaks:!0,gfm:!0}),a.value),{addAttr:[`data-document-path`]}));function s(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let n=t.getAttribute(`data-document-path`),a=i.selectedWorkspaceId;!n||!a||r.openDocumentByPath(a,n)}return(n,r)=>(t(),m(`div`,{class:`markdown-message`,onClick:s},[_(`div`,{innerHTML:o.value},null,8,U),e.item.streaming?(t(),l(x,{key:0,size:`xs`,class:`q-ml-xs`})):u(``,!0)]))}}),[[`__scopeId`,`data-v-1b7bd8ca`]]),oe={key:0,class:`text-caption text-grey-5`,style:{"font-style":`italic`}},G=[`innerHTML`],K={key:1,style:{"white-space":`pre-wrap`}},se=L(v({__name:`ThinkingItem`,props:{item:{}},setup(e){let n=e,r=p(()=>n.item.text.trim().slice(0,100)),i=p(()=>n.item.text.trim().length>0),a=p(()=>n.item.text.trim().length>100),s=p(()=>P(n.item.text));return(n,d)=>i.value?(t(),m(`div`,oe,[a.value?(t(),l(N,{key:0,dense:``,"dense-toggle":``,label:r.value,"header-class":`text-grey-5 text-caption`,style:{"font-style":`italic`}},{default:o(()=>[_(`div`,{class:`q-py-xs markdown-thinking`,innerHTML:s.value},null,8,G)]),_:1},8,[`label`])):(t(),m(`span`,K,c(e.item.text),1))])):u(``,!0)}}),[[`__scopeId`,`data-v-7f45ed94`]]);function ce(e,t){let n=e.split(`
2
+ `),r=t.split(`
3
+ `),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array(a+1).fill(0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)n[e]===r[t]?o[e][t]=o[e+1][t+1]+1:o[e][t]=Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push({type:`context`,content:n[c]}),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push({type:`del`,content:n[c]}),c++):(s.push({type:`add`,content:r[l]}),l++);for(;c<i;)s.push({type:`del`,content:n[c++]});for(;l<a;)s.push({type:`add`,content:r[l++]});return s}function q(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??``;return{toolName:`Edit`,filePath:e,oldString:t,newString:r,replaceAll:n.replace_all??!1,additions:r?r.split(`
4
+ `).length:0,deletions:t?t.split(`
5
+ `).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(`
6
+ `).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 le(e,t){if(!e||!t?.projectPath)return e;let n=t.worktreePath;if(n){let t=J(e,n);if(t!==e)return t}let r=J(e,`${t.projectPath}/${C}/${t.workingBranch}`);return r===e?J(e,t.projectPath):r}function J(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(Y).join(`[\\\\/]+`)}`:``}function Y(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}var X={class:`tool-name`},de=[`title`],fe={key:0,class:`tool-stat-add`},Z={key:1,class:`tool-stat-del`},Q={class:`diff-sign`},$={class:`tool-name`},pe=[`title`],me=L(v({__name:`ToolCallItem`,props:{item:{}},setup(e){let i=e,o=a(!1),g=k(),v=p(()=>q(i.item.name,i.item.input)),y=p(()=>v.value?le(v.value.filePath,g.selectedWorkspace):``),x={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`,ToolSearch:`search`,WebFetch:`public`,WebSearch:`travel_explore`,NotebookRead:`book`,NotebookEdit:`edit_note`,SendMessage:`send`,ExitPlanMode:`check_circle_outline`,KillShell:`stop_circle`,BashOutput:`terminal`},C=p(()=>x[i.item.name]??`build`),w=p(()=>{if(v.value)return``;let e=i.item.input,t=T(e);return t?le(t,g.selectedWorkspace):``});function T(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 E=p(()=>{let e=v.value;return e?e.toolName===`Edit`&&e.oldString!==void 0&&e.newString!==void 0?ce(e.oldString,e.newString):e.toolName===`Write`&&e.content!==void 0?e.content.split(`
7
+ `).map(e=>({type:`add`,content:e})):e.toolName===`Bash:rm`?[{type:`del`,content:`File deleted`}]:null:null}),D=p(()=>{let e=i.item.result;if(!e)return``;if(typeof e.output==`string`)return e.output;try{return JSON.stringify(e.output)}catch{return String(e.output)}}),O=new Set([`Read`]),A=p(()=>!!(i.item.result&&D.value)&&(!O.has(i.item.name)||i.item.result?.isError===!0));function j(){o.value=!o.value}return n(()=>i.item.result?.isError===!0,e=>{e&&(o.value=!0)},{immediate:!0}),(n,i)=>v.value?(t(),m(`div`,{key:0,class:s([`tool-row`,{"tool-row-expanded":o.value}])},[_(`div`,{class:`tool-header`,onClick:j},[d(S,{name:C.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,X,c(v.value.toolName===`Bash:rm`?`Bash`:v.value.toolName),1),_(`span`,{class:`tool-path`,title:v.value.filePath},c(y.value),9,de),v.value.additions>0?(t(),m(`span`,fe,`+`+c(v.value.additions),1)):u(``,!0),v.value.deletions>0?(t(),m(`span`,Z,`-`+c(v.value.deletions),1)):u(``,!0),e.item.result?.isError?(t(),l(S,{key:2,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-xs`})):e.item.result?(t(),l(S,{key:3,name:`check`,color:`positive`,size:`xs`,class:`q-ml-xs`})):u(``,!0),d(S,{name:o.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-auto text-grey-6`},null,8,[`name`])]),o.value&&E.value?(t(),m(`div`,{key:0,class:`tool-diff`,onClick:i[0]||=b(()=>{},[`stop`])},[(t(!0),m(h,null,r(E.value,(e,n)=>(t(),m(`div`,{key:n,class:s([`diff-line`,{"diff-del":e.type===`del`,"diff-add":e.type===`add`,"diff-context":e.type===`context`}])},[_(`span`,Q,c(e.type===`del`?`-`:e.type===`add`?`+`:` `),1),f(c(e.content),1)],2))),128))])):u(``,!0)],2)):(t(),m(`div`,{key:1,class:s([`tool-row tool-row-generic`,{"tool-row-expanded":o.value,"tool-row--toggleable":A.value}])},[_(`div`,{class:`tool-header`,onClick:i[1]||=e=>A.value&&j()},[d(S,{name:C.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,$,c(e.item.name),1),w.value?(t(),m(`span`,{key:0,class:`tool-arg`,title:T(e.item.input)||w.value},c(w.value),9,pe)):u(``,!0),e.item.result?.isError?(t(),l(S,{key:1,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-auto`})):e.item.result?(t(),l(S,{key:2,name:`check`,color:`positive`,size:`xs`,class:`q-ml-auto`})):u(``,!0),A.value?(t(),l(S,{key:3,name:o.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-xs text-grey-6`},null,8,[`name`])):u(``,!0)]),o.value&&A.value?(t(),m(`div`,{key:0,class:`tool-output`,onClick:i[2]||=b(()=>{},[`stop`])},c(D.value),1)):u(``,!0)],2))}}),[[`__scopeId`,`data-v-b1fcd20d`]]);function he(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 ge=[`innerHTML`],_e=[`innerHTML`],ve=[`src`],ye=L(v({__name:`UserMessageItem`,props:{item:{}},setup(e){let n=e,r=k(),i=p(()=>n.item.sender===`system-prompt`),s=p(()=>P(he(n.item.content,r.selectedWorkspaceId??``))),c=a(null),f=a(!1);function g(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(c.value=n.src,f.value=!0)}return(e,n)=>(t(),m(h,null,[i.value?(t(),l(N,{key:0,dense:``,"dense-toggle":``,label:e.$t(`chat.systemPrompt`),"header-class":`text-grey-5 text-caption`},{default:o(()=>[_(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:s.value},null,8,ge)]),_:1},8,[`label`])):(t(),m(`div`,{key:1,class:`markdown-message`,onClick:g},[_(`div`,{innerHTML:s.value},null,8,_e)])),d(O,{modelValue:f.value,"onUpdate:modelValue":n[1]||=e=>f.value=e},{default:o(()=>[c.value?(t(),m(`img`,{key:0,src:c.value,alt:``,class:`image-lightbox-img`,onClick:n[0]||=e=>f.value=!1},null,8,ve)):u(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-f34be4c5`]]),be={class:`turn-header`},xe={key:0,class:`turn-time`},Se={class:`turn-time turn-time-updated`},Ce={key:2,class:`turn-actions`},we={class:`turn-body`},Te={key:0,class:`turn-scroll-top`},Ee=L(v({__name:`TurnCard`,props:{turn:{}},emits:[`scrollTo`],setup(e,{emit:n}){let i=e,v=n,{t:b}=E(),x=a(null);function C(){let e=x.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;v(`scrollTo`,Math.max(0,n-8))}let w=p(()=>{switch(i.turn.speaker){case`user`:return{label:b(`chat.you`),accent:`#ce93d8`,badgeClass:`turn-badge-user`};case`agent`:return{label:b(`chat.agent`),accent:`#7986cb`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:b(`chat.systemPrompt`),accent:`#757575`,badgeClass:`turn-badge-system`};case`session`:return{label:b(`chat.session`),accent:`#616161`,badgeClass:`turn-badge-session`}}});function D(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 O=p(()=>D(i.turn.ts)),k=p(()=>{let e=i.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}),A=p(()=>{let e=i.turn.ts,t=k.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?``:D(t,r-n<6e4)}),j=p(()=>A.value!==``),N=p(()=>i.turn.items.filter(e=>e.type===`tool`).length);return(n,i)=>(t(),m(`div`,{ref_key:`cardEl`,ref:x,class:s([`turn-card`,{"turn-card--user":e.turn.speaker===`user`}]),style:y({"--turn-accent":w.value.accent})},[_(`div`,be,[_(`span`,{class:s([`turn-badge`,w.value.badgeClass])},c(w.value.label),3),O.value?(t(),m(`span`,xe,c(O.value),1)):u(``,!0),j.value?(t(),m(h,{key:1},[d(S,{name:`arrow_forward`,size:`10px`,color:`grey-7`,class:`turn-time-arrow`}),_(`span`,Se,[f(c(A.value)+` `,1),d(M,null,{default:o(()=>[f(c(g(b)(`chat.lastUpdatedAt`,{time:A.value})),1)]),_:1})])],64)):u(``,!0),N.value>0?(t(),m(`span`,Ce,` · `+c(g(b)(`chat.nActions`,{n:N.value})),1)):u(``,!0)]),_(`div`,we,[(t(!0),m(h,null,r(e.turn.items,(e,n)=>(t(),m(h,{key:n},[e.type===`text`?(t(),l(W,{key:0,item:e},null,8,[`item`])):e.type===`thinking`?(t(),l(se,{key:1,item:e},null,8,[`item`])):e.type===`tool`?(t(),l(me,{key:2,item:e},null,8,[`item`])):e.type===`user`?(t(),l(ye,{key:3,item:e},null,8,[`item`])):e.type===`session`?(t(),l(B,{key:4,item:e},null,8,[`item`])):u(``,!0)],64))),128))]),e.turn.items.length>4?(t(),m(`div`,Te,[d(T,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`grey-6`,class:`turn-scroll-top-btn`,onClick:C},{default:o(()=>[d(M,null,{default:o(()=>[f(c(g(b)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):u(``,!0)],6))}}),[[`__scopeId`,`data-v-4729e0cc`]]),De={key:0,class:`activity-feed-switching`},Oe={key:1,class:`activity-feed-wrap`},ke={key:0,class:`text-center q-py-sm text-caption text-grey-6`},Ae={class:`q-pa-md`},je={key:1,class:`q-px-md q-pb-md`},Me={class:`activity-feed-nav-cluster`},Ne=60,Pe=200,Fe=200,Ie=400,Le=200,Re=L(v({__name:`ActivityFeed`,props:{workspaceId:{}},setup(s){let g=s,v=A(),y=w(),b=k(),S=p(()=>b.selectedSessionId),C=p(()=>b.sessions.find(e=>e.id===S.value)?.engineSessionId??null),E=p(()=>{let e=b.sessions;return e.length===0?!1:S.value===e[e.length-1].id});function O(e){return S.value?e?e===S.value||e===C.value:E.value:!0}let M=p(()=>(b.activityFeeds[g.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&O(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,ts:e.timestamp,sessionId:e.sessionId}))),ee=p(()=>D(b.workspaces.find(e=>e.id===g.workspaceId)?.status)),P=p(()=>{let e=v.eventsFor(g.workspaceId),t=v.timestampsFor(g.workspaceId),n=v.sessionIdsFor(g.workspaceId),r=[],i=[];for(let a=0;a<e.length;a++)O(n[a])&&(r.push(e[a]),i.push(t[a]));let a=re(ne(r,i,ee.value),M.value);return ie(y.showVerboseSystemMessages?a:a.filter(e=>e.type!==`session`))}),F=p(()=>y.showVerboseSystemMessages?v.eventsFor(g.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),I=a(null),L=a(!0),R=a(!1),z=!1,B=a(!0),V=a(new Map);function ae(e){L.value=e.verticalSize-e.verticalPosition-e.verticalContainerSize<=Ne,z&&e.verticalPosition<=Pe&&!R.value&&U()&&G()}function H(e,t){return`${e}:${t}`}function U(){let e=S.value;return e?V.value.get(H(g.workspaceId,e))??!0:v.hasMoreOlderFor(g.workspaceId)}function W(e,t,n){V.value.set(H(e,t),n)}function oe(e){if(!S.value)return v.oldestIdFor(e);let t=v.eventIdsFor(e),n=v.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!O(n[e]))continue;let r=t[e];if(r)return r}}async function G(){let t=g.workspaceId,n=S.value,r=oe(t);if(!r)return;R.value=!0;let i=Date.now();try{let i=I.value,a=i?.getScroll().verticalSize??0,o=i?.getScroll().verticalPosition??0,s=new URLSearchParams({before:r,limit:`200`});n&&s.set(`session`,n);let c=fetch(`/api/workspaces/${t}/events?${s.toString()}`),l=new Promise(e=>setTimeout(e,Fe)),[u]=await Promise.all([c,l]);if(!u.ok){n?W(t,n,!1):v.prepend(t,[],[],{oldestId:r,hasMoreOlder:!1});return}let d=await u.json(),f=d.events??[],p=f.filter(e=>e.type===`agent:event`&&e.workspaceId===t),m=f.filter(e=>e.type===`user:message`&&e.workspaceId===t),h=p.map(e=>e.payload),g=p.map(e=>e.createdAt),_=p.map(e=>e.sessionId??null),y=p.map(e=>e.id),x=f.length>0?f[0].id:r;n&&W(t,n,d.hasMore),v.prepend(t,h,g,{oldestId:x,hasMoreOlder:n?v.hasMoreOlderFor(t):d.hasMore,sessionIds:_,eventIds:y});for(let e of m){let n=e.payload;typeof n.content==`string`&&b.addActivityItem(t,{id:e.id,type:`text`,content:n.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:n.sender??`user`}})}if(await e(),i){let e=i.getScroll().verticalSize-a;if(e>0){let t=Math.max(o+e,Pe+50);i.setScrollPosition(`vertical`,t,0)}}}catch(e){console.error(`[ActivityFeed] failed to load older events:`,e)}finally{let e=Date.now()-i,t=Math.max(0,Fe-e);await new Promise(e=>setTimeout(e,t+Ie)),R.value=!1}}async function K(t=0){await e();let n=I.value;if(!n)return;let r=n.getScroll();n.setScrollPosition(`vertical`,r.verticalSize,t)}function se(e){let t=I.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}let ce=a([]),q=a(null);function le(){let e=P.value,t=ce.value,n=[];if(t.length===e.length){for(let r=0;r<e.length;r++){if(e[r].speaker!==`user`)continue;let i=t[r]?.$el;i&&n.push(i)}if(n.length>0)return n}let r=q.value?.parentElement;if(r){let e=r.querySelectorAll(`.turn-card--user`);for(let t of e)n.push(t)}return n}function J(){let e=I.value;if(!e)return null;let t=q.value;if(!t)return null;let n=e.getScroll().verticalPosition,r=t.getBoundingClientRect().top,i=null;for(let e of le()){let t=e.getBoundingClientRect().top-r;if(t<n-40)i=t;else break}return i}async function ue(){let t=I.value;if(!t)return;let n=J();if(n===null)for(let t=0;t<15&&U();t++){for(;R.value;)await new Promise(e=>setTimeout(e,50));if(await G(),await e(),n=J(),n!==null)break}n!==null&&t.setScrollPosition(`vertical`,Math.max(0,n-12),250)}async function Y(){z=!1,await e(),await K(0),requestAnimationFrame(()=>{requestAnimationFrame(()=>{z=!0})})}let X=p(()=>{let e=v.sessionIdsFor(g.workspaceId);if(!S.value)return e.length;let t=0;for(let n of e)O(n)&&t++;return t}),de=p(()=>v.eventsFor(g.workspaceId).length);async function fe(){B.value=!0;let e=Date.now();await new Promise(e=>setTimeout(e,Le));let t=e+5e3;for(;de.value===0&&Date.now()<t;)await new Promise(e=>setTimeout(e,50));B.value=!1}n(B,async e=>{!e&&X.value>0&&await Y(),!e&&X.value===0&&S.value&&$()}),i(()=>{fe(),X.value>0&&Y(),S.value&&$()});let Z=!1;n(X,async(e,t)=>{if(!Z&&e>0){Z=!0,await Y();return}e>t&&L.value&&!R.value&&await K(180)}),n(()=>g.workspaceId,()=>{L.value=!0,Z=!1,z=!1,fe(),X.value>0&&Y()}),n(()=>b.selectedSessionId,async()=>{L.value=!0,z=!1,await Y(),$()});let Q=new Set;async function $(){let t=S.value;if(!t||X.value>0)return;let n=H(g.workspaceId,t);if(!Q.has(n)){Q.add(n);try{let n=await fetch(`/api/workspaces/${g.workspaceId}/events?session=${encodeURIComponent(t)}&limit=500`);if(!n.ok)return;let r=await n.json(),i=r.events??[];if(i.length===0)return;let a=i.filter(e=>e.type===`agent:event`&&e.workspaceId===g.workspaceId),o=i.filter(e=>e.type===`user:message`&&e.workspaceId===g.workspaceId),s=a.map(e=>e.payload),c=a.map(e=>e.createdAt),l=a.map(e=>e.sessionId??null),u=a.map(e=>e.id);W(g.workspaceId,t,r.hasMore),s.length>0&&v.prepend(g.workspaceId,s,c,{oldestId:i[0].id,hasMoreOlder:v.hasMoreOlderFor(g.workspaceId),sessionIds:l,eventIds:u});for(let e of o){let t=e.payload;typeof t.content==`string`&&b.addActivityItem(g.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await e(),await K(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Q.delete(n)}}}n(p(()=>M.value.filter(e=>e.sender!==`system-prompt`).length),async(e,t)=>{e>t&&(L.value=!0,await K(180))});async function pe(){L.value=!0,await K(250)}return(e,n)=>B.value?(t(),m(`div`,De,[d(j,{size:`40px`,color:`indigo-4`})])):(t(),m(`div`,Oe,[d(te,{ref_key:`scrollRef`,ref:I,class:`activity-feed-scroll`,onScroll:ae},{default:o(()=>[_(`div`,{ref_key:`contentOriginRef`,ref:q,class:`content-origin-marker`},null,512),R.value?(t(),m(`div`,ke,[d(x,{size:`sm`}),f(` `+c(e.$t(`activity.loading_older`)),1)])):u(``,!0),_(`div`,Ae,[(t(!0),m(h,null,r(P.value,(e,n)=>(t(),l(Ee,{key:n,ref_for:!0,ref_key:`turnRefs`,ref:ce,turn:e,onScrollTo:se},null,8,[`turn`]))),128))]),F.value.length?(t(),m(`div`,je,[d(N,{label:e.$t(`activity.raw_lines`,{n:F.value.length}),dense:``},{default:o(()=>[(t(!0),m(h,null,r(F.value,(e,n)=>(t(),m(`div`,{key:n,class:`text-caption text-grey q-pa-xs`},c(e),1))),128))]),_:1},8,[`label`])])):u(``,!0)]),_:1},512),_(`div`,Me,[L.value?u(``,!0):(t(),l(T,{key:0,round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.scroll_to_bottom`),onClick:pe},null,8,[`title`])),d(T,{round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.prev_user_message`),onClick:ue},null,8,[`title`])])]))}}),[[`__scopeId`,`data-v-2cb1aa26`]]);export{Re as default};
@@ -1 +1 @@
1
- .markdown-message[data-v-e9f9bd11]{color:#e0e0e0;word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-e9f9bd11] *{max-width:100%}.markdown-message[data-v-e9f9bd11] p{margin:0 0 .5em}.markdown-message[data-v-e9f9bd11] p:last-child{margin-bottom:0}.markdown-message[data-v-e9f9bd11] pre{background:#00000059;border-radius:4px;margin:.5em 0;padding:.5em .75em;overflow-x:auto}.markdown-message[data-v-e9f9bd11] code{word-break:break-all;background:#0000004d;border-radius:3px;padding:.1em .3em;font-size:.9em}.markdown-message[data-v-e9f9bd11] pre code{background:0 0;padding:0}.markdown-message[data-v-e9f9bd11] ul,.markdown-message[data-v-e9f9bd11] ol{margin:.25em 0 .5em;padding-left:1.5em}.markdown-message[data-v-e9f9bd11] li{margin:.15em 0}.markdown-message[data-v-e9f9bd11] a{color:#7986cb;text-decoration:underline}.markdown-message[data-v-e9f9bd11] .document-link{color:#9fa8da;cursor:pointer;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-message[data-v-e9f9bd11] .document-link:hover{color:#c5cae9;-webkit-text-decoration:underline;text-decoration:underline}.markdown-message[data-v-e9f9bd11] h1,.markdown-message[data-v-e9f9bd11] h2,.markdown-message[data-v-e9f9bd11] h3,.markdown-message[data-v-e9f9bd11] h4,.markdown-message[data-v-e9f9bd11] h5,.markdown-message[data-v-e9f9bd11] h6{margin:.5em 0 .3em;font-weight:600;line-height:1.3}.markdown-message[data-v-e9f9bd11] h1{font-size:1.25em}.markdown-message[data-v-e9f9bd11] h2{font-size:1.15em}.markdown-message[data-v-e9f9bd11] h3{font-size:1.08em}.markdown-message[data-v-e9f9bd11] h4,.markdown-message[data-v-e9f9bd11] h5,.markdown-message[data-v-e9f9bd11] h6{font-size:1em}.markdown-message[data-v-e9f9bd11] blockquote{color:#ffffffb3;border-left:3px solid #fff3;margin:.5em 0;padding-left:.75em}.markdown-message[data-v-e9f9bd11] table{border-collapse:collapse;margin:.5em 0}.markdown-message[data-v-e9f9bd11] th,.markdown-message[data-v-e9f9bd11] td{border:1px solid #ffffff26;padding:.25em .5em}.markdown-thinking[data-v-4e64694c] p{margin:0 0 .4em}.markdown-thinking[data-v-4e64694c] p:last-child{margin-bottom:0}.markdown-thinking[data-v-4e64694c] code{background:#ffffff14;border-radius:3px;padding:.1em .3em}.tool-row[data-v-b1fcd20d]{border-radius:4px;margin:0;font-size:12px}.tool-header[data-v-b1fcd20d]{color:#bbb;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-b1fcd20d],.tool-row--toggleable .tool-header[data-v-b1fcd20d]{cursor:pointer}.tool-row:has(.tool-diff) .tool-header[data-v-b1fcd20d]{cursor:pointer}.tool-row:not(.tool-row-generic) .tool-header[data-v-b1fcd20d]:hover,.tool-row--toggleable .tool-header[data-v-b1fcd20d]:hover{background:#ffffff08}.tool-icon[data-v-b1fcd20d]{color:#9fbce0;flex-shrink:0}.tool-name[data-v-b1fcd20d]{color:#d0d0d0;flex-shrink:0;font-weight:600}.tool-arg[data-v-b1fcd20d],.tool-path[data-v-b1fcd20d]{color:#999;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11.5px;overflow:hidden}.tool-path[data-v-b1fcd20d],.tool-arg[data-v-b1fcd20d]{flex:1}.tool-stat-add[data-v-b1fcd20d]{color:#66bb6a;flex-shrink:0;font-size:11px;font-weight:600}.tool-stat-del[data-v-b1fcd20d]{color:#ef5350;flex-shrink:0;font-size:11px;font-weight:600}.tool-diff[data-v-b1fcd20d]{background:#0003;border-radius:4px;max-height:400px;margin-top:4px;padding:8px 0;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px;line-height:1.5;overflow:auto}.diff-line[data-v-b1fcd20d]{white-space:pre;color:#bbb;padding:0 12px}.diff-sign[data-v-b1fcd20d]{color:#555;-webkit-user-select:none;user-select:none;width:14px;display:inline-block}.diff-add[data-v-b1fcd20d]{color:#c8e6c9;background:#66bb6a1a}.diff-add .diff-sign[data-v-b1fcd20d]{color:#66bb6a}.diff-del[data-v-b1fcd20d]{color:#ffcdd2;background:#ef53501a}.diff-del .diff-sign[data-v-b1fcd20d]{color:#ef5350}.tool-output[data-v-b1fcd20d]{color:#aaa;white-space:pre-wrap;background:#00000026;border-radius:4px;max-height:8em;margin-top:4px;padding:6px 10px;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px;overflow:auto}.markdown-message[data-v-c2e7c407]{color:#e0e0e0;word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-c2e7c407] *{max-width:100%}.markdown-message[data-v-c2e7c407] 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-c2e7c407] code{word-break:break-all}.markdown-message[data-v-c2e7c407] p{margin:0 0 .4em}.markdown-message[data-v-c2e7c407] p:last-child{margin-bottom:0}.markdown-message[data-v-c2e7c407] code{background:#00000040;border-radius:3px;padding:.1em .3em}.markdown-message[data-v-c2e7c407] h1,.markdown-message[data-v-c2e7c407] h2,.markdown-message[data-v-c2e7c407] h3,.markdown-message[data-v-c2e7c407] h4,.markdown-message[data-v-c2e7c407] h5,.markdown-message[data-v-c2e7c407] h6{margin:.4em 0 .25em;font-weight:600;line-height:1.3}.markdown-message[data-v-c2e7c407] h1{font-size:1.25em}.markdown-message[data-v-c2e7c407] h2{font-size:1.15em}.markdown-message[data-v-c2e7c407] h3{font-size:1.08em}.markdown-message[data-v-c2e7c407] h4,.markdown-message[data-v-c2e7c407] h5,.markdown-message[data-v-c2e7c407] h6{font-size:1em}.markdown-user-prompt[data-v-c2e7c407]{color:#aaa;font-size:12px;font-style:italic}.markdown-user-prompt[data-v-c2e7c407] p{margin:0 0 .4em}.markdown-user-prompt[data-v-c2e7c407] 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-4729e0cc]{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-header[data-v-4729e0cc]{color:#888;background:#ffffff08;border-bottom:1px solid #ffffff0d;align-items:center;gap:8px;padding:8px 14px;font-size:11px;display:flex}.turn-badge[data-v-4729e0cc]{letter-spacing:.3px;border-radius:3px;padding:2px 8px;font-size:11px;font-weight:700}.turn-badge-user[data-v-4729e0cc]{color:#ce93d8;background:#ce93d826}.turn-badge-agent[data-v-4729e0cc]{color:#7986cb;background:#7986cb26}.turn-badge-system[data-v-4729e0cc]{color:#bdbdbd;background:#75757533;font-style:italic}.turn-badge-session[data-v-4729e0cc]{color:#9e9e9e;background:#61616133}.turn-time[data-v-4729e0cc]{color:#666;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px}.turn-time-arrow[data-v-4729e0cc]{opacity:.7;margin:0 -2px}.turn-time-updated[data-v-4729e0cc]{color:#8891a3}.turn-actions[data-v-4729e0cc]{color:#777;font-size:11px}.turn-body[data-v-4729e0cc]{flex-direction:column;gap:12px;min-width:0;padding:14px 18px;display:flex}.turn-body[data-v-4729e0cc]>*{min-width:0;max-width:100%}.turn-body[data-v-4729e0cc] .tool-row+.tool-row{margin-top:-8px}.turn-scroll-top[data-v-4729e0cc]{justify-content:flex-start;padding:0 8px 6px;display:flex}.turn-scroll-top-btn[data-v-4729e0cc]{opacity:.5;transition:opacity .15s}.turn-scroll-top-btn[data-v-4729e0cc]:hover{opacity:1}.activity-feed-wrap[data-v-890bdcbd]{width:100%;height:100%;position:relative}.activity-feed-scroll[data-v-890bdcbd]{width:100%;height:100%}.activity-feed-nav-cluster[data-v-890bdcbd]{z-index:2;align-items:center;gap:8px;display:flex;position:absolute;bottom:14px;right:14px}.activity-feed-nav-btn[data-v-890bdcbd]{opacity:.8;transition:opacity .12s}.activity-feed-nav-btn[data-v-890bdcbd]:hover{opacity:1}.content-origin-marker[data-v-890bdcbd]{pointer-events:none;width:0;height:0;margin:0;padding:0}.activity-feed-scroll[data-v-890bdcbd] .q-scrollarea__content{max-width:100%;overflow-x:hidden}.activity-feed-switching[data-v-890bdcbd]{justify-content:center;align-items:center;width:100%;height:100%;display:flex}
1
+ .markdown-message[data-v-1b7bd8ca]{color:#e0e0e0;word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-1b7bd8ca] *{max-width:100%}.markdown-message[data-v-1b7bd8ca] p{margin:0 0 .5em}.markdown-message[data-v-1b7bd8ca] p:last-child{margin-bottom:0}.markdown-message[data-v-1b7bd8ca] pre{background:#00000059;border-radius:4px;margin:.5em 0;padding:.5em .75em;overflow-x:auto}.markdown-message[data-v-1b7bd8ca] code{word-break:break-all;background:#0000004d;border-radius:3px;padding:.1em .3em;font-size:.9em}.markdown-message[data-v-1b7bd8ca] pre code{background:0 0;padding:0}.markdown-message[data-v-1b7bd8ca] ul,.markdown-message[data-v-1b7bd8ca] ol{margin:.25em 0 .5em;padding-left:1.5em}.markdown-message[data-v-1b7bd8ca] li{margin:.15em 0}.markdown-message[data-v-1b7bd8ca] a{color:#7986cb;text-decoration:underline}.markdown-message[data-v-1b7bd8ca] .document-link{color:#9fa8da;cursor:pointer;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-message[data-v-1b7bd8ca] .document-link:hover{color:#c5cae9;-webkit-text-decoration:underline;text-decoration:underline}.markdown-message[data-v-1b7bd8ca] h1,.markdown-message[data-v-1b7bd8ca] h2,.markdown-message[data-v-1b7bd8ca] h3,.markdown-message[data-v-1b7bd8ca] h4,.markdown-message[data-v-1b7bd8ca] h5,.markdown-message[data-v-1b7bd8ca] h6{margin:.5em 0 .3em;font-weight:600;line-height:1.3}.markdown-message[data-v-1b7bd8ca] h1{font-size:1.25em}.markdown-message[data-v-1b7bd8ca] h2{font-size:1.15em}.markdown-message[data-v-1b7bd8ca] h3{font-size:1.08em}.markdown-message[data-v-1b7bd8ca] h4,.markdown-message[data-v-1b7bd8ca] h5,.markdown-message[data-v-1b7bd8ca] h6{font-size:1em}.markdown-message[data-v-1b7bd8ca] blockquote{color:#ffffffb3;border-left:3px solid #fff3;margin:.5em 0;padding-left:.75em}.markdown-message[data-v-1b7bd8ca] table{border-collapse:collapse;margin:.5em 0}.markdown-message[data-v-1b7bd8ca] th,.markdown-message[data-v-1b7bd8ca] td{border:1px solid #ffffff26;padding:.25em .5em}.markdown-thinking[data-v-7f45ed94] p{margin:0 0 .4em}.markdown-thinking[data-v-7f45ed94] p:last-child{margin-bottom:0}.markdown-thinking[data-v-7f45ed94] code{background:#ffffff14;border-radius:3px;padding:.1em .3em}.tool-row[data-v-b1fcd20d]{border-radius:4px;margin:0;font-size:12px}.tool-header[data-v-b1fcd20d]{color:#bbb;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-b1fcd20d],.tool-row--toggleable .tool-header[data-v-b1fcd20d]{cursor:pointer}.tool-row:has(.tool-diff) .tool-header[data-v-b1fcd20d]{cursor:pointer}.tool-row:not(.tool-row-generic) .tool-header[data-v-b1fcd20d]:hover,.tool-row--toggleable .tool-header[data-v-b1fcd20d]:hover{background:#ffffff08}.tool-icon[data-v-b1fcd20d]{color:#9fbce0;flex-shrink:0}.tool-name[data-v-b1fcd20d]{color:#d0d0d0;flex-shrink:0;font-weight:600}.tool-arg[data-v-b1fcd20d],.tool-path[data-v-b1fcd20d]{color:#999;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11.5px;overflow:hidden}.tool-path[data-v-b1fcd20d],.tool-arg[data-v-b1fcd20d]{flex:1}.tool-stat-add[data-v-b1fcd20d]{color:#66bb6a;flex-shrink:0;font-size:11px;font-weight:600}.tool-stat-del[data-v-b1fcd20d]{color:#ef5350;flex-shrink:0;font-size:11px;font-weight:600}.tool-diff[data-v-b1fcd20d]{background:#0003;border-radius:4px;max-height:400px;margin-top:4px;padding:8px 0;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px;line-height:1.5;overflow:auto}.diff-line[data-v-b1fcd20d]{white-space:pre;color:#bbb;padding:0 12px}.diff-sign[data-v-b1fcd20d]{color:#555;-webkit-user-select:none;user-select:none;width:14px;display:inline-block}.diff-add[data-v-b1fcd20d]{color:#c8e6c9;background:#66bb6a1a}.diff-add .diff-sign[data-v-b1fcd20d]{color:#66bb6a}.diff-del[data-v-b1fcd20d]{color:#ffcdd2;background:#ef53501a}.diff-del .diff-sign[data-v-b1fcd20d]{color:#ef5350}.tool-output[data-v-b1fcd20d]{color:#aaa;white-space:pre-wrap;background:#00000026;border-radius:4px;max-height:8em;margin-top:4px;padding:6px 10px;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px;overflow:auto}.markdown-message[data-v-f34be4c5]{color:#e0e0e0;word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-f34be4c5] *{max-width:100%}.markdown-message[data-v-f34be4c5] 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-f34be4c5] code{word-break:break-all}.markdown-message[data-v-f34be4c5] p{margin:0 0 .4em}.markdown-message[data-v-f34be4c5] p:last-child{margin-bottom:0}.markdown-message[data-v-f34be4c5] code{background:#00000040;border-radius:3px;padding:.1em .3em}.markdown-message[data-v-f34be4c5] h1,.markdown-message[data-v-f34be4c5] h2,.markdown-message[data-v-f34be4c5] h3,.markdown-message[data-v-f34be4c5] h4,.markdown-message[data-v-f34be4c5] h5,.markdown-message[data-v-f34be4c5] h6{margin:.4em 0 .25em;font-weight:600;line-height:1.3}.markdown-message[data-v-f34be4c5] h1{font-size:1.25em}.markdown-message[data-v-f34be4c5] h2{font-size:1.15em}.markdown-message[data-v-f34be4c5] h3{font-size:1.08em}.markdown-message[data-v-f34be4c5] h4,.markdown-message[data-v-f34be4c5] h5,.markdown-message[data-v-f34be4c5] h6{font-size:1em}.markdown-user-prompt[data-v-f34be4c5]{color:#aaa;font-size:12px;font-style:italic}.markdown-user-prompt[data-v-f34be4c5] p{margin:0 0 .4em}.markdown-user-prompt[data-v-f34be4c5] 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-4729e0cc]{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-header[data-v-4729e0cc]{color:#888;background:#ffffff08;border-bottom:1px solid #ffffff0d;align-items:center;gap:8px;padding:8px 14px;font-size:11px;display:flex}.turn-badge[data-v-4729e0cc]{letter-spacing:.3px;border-radius:3px;padding:2px 8px;font-size:11px;font-weight:700}.turn-badge-user[data-v-4729e0cc]{color:#ce93d8;background:#ce93d826}.turn-badge-agent[data-v-4729e0cc]{color:#7986cb;background:#7986cb26}.turn-badge-system[data-v-4729e0cc]{color:#bdbdbd;background:#75757533;font-style:italic}.turn-badge-session[data-v-4729e0cc]{color:#9e9e9e;background:#61616133}.turn-time[data-v-4729e0cc]{color:#666;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px}.turn-time-arrow[data-v-4729e0cc]{opacity:.7;margin:0 -2px}.turn-time-updated[data-v-4729e0cc]{color:#8891a3}.turn-actions[data-v-4729e0cc]{color:#777;font-size:11px}.turn-body[data-v-4729e0cc]{flex-direction:column;gap:12px;min-width:0;padding:14px 18px;display:flex}.turn-body[data-v-4729e0cc]>*{min-width:0;max-width:100%}.turn-body[data-v-4729e0cc] .tool-row+.tool-row{margin-top:-8px}.turn-scroll-top[data-v-4729e0cc]{justify-content:flex-start;padding:0 8px 6px;display:flex}.turn-scroll-top-btn[data-v-4729e0cc]{opacity:.5;transition:opacity .15s}.turn-scroll-top-btn[data-v-4729e0cc]:hover{opacity:1}.activity-feed-wrap[data-v-2cb1aa26]{width:100%;height:100%;position:relative}.activity-feed-scroll[data-v-2cb1aa26]{width:100%;height:100%}.activity-feed-nav-cluster[data-v-2cb1aa26]{z-index:2;align-items:center;gap:8px;display:flex;position:absolute;bottom:14px;right:14px}.activity-feed-nav-btn[data-v-2cb1aa26]{opacity:.8;transition:opacity .12s}.activity-feed-nav-btn[data-v-2cb1aa26]:hover{opacity:1}.content-origin-marker[data-v-2cb1aa26]{pointer-events:none;width:0;height:0;margin:0;padding:0}.activity-feed-scroll[data-v-2cb1aa26] .q-scrollarea__content{max-width:100%;overflow-x:hidden}.activity-feed-switching[data-v-2cb1aa26]{justify-content:center;align-items:center;width:100%;height:100%;display:flex}
@@ -1 +1 @@
1
- import{F as e,b as t}from"./QIcon-BJuyqdsT.js";import{C as n,w as r}from"./notifications-CG-oL2m2.js";function i(e){if(e===!1)return 0;if(e===!0||e===void 0)return 1;let t=parseInt(e,10);return isNaN(t)?0:t}var a=e({name:`close-popup`,beforeMount(e,{value:a}){let o={depth:i(a),handler(t){o.depth!==0&&setTimeout(()=>{let i=r(e);i!==void 0&&n(i,t,o.depth)})},handlerKey(e){t(e,13)===!0&&o.handler(e)}};e.__qclosepopup=o,e.addEventListener(`click`,o.handler),e.addEventListener(`keyup`,o.handlerKey)},updated(e,{value:t,oldValue:n}){t!==n&&(e.__qclosepopup.depth=i(t))},beforeUnmount(e){let t=e.__qclosepopup;e.removeEventListener(`click`,t.handler),e.removeEventListener(`keyup`,t.handlerKey),delete e.__qclosepopup}});export{a as t};
1
+ import{F as e,b as t}from"./QIcon-BJuyqdsT.js";import{C as n,w as r}from"./notifications-OnPq4FrH.js";function i(e){if(e===!1)return 0;if(e===!0||e===void 0)return 1;let t=parseInt(e,10);return isNaN(t)?0:t}var a=e({name:`close-popup`,beforeMount(e,{value:a}){let o={depth:i(a),handler(t){o.depth!==0&&setTimeout(()=>{let i=r(e);i!==void 0&&n(i,t,o.depth)})},handlerKey(e){t(e,13)===!0&&o.handler(e)}};e.__qclosepopup=o,e.addEventListener(`click`,o.handler),e.addEventListener(`keyup`,o.handlerKey)},updated(e,{value:t,oldValue:n}){t!==n&&(e.__qclosepopup.depth=i(t))},beforeUnmount(e){let t=e.__qclosepopup;e.removeEventListener(`click`,t.handler),e.removeEventListener(`keyup`,t.handlerKey),delete e.__qclosepopup}});export{a as t};