@loicngr/kobo 2.1.5 → 2.1.7

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 (105) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/mcp-server/kobo-tasks-handlers.js +17 -4
  3. package/dist/mcp-server/kobo-tasks-server.js +14 -2
  4. package/dist/server/routes/workspaces.js +45 -34
  5. package/dist/server/services/agent/engines/claude-code/event-mapper.js +33 -0
  6. package/dist/server/services/agent/engines/codex/engine.js +10 -1
  7. package/dist/server/services/agent/engines/codex/event-mapper.js +2 -0
  8. package/dist/server/services/agent/streaming-batcher.js +14 -3
  9. package/dist/server/services/dev-server-service.js +7 -1
  10. package/dist/shared/codex-models.js +0 -48
  11. package/dist/shared/models.js +6 -54
  12. package/package.json +6 -6
  13. package/src/client/dist/pwa/assets/ActivityFeed-B8tJgXwD.js +2 -0
  14. package/src/client/dist/pwa/assets/ActivityFeed-BfMUdSXQ.css +1 -0
  15. package/src/client/dist/pwa/assets/{ChangelogPage-BL5Jh8-v.js → ChangelogPage-YjWYsB_s.js} +1 -1
  16. package/src/client/dist/pwa/assets/{ComparisonPanel-Bv2SZrVZ.js → ComparisonPanel-azVvpfGW.js} +1 -1
  17. package/src/client/dist/pwa/assets/CreatePage-BVNtCTdA.js +2 -0
  18. package/src/client/dist/pwa/assets/CreatePage-C7fLOpai.css +1 -0
  19. package/src/client/dist/pwa/assets/{DashboardPage-BEXXRguE.js → DashboardPage-DydscFLA.js} +1 -1
  20. package/src/client/dist/pwa/assets/{DiffViewer-B92w_jd7.js → DiffViewer-DfMqxOYw.js} +3 -3
  21. package/src/client/dist/pwa/assets/DocumentsPanel-DCGgj9pc.js +1 -0
  22. package/src/client/dist/pwa/assets/{DrawerToggleButton-Btx8QwHo.js → DrawerToggleButton-CQVSfKpY.js} +1 -1
  23. package/src/client/dist/pwa/assets/GitPanel-D6ZCZPbj.js +3 -0
  24. package/src/client/dist/pwa/assets/{HealthPage-CDhgJQkL.js → HealthPage-D_A6DTiH.js} +1 -1
  25. package/src/client/dist/pwa/assets/MainLayout-CFLswVqP.css +1 -0
  26. package/src/client/dist/pwa/assets/MainLayout-CeHg73-f.js +2 -0
  27. package/src/client/dist/pwa/assets/{QBanner-7EWRdc4U.js → QBanner-CtppfNDn.js} +1 -1
  28. package/src/client/dist/pwa/assets/{QBtnDropdown-vjat4GZl.js → QBtnDropdown-D8I04HPE.js} +1 -1
  29. package/src/client/dist/pwa/assets/{QChip-CG1kWhFm.js → QChip-Btg1Lp_6.js} +1 -1
  30. package/src/client/dist/pwa/assets/{QExpansionItem-BQuy89vv.js → QExpansionItem-DmzCAbka.js} +1 -1
  31. package/src/client/dist/pwa/assets/{QItemSection-BrVklYfM.js → QItemSection-BhGnulVQ.js} +1 -1
  32. package/src/client/dist/pwa/assets/{QList-BVX-b8nc.js → QList-Cw2xs69W.js} +1 -1
  33. package/src/client/dist/pwa/assets/{QMarkupTable-B8_FKLvo.js → QMarkupTable-Ck2E2TaI.js} +1 -1
  34. package/src/client/dist/pwa/assets/QScrollArea-BmBuij9J.js +1 -0
  35. package/src/client/dist/pwa/assets/{QSelect-DBW_TZri.js → QSelect-DUuWu79L.js} +1 -1
  36. package/src/client/dist/pwa/assets/{QTooltip-DzYbguTR.js → QTooltip-Dhy3hNTO.js} +1 -1
  37. package/src/client/dist/pwa/assets/{SchedulePanel-BX6iKh6S.js → SchedulePanel-BCiV6rMr.js} +1 -1
  38. package/src/client/dist/pwa/assets/{SearchPage-_In2gH2P.js → SearchPage-of0gPqV4.js} +1 -1
  39. package/src/client/dist/pwa/assets/{SettingsPage-BqkNYzC5.js → SettingsPage-BPg3-Lhb.js} +1 -1
  40. package/src/client/dist/pwa/assets/{SplitWorkspacePage-DRoXbSRP.js → SplitWorkspacePage-BcdPVE9B.js} +1 -1
  41. package/src/client/dist/pwa/assets/{TerminalPanel-CfqiuSuk.js → TerminalPanel-auHWDK9i.js} +1 -1
  42. package/src/client/dist/pwa/assets/ToolCallItem-BQmqNcfF.js +8 -0
  43. package/src/client/dist/pwa/assets/ToolCallItem-DhjdG8vp.css +1 -0
  44. package/src/client/dist/pwa/assets/{TouchPan-DcOCRf7x.js → TouchPan-DKpZbMAf.js} +1 -1
  45. package/src/client/dist/pwa/assets/{TourReplayButton-gp7m6tSV.js → TourReplayButton-uCZpvYlj.js} +1 -1
  46. package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-IcoMGkJh.js → WorkspaceAttentionLabels-jDKgkf3G.js} +1 -1
  47. package/src/client/dist/pwa/assets/{WorkspacePage-BnW-gp3u.css → WorkspacePage-Ba0jAHhn.css} +1 -1
  48. package/src/client/dist/pwa/assets/WorkspacePage-DQ-jvLEj.js +4 -0
  49. package/src/client/dist/pwa/assets/{browser-BveHkbe-.js → browser-CU7taghA.js} +1 -1
  50. package/src/client/dist/pwa/assets/{build-path-tree-Dtp7rlxS.js → build-path-tree-fw6KlTB4.js} +1 -1
  51. package/src/client/dist/pwa/assets/{custom-sounds-CBoWzJ85.js → custom-sounds-Dfi-grUv.js} +1 -1
  52. package/src/client/dist/pwa/assets/{de-BJsWkZro.js → de-C_20Oxyv.js} +2 -2
  53. package/src/client/dist/pwa/assets/{editor.api-CbkPxoJf.js → editor.api-CBaHnF7Q.js} +1 -1
  54. package/src/client/dist/pwa/assets/{efforts-BCli1Unj.js → efforts-CNJXztO0.js} +1 -1
  55. package/src/client/dist/pwa/assets/{es-tNYw7itB.js → es-B3_j4CWr.js} +2 -2
  56. package/src/client/dist/pwa/assets/{expand-template-CfaEhb2f.js → expand-template-BtpC0GKE.js} +1 -1
  57. package/src/client/dist/pwa/assets/{format-BRl2DUlm.js → format-MtzcnR3z.js} +1 -1
  58. package/src/client/dist/pwa/assets/{formatters-C5TjPSR_.js → formatters-BZm4POkA.js} +1 -1
  59. package/src/client/dist/pwa/assets/{fr-B5sFski4.js → fr-DbPOG-xd.js} +2 -2
  60. package/src/client/dist/pwa/assets/{freemarker2-CDmvOQyn.js → freemarker2-BtVwKuc0.js} +1 -1
  61. package/src/client/dist/pwa/assets/{handlebars-Bx_w8mx1.js → handlebars-Bhl5yJrV.js} +1 -1
  62. package/src/client/dist/pwa/assets/{html-DVRfiRkS.js → html-DKXLlkkM.js} +1 -1
  63. package/src/client/dist/pwa/assets/i18n-B9eF3pgw.js +1 -0
  64. package/src/client/dist/pwa/assets/{i18n-D21JKsVt.js → i18n-CPFleoLe.js} +4 -4
  65. package/src/client/dist/pwa/assets/{index-C7vFf6p2.js → index-BVJWwOvK.js} +2 -2
  66. package/src/client/dist/pwa/assets/{it-Bc2FncM9.js → it--6niM3qU.js} +2 -2
  67. package/src/client/dist/pwa/assets/{javascript-CPvttU5J.js → javascript-Cdfy_faM.js} +1 -1
  68. package/src/client/dist/pwa/assets/{latest-request-CN4-B22z.js → latest-request-7rgsEfKZ.js} +1 -1
  69. package/src/client/dist/pwa/assets/{liquid-CI0gQddb.js → liquid-0NoEATfB.js} +1 -1
  70. package/src/client/dist/pwa/assets/{mdx-BveBwYJH.js → mdx-hvP0EAwa.js} +1 -1
  71. package/src/client/dist/pwa/assets/{monaco-contributions-CHXFh4P2.js → monaco-contributions-Be_o6s3m.js} +1 -1
  72. package/src/client/dist/pwa/assets/{monaco.contribution-DOK8t_9B.js → monaco.contribution-DRQLCBp8.js} +2 -2
  73. package/src/client/dist/pwa/assets/{network-auth-CItnNZDh.js → network-auth-DVXmkvPH.js} +1 -1
  74. package/src/client/dist/pwa/assets/{notify-theme-BO6r1Rd7.js → notify-theme-I_sWGwy6.js} +1 -1
  75. package/src/client/dist/pwa/assets/{permissionModes-Cc5VaOdC.js → permissionModes-BDi77lhg.js} +1 -1
  76. package/src/client/dist/pwa/assets/preload-helper-DjzSQ1t3.js +1 -0
  77. package/src/client/dist/pwa/assets/{project-color-Bnv02P9D.js → project-color-i2bg2TfP.js} +1 -1
  78. package/src/client/dist/pwa/assets/{python-BesLCxpi.js → python-Bz-bbYMw.js} +1 -1
  79. package/src/client/dist/pwa/assets/{razor-Dv1PMxuY.js → razor-CyafkF-1.js} +1 -1
  80. package/src/client/dist/pwa/assets/{typescript-26L2Xsu9.js → typescript-BSLweq4l.js} +1 -1
  81. package/src/client/dist/pwa/assets/{use-checkbox-BLeif5-t.js → use-checkbox-BAt9fjK-.js} +1 -1
  82. package/src/client/dist/pwa/assets/{use-search-index-status-Mv3pIO6p.js → use-search-index-status-BzsGSHGh.js} +1 -1
  83. package/src/client/dist/pwa/assets/{use-tours-B5uIqxRS.js → use-tours-CM4E53aB.js} +1 -1
  84. package/src/client/dist/pwa/assets/use-worktree-restore-C__ResAR.js +1 -0
  85. package/src/client/dist/pwa/assets/{workspace-templates-CDY-MiH2.js → workspace-templates-TOa0yVyr.js} +1 -1
  86. package/src/client/dist/pwa/assets/{xml-CkRojTcc.js → xml-DOY7F605.js} +1 -1
  87. package/src/client/dist/pwa/assets/{yaml-DKkQHwew.js → yaml-CnWF0ZRJ.js} +1 -1
  88. package/src/client/dist/pwa/index.html +5 -5
  89. package/src/client/dist/pwa/sw.js +1 -1
  90. package/src/mcp-server/kobo-tasks-handlers.ts +24 -9
  91. package/src/mcp-server/kobo-tasks-server.ts +14 -2
  92. package/src/client/dist/pwa/assets/ActivityFeed-B4lTKMVE.css +0 -1
  93. package/src/client/dist/pwa/assets/ActivityFeed-DXWXioW9.js +0 -8
  94. package/src/client/dist/pwa/assets/CreatePage-CMyl2j8Z.css +0 -1
  95. package/src/client/dist/pwa/assets/CreatePage-d43ITOC1.js +0 -2
  96. package/src/client/dist/pwa/assets/DocumentsPanel-C9JXyqHg.js +0 -1
  97. package/src/client/dist/pwa/assets/GitPanel-Cgz9crLN.js +0 -3
  98. package/src/client/dist/pwa/assets/MainLayout-DTTtj9sq.js +0 -2
  99. package/src/client/dist/pwa/assets/MainLayout-knNNtkre.css +0 -1
  100. package/src/client/dist/pwa/assets/WorkspacePage-CR2xAzW_.js +0 -4
  101. package/src/client/dist/pwa/assets/documents-sb2sQ4JC.js +0 -1
  102. package/src/client/dist/pwa/assets/i18n-Bm-L4nW3.js +0 -1
  103. package/src/client/dist/pwa/assets/pending-diff-open-DpS4iyDM.js +0 -1
  104. package/src/client/dist/pwa/assets/preload-helper-BZ1Pz5am.js +0 -1
  105. package/src/client/dist/pwa/assets/use-worktree-restore-BBChALUM.js +0 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ All notable changes to Kōbō are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/). Each release is an `## <version>`
5
5
  section — the in-app "What's new" dialog reads this file.
6
6
 
7
+ ## 2.1.7
8
+
9
+ - feat: improve workspace agent visibility
10
+ - fix(dev-server): tolerate EPERM process probes
11
+
12
+ ## 2.1.6
13
+
14
+ - feat(subagents): add focused activity views
15
+
7
16
  ## 2.1.5
8
17
 
9
18
  - fix(workspace): improve reliability and git controls
@@ -31,6 +31,15 @@ export function listTasksHandler(db, workspaceId) {
31
31
  .all(workspaceId);
32
32
  return rows.map(rowToDto);
33
33
  }
34
+ function parseWorkspaceTags(raw) {
35
+ try {
36
+ const tags = JSON.parse(raw);
37
+ return Array.isArray(tags) ? tags.filter((tag) => typeof tag === 'string') : [];
38
+ }
39
+ catch {
40
+ return [];
41
+ }
42
+ }
34
43
  /**
35
44
  * List workspaces for the global `list_workspaces` MCP tool. Reads SQLite
36
45
  * directly (no backend HTTP dependency) so it works even when the Kōbō
@@ -39,15 +48,19 @@ export function listTasksHandler(db, workspaceId) {
39
48
  export function listWorkspacesHandler(db, opts) {
40
49
  // updated_at can collide at second resolution (e.g. bulk-seeded test data); rowid DESC breaks ties, most-recently-inserted first.
41
50
  const sql = opts.includeArchived
42
- ? 'SELECT id, name, status, created_at FROM workspaces ORDER BY updated_at DESC, rowid DESC'
43
- : 'SELECT id, name, status, created_at FROM workspaces WHERE archived_at IS NULL ORDER BY updated_at DESC, rowid DESC';
51
+ ? 'SELECT id, name, status, tags, created_at FROM workspaces ORDER BY updated_at DESC, rowid DESC'
52
+ : 'SELECT id, name, status, tags, created_at FROM workspaces WHERE archived_at IS NULL ORDER BY updated_at DESC, rowid DESC';
44
53
  const rows = db.prepare(sql).all();
45
- return rows.map((row) => ({
54
+ const tag = opts.tag?.trim();
55
+ return rows
56
+ .map((row) => ({
46
57
  id: row.id,
47
58
  title: row.name,
48
59
  status: row.status,
60
+ tags: parseWorkspaceTags(row.tags),
49
61
  createdAt: row.created_at,
50
- }));
62
+ }))
63
+ .filter((workspace) => !tag || workspace.tags.includes(tag));
51
64
  }
52
65
  /**
53
66
  * Flip the workspace's `auto_loop_ready` flag. Called at the end of a
@@ -536,7 +536,7 @@ const GLOBAL_TOOLS = [
536
536
  ...WORKSPACE_DIALOGUE_TOOLS,
537
537
  {
538
538
  name: 'list_workspaces',
539
- description: 'List Kōbō workspaces with their id, title, status, and creation date. Works even when the Kōbō backend server is not running (reads the database directly). Use this to discover existing workspaces before creating one or targeting one with archive_workspace/stop_workspace.',
539
+ description: 'List Kōbō workspaces with their id, title, status, tags, and creation date. Set tag to return only workspaces carrying that exact tag. Works even when the Kōbō backend server is not running (reads the database directly). Use this to discover existing workspaces before creating one or targeting one with archive_workspace/stop_workspace.',
540
540
  inputSchema: {
541
541
  type: 'object',
542
542
  properties: {
@@ -544,6 +544,11 @@ const GLOBAL_TOOLS = [
544
544
  type: 'boolean',
545
545
  description: 'Include archived workspaces (default false).',
546
546
  },
547
+ tag: {
548
+ type: 'string',
549
+ minLength: 1,
550
+ description: 'Return only workspaces with this exact tag, for example "API".',
551
+ },
547
552
  },
548
553
  required: [],
549
554
  },
@@ -566,6 +571,11 @@ const GLOBAL_TOOLS = [
566
571
  reasoning_effort: { type: 'string', description: 'Reasoning effort override (optional).' },
567
572
  engine: { type: 'string', description: 'Agent engine id, e.g. "claude-code" or "codex" (optional).' },
568
573
  description: { type: 'string', description: 'Task description / initial brainstorming prompt (optional).' },
574
+ tags: {
575
+ type: 'array',
576
+ items: { type: 'string' },
577
+ description: 'Workspace tags to assign at creation (optional).',
578
+ },
569
579
  tasks: { type: 'array', items: { type: 'string' }, description: 'Initial manual task titles (optional).' },
570
580
  acceptance_criteria: {
571
581
  type: 'array',
@@ -938,7 +948,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
938
948
  }
939
949
  if (name === 'list_workspaces') {
940
950
  const includeArchived = a.include_archived === true;
941
- return ok(listWorkspacesHandler(db, { includeArchived }));
951
+ const tag = typeof a.tag === 'string' ? a.tag : undefined;
952
+ return ok(listWorkspacesHandler(db, { includeArchived, tag }));
942
953
  }
943
954
  if (name === 'create_workspace') {
944
955
  const workspaceName = a.name;
@@ -958,6 +969,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
958
969
  reasoningEffort: a.reasoning_effort,
959
970
  engine: a.engine,
960
971
  description: a.description,
972
+ tags: a.tags,
961
973
  tasks: a.tasks,
962
974
  acceptanceCriteria: a.acceptance_criteria,
963
975
  agentPermissionMode: a.agent_permission_mode,
@@ -492,6 +492,9 @@ app.post('/', migrationGuard, creationBodyLimit, async (c) => {
492
492
  if (body.comparisonId !== undefined && typeof body.comparisonId !== 'string') {
493
493
  return c.json({ error: 'comparisonId must be a string' }, 400);
494
494
  }
495
+ if (body.tags !== undefined && (!Array.isArray(body.tags) || body.tags.some((tag) => typeof tag !== 'string'))) {
496
+ return c.json({ error: 'tags must be an array of strings' }, 400);
497
+ }
495
498
  creationId = typeof body.creationId === 'string' && body.creationId.length > 0 ? body.creationId : undefined;
496
499
  currentStep = 'validate';
497
500
  emitCreateProgress(creationId, currentStep);
@@ -841,7 +844,7 @@ app.post('/', migrationGuard, creationBodyLimit, async (c) => {
841
844
  // Skip any tag the user has removed from the catalogue so we respect
842
845
  // their choice (they may have pruned "notion"/"sentry" on purpose).
843
846
  const catalogTags = new Set(globalSettings.tags ?? []);
844
- const autoTags = [];
847
+ const autoTags = [...(body.tags ?? [])];
845
848
  if (body.notionUrl && catalogTags.has('notion'))
846
849
  autoTags.push('notion');
847
850
  if (body.sentryUrl && catalogTags.has('sentry'))
@@ -2418,8 +2421,34 @@ app.get('/:id/events', (c) => {
2418
2421
  // optional: scope to a session view. Session views also include
2419
2422
  // workspace-level rows where session_id IS NULL (legacy/pre-session items).
2420
2423
  const session = c.req.query('session');
2424
+ // Optional, narrow history to one child. Nested tool calls keep their
2425
+ // parent origin, so match both that origin and their own toolCallId.
2426
+ const subagentToolCallId = c.req.query('subagentToolCallId');
2427
+ const subagentThreadIds = (c.req.query('subagentThreadIds') ?? '')
2428
+ .split(',')
2429
+ .map((threadId) => threadId.trim())
2430
+ .filter(Boolean)
2431
+ .slice(0, 50);
2421
2432
  const parsedLimit = parseInt(c.req.query('limit') ?? '100', 10);
2422
2433
  const limit = Math.max(1, Math.min(Number.isFinite(parsedLimit) ? parsedLimit : 100, 500));
2434
+ const sessionClause = session ? ' AND (session_id = ? OR session_id IS NULL)' : '';
2435
+ const sessionParams = session ? [session] : [];
2436
+ const originPayload = "CASE WHEN json_valid(payload) THEN payload ELSE '{}' END";
2437
+ const originClauses = [];
2438
+ const originParams = [];
2439
+ if (subagentToolCallId) {
2440
+ originClauses.push(`(json_extract(${originPayload}, '$.origin.toolCallId') = ? OR json_extract(${originPayload}, '$.toolCallId') = ?)`);
2441
+ originParams.push(subagentToolCallId, subagentToolCallId);
2442
+ }
2443
+ if (subagentThreadIds.length > 0) {
2444
+ originClauses.push(`json_extract(${originPayload}, '$.origin.threadId') IN (${subagentThreadIds.map(() => '?').join(', ')})`);
2445
+ originParams.push(...subagentThreadIds);
2446
+ }
2447
+ const originClause = originClauses.length
2448
+ ? ` AND type = 'agent:event' AND json_extract(${originPayload}, '$.origin.kind') = 'subagent' AND (${originClauses.join(' OR ')})`
2449
+ : '';
2450
+ const scopedClause = `${sessionClause}${originClause}`;
2451
+ const scopedParams = [...sessionParams, ...originParams];
2423
2452
  const db = getDb();
2424
2453
  let rows;
2425
2454
  if (around) {
@@ -2427,13 +2456,9 @@ app.get('/:id/events', (c) => {
2427
2456
  if (!targetRow)
2428
2457
  return c.json({ events: [], hasMore: false });
2429
2458
  const halfWindow = Math.floor(limit / 2);
2430
- rows = session
2431
- ? db
2432
- .prepare('SELECT * FROM ws_events WHERE workspace_id = ? AND (session_id = ? OR session_id IS NULL) AND rowid BETWEEN ? AND ? ORDER BY rowid ASC')
2433
- .all(id, session, targetRow.rowid - halfWindow, targetRow.rowid + halfWindow)
2434
- : db
2435
- .prepare('SELECT * FROM ws_events WHERE workspace_id = ? AND rowid BETWEEN ? AND ? ORDER BY rowid ASC')
2436
- .all(id, targetRow.rowid - halfWindow, targetRow.rowid + halfWindow);
2459
+ rows = db
2460
+ .prepare(`SELECT * FROM ws_events WHERE workspace_id = ?${scopedClause} AND rowid BETWEEN ? AND ? ORDER BY rowid ASC`)
2461
+ .all(id, ...scopedParams, targetRow.rowid - halfWindow, targetRow.rowid + halfWindow);
2437
2462
  }
2438
2463
  else if (before) {
2439
2464
  // Get the rowid of the cursor event
@@ -2441,25 +2466,17 @@ app.get('/:id/events', (c) => {
2441
2466
  if (!cursorRow) {
2442
2467
  return c.json({ events: [], hasMore: false });
2443
2468
  }
2444
- rows = session
2445
- ? db
2446
- .prepare('SELECT * FROM ws_events WHERE workspace_id = ? AND (session_id = ? OR session_id IS NULL) AND rowid < ? ORDER BY rowid DESC LIMIT ?')
2447
- .all(id, session, cursorRow.rowid, limit)
2448
- : db
2449
- .prepare('SELECT * FROM ws_events WHERE workspace_id = ? AND rowid < ? ORDER BY rowid DESC LIMIT ?')
2450
- .all(id, cursorRow.rowid, limit);
2469
+ rows = db
2470
+ .prepare(`SELECT * FROM ws_events WHERE workspace_id = ?${scopedClause} AND rowid < ? ORDER BY rowid DESC LIMIT ?`)
2471
+ .all(id, ...scopedParams, cursorRow.rowid, limit);
2451
2472
  }
2452
2473
  else {
2453
2474
  // No cursor — return events. When filtering by session, we want the
2454
2475
  // MOST RECENT events of that session first (so the feed renders from
2455
2476
  // the end), reversed to chronological order below.
2456
- rows = session
2457
- ? db
2458
- .prepare('SELECT * FROM ws_events WHERE workspace_id = ? AND (session_id = ? OR session_id IS NULL) ORDER BY rowid DESC LIMIT ?')
2459
- .all(id, session, limit)
2460
- : db
2461
- .prepare('SELECT * FROM ws_events WHERE workspace_id = ? ORDER BY rowid DESC LIMIT ?')
2462
- .all(id, limit);
2477
+ rows = db
2478
+ .prepare(`SELECT * FROM ws_events WHERE workspace_id = ?${scopedClause} ORDER BY rowid DESC LIMIT ?`)
2479
+ .all(id, ...scopedParams, limit);
2463
2480
  }
2464
2481
  // Reverse to chronological order (we queried DESC for "before" pagination,
2465
2482
  // or for the "session + no cursor" case where we fetched the newest first).
@@ -2488,13 +2505,9 @@ app.get('/:id/events', (c) => {
2488
2505
  if (before || around) {
2489
2506
  const firstRow = db.prepare('SELECT rowid FROM ws_events WHERE id = ?').get(rows[0].id);
2490
2507
  if (firstRow) {
2491
- const older = session
2492
- ? db
2493
- .prepare('SELECT 1 as c FROM ws_events WHERE workspace_id = ? AND (session_id = ? OR session_id IS NULL) AND rowid < ? LIMIT 1')
2494
- .get(id, session, firstRow.rowid)
2495
- : db
2496
- .prepare('SELECT 1 as c FROM ws_events WHERE workspace_id = ? AND rowid < ? LIMIT 1')
2497
- .get(id, firstRow.rowid);
2508
+ const older = db
2509
+ .prepare(`SELECT 1 as c FROM ws_events WHERE workspace_id = ?${scopedClause} AND rowid < ? LIMIT 1`)
2510
+ .get(id, ...scopedParams, firstRow.rowid);
2498
2511
  hasMore = older !== undefined;
2499
2512
  }
2500
2513
  }
@@ -2502,11 +2515,9 @@ app.get('/:id/events', (c) => {
2502
2515
  // `LIMIT 1 OFFSET <page size>` answers "is there anything past this
2503
2516
  // page?" by reading one row, where COUNT(*) walked every event the
2504
2517
  // workspace ever produced — on each page, synchronously.
2505
- const beyond = session
2506
- ? db
2507
- .prepare('SELECT 1 as c FROM ws_events WHERE workspace_id = ? AND (session_id = ? OR session_id IS NULL) LIMIT 1 OFFSET ?')
2508
- .get(id, session, rows.length)
2509
- : db.prepare('SELECT 1 as c FROM ws_events WHERE workspace_id = ? LIMIT 1 OFFSET ?').get(id, rows.length);
2518
+ const beyond = db
2519
+ .prepare(`SELECT 1 as c FROM ws_events WHERE workspace_id = ?${scopedClause} LIMIT 1 OFFSET ?`)
2520
+ .get(id, ...scopedParams, rows.length);
2510
2521
  hasMore = beyond !== undefined;
2511
2522
  }
2512
2523
  }
@@ -94,6 +94,8 @@ const TASK_UPDATED_TERMINAL_STATUS = {
94
94
  };
95
95
  export function createMapperState() {
96
96
  return {
97
+ childMessageOrigins: new Map(),
98
+ childToolOrigins: new Map(),
97
99
  sessionStartedEmitted: false,
98
100
  openMessages: new Map(),
99
101
  sawErrorResult: false,
@@ -147,6 +149,37 @@ function tryEmitQuotaError(state, events, message) {
147
149
  * `state` as needed.
148
150
  */
149
151
  export function mapSdkMessage(msg, state) {
152
+ const parsed = msg;
153
+ const parentId = typeof parsed.parent_tool_use_id === 'string' ? parsed.parent_tool_use_id : undefined;
154
+ const origin = parentId ? { kind: 'subagent', toolCallId: parentId } : undefined;
155
+ const streamed = parsed.event;
156
+ if (origin && streamed?.type === 'message_start' && streamed.message?.id) {
157
+ state.childMessageOrigins.set(streamed.message.id, origin);
158
+ }
159
+ const events = mapSdkMessagePayload(msg, state);
160
+ return events.map((event) => {
161
+ if (event.kind === 'message:text' || event.kind === 'message:thinking' || event.kind === 'message:end') {
162
+ const eventOrigin = origin ?? state.childMessageOrigins.get(event.messageId);
163
+ if (origin)
164
+ state.childMessageOrigins.set(event.messageId, origin);
165
+ if (event.kind === 'message:end')
166
+ state.childMessageOrigins.delete(event.messageId);
167
+ return eventOrigin ? { ...event, origin: eventOrigin } : event;
168
+ }
169
+ if (event.kind === 'tool:call') {
170
+ if (origin)
171
+ state.childToolOrigins.set(event.toolCallId, origin);
172
+ return origin ? { ...event, origin } : event;
173
+ }
174
+ if (event.kind === 'tool:result') {
175
+ const eventOrigin = origin ?? state.childToolOrigins.get(event.toolCallId);
176
+ state.childToolOrigins.delete(event.toolCallId);
177
+ return eventOrigin ? { ...event, origin: eventOrigin } : event;
178
+ }
179
+ return event;
180
+ });
181
+ }
182
+ function mapSdkMessagePayload(msg, state) {
150
183
  // Treat as a generic record — the SDK discriminated union is too broad to
151
184
  // narrow per branch here.
152
185
  const parsed = msg;
@@ -276,7 +276,16 @@ export function createCodexEngine() {
276
276
  }
277
277
  // Child output remains visible, but its failures/compaction/markers
278
278
  // cannot control the parent session or its auto-loop lifecycle.
279
- return map(state).filter((event) => event.kind !== 'error' && !event.kind.startsWith('session:'));
279
+ return map(state)
280
+ .filter((event) => event.kind !== 'error' && !event.kind.startsWith('session:'))
281
+ .map((event) => {
282
+ const origin = { kind: 'subagent', threadId: notification.threadId };
283
+ if (event.kind === 'message:text' || event.kind === 'message:thinking' || event.kind === 'message:end')
284
+ return { ...event, origin };
285
+ if (event.kind === 'tool:call' || event.kind === 'tool:result')
286
+ return { ...event, origin };
287
+ return event;
288
+ });
280
289
  };
281
290
  const itemKey = (threadId, turnId, itemId) => JSON.stringify([threadId, turnId, itemId]);
282
291
  const client = createAppServerClient({
@@ -134,6 +134,7 @@ export function handleItemStarted(item, state) {
134
134
  events.push({
135
135
  kind: 'subagent:progress',
136
136
  toolCallId,
137
+ threadIds: item.receiverThreadIds,
137
138
  status: 'running',
138
139
  description: item.prompt ?? item.tool,
139
140
  taskType: item.tool,
@@ -271,6 +272,7 @@ export function handleItemCompleted(item, state) {
271
272
  events.push({
272
273
  kind: 'subagent:progress',
273
274
  toolCallId,
275
+ threadIds: item.receiverThreadIds,
274
276
  status: hasActiveAgent ? 'running' : 'done',
275
277
  description: item.prompt ?? item.tool,
276
278
  taskType: item.tool,
@@ -8,19 +8,30 @@ export function createStreamingBatcher(emit, options = {}) {
8
8
  return;
9
9
  clearTimeout(entry.timer);
10
10
  pending.delete(messageId);
11
- emit({ kind: 'message:text', messageId, text: entry.text, streaming: true });
11
+ emit({
12
+ kind: 'message:text',
13
+ messageId,
14
+ text: entry.text,
15
+ streaming: true,
16
+ ...(entry.origin ? { origin: entry.origin } : {}),
17
+ });
12
18
  };
13
19
  return {
14
20
  push(event) {
15
21
  if (event.kind === 'message:text' && event.streaming) {
16
- const entry = pending.get(event.messageId);
22
+ let entry = pending.get(event.messageId);
23
+ if (entry &&
24
+ (entry.origin?.toolCallId !== event.origin?.toolCallId || entry.origin?.threadId !== event.origin?.threadId)) {
25
+ flush(event.messageId);
26
+ entry = undefined;
27
+ }
17
28
  if (entry) {
18
29
  entry.text += event.text;
19
30
  return;
20
31
  }
21
32
  const timer = setTimeout(() => flush(event.messageId), windowMs);
22
33
  timer.unref?.();
23
- pending.set(event.messageId, { text: event.text, timer });
34
+ pending.set(event.messageId, { text: event.text, origin: event.origin, timer });
24
35
  return;
25
36
  }
26
37
  if (event.kind === 'message:end')
@@ -306,7 +306,13 @@ async function hasLiveProcessGroup(groupId) {
306
306
  catch (err) {
307
307
  if (err.code === 'ESRCH')
308
308
  return false;
309
- throw err;
309
+ // Some CI process supervisors reject the signal-0 probe after the shell
310
+ // exits even though `ps` can still inspect its former group. `ps` below is
311
+ // our authoritative liveness check and distinguishes live descendants
312
+ // from a reaped group without turning that platform quirk into a failed
313
+ // dev-server stop.
314
+ if (err.code !== 'EPERM')
315
+ throw err;
310
316
  }
311
317
  const processes = await runCommand('ps', ['-eo', 'pgid=,stat='], { timeout: 1000 });
312
318
  return processes.split('\n').some((line) => {
@@ -39,52 +39,4 @@ export const CODEX_MODELS = [
39
39
  i18nLabelKey: 'model.gpt6luna',
40
40
  i18nDescriptionKey: 'model.gpt6lunaDescription',
41
41
  },
42
- {
43
- id: 'gpt-5.6-sol',
44
- label: 'GPT-5.6 Sol',
45
- i18nLabelKey: 'model.gpt56sol',
46
- i18nDescriptionKey: 'model.gpt56solDescription',
47
- },
48
- {
49
- id: 'gpt-5.6-terra',
50
- label: 'GPT-5.6 Terra',
51
- i18nLabelKey: 'model.gpt56terra',
52
- i18nDescriptionKey: 'model.gpt56terraDescription',
53
- },
54
- {
55
- id: 'gpt-5.6-luna',
56
- label: 'GPT-5.6 Luna',
57
- i18nLabelKey: 'model.gpt56luna',
58
- i18nDescriptionKey: 'model.gpt56lunaDescription',
59
- },
60
- {
61
- id: 'gpt-5.5',
62
- label: 'GPT-5.5',
63
- i18nLabelKey: 'model.gpt55',
64
- i18nDescriptionKey: 'model.gpt55Description',
65
- },
66
- {
67
- id: 'gpt-5.4',
68
- label: 'GPT-5.4',
69
- i18nLabelKey: 'model.gpt54',
70
- i18nDescriptionKey: 'model.gpt54Description',
71
- },
72
- {
73
- id: 'gpt-5.4-mini',
74
- label: 'GPT-5.4 mini',
75
- i18nLabelKey: 'model.gpt54mini',
76
- i18nDescriptionKey: 'model.gpt54miniDescription',
77
- },
78
- {
79
- id: 'gpt-5.2',
80
- label: 'GPT-5.2',
81
- i18nLabelKey: 'model.gpt52',
82
- i18nDescriptionKey: 'model.gpt52Description',
83
- },
84
- {
85
- id: 'gpt-5.3-codex',
86
- label: 'GPT-5.3 Codex',
87
- i18nLabelKey: 'model.gpt53codex',
88
- i18nDescriptionKey: 'model.gpt53codexDescription',
89
- },
90
42
  ];
@@ -11,6 +11,12 @@ export const CLAUDE_MODELS = [
11
11
  i18nLabelKey: 'model.fable51',
12
12
  i18nDescriptionKey: 'model.fable51Description',
13
13
  },
14
+ {
15
+ id: 'claude-mythos-5-1',
16
+ label: 'Mythos 5.1 (Glasswing)',
17
+ i18nLabelKey: 'model.mythos51',
18
+ i18nDescriptionKey: 'model.mythos51Description',
19
+ },
14
20
  {
15
21
  id: 'claude-mythos-5',
16
22
  label: 'Mythos 5 (Glasswing)',
@@ -23,66 +29,12 @@ export const CLAUDE_MODELS = [
23
29
  i18nLabelKey: 'model.opus55',
24
30
  i18nDescriptionKey: 'model.opus55Description',
25
31
  },
26
- {
27
- id: 'claude-opus-5',
28
- label: 'Opus 5',
29
- i18nLabelKey: 'model.opus5',
30
- i18nDescriptionKey: 'model.opus5Description',
31
- },
32
- {
33
- id: 'claude-opus-4-8',
34
- label: 'Opus 4.8',
35
- i18nLabelKey: 'model.opus48',
36
- i18nDescriptionKey: 'model.opus48Description',
37
- },
38
- {
39
- id: 'claude-opus-4-7',
40
- label: 'Opus 4.7 (Classic)',
41
- i18nLabelKey: 'model.opus47Classic',
42
- i18nDescriptionKey: 'model.opus47ClassicDescription',
43
- },
44
- {
45
- id: 'claude-opus-4-7[1m]',
46
- label: 'Opus 4.7 (1M)',
47
- i18nLabelKey: 'model.opus471m',
48
- i18nDescriptionKey: 'model.opus471mDescription',
49
- },
50
- {
51
- id: 'claude-opus-4-6',
52
- label: 'Opus 4.6 (Classic)',
53
- i18nLabelKey: 'model.opusClassic',
54
- i18nDescriptionKey: 'model.opusClassicDescription',
55
- },
56
- {
57
- id: 'claude-opus-4-6[1m]',
58
- label: 'Opus 4.6 (1M)',
59
- i18nLabelKey: 'model.opus1m',
60
- i18nDescriptionKey: 'model.opus1mDescription',
61
- },
62
32
  {
63
33
  id: 'claude-sonnet-5-5',
64
34
  label: 'Sonnet 5.5',
65
35
  i18nLabelKey: 'model.sonnet55',
66
36
  i18nDescriptionKey: 'model.sonnet55Description',
67
37
  },
68
- {
69
- id: 'claude-sonnet-5',
70
- label: 'Sonnet 5',
71
- i18nLabelKey: 'model.sonnet5',
72
- i18nDescriptionKey: 'model.sonnet5Description',
73
- },
74
- {
75
- id: 'claude-sonnet-4-6',
76
- label: 'Sonnet 4.6 (Classic)',
77
- i18nLabelKey: 'model.sonnetClassic',
78
- i18nDescriptionKey: 'model.sonnetClassicDescription',
79
- },
80
- {
81
- id: 'claude-sonnet-4-6[1m]',
82
- label: 'Sonnet 4.6 (1M)',
83
- i18nLabelKey: 'model.sonnet1m',
84
- i18nDescriptionKey: 'model.sonnet1mDescription',
85
- },
86
38
  {
87
39
  id: 'claude-haiku-4-5-20251001',
88
40
  label: 'Haiku 4.5',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loicngr/kobo",
3
- "version": "2.1.5",
3
+ "version": "2.1.7",
4
4
  "description": "Kōbō — local multi-workspace orchestrator for Claude Code and OpenAI Codex, with isolated Git worktrees, live review and optional automation.",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-or-later",
@@ -81,12 +81,12 @@
81
81
  "security:scan": "node scripts/scan-secrets.mjs"
82
82
  },
83
83
  "dependencies": {
84
- "@anthropic-ai/claude-agent-sdk": "^0.3.285",
84
+ "@anthropic-ai/claude-agent-sdk": "^0.3.287",
85
85
  "@emnapi/core": "^1.11.3",
86
86
  "@emnapi/runtime": "^1.11.3",
87
87
  "@hono/node-server": "^2.1.3",
88
88
  "@modelcontextprotocol/sdk": "^1.31.0",
89
- "@openai/codex": "^0.159.2",
89
+ "@openai/codex": "^0.159.3",
90
90
  "better-sqlite3": "^13.0.3",
91
91
  "cron-parser": "^5.10.1",
92
92
  "hono": "^4.13.12",
@@ -97,16 +97,16 @@
97
97
  "devDependencies": {
98
98
  "@biomejs/biome": "2.5.14",
99
99
  "@types/better-sqlite3": "^9.6.0",
100
- "@types/node": "^26.6.3",
100
+ "@types/node": "^26.6.4",
101
101
  "@types/ws": "^8.18.2",
102
102
  "@vitest/runner": "^4.1.11",
103
103
  "concurrently": "^10.0.5",
104
104
  "tsx": "^4.23.15",
105
105
  "typescript": "^7.0.2",
106
- "vitest": "^5.0.2"
106
+ "vitest": "^5.0.3"
107
107
  },
108
108
  "overrides": {
109
109
  "@hono/node-server": "^2.1.3",
110
- "shell-quote": "^1.10.0"
110
+ "shell-quote": "^1.11.0"
111
111
  }
112
112
  }
@@ -0,0 +1,2 @@
1
+ import{M as e,S as t,dt as n,lt as r,p as i,st as a,v as o}from"./reactivity.esm-bundler-tuEvQ47f.js";import{C as s,F as c,J as l,L as u,N as d,O as f,P as p,T as m,Y as h,_ as g,d as _,f as v,g as y,j as ee,k as b,l as x,p as S,r as C,u as w,y as T,z as te}from"./runtime-core.esm-bundler-DOiZcasU.js";import{T as ne,Y as re,l as ie,lt as ae,mt as E,nt as D,t as O,x as oe}from"./QBtn-b_baQtAt.js";import{l as se,n as ce,o as le,t as ue}from"./latest-request-7rgsEfKZ.js";import{a as de}from"./i18n-CPFleoLe.js";import{O as fe,Rt as pe,T as me,Vt as k,ct as he,g as ge,jt as A,nt as _e,p as ve,rt as ye}from"./index-BVJWwOvK.js";import{t as be}from"./QSpinnerDots-BsKmccpX.js";import{t as xe}from"./QList-Cw2xs69W.js";import{t as Se}from"./QMarkupTable-Ck2E2TaI.js";import{c as j,l as M,s as N}from"./QSelect-DUuWu79L.js";import{t as P}from"./QItemSection-BhGnulVQ.js";import{t as Ce}from"./QExpansionItem-DmzCAbka.js";import{a as F,c as we,r as I,t as L}from"./ToolCallItem-BQmqNcfF.js";import{t as Te}from"./QScrollArea-BmBuij9J.js";import{a as R}from"./use-tours-CM4E53aB.js";import{f as Ee,g as z,m as De,p as Oe,u as ke}from"./use-worktree-restore-C__ResAR.js";import{t as B}from"./QTooltip-Dhy3hNTO.js";import{n as V}from"./render-chat-markdown-CafzCcmu.js";import{n as H}from"./WorkspacePage-DQ-jvLEj.js";function Ae(e,t){return s(`div`,e,[s(`table`,{class:`q-table`},t)])}var je={list:xe,table:Se},U=[`list`,`table`,`__qtable`],Me=D({name:`QVirtualScroll`,props:{...M,type:{type:String,default:`list`,validator:e=>U.includes(e)},items:{type:Array,default:()=>[]},itemsFn:Function,itemsSize:Number,scrollTarget:k},setup(e,{slots:n,attrs:r}){let i,a=t(null),o=x(()=>e.itemsSize>=0&&e.itemsFn!==void 0?Number.parseInt(e.itemsSize,10):Array.isArray(e.items)?e.items.length:0),{virtualScrollSliceRange:c,localResetVirtualScroll:u,padVirtualScroll:f,onVirtualScrollEvt:m}=j({virtualScrollLength:o,getVirtualScrollTarget:y,getVirtualScrollEl:v}),h=x(()=>{if(o.value===0)return[];let t=(e,t)=>({index:c.value.from+t,item:e});return e.itemsFn===void 0?e.items.slice(c.value.from,c.value.to).map(t):e.itemsFn(c.value.from,c.value.to-c.value.from).map(t)}),g=x(()=>`q-virtual-scroll q-virtual-scroll`+(e.virtualScrollHorizontal?`--horizontal`:`--vertical`)+(e.scrollTarget===void 0?` scroll`:``)),_=x(()=>e.scrollTarget===void 0?{tabindex:0}:{});l(o,()=>{u()}),l(()=>e.scrollTarget,()=>{C(),S()});function v(){let e=a.value;return e===null?null:e.$el||e}function y(){return i}function S(){let t=v();t!==null&&(i=pe(t,e.scrollTarget),i.addEventListener(`scroll`,m,re.passive))}function C(){i!==void 0&&(i.removeEventListener(`scroll`,m,re.passive),i=void 0)}function w(){let t=f(e.type===`list`?`div`:`tbody`,h.value.map(n.default));return n.before!==void 0&&(t=n.before().concat(t)),oe(n.after,t)}return u(),p(()=>{S()}),b(()=>{S()}),d(()=>{C()}),ee(()=>{C()}),()=>{if(n.default===void 0){console.error(`QVirtualScroll: default scoped slot is required for rendering`);return}return e.type===`__qtable`?Ae({ref:a,class:`q-table__middle `+g.value,tabindex:0},w()):s(je[e.type],{...r,ref:a,class:[r.class,g.value],..._.value},w)}}}),Ne=new Set([`setup`,`cleanup`,`archive`]);function Pe(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:Ne.has(e.sender)||me(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function Fe(e){let t=[],n=null,r=null;for(let i of e){let e=Pe(i),a=e===`session`||e===`system-prompt`,o=e===`script`&&i.type===`user`?`script:${i.sender}`:e===`user`&&i.type===`user`&&i.source?JSON.stringify([`mcp`,i.source.transport,i.source.clientName]):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 W(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 Ie(e){let t=e.items[0];return`${e.speaker}:${t?W(t):`empty`}`}function Le(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 Re(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=l(e,e=>{t(e)&&s(!0)},{immediate:!0}),i){o();return}a=setTimeout(()=>s(!1),n)})}var ze={class:`agent-error-item`},Be={class:`agent-error-item__body`},Ve={class:`agent-error-item__title`},G={class:`agent-error-item__message`},He=R(T({__name:`AgentErrorItem`,props:{item:{}},setup(e){let t=e,{te:r}=de(),i=x(()=>{let e=`agent.error.${t.item.category}`;return r(e)?e:`agent.error.other`});return(t,r)=>(u(),S(`div`,ze,[g(ie,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),w(`div`,Be,[w(`div`,Ve,n(t.$t(i.value)),1),w(`div`,G,n(e.item.message),1)])]))}}),[[`__scopeId`,`data-v-e98c3c02`]]),Ue={class:`text-caption text-kobo-3`},We=T({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,r=x(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return z(t.item.detail);case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(u(),S(`span`,Ue,n(e.$t(r.value)),1))}}),K=new Map;function q(){for(let e of K.values())e.controller.abort(),e.blobUrl&&URL.revokeObjectURL(e.blobUrl);K.clear()}typeof window<`u`&&(window.addEventListener(`kobo:auth-token-changed`,q),window.addEventListener(`storage`,e=>{(e.key===`kobo:network-token`||e.key===null)&&q()}));function J(e){if(!/^\/api\/workspaces\/[^/]+\/images\/file\?path=/.test(e))throw Error(`Invalid workspace image URL`);let t=K.get(e);if(!t){let n=new AbortController,r={references:0,controller:n,ready:Promise.resolve(``)};r.ready=le(e,{signal:n.signal}).then(async e=>{if(!e.ok)throw await se(e);let t=await e.blob();if(n.signal.aborted)throw new DOMException(`Aborted`,`AbortError`);return r.blobUrl=URL.createObjectURL(t),r.blobUrl}),t=r,K.set(e,t)}t.references++;let n=!1,r=t;return{ready:r.ready,release(){n||(n=!0,!(--r.references>0)&&(r.controller.abort(),r.blobUrl&&URL.revokeObjectURL(r.blobUrl),K.get(e)===r&&K.delete(e)))}}}function Ge(e,t){let n=o(``),r=o(0),a=()=>{r.value++},s=e=>{(e.key===`kobo:network-token`||e.key===null)&&a()};return window.addEventListener(`kobo:auth-token-changed`,a),window.addEventListener(`storage`,s),i(()=>{window.removeEventListener(`kobo:auth-token-changed`,a),window.removeEventListener(`storage`,s)}),l([e,r],([e],r,i)=>{let a=!0,o=[],s=document.createElement(`template`);s.innerHTML=e;for(let e of s.content.querySelectorAll(`img`)){let r=e.getAttribute(`src`)??``;if(e.removeAttribute(`data-kobo-image-url`),!/^\/api\/workspaces\/[^/]+\/images\/file\?path=/.test(r))continue;e.removeAttribute(`src`);let i=J(r);o.push(i),i.ready.then(t=>{a&&(e.setAttribute(`src`,t),e.setAttribute(`data-kobo-image-url`,r),n.value=s.innerHTML)}).catch(()=>{a&&(e.setAttribute(`title`,t()),n.value=s.innerHTML)})}n.value=s.innerHTML,i(()=>{a=!1;for(let e of o)e.release()})},{immediate:!0}),n}function Ke(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 qe=[`innerHTML`],Je=[`innerHTML`],Ye=[`src`],Y=R(T({__name:`UserMessageItem`,props:{item:{},workspaceId:{}},setup(t){let n=t,{t:r}=de(),a=x(()=>n.item.sender===`system-prompt`),s=Ge(x(()=>{let e=Ke(n.item.content,n.workspaceId);return V(e)}),()=>r(`network.login.unreachable`)),c=o(null),d=o(!1),f;function p(){d.value=!1,c.value=null,f?.release(),f=void 0}l(d,e=>{e||p()}),l(()=>n.workspaceId,p);let m=e=>{(e.key===`kobo:network-token`||e.key===null)&&p()};window.addEventListener(`storage`,m),window.addEventListener(`kobo:auth-token-changed`,p),i(()=>{p(),window.removeEventListener(`kobo:auth-token-changed`,p),window.removeEventListener(`storage`,m)});function y(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(p(),n.dataset.koboImageUrl&&(f=J(n.dataset.koboImageUrl),f.ready.catch(p)),c.value=n.src,d.value=!0)}return(t,n)=>(u(),S(C,null,[a.value?(u(),_(Ce,{key:0,dense:``,"dense-toggle":``,label:t.$t(`chat.systemPrompt`),"header-class":`text-kobo-2 text-caption`},{default:h(()=>[w(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:e(s)},null,8,qe)]),_:1},8,[`label`])):(u(),S(`div`,{key:1,class:`markdown-message`,onClick:y},[w(`div`,{innerHTML:e(s)},null,8,Je)])),g(A,{modelValue:d.value,"onUpdate:modelValue":n[1]||=e=>d.value=e},{default:h(()=>[c.value?(u(),S(`img`,{key:0,src:c.value,alt:``,class:`image-lightbox-img`,onClick:n[0]||=e=>d.value=!1},null,8,Ye)):v(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-db2fc622`]]),Xe=[`data-event-ids`],X={class:`turn-header`},Ze={key:0,class:`turn-time`},Qe={class:`turn-time turn-time-updated`},$e={key:2,class:`turn-actions`},et={class:`turn-body`},tt={class:`ellipsis`},nt={class:`q-px-sm q-pb-sm`},rt={key:0,class:`turn-scroll-top`},it=R(T({__name:`TurnCard`,props:{turn:{},workspaceId:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(t,{emit:i}){let s=t,c=i,{t:l}=de(),d=_e(),f=m(F),p=o(null);function ee(){let e=p.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 b=x(()=>{switch(s.turn.speaker){case`user`:{let e=s.turn.items[0],t=e?.type===`user`?e.source:void 0;return{label:t?l(`chat.externalLlm`,{name:t.clientName}):l(`chat.you`),accent:`var(--kobo-turn-user)`,badgeClass:`turn-badge-user`}}case`agent`:return{label:l(`chat.agent`),accent:`var(--kobo-turn-agent)`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:l(`chat.systemPrompt`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-system`};case`session`:return{label:l(`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:me(t)?l(`chat.hookScript`,{event:t.slice(5)}):l(t===`archive`?`chat.archiveScript`:t===`setup`?`chat.setupScript`:`chat.cleanupScript`),accent:`var(--kobo-success)`,badgeClass:`turn-badge-script`}}}});function T(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 ne=x(()=>T(s.turn.ts)),re=x(()=>s.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),ae=x(()=>{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}),D=x(()=>{let e=s.turn.ts,t=ae.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?``:T(t,r-n<6e4)}),oe=x(()=>D.value!==``);function se(e){return we(e.origin,Object.values(d.subagents[s.workspaceId]??{}),e.type===`tool`?e.toolCallId:void 0)}function ce(e){return e||l(`chat.subagentActivity`)}function le(e){return(e.type===`text`||e.type===`thinking`||e.type===`tool`)&&e.origin?.kind===`subagent`}let ue=x(()=>{let e=[];for(let t of s.turn.items){if(!le(t)){e.push({type:`item`,key:W(t),item:t});continue}let n=se(t),r=n?.toolUseId??t.origin?.toolCallId??t.origin?.threadId??W(t),i=e.at(-1);if(i?.type===`subagent`&&i.subagentKey===r){i.items.push(t);continue}e.push({type:`subagent`,key:`subagent:${W(t)}`,items:[t],subagentKey:r,subagentName:n?.description||null})}return e});function fe(e){e.origin&&f?.({origin:e.origin})}let pe=x(()=>s.turn.items.filter(e=>e.type===`tool`).length);return(i,o)=>(u(),S(`div`,{ref_key:`cardEl`,ref:p,class:a([`turn-card`,{"turn-card--user":t.turn.speaker===`user`,"turn-card--highlighted":t.highlighted}]),"data-event-ids":re.value,style:r({"--turn-accent":b.value.accent})},[w(`div`,X,[w(`span`,{class:a([`turn-badge`,b.value.badgeClass])},n(b.value.label),3),ne.value?(u(),S(`span`,Ze,n(ne.value),1)):v(``,!0),oe.value?(u(),S(C,{key:1},[g(ie,{name:`arrow_forward`,size:`10px`,color:`kobo-3`,class:`turn-time-arrow`}),w(`span`,Qe,[y(n(D.value)+` `,1),g(B,null,{default:h(()=>[y(n(e(l)(`chat.lastUpdatedAt`,{time:D.value})),1)]),_:1})])],64)):v(``,!0),pe.value>0?(u(),S(`span`,$e,` · `+n(e(l)(`chat.nActions`,{n:pe.value})),1)):v(``,!0)]),w(`div`,et,[(u(!0),S(C,null,te(ue.value,r=>(u(),S(C,{key:r.key},[r.type===`subagent`?(u(),_(Ce,{key:0,class:`turn-subagent-activity`,dense:``,label:ce(r.subagentName),caption:e(l)(`chat.nActions`,{n:r.items.length}),"default-opened":t.highlighted&&r.items.some(e=>e.eventIds?.some(e=>re.value.includes(e)))},{header:h(()=>[g(P,null,{default:h(()=>[g(N,{class:`row items-center no-wrap`,title:r.subagentName??void 0},{default:h(()=>[g(ie,{name:`hub`,size:`16px`,class:`q-mr-xs`},{default:h(()=>[g(B,null,{default:h(()=>[y(n(e(l)(`chat.subagentActivity`)),1)]),_:1})]),_:1}),w(`span`,tt,n(ce(r.subagentName)),1)]),_:2},1032,[`title`]),g(N,{caption:``},{default:h(()=>[y(n(e(l)(`chat.nActions`,{n:r.items.length})),1)]),_:2},1024)]),_:2},1024),g(P,{side:``},{default:h(()=>[g(O,{flat:``,round:``,dense:``,size:`sm`,icon:`open_in_new`,"aria-label":e(l)(`chat.openSubagentActivity`),onClick:E(e=>fe(r.items[0]),[`stop`])},{default:h(()=>[g(B,null,{default:h(()=>[y(n(e(l)(`chat.openSubagentActivity`)),1)]),_:1})]),_:1},8,[`aria-label`,`onClick`])]),_:2},1024)]),default:h(()=>[w(`div`,nt,[(u(!0),S(C,null,te(r.items,t=>(u(),S(C,{key:e(W)(t)},[t.type===`text`?(u(),_(I,{key:0,item:t},null,8,[`item`])):t.type===`thinking`?(u(),_(H,{key:1,item:t},null,8,[`item`])):t.type===`tool`?(u(),_(L,{key:2,item:t},null,8,[`item`])):v(``,!0)],64))),128))])]),_:2},1032,[`label`,`caption`,`default-opened`])):(u(),S(C,{key:1},[r.item.type===`text`?(u(),_(I,{key:0,item:r.item},null,8,[`item`])):r.item.type===`thinking`?(u(),_(H,{key:1,item:r.item},null,8,[`item`])):r.item.type===`tool`?(u(),_(L,{key:2,item:r.item},null,8,[`item`])):r.item.type===`user`?(u(),_(Y,{key:3,item:r.item,"workspace-id":t.workspaceId},null,8,[`item`,`workspace-id`])):r.item.type===`session`?(u(),_(We,{key:4,item:r.item},null,8,[`item`])):r.item.type===`error`?(u(),_(He,{key:5,item:r.item},null,8,[`item`])):v(``,!0)],64))],64))),128))]),t.turn.items.length>4?(u(),S(`div`,rt,[g(O,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`kobo-3`,class:`turn-scroll-top-btn`,onClick:ee},{default:h(()=>[g(B,null,{default:h(()=>[y(n(e(l)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):v(``,!0)],14,Xe))}}),[[`__scopeId`,`data-v-7f80d364`]]),at={key:0,class:`activity-feed-switching`},ot={key:1,class:`activity-feed-wrap`,"data-tour":`ws-chat`},st={key:0,role:`alert`,class:`q-pa-sm text-negative`},ct={key:1,"data-testid":`history-load-error`,role:`alert`,class:`q-pa-sm text-negative`},lt=[`aria-label`],ut={key:0,class:`activity-feed-compacting`},dt={class:`text-caption text-kobo-2`},ft={key:0,class:`text-center q-py-sm text-caption text-kobo-3`},pt={key:1,class:`activity-feed-empty q-pa-md`},mt={class:`activity-feed-empty__title`},ht={class:`activity-feed-empty__hint`},gt={key:2,class:`q-px-md q-pb-md`},_t=[`title`],vt={class:`ellipsis`},yt={class:`activity-feed-nav-cluster`},bt=300,xt=60,St=200,Ct=180,wt=255,Tt=3,Et=60,Dt=R(T({__name:`ActivityFeed`,props:{workspaceId:{}},setup(t){let{t:r}=de(),i=t,a=he(),s=fe(),d=_e(),m=ve(),ee=x(()=>m.isCompacting(i.workspaceId)),b=x(()=>d.selectedSessionId),T=x(()=>d.sessions.find(e=>e.id===b.value)?.engineSessionId??null),re=x(()=>{let e=d.sessions;return e.length!==0&&b.value===e[e.length-1].id});function E(e){return b.value?e?e===b.value||e===T.value:re.value:!0}let D=x(()=>(d.activityFeeds[i.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&E(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,source:ge(e.meta?.source),ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),oe=x(()=>{let e=d.workspaces.find(e=>e.id===i.workspaceId);return ye(e?.status)}),pe=ke(),me=``,k=x(()=>{let e=a.eventsFor(i.workspaceId),t=a.timestampsFor(i.workspaceId),n=a.sessionIdsFor(i.workspaceId),r=a.eventIdsFor(i.workspaceId),o=[],c=[],l=[];for(let i=0;i<e.length;i++)E(n[i])&&(o.push(e[i]),c.push(t[i]),l.push(r[i]??null));let u=`${i.workspaceId}::${b.value??`*`}`;u!==me&&(pe=ke(),me=u);let d=Ee(pe,o,c,oe.value,l);return Fe(De(d,D.value).filter(e=>e.type===`thinking`?!1:e.type===`session`?e.kind===`ended`?s.showVerboseSystemMessages||!Oe(e.detail):s.showVerboseSystemMessages:!0))}),A=x(()=>s.showVerboseSystemMessages?a.eventsFor(i.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),xe=x(()=>{for(let e=k.value.length-1;e>=0;e--){let t=k.value[e]?.items??[];for(let e=t.length-1;e>=0;e--){let n=t[e];if(n){if(n.type===`text`&&!n.streaming)return{key:`text:${n.messageId}`,text:n.text};if(n.type===`tool`)return n.result?{key:`tool:${n.toolCallId}:end`,text:n.result.isError?r(`activity.a11y.toolFailed`,{name:n.name}):r(`activity.a11y.toolFinished`,{name:n.name})}:{key:`tool:${n.toolCallId}:start`,text:r(`activity.a11y.toolStarted`,{name:n.name})}}}}return null}),Se=o(``);l(xe,(e,t)=>{if(!e||e.key===t?.key)return;let n=e.key.startsWith(`text:`)?r(`activity.a11y.agentSaid`,{text:e.text.slice(0,bt)}):e.text;Se.value=n});let j=o(null),M=o(!0),N=o(!1),P=o(``),F=ue(),we=ue(),I=ue(),L=o(null),R=!1,z=!0,B=null,V=o(new Set),H=new Map,Ae=x(()=>{if(k.value.length>0||A.value.length>0)return!1;let e=i.workspaceId,t=b.value;return!!d.loadingSessions[e]||m.isSyncPending(e)||t!==null&&V.value.has(W(e,t))}),je=o(new Map),U=x(()=>k.value.length>0||A.value.length>0),Ne=x(()=>!!d.loadingSessions[i.workspaceId]||m.isSyncPending(i.workspaceId)||b.value!==null&&V.value.has(W(i.workspaceId,b.value)));function Pe(e){tt();let t=B;if(B=e.verticalSize,t!==null&&e.verticalSize>t&&M.value&&!N.value){q(0);return}let n=e.verticalSize-e.verticalPosition-e.verticalContainerSize;M.value=n<=xt,R&&e.verticalPosition<=St&&!N.value&&!P.value&&ze()&&He()}function W(e,t){return`${e}:${t}`}function ze(){let e=b.value;return e?je.value.get(W(i.workspaceId,e))??!0:a.hasMoreOlderFor(i.workspaceId)}function Be(e,t,n){je.value.set(W(e,t),n)}function Ve(e){if(!b.value)return a.oldestIdFor(e);let t=a.eventIdsFor(e),n=a.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!E(n[e]))continue;let r=t[e];if(r)return r}}let G=null;function He(){if(G)return G;let e=K().finally(()=>{G===e&&(G=null)});return G=e,G}function Ue(){F.abort(),we.abort(),I.abort(),N.value=!1,G=null;for(let e of V.value)Ft.delete(e);H.clear(),V.value.clear(),P.value=``}function We(){P.value=``,Nt.value===0&&b.value?It():He()}l(()=>[i.workspaceId,b.value],Ue,{flush:`sync`});async function K(){let e=i.workspaceId,t=b.value,n=Ve(e);if(!n)return;let r=F.begin();P.value=``,N.value=!0;try{let i=j.value;if(await f(),!F.isCurrent(r))return;let o=i?.getScroll().verticalSize??0,s=i?.getScroll().verticalPosition??0,c=new URLSearchParams({before:n,limit:`200`});t&&c.set(`session`,t);let l=await le(`/api/workspaces/${e}/events?${c.toString()}`,{signal:r});if(!l.ok)throw await se(l);let u=await l.json();if(!F.isCurrent(r))return;let p=u.events??[],m=p.filter(t=>t.type===`agent:event`&&t.workspaceId===e),h=p.filter(t=>t.type===`user:message`&&t.workspaceId===e),g=m.map(e=>e.payload),_=m.map(e=>e.createdAt),v=m.map(e=>e.sessionId??null),y=m.map(e=>e.id),ee=p.length>0?p[0].id:n;t&&Be(e,t,u.hasMore),a.prepend(e,g,_,{oldestId:ee,hasMoreOlder:t?a.hasMoreOlderFor(e):u.hasMore,sessionIds:v,eventIds:y});for(let t of h){let n=t.payload;typeof n.content==`string`&&d.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`,source:ge(n.source)}})}if(await f(),i&&F.isCurrent(r)){let e=i.getScroll().verticalSize-o;if(e>0){let t=Math.max(s+e,250);i.setScrollPosition(`vertical`,t,0)}}}catch(e){if(ce(e)||!F.isCurrent(r))return;P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] failed to load older events:`,e)}finally{F.isCurrent(r)&&(N.value=!1)}}async function q(e=0){let t=$();if(await f(),t!==$())return;let n=j.value;if(!n)return;let r=n.getScroll();n.setScrollPosition(`vertical`,r.verticalSize,e)}let J=null,Ge=0;function Ke(){if(J!=null)return;let e=performance.now()-Ge<Ct,t=$();J=requestAnimationFrame(()=>{J=null,t===$()&&(Ge=performance.now(),q(e?0:180))})}function qe(e){Q(),M.value=!1;let t=j.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function Je(){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 Ye(e){let t=e.detail;if(t?.workspaceId!==i.workspaceId||!t.eventId)return;Q(),M.value=!1;let n=I.begin(),r=i.workspaceId;try{let e=new URLSearchParams({around:t.eventId,limit:`200`});t.sessionId&&e.set(`session`,t.sessionId);let o=await le(`/api/workspaces/${r}/events?${e}`,{signal:n});if(!o.ok)throw await se(o);let s=await o.json();if(!I.isCurrent(n))return;let c=s.events??[],l=c.filter(e=>e.type===`agent:event`&&e.workspaceId===i.workspaceId),u=c.filter(e=>e.type===`user:message`&&e.workspaceId===i.workspaceId);a.reset(i.workspaceId,l.map(e=>e.payload),l.map(e=>e.createdAt),{oldestId:c[0]?.id,hasMoreOlder:s.hasMore??!1,sessionIds:l.map(e=>e.sessionId),eventIds:l.map(e=>e.id)});for(let e of u){let t=e.payload;typeof t.content==`string`&&d.addActivityItem(i.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`,source:ge(t.source)}})}if(await Re(Ae,e=>!e,5e3),await f(),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e()))),!I.isCurrent(n))return;let p=k.value.findIndex(e=>e.items.some(e=>e.eventIds?.includes(t.eventId)??!1));if(p<0)return;L.value=t.eventId,Y.value?.scrollTo(p,`center`),X.value=p,window.setTimeout(()=>{L.value===t.eventId&&(L.value=null)},1800)}catch(e){if(ce(e)||!I.isCurrent(n))return;P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Y=o(null),Xe=o(null),X=o(0),Ze=o(!1),Qe=x(()=>{for(let e=k.value.length-1;e>=0;e--)if(k.value[e]?.speaker===`user`)return e;return-1}),$e=null;function et(){let e=Qe.value,t=j.value?.getScrollTarget(),n=t?.querySelector(`[data-turn-index="${e}"]`);if(!n||!t){Ze.value=!1;return}let r=t.getBoundingClientRect(),i=n.getBoundingClientRect();Ze.value=i.bottom>r.top&&i.top<r.bottom}function tt(){$e===null&&($e=requestAnimationFrame(()=>{$e=null,et()}))}function nt(e){return e.length<=wt?e:`${e.slice(0,252)}...`}let rt=x(()=>{let e=Qe.value,t=k.value[e];if(!t||X.value<=e||Ze.value)return null;let n=t.items.filter(e=>e.type===`user`).map(e=>e.content).join(`
2
+ `).trim();return n?{index:e,content:nt(n)}:null});function Dt(e){X.value=e.index,tt()}function Ot(){let e=rt.value;e&&(Q(),M.value=!1,Y.value?.scrollTo(e.index,`start`),X.value=e.index)}let kt=o(!1);async function At(){if(kt.value)return;Q(),M.value=!1,kt.value=!0;let e=i.workspaceId;try{let t=Le(k.value,X.value);if(t<0)for(let n=0;n<15;n++){if(e!==i.workspaceId)return;if(!ze())break;let n=k.value.length;if(await He(),await f(),e!==i.workspaceId)return;if(X.value+=k.value.length-n,t=Le(k.value,X.value),t>=0)break}t>=0&&e===i.workspaceId&&(Y.value?.scrollTo(t,`start`),X.value=t)}finally{kt.value=!1}}let Z=0;function jt(){return new Promise(e=>requestAnimationFrame(()=>e()))}async function Mt(){if(!z||!U.value||Ae.value)return;R=!1;let e=++Z;if(await f(),e!==Z||!z||(Xe.value=j.value?.getScrollTarget()??null,await f(),e!==Z||!z))return;k.value.length>0&&Y.value?.scrollTo(k.value.length-1,`end-force`),await jt();let t=-1,n=0;for(let r=0;r<Et&&n<Tt;r++){if(e!==Z||!z)return;await q(0),await jt();let r=j.value?.getScroll().verticalSize??0;B=r,n=r===t?n+1:0,t=r}e===Z&&!Ne.value&&(R=!0,z=!1)}function Q(){z=!1,!R&&(Z++,R=!0)}let Nt=x(()=>{let e=a.sessionIdsFor(i.workspaceId);if(!b.value)return e.length;let t=0;for(let n of e)E(n)&&t++;return t});l(Ae,async e=>{!e&&U.value&&await Mt(),e||f(()=>{Xe.value=j.value?.getScrollTarget()??null,tt()})}),l(Ne,(e,t)=>{!e&&t&&z&&U.value&&Mt()}),p(()=>{window.addEventListener(`kobo:focus-history-event`,Ye),f(()=>{Xe.value=j.value?.getScrollTarget()??null,tt()}),U.value&&Mt(),b.value&&It();let e=new URLSearchParams(window.location.hash.split(`?`)[1]??``).get(`eventId`);e&&(Ye(new CustomEvent(`kobo:focus-history-event`,{detail:{workspaceId:i.workspaceId,sessionId:b.value,eventId:e}})),Je())}),l([Nt,()=>D.value.length,()=>A.value.length],async([e,t,n])=>{N.value||z&&e+t+n>0&&await Mt()}),l([Qe,()=>k.value.length],()=>{f(tt)});let Pt=x(()=>a.liveAppendCountFor(i.workspaceId,E)),$=()=>`${i.workspaceId}\u0000${d.selectedSessionId??``}`;l([Pt,$],([e,t],[n,r])=>{t===r&&e>n&&M.value&&!N.value&&Ke()}),c(()=>{Z++,z=!1,Ue(),window.removeEventListener(`kobo:focus-history-event`,Ye),J!=null&&(cancelAnimationFrame(J),J=null),$e!==null&&cancelAnimationFrame($e)}),l([()=>i.workspaceId,()=>d.selectedSessionId],()=>{Z++,z=!0,B=null,M.value=!0,R=!1,It(),U.value&&Mt()});let Ft=new Set;async function It(){let e=i.workspaceId,t=b.value;if(!t||Nt.value>0)return;let n=W(e,t);if(Ft.has(n))return;Ft.add(n),V.value.add(n);let r=we.begin();H.set(n,r),P.value=``;try{let n=await le(`/api/workspaces/${e}/events?session=${encodeURIComponent(t)}&limit=500`,{signal:r});if(!n.ok)throw await se(n);let i=await n.json();if(!we.isCurrent(r))return;let o=i.events??[];if(o.length===0)return;let s=o.filter(t=>t.type===`agent:event`&&t.workspaceId===e),c=o.filter(t=>t.type===`user:message`&&t.workspaceId===e),l=s.map(e=>e.payload),u=s.map(e=>e.createdAt),f=s.map(e=>e.sessionId??null),p=s.map(e=>e.id);Be(e,t,i.hasMore),l.length>0&&a.prepend(e,l,u,{oldestId:o[0].id,hasMoreOlder:a.hasMoreOlderFor(e),sessionIds:f,eventIds:p});for(let t of c){let n=t.payload;typeof n.content==`string`&&d.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`,source:ge(n.source)}})}}catch(e){if(ce(e)||!we.isCurrent(r)){(!H.has(n)||H.get(n)===r)&&Ft.delete(n);return}P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Ft.delete(n)}finally{H.get(n)===r&&(H.delete(n),V.value.delete(n))}}let Lt=x(()=>D.value.filter(e=>e.sender===`user`).length),Rt=x(()=>(d.activityFeeds[i.workspaceId]??[]).filter(e=>e.meta?.sender===`user`&&e.meta.pending&&E(e.sessionId)).length);l([Lt,Rt,$],async([e,t,n],[r,i,a])=>{let o=t>i;n===a&&(o||e>r&&M.value&&!N.value&&!Ne.value)&&(M.value=!0,await q(o?180:0))});async function zt(){M.value=!0,await q(250)}return(t,r)=>Ae.value?(u(),S(`div`,at,[g(be,{size:`40px`,color:`primary`})])):(u(),S(`div`,ot,[e(d).sessionsErrors[i.workspaceId]?(u(),S(`div`,st,[y(n(t.$t(`activity.sessionsError`,{message:e(d).sessionsErrors[i.workspaceId]}))+` `,1),g(O,{flat:``,dense:``,label:t.$t(`common.retry`),onClick:r[0]||=t=>e(d).fetchSessions(i.workspaceId)},null,8,[`label`])])):v(``,!0),P.value?(u(),S(`div`,ct,[y(n(t.$t(`activity.historyError`,{message:P.value}))+` `,1),g(O,{flat:``,dense:``,label:t.$t(`common.retry`),onClick:We},null,8,[`label`])])):v(``,!0),w(`div`,{"data-testid":`activity-live-region`,class:`sr-only`,role:`log`,"aria-live":`polite`,"aria-atomic":`true`,"aria-label":t.$t(`activity.a11y.region`)},n(Se.value),9,lt),g(ae,{name:`fade`},{default:h(()=>[ee.value?(u(),S(`div`,ut,[g(be,{size:`18px`,color:`primary`}),w(`span`,dt,n(t.$t(`activity.compacting`)),1)])):v(``,!0)]),_:1}),g(Te,{ref_key:`scrollRef`,ref:j,class:`activity-feed-scroll`,onScroll:Pe,onWheelPassive:Q,onTouchstartPassive:Q,onKeydown:Q},{default:h(()=>[N.value?(u(),S(`div`,ft,[g(ne,{size:`sm`}),y(` `+n(t.$t(`activity.loading_older`)),1)])):v(``,!0),g(Me,{ref_key:`virtualScrollRef`,ref:Y,class:`q-pa-md`,items:k.value,"scroll-target":Xe.value??void 0,"virtual-scroll-item-size":160,"virtual-scroll-slice-size":20,onVirtualScroll:Dt},{default:h(({item:t,index:n})=>[(u(),_(it,{key:e(Ie)(t),turn:t,"workspace-id":i.workspaceId,"data-turn-index":n,highlighted:t.items.some(e=>e.eventIds?.includes(L.value??``)??!1),onScrollTo:qe},null,8,[`turn`,`workspace-id`,`data-turn-index`,`highlighted`]))]),_:1},8,[`items`,`scroll-target`]),k.value.length===0&&A.value.length===0&&!N.value?(u(),S(`div`,pt,[g(ie,{name:`forum`,size:`28px`}),w(`div`,mt,n(t.$t(`activity.empty`)),1),w(`div`,ht,n(t.$t(`activity.emptyHint`)),1)])):v(``,!0),A.value.length?(u(),S(`div`,gt,[g(Ce,{label:t.$t(`activity.raw_lines`,{n:A.value.length}),dense:``},{default:h(()=>[(u(!0),S(C,null,te(A.value,(e,t)=>(u(),S(`div`,{key:t,class:`text-caption text-kobo-3 q-pa-xs`},n(e),1))),128))]),_:1},8,[`label`])])):v(``,!0)]),_:1},512),rt.value?(u(),S(`button`,{key:2,type:`button`,class:`activity-feed-last-user`,"data-testid":`latest-user-message`,title:rt.value.content,onClick:Ot},[g(ie,{name:`person`,size:`16px`}),w(`span`,vt,n(rt.value.content),1)],8,_t)):v(``,!0),w(`div`,yt,[M.value?v(``,!0):(u(),_(O,{key:0,round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:t.$t(`activity.scroll_to_bottom`),"aria-label":t.$t(`activity.scroll_to_bottom`),onClick:zt},null,8,[`title`,`aria-label`])),g(O,{round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:t.$t(`activity.prev_user_message`),"aria-label":t.$t(`activity.prev_user_message`),loading:kt.value,disable:kt.value,onClick:At},null,8,[`title`,`aria-label`,`loading`,`disable`])])]))}}),[[`__scopeId`,`data-v-58324159`]]);export{Dt as default};
@@ -0,0 +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-db2fc622]{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-db2fc622] *{max-width:100%}.markdown-message[data-v-db2fc622] 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-db2fc622] code{word-break:break-all}.markdown-message[data-v-db2fc622] p{margin:0 0 .4em}.markdown-message[data-v-db2fc622] p:last-child{margin-bottom:0}.markdown-message[data-v-db2fc622] code{background:#00000040;border-radius:3px;padding:.1em .3em}.markdown-message[data-v-db2fc622] h1,.markdown-message[data-v-db2fc622] h2,.markdown-message[data-v-db2fc622] h3,.markdown-message[data-v-db2fc622] h4,.markdown-message[data-v-db2fc622] h5,.markdown-message[data-v-db2fc622] h6{margin:.4em 0 .25em;font-weight:600;line-height:1.3}.markdown-message[data-v-db2fc622] h1{font-size:1.25em}.markdown-message[data-v-db2fc622] h2{font-size:1.15em}.markdown-message[data-v-db2fc622] h3{font-size:1.08em}.markdown-message[data-v-db2fc622] h4,.markdown-message[data-v-db2fc622] h5,.markdown-message[data-v-db2fc622] h6{font-size:1em}.markdown-user-prompt[data-v-db2fc622]{color:var(--kobo-text-2);font-size:12px;font-style:italic}.markdown-user-prompt[data-v-db2fc622] p{margin:0 0 .4em}.markdown-user-prompt[data-v-db2fc622] 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-7f80d364]{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-7f80d364]{animation:1.8s ease-out search-highlight-7f80d364}@keyframes search-highlight-7f80d364{0%,45%{box-shadow:0 0 0 2px #818cf8f2,0 0 22px #818cf873}to{box-shadow:none}}.turn-header[data-v-7f80d364]{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-7f80d364]{letter-spacing:.3px;border-radius:3px;padding:2px 8px;font-size:11px;font-weight:700}.turn-badge-user[data-v-7f80d364]{color:var(--kobo-turn-user);background:#ce93d826}.turn-badge-agent[data-v-7f80d364]{color:var(--kobo-turn-agent);background:#7986cb26}.turn-badge-system[data-v-7f80d364]{color:var(--kobo-text-2);background:#75757533;font-style:italic}.turn-badge-session[data-v-7f80d364]{color:var(--kobo-text-3);background:#61616133}.turn-badge-script[data-v-7f80d364]{color:var(--kobo-success);background:#4db6ac26}.turn-time[data-v-7f80d364]{color:var(--kobo-text-disabled);font-family:var(--kobo-font-mono);font-size:11px}.turn-time-arrow[data-v-7f80d364]{opacity:.7;margin:0 -2px}.turn-time-updated[data-v-7f80d364]{color:var(--kobo-text-3)}.turn-actions[data-v-7f80d364]{color:var(--kobo-text-3);font-size:11px}.turn-body[data-v-7f80d364]{flex-direction:column;gap:12px;min-width:0;padding:14px 18px;display:flex}.turn-body[data-v-7f80d364]>*{min-width:0;max-width:100%}.turn-body[data-v-7f80d364] .tool-row+.tool-row{margin-top:-8px}.turn-scroll-top[data-v-7f80d364]{justify-content:flex-start;padding:0 8px 6px;display:flex}.turn-scroll-top-btn[data-v-7f80d364]{opacity:.5;transition:opacity .15s}.turn-scroll-top-btn[data-v-7f80d364]:hover{opacity:1}.activity-feed-wrap[data-v-58324159]{flex-direction:column;width:100%;height:100%;display:flex;position:relative}.activity-feed-scroll[data-v-58324159]{flex:1;width:100%;height:100%;min-height:0}.activity-feed-nav-cluster[data-v-58324159]{z-index:2;align-items:center;gap:8px;display:flex;position:absolute;bottom:14px;right:14px}.activity-feed-last-user[data-v-58324159]{z-index:2;border:1px solid var(--kobo-border);background:var(--kobo-surface-2);min-width:0;color:var(--kobo-text-2);font:inherit;text-align:left;cursor:pointer;border-radius:6px;align-items:center;gap:8px;padding:7px 10px;font-size:12px;display:flex;position:absolute;top:14px;left:14px;right:104px;box-shadow:0 4px 16px #00000059}.activity-feed-last-user[data-v-58324159]:hover{color:var(--kobo-text);border-color:var(--q-primary)}.activity-feed-last-user[data-v-58324159]:focus-visible{outline:2px solid var(--q-primary);outline-offset:2px}.activity-feed-compacting[data-v-58324159]{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-58324159],.fade-leave-active[data-v-58324159]{transition:opacity .16s}.fade-enter-from[data-v-58324159],.fade-leave-to[data-v-58324159]{opacity:0}.activity-feed-nav-btn[data-v-58324159]{opacity:.8;transition:opacity .12s}.activity-feed-nav-btn[data-v-58324159]:hover{opacity:1}.activity-feed-scroll[data-v-58324159] .q-scrollarea__content{max-width:100%;overflow-x:hidden}.activity-feed-switching[data-v-58324159]{justify-content:center;align-items:center;width:100%;height:100%;display:flex}.activity-feed-empty[data-v-58324159]{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-58324159]{font-family:var(--kobo-font-sans);color:var(--kobo-text-2);font-size:14px;font-weight:500}.activity-feed-empty__hint[data-v-58324159]{font-family:var(--kobo-font-sans);color:var(--kobo-text-3);max-width:380px;font-size:13px}