@loicngr/kobo 2.1.16 → 2.1.19

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 (131) hide show
  1. package/AGENTS.md +101 -0
  2. package/CHANGELOG.md +12 -0
  3. package/CONFIGURATION.md +108 -17
  4. package/README.md +6 -2
  5. package/dist/mcp-server/kobo-tasks-server.js +28 -6
  6. package/dist/mcp-server/progress-bar-client.js +20 -0
  7. package/dist/mcp-server/workspace-dialogue-client.js +27 -7
  8. package/dist/server/db/migrations.js +2 -0
  9. package/dist/server/db/progress-bar-schema.js +27 -0
  10. package/dist/server/db/schema.js +2 -0
  11. package/dist/server/index.js +6 -0
  12. package/dist/server/middleware/workspace-creation-maintenance.js +13 -0
  13. package/dist/server/routes/maintenance.js +32 -0
  14. package/dist/server/routes/mcp.js +19 -9
  15. package/dist/server/routes/progress-bars.js +56 -0
  16. package/dist/server/routes/pull-requests.js +2 -1
  17. package/dist/server/routes/workspaces.js +35 -64
  18. package/dist/server/services/agent/engines/claude-code/engine.js +59 -16
  19. package/dist/server/services/agent/engines/claude-code/options-builder.js +13 -34
  20. package/dist/server/services/agent/engines/claude-code/stop-hook.js +8 -27
  21. package/dist/server/services/agent/engines/codex/engine.js +8 -1
  22. package/dist/server/services/agent/engines/codex/options-builder.js +11 -16
  23. package/dist/server/services/agent/orchestrator.js +73 -11
  24. package/dist/server/services/auto-loop-final-review-service.js +1 -1
  25. package/dist/server/services/auto-loop-service.js +10 -10
  26. package/dist/server/services/cron-service.js +7 -0
  27. package/dist/server/services/progress-bar-mcp-service.js +44 -0
  28. package/dist/server/services/progress-bar-runtime.js +29 -0
  29. package/dist/server/services/progress-bar-service.js +232 -0
  30. package/dist/server/services/quota-backoff-service.js +7 -0
  31. package/dist/server/services/update-maintenance-service.js +44 -0
  32. package/dist/server/services/wakeup-service.js +21 -3
  33. package/dist/server/services/workspace-creation-mcp-service.js +16 -0
  34. package/dist/shared/auto-loop-prompts.js +12 -12
  35. package/dist/shared/launch-instructions.js +57 -0
  36. package/dist/shared/progress-bar-prompts.js +7 -0
  37. package/dist/shared/progress-bar-tools.js +73 -0
  38. package/dist/shared/progress-bars.js +3 -0
  39. package/docker-compose.vps.yml +60 -0
  40. package/docs/deployment-vps.md +506 -0
  41. package/docs/getting-started.md +9 -0
  42. package/package.json +8 -1
  43. package/scripts/kobo-gh +9 -0
  44. package/scripts/kobo-github-app-token +190 -0
  45. package/scripts/kobo-install +392 -0
  46. package/scripts/kobo-update +499 -0
  47. package/src/client/dist/pwa/assets/ActivityFeed-DiUxnmGJ.js +2 -0
  48. package/src/client/dist/pwa/assets/{AutoLoopFinalReviewSettings-Bbrq02Xt.js → AutoLoopFinalReviewSettings-f4VDR5dd.js} +1 -1
  49. package/src/client/dist/pwa/assets/{ChangelogPage-BJfkAVYp.js → ChangelogPage-dEWpjTlp.js} +1 -1
  50. package/src/client/dist/pwa/assets/ComparisonPanel-Bwbqgvr4.js +2 -0
  51. package/src/client/dist/pwa/assets/CreatePage-CCCCAhrx.js +2 -0
  52. package/src/client/dist/pwa/assets/{DashboardPage-DZYsAh5y.js → DashboardPage-3byhBITf.js} +1 -1
  53. package/src/client/dist/pwa/assets/DiffViewer-Bj_NTfCF.js +8 -0
  54. package/src/client/dist/pwa/assets/{DocumentsPanel-BfPlfiwU.js → DocumentsPanel-xyLDV3T1.js} +1 -1
  55. package/src/client/dist/pwa/assets/{DrawerToggleButton-DG1Qjqk1.js → DrawerToggleButton-pwBSstQP.js} +1 -1
  56. package/src/client/dist/pwa/assets/GitPanel-DvIdrkVw.js +3 -0
  57. package/src/client/dist/pwa/assets/{HealthPage-_VXG1Zeo.js → HealthPage-BSwhZQRo.js} +1 -1
  58. package/src/client/dist/pwa/assets/MainLayout-DZJ9BvZw.js +2 -0
  59. package/src/client/dist/pwa/assets/{MainLayout-DnmUxNk7.css → MainLayout-DoAEgANU.css} +1 -1
  60. package/src/client/dist/pwa/assets/{MemoryPanel-CVlvGZXZ.js → MemoryPanel-ChR032zP.js} +1 -1
  61. package/src/client/dist/pwa/assets/{MemoryProposalList-SJsnzUAM.js → MemoryProposalList-BRKSoS4K.js} +1 -1
  62. package/src/client/dist/pwa/assets/{QBtnDropdown-DBgGsN-D.js → QBtnDropdown-o3QH0Bb8.js} +1 -1
  63. package/src/client/dist/pwa/assets/{QExpansionItem-DKgaMJ_l.js → QExpansionItem-BhFHiyq9.js} +1 -1
  64. package/src/client/dist/pwa/assets/{QScrollArea-D3OCsq3g.js → QScrollArea-BsNY6slB.js} +1 -1
  65. package/src/client/dist/pwa/assets/QSelect-bEq3g_io.js +1 -0
  66. package/src/client/dist/pwa/assets/{QTooltip-X5ps8Opw.js → QTooltip-xYZ6KNu2.js} +1 -1
  67. package/src/client/dist/pwa/assets/{SchedulePanel-Ww_FVlW5.js → SchedulePanel-DIFv_FTY.js} +1 -1
  68. package/src/client/dist/pwa/assets/{SearchPage-B87WEHf3.js → SearchPage-BqTBIt5E.js} +1 -1
  69. package/src/client/dist/pwa/assets/SettingsPage-BLD0xWFB.js +9 -0
  70. package/src/client/dist/pwa/assets/{SplitWorkspacePage-B99AJUbG.js → SplitWorkspacePage-D6r2pl5g.js} +1 -1
  71. package/src/client/dist/pwa/assets/{TerminalPanel-DnA1MXwu.js → TerminalPanel-CladK9Vv.js} +9 -9
  72. package/src/client/dist/pwa/assets/{ToolCallItem-2vFggaur.js → ToolCallItem-CdzEME4w.js} +3 -3
  73. package/src/client/dist/pwa/assets/{TouchPan-BSU3z0aO.js → TouchPan-B4gQPwGN.js} +1 -1
  74. package/src/client/dist/pwa/assets/{TourReplayButton-DQwS2xdD.js → TourReplayButton-B0j6AZwU.js} +1 -1
  75. package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-CM1tGDje.js → WorkspaceAttentionLabels-CeVhoidC.js} +1 -1
  76. package/src/client/dist/pwa/assets/WorkspacePage-CK5XCuG_.js +4 -0
  77. package/src/client/dist/pwa/assets/{WorkspacePage-B248nKIU.css → WorkspacePage-w2bF8wTd.css} +1 -1
  78. package/src/client/dist/pwa/assets/{browser-DPsupI9Q.js → browser-B8tNScg1.js} +1 -1
  79. package/src/client/dist/pwa/assets/{build-path-tree-CbIdt-p9.js → build-path-tree-kmVAcvs_.js} +1 -1
  80. package/src/client/dist/pwa/assets/custom-sounds-CJwd-8TL.js +1 -0
  81. package/src/client/dist/pwa/assets/{de-D3Ycvf_F.js → de-BaO6kbdO.js} +2 -2
  82. package/src/client/dist/pwa/assets/{editor.api-DvgAtnOj.js → editor.api-CgbPVW9s.js} +1 -1
  83. package/src/client/dist/pwa/assets/{efforts-nmxfTVQD.js → efforts-y7Wf_Rgd.js} +2 -2
  84. package/src/client/dist/pwa/assets/{es-TgeS5898.js → es-DTvSsbD0.js} +2 -2
  85. package/src/client/dist/pwa/assets/{expand-template-Brn5O4a1.js → expand-template-BFbbjj7X.js} +1 -1
  86. package/src/client/dist/pwa/assets/{format-DfJ3Fv1i.js → format-BETfc4z9.js} +1 -1
  87. package/src/client/dist/pwa/assets/{formatters-B30bNFhq.js → formatters-C22K9vlC.js} +1 -1
  88. package/src/client/dist/pwa/assets/{fr-CJafryNb.js → fr-BH7V66Yz.js} +2 -2
  89. package/src/client/dist/pwa/assets/{freemarker2-DnZe74tc.js → freemarker2-DiRVvp9O.js} +1 -1
  90. package/src/client/dist/pwa/assets/{handlebars-CmuMfxzO.js → handlebars-BLYRroSQ.js} +1 -1
  91. package/src/client/dist/pwa/assets/{html-BD7OJFGX.js → html-DAW2vteY.js} +1 -1
  92. package/src/client/dist/pwa/assets/i18n-DnuTmw3O.js +1 -0
  93. package/src/client/dist/pwa/assets/index-CbyHfF8I.js +20 -0
  94. package/src/client/dist/pwa/assets/{it-C5mN8Y61.js → it-DZGbkkoy.js} +2 -2
  95. package/src/client/dist/pwa/assets/{javascript-Y-OrgcmW.js → javascript-Bb5J8n0k.js} +1 -1
  96. package/src/client/dist/pwa/assets/{liquid-BqOkKePH.js → liquid-Dqx3adnJ.js} +1 -1
  97. package/src/client/dist/pwa/assets/{mdx-D-58x2h0.js → mdx-D_7igLV3.js} +1 -1
  98. package/src/client/dist/pwa/assets/{monaco-contributions-BW4ZMKO6.js → monaco-contributions-C-0CbrAy.js} +1 -1
  99. package/src/client/dist/pwa/assets/{monaco.contribution-DJWqzNG-.js → monaco.contribution-c6JPBEZI.js} +2 -2
  100. package/src/client/dist/pwa/assets/network-auth-BE4qbvWm.js +1 -0
  101. package/src/client/dist/pwa/assets/notify-theme-BVFBBGrJ.js +1 -0
  102. package/src/client/dist/pwa/assets/{project-color-gB5yeZPN.js → project-color-CHOR2jB5.js} +1 -1
  103. package/src/client/dist/pwa/assets/{python-BkBj1wDC.js → python-Dqubw1F5.js} +1 -1
  104. package/src/client/dist/pwa/assets/{razor-DOOJ_0b8.js → razor-BQphc9Kt.js} +1 -1
  105. package/src/client/dist/pwa/assets/{typescript-Bheo43G5.js → typescript-Dep00l0X.js} +1 -1
  106. package/src/client/dist/pwa/assets/{use-search-index-status-CBpnyV-R.js → use-search-index-status-CQHHvHwM.js} +1 -1
  107. package/src/client/dist/pwa/assets/{use-tours-BHNasCvg.js → use-tours-IVbXTmz2.js} +2 -2
  108. package/src/client/dist/pwa/assets/{use-worktree-restore-CgABsWno.js → use-worktree-restore-B3s5xDGR.js} +1 -1
  109. package/src/client/dist/pwa/assets/{workspace-templates-BnFkigYg.js → workspace-templates-CyuTpztP.js} +1 -1
  110. package/src/client/dist/pwa/assets/{xml-vHt2BD26.js → xml-BMS2dvaF.js} +1 -1
  111. package/src/client/dist/pwa/assets/{yaml-BvhLPzP9.js → yaml-CYw92aic.js} +1 -1
  112. package/src/client/dist/pwa/index.html +1 -1
  113. package/src/client/dist/pwa/sw.js +1 -1
  114. package/src/mcp-server/README.md +88 -1
  115. package/src/mcp-server/kobo-tasks-server.ts +34 -6
  116. package/src/mcp-server/progress-bar-client.ts +27 -0
  117. package/src/mcp-server/workspace-dialogue-client.ts +26 -7
  118. package/src/client/dist/pwa/assets/ActivityFeed-tJmJhmtl.js +0 -2
  119. package/src/client/dist/pwa/assets/ComparisonPanel-BfP8APKY.js +0 -2
  120. package/src/client/dist/pwa/assets/CreatePage-BbtNfo0x.js +0 -2
  121. package/src/client/dist/pwa/assets/DiffViewer-4Q-GJgyA.js +0 -8
  122. package/src/client/dist/pwa/assets/GitPanel-ECnbb_rx.js +0 -3
  123. package/src/client/dist/pwa/assets/MainLayout-BH6grTuW.js +0 -2
  124. package/src/client/dist/pwa/assets/QSelect-Cb_s_nox.js +0 -1
  125. package/src/client/dist/pwa/assets/SettingsPage-BAZLGUyg.js +0 -9
  126. package/src/client/dist/pwa/assets/WorkspacePage-CQN6EKIn.js +0 -4
  127. package/src/client/dist/pwa/assets/custom-sounds-DR68eyF8.js +0 -1
  128. package/src/client/dist/pwa/assets/i18n-BY4JeBjk.js +0 -1
  129. package/src/client/dist/pwa/assets/index-C5fAw9ll.js +0 -20
  130. package/src/client/dist/pwa/assets/network-auth-BGPRhUa6.js +0 -1
  131. package/src/client/dist/pwa/assets/notify-theme-DZDzfxph.js +0 -1
@@ -0,0 +1,499 @@
1
+ #!/usr/bin/env python3
2
+ """Update a standalone Kōbō Compose deployment. Python 3.10+; standard library only."""
3
+ import argparse
4
+ import fcntl
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ import re
9
+ import shutil
10
+ import signal
11
+ import subprocess
12
+ import sys
13
+ import tarfile
14
+ import tempfile
15
+ import time
16
+ import urllib.error
17
+ import urllib.request
18
+ import uuid
19
+ from datetime import datetime, timezone
20
+
21
+ REPO = 'https://github.com/loicngr/kobo.git'
22
+ VERSION = re.compile(r'\d+\.\d+\.\d+')
23
+ COMPOSE_ENV_KEYS = ('KOBO_DOMAIN', 'KOBO_PROJECTS_DIR', 'TRAEFIK_NETWORK',
24
+ 'TRAEFIK_ENTRYPOINT', 'TRAEFIK_CERTRESOLVER')
25
+ BUSY_JS = r"""
26
+ const db = new (require('better-sqlite3'))('/root/.config/kobo/kobo.db', {readonly:true});
27
+ const sessions = db.prepare("SELECT count(*) n FROM agent_sessions WHERE status = 'running'").get().n;
28
+ const workspaces = db.prepare("SELECT count(*) n FROM workspaces WHERE status IN ('extracting','brainstorming','executing','compacting')").get().n;
29
+ console.log(JSON.stringify({sessions, workspaces}));
30
+ """
31
+ MIGRATION_JS = r"""
32
+ import Database from 'better-sqlite3';
33
+ import { runMigrations } from './dist/server/db/migrations.js';
34
+ const db = new Database('/check/kobo.db');
35
+ db.pragma('foreign_keys = ON');
36
+ runMigrations(db);
37
+ if (db.pragma('integrity_check', {simple:true}) !== 'ok') throw new Error('SQLite integrity check failed');
38
+ if (db.pragma('foreign_key_check').length) throw new Error('SQLite foreign key check failed');
39
+ db.close();
40
+ """
41
+ API_JS = r"""
42
+ (async () => {
43
+ const fs = require('fs');
44
+ const token = JSON.parse(fs.readFileSync('/root/.config/kobo/settings.json','utf8')).global.networkAccessToken;
45
+ if (!token) throw new Error('Missing network token');
46
+ const headers = {'X-Kobo-Token': token};
47
+ const r = await fetch(`http://127.0.0.1:${process.env.SERVER_PORT || 3000}/api/workspaces`,{headers,signal:AbortSignal.timeout(10000)});
48
+ if (!r.ok) throw new Error('Authenticated API check failed');
49
+ console.log('API authenticated: OK');
50
+ })().catch(() => process.exit(1));
51
+ """
52
+ MAINTENANCE_JS = r"""
53
+ (async () => {
54
+ const fs = require('fs');
55
+ const token = JSON.parse(fs.readFileSync('/root/.config/kobo/settings.json','utf8')).global.networkAccessToken;
56
+ if (!token) throw new Error('Missing network token');
57
+ const method = process.argv[1];
58
+ const options = {method, headers:{'X-Kobo-Token':token,'Content-Type':'application/json'}, signal:AbortSignal.timeout(10000)};
59
+ if (method !== 'GET') options.body = JSON.stringify({owner:process.argv[2]});
60
+ const r = await fetch(`http://127.0.0.1:${process.env.SERVER_PORT || 3000}/api/maintenance/update`,options);
61
+ const body = await r.json().catch(() => ({}));
62
+ console.log(JSON.stringify({status:r.status, protocol:body.protocol}));
63
+ })().catch(() => process.exit(1));
64
+ """
65
+
66
+
67
+ class UpdateError(Exception):
68
+ pass
69
+
70
+
71
+ def atomic_write(path, data, mode=0o600):
72
+ fd, name = tempfile.mkstemp(prefix='.' + path.name + '.', dir=path.parent)
73
+ try:
74
+ with os.fdopen(fd, 'wb') as f:
75
+ f.write(data)
76
+ f.flush()
77
+ os.fsync(f.fileno())
78
+ os.chmod(name, mode)
79
+ os.replace(name, path)
80
+ parent_fd = os.open(path.parent, os.O_DIRECTORY)
81
+ try:
82
+ os.fsync(parent_fd)
83
+ finally:
84
+ os.close(parent_fd)
85
+ finally:
86
+ if os.path.exists(name):
87
+ os.unlink(name)
88
+
89
+
90
+ def image_config(text, image):
91
+ # Refuse an unexpected/multi-service structure instead of modifying other services.
92
+ pattern = r'(?m)^ image: [^\n]+$'
93
+ if len(re.findall(pattern, text)) != 1:
94
+ raise UpdateError('Unexpected Compose layout: exactly one indented image entry is required.')
95
+ return re.sub(pattern, ' image: ' + image, text)
96
+
97
+
98
+ def normalize_version(value):
99
+ value = value.removeprefix('v')
100
+ if not VERSION.fullmatch(value):
101
+ raise UpdateError('Expected a stable version such as 2.1.16 (or latest).')
102
+ return value
103
+
104
+
105
+ def say(message):
106
+ print(message, flush=True)
107
+
108
+
109
+ class Updater:
110
+ def __init__(self, stack):
111
+ self.stack = stack.resolve()
112
+ self.compose_file = self.stack / 'compose.yaml'
113
+ self.log = None
114
+ self.job = None
115
+ self.state = {}
116
+ self.container = None
117
+ self.project_name = None
118
+ self.public_url = None
119
+ self.maintenance_owner = uuid.uuid4().hex
120
+
121
+ def command_environment(self, args):
122
+ env = os.environ.copy()
123
+ if args[:2] == ['docker', 'compose']:
124
+ # Deployment inputs come from the selected stack's .env, never an
125
+ # unrelated caller shell. Keep Docker context and authentication.
126
+ for key in list(env):
127
+ if key in COMPOSE_ENV_KEYS or key.startswith('COMPOSE_'):
128
+ del env[key]
129
+ return env
130
+
131
+ def run(self, args, timeout=120, data=None):
132
+ try:
133
+ result = subprocess.run(args, input=data, stdout=subprocess.PIPE,
134
+ stderr=subprocess.PIPE, timeout=timeout, check=False,
135
+ env=self.command_environment(args))
136
+ except subprocess.TimeoutExpired as e:
137
+ raise UpdateError(f'Timeout: {args[0]}') from e
138
+ if result.returncode:
139
+ if self.log:
140
+ with self.log.open('ab') as f:
141
+ f.write(result.stderr)
142
+ raise UpdateError(f'Failed: {args[0]} (code {result.returncode}). See the private update log.')
143
+ return result.stdout.decode().strip()
144
+
145
+ def logged(self, args, timeout=3600):
146
+ with self.log.open('ab') as f:
147
+ try:
148
+ subprocess.run(args, stdout=f, stderr=subprocess.STDOUT,
149
+ timeout=timeout, check=True, env=self.command_environment(args))
150
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
151
+ raise UpdateError(f'Failed: {args[0]}. Log: {self.log}') from e
152
+
153
+ def compose(self, *args, file=None):
154
+ project = ['-p', self.project_name] if self.project_name else []
155
+ return ['docker', 'compose', '--project-directory', str(self.stack),
156
+ '--env-file', str(self.stack / '.env'),
157
+ *project, '-f', str(file or self.compose_file), *args]
158
+
159
+ def record(self, phase, **values):
160
+ self.state.update(values, phase=phase)
161
+ atomic_write(self.job / 'state.json', json.dumps(self.state, indent=2).encode())
162
+
163
+ def idle(self):
164
+ counts = json.loads(self.run(['docker', 'exec', self.container, 'node', '-e', BUSY_JS]))
165
+ if counts['sessions'] or counts['workspaces']:
166
+ raise UpdateError('Kōbō is busy: wait for agents or stop them in the UI, then retry.')
167
+
168
+ def maintenance(self, method):
169
+ reply = json.loads(self.run(['docker', 'exec', self.container, 'node', '-e',
170
+ MAINTENANCE_JS, method, self.maintenance_owner]))
171
+ if method == 'DELETE' and reply['status'] == 409:
172
+ return # Our reservation was never acquired or has already been released.
173
+ if reply['status'] == 404 or (reply['status'] == 200 and reply.get('protocol') != 1):
174
+ raise UpdateError('Runtime lacks atomic update reservation. Use --prepare and perform the first upgrade manually; see the VPS guide.')
175
+ if reply['status'] != 200:
176
+ raise UpdateError(f'Update reservation {method} refused (HTTP {reply["status"]}).')
177
+
178
+ def inspect(self):
179
+ ids = self.run(self.compose('ps', '-q', 'kobo')).splitlines()
180
+ if len(ids) != 1:
181
+ raise UpdateError('Exactly one running Kōbō container is required.')
182
+ self.container = ids[0]
183
+ info = json.loads(self.run(['docker', 'inspect', self.container]))[0]
184
+ if not info['State']['Running']:
185
+ raise UpdateError('The Kōbō container is stopped.')
186
+ mounts = [m for m in info['Mounts'] if m['Destination'] == '/root']
187
+ if len(mounts) != 1 or mounts[0]['Type'] != 'volume':
188
+ raise UpdateError('Expected a named /root volume; no changes made.')
189
+ if any(m['Destination'].startswith('/root/') for m in info['Mounts']):
190
+ raise UpdateError('Nested /root mounts would escape the home backup. This layout is not supported.')
191
+ config = json.loads(self.run(self.compose('config', '--format', 'json')))
192
+ self.config_snapshot = config
193
+ if set(config['services']) != {'kobo'}:
194
+ raise UpdateError('This script requires a standalone stack containing only the kobo service.')
195
+ configured_mounts = config['services']['kobo'].get('volumes', [])
196
+ home = [v for v in configured_mounts if v['target'] == '/root']
197
+ if (len(home) != 1 or home[0]['type'] != 'volume'
198
+ or config.get('volumes', {}).get(home[0]['source'], {}).get('name') != mounts[0]['Name']
199
+ or any(v['target'].startswith('/root/') for v in configured_mounts)):
200
+ raise UpdateError('Compose home mounts do not match the running persistent volume.')
201
+ self.project_name = config['name']
202
+ env = config['services']['kobo'].get('environment', {})
203
+ host = env.get('KOBO_NETWORK_ACCESS_PROXY_HOST', '')
204
+ if not re.fullmatch(r'[A-Za-z0-9.-]+(?::[0-9]+)?', host):
205
+ raise UpdateError('Set KOBO_NETWORK_ACCESS_PROXY_HOST to your HTTPS hostname.')
206
+ if env.get('KOBO_HOME') != '/root/.config/kobo':
207
+ raise UpdateError('This updater requires KOBO_HOME=/root/.config/kobo and a named /root volume.')
208
+ self.public_url = 'https://' + host
209
+ if config['services']['kobo']['image'] != info['Config']['Image']:
210
+ raise UpdateError('Compose and the running container use different images. Reconcile them before updating.')
211
+ self.old_image = info['Image'] # immutable digest, for backup/migration helpers
212
+ self.volume = mounts[0]['Name']
213
+ self.original = self.compose_file.read_bytes()
214
+ self.original_mode = self.compose_file.stat().st_mode & 0o777
215
+ image_config(self.original.decode(), 'kobo:validation')
216
+ self.installed = normalize_version(self.run(['docker', 'exec', self.container,
217
+ 'node', '-p', 'require("./package.json").version']))
218
+ self.idle()
219
+ self.run(['docker', 'compose', 'version'])
220
+ used_kib = int(self.run(['docker', 'exec', self.container, 'du', '-sk', '/root']).split()[0])
221
+ # Sources/build layers plus archive + migration copy; compression is not assumed.
222
+ required = 6 * 1024**3 + used_kib * 1024 * 2
223
+ docker_root = self.run(['docker', 'info', '--format', '{{.DockerRootDir}}'])
224
+ docker_free = int(self.run(['docker', 'run', '--rm', '--network', 'none',
225
+ '--read-only', '--entrypoint', 'node', '--mount',
226
+ f'type=bind,src={docker_root},dst=/docker,readonly', self.old_image,
227
+ '-e', "const s=require('fs').statfsSync('/docker');console.log(s.bavail*s.bsize)"]))
228
+ if min(shutil.disk_usage(self.stack).free, docker_free) < required:
229
+ raise UpdateError(f'Insufficient disk space: need at least {required / 1024**3:.1f} GiB free.')
230
+ return info['Config']['Image']
231
+
232
+ def resolve(self, requested):
233
+ if requested == 'latest':
234
+ with urllib.request.urlopen('https://registry.npmjs.org/@loicngr%2fkobo/latest', timeout=30) as r:
235
+ requested = json.load(r)['version']
236
+ version = normalize_version(requested)
237
+ if tuple(map(int, version.split('.'))) < tuple(map(int, self.installed.split('.'))):
238
+ raise UpdateError('Downgrade refused: an older image may be incompatible with the current database.')
239
+ # Public repository: no App token is needed to discover/retrieve a release.
240
+ refs = self.run(['git', '-c', 'credential.helper=', 'ls-remote', '--exit-code',
241
+ REPO, f'refs/tags/v{version}', f'refs/tags/v{version}^{{}}'])
242
+ pairs = [line.split() for line in refs.splitlines()]
243
+ sha = next((sha for sha, ref in pairs if ref.endswith('^{}')), pairs[0][0])
244
+ if not re.fullmatch('[a-f0-9]{40}', sha):
245
+ raise UpdateError('Invalid release reference.')
246
+ return version, sha
247
+
248
+ def build(self, version, sha):
249
+ source = self.job / 'source'
250
+ source.mkdir()
251
+ self.logged(['git', 'init', str(source)])
252
+ self.logged(['git', '-C', str(source), '-c', 'credential.helper=', 'fetch',
253
+ '--depth=1', REPO, f'refs/tags/v{version}'])
254
+ self.logged(['git', '-C', str(source), 'checkout', '--detach', 'FETCH_HEAD'])
255
+ actual = self.run(['git', '-C', str(source), 'rev-parse', 'HEAD'])
256
+ if actual != sha or json.loads((source / 'package.json').read_text())['version'] != version:
257
+ raise UpdateError('Release changed or package.json does not match the tag.')
258
+ # Never overwrite a tag still used by production or a rollback configuration.
259
+ build_id = self.job.name.lower()
260
+ base = f'kobo:{version}-{sha[:12]}-base-{build_id}'
261
+ image = f'kobo:{version}-{sha[:12]}-vps-{build_id}'
262
+ self.logged(['docker', 'build', '--progress=plain', '-t', base, str(source)])
263
+ overlay = self.job / 'overlay'
264
+ overlay.mkdir()
265
+ (overlay / 'Dockerfile').write_text(
266
+ f'FROM {base}\nRUN docker compose version >/dev/null 2>&1 || '
267
+ '(apt-get update && apt-get install -y --no-install-recommends docker-compose-plugin '
268
+ '&& rm -rf /var/lib/apt/lists/*)\n')
269
+ self.logged(['docker', 'build', '--progress=plain', '-t', image, str(overlay)])
270
+ self.run(['docker', 'run', '--rm', '--network', 'none', '--entrypoint', 'docker',
271
+ image, 'compose', 'version'])
272
+ self.run(['docker', 'run', '--rm', '--network', 'none', '--entrypoint', 'node', image,
273
+ '-e', "require('better-sqlite3')(':memory:').close();require('node-pty')"])
274
+ return image
275
+
276
+ def backup(self):
277
+ archive = self.job / 'home.tar.gz'
278
+ with archive.open('xb') as f, self.log.open('ab') as err:
279
+ try:
280
+ subprocess.run(['docker', 'run', '--rm', '--network', 'none', '--read-only',
281
+ '--mount', f'type=volume,src={self.volume},dst=/data,readonly',
282
+ '--entrypoint', 'tar', self.old_image, '-C', '/data', '-czf', '-', '.'],
283
+ stdout=f, stderr=err, check=True, timeout=3600)
284
+ f.flush()
285
+ os.fsync(f.fileno())
286
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
287
+ raise UpdateError('Failed: backup. The previous version will be restarted.') from e
288
+ check = self.job / 'migration-check'
289
+ check.mkdir()
290
+ # Read the complete gzip stream; extract only DB files as regular files, never symlinks.
291
+ with tarfile.open(archive, 'r|gz') as tar:
292
+ for member in tar:
293
+ if member.name in {f'./.config/kobo/{name}' for name in ('kobo.db','kobo.db-wal','kobo.db-shm')}:
294
+ if not member.isfile():
295
+ raise UpdateError('Unexpected SQLite file in backup.')
296
+ with tar.extractfile(member) as src, (check / Path(member.name).name).open('wb') as dst:
297
+ shutil.copyfileobj(src, dst)
298
+ if not (check / 'kobo.db').is_file():
299
+ raise UpdateError('Backup does not contain the Kōbō database.')
300
+ return check
301
+
302
+ def verify(self, version):
303
+ self.container = self.run(self.compose('ps', '-q', 'kobo'))
304
+ self.run(['docker', 'exec', self.container, 'node', '-e', API_JS])
305
+ last_error = None
306
+ for attempt in range(15):
307
+ try:
308
+ with urllib.request.urlopen(self.public_url + '/api/health', timeout=10) as r:
309
+ health = json.load(r)
310
+ if health.get('version') != version or health.get('status') != 'ok':
311
+ raise UpdateError('Unexpected HTTPS version or health status.')
312
+ try:
313
+ with urllib.request.urlopen(self.public_url + '/api/workspaces', timeout=10):
314
+ raise UpdateError('The public API no longer requires authentication.')
315
+ except urllib.error.HTTPError as e:
316
+ if e.code != 401:
317
+ raise UpdateError('Unexpected public API authentication response.') from e
318
+ return
319
+ except Exception as e:
320
+ last_error = e
321
+ if attempt < 14:
322
+ time.sleep(2)
323
+ raise UpdateError('HTTPS verification failed.') from last_error
324
+
325
+ def execute(self, requested, check_only=False, prepare_only=False):
326
+ for state in (self.stack / 'updates').glob('*/state.json'):
327
+ if json.loads(state.read_text())['phase'] not in {'complete','prepared','aborted'}:
328
+ raise UpdateError(f'Interrupted update requires review: {state}. See RECOVERY.md in that directory.')
329
+ old_tag = self.inspect()
330
+ version, sha = self.resolve(requested)
331
+ say(f'Installed: {self.installed} — target: {version} ({sha[:12]}).')
332
+ if not prepare_only:
333
+ self.maintenance('GET')
334
+ if check_only:
335
+ say('Checks passed. No shutdown or update performed.')
336
+ return
337
+ if version == self.installed and not prepare_only:
338
+ say('This version is already installed. Nothing to do.')
339
+ return
340
+ updates = self.stack / 'updates'
341
+ updates.mkdir(mode=0o700, exist_ok=True)
342
+ self.job = Path(tempfile.mkdtemp(prefix=datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ-'), dir=updates))
343
+ self.log = self.job / 'update.log'
344
+ self.log.touch(mode=0o600)
345
+ (self.job / 'compose.before.yaml').write_bytes(self.original)
346
+ self.record('building', previous_image=old_tag, previous_digest=self.old_image,
347
+ volume=self.volume, version=version, sha=sha,
348
+ maintenance_owner=self.maintenance_owner,
349
+ backup_complete=False, new_version_start_attempted=False)
350
+ self.recovery_notes()
351
+ stopped = False
352
+ launching = False
353
+ maintenance_requested = False
354
+ try:
355
+ say(f'Building the image; Kōbō remains available. Log: {self.log}')
356
+ image = self.build(version, sha)
357
+ candidate = self.job / 'compose.next.yaml'
358
+ candidate.write_text(image_config(self.original.decode(), image))
359
+ self.run(self.compose('config', '--quiet', file=candidate))
360
+ if prepare_only:
361
+ self.record('prepared', image=image)
362
+ say(f'Image ready: {image}. Service was not interrupted.')
363
+ return
364
+ self.idle() # Agents may have started while the build ran.
365
+ if self.compose_file.read_bytes() != self.original:
366
+ raise UpdateError('Compose changed during the build. Aborting without deployment.')
367
+ if json.loads(self.run(self.compose('config', '--format', 'json'))) != self.config_snapshot:
368
+ raise UpdateError('Resolved Compose configuration changed during the build (environment or files). Aborting.')
369
+ self.record('reserving', image=image)
370
+ # Set before sending: a lost response can still mean the server acquired our reservation.
371
+ maintenance_requested = True
372
+ self.maintenance('POST')
373
+ self.record('stopping')
374
+ stopped = True # Also recover if the stop command itself fails/interruption arrives.
375
+ say('Stopping Kōbō, backing up /root and testing migrations on a database copy…')
376
+ self.run(self.compose('stop', '-t', '60', 'kobo'))
377
+ info = json.loads(self.run(['docker', 'inspect', self.container]))[0]
378
+ if info['State']['Running']:
379
+ raise UpdateError('The container did not stop.')
380
+ check = self.backup()
381
+ self.record('backed-up', backup_complete=True)
382
+ self.logged(['docker', 'run', '--rm', '--network', 'none',
383
+ '--mount', f'type=bind,src={check},dst=/check', '--entrypoint', 'node',
384
+ image, '--input-type=module', '-e', MIGRATION_JS], timeout=180)
385
+ atomic_write(self.compose_file, candidate.read_bytes(), self.original_mode)
386
+ # From here migrations/automatic jobs may have run. Never automatically restore old data.
387
+ launching = True
388
+ self.record('launching', new_version_start_attempted=True)
389
+ say('Starting the new version and checking the API and HTTPS…')
390
+ self.logged(self.compose('up', '-d', '--no-deps', '--wait', '--wait-timeout', '180', 'kobo'), timeout=240)
391
+ self.verify(version)
392
+ self.record('complete')
393
+ say(f'Kōbō {version} is up to date. Backup: {self.job / "home.tar.gz"}')
394
+ except BaseException:
395
+ if stopped and not launching:
396
+ # Only safe before any process from the new version has used production data.
397
+ try:
398
+ # A failed/interrupted CLI does not prove the daemon's stop completed.
399
+ # Keep admission reserved until the old process is confirmed gone.
400
+ info = json.loads(self.run(['docker', 'inspect', self.container]))[0]
401
+ if info['State']['Running']:
402
+ self.run(['docker', 'stop', '-t', '60', self.container])
403
+ info = json.loads(self.run(['docker', 'inspect', self.container]))[0]
404
+ if info['State']['Running']:
405
+ raise UpdateError('Previous process shutdown is unconfirmed.')
406
+ atomic_write(self.compose_file, self.original, self.original_mode)
407
+ self.logged(self.compose('up', '-d', '--no-deps', '--wait', '--wait-timeout', '180', 'kobo'), timeout=240)
408
+ self.record('aborted')
409
+ say('Update aborted; previous version restarted, original database unchanged.')
410
+ except BaseException:
411
+ self.record('recovery-required')
412
+ say(f'Restart requires review. Instructions: {self.job / "RECOVERY.md"}')
413
+ elif launching:
414
+ self.record('recovery-required')
415
+ say(f'New version requires diagnosis. Data retained; no automatic database rollback. See {self.job / "RECOVERY.md"}')
416
+ else:
417
+ try:
418
+ if maintenance_requested:
419
+ self.maintenance('DELETE')
420
+ self.record('aborted')
421
+ except BaseException:
422
+ self.record('recovery-required')
423
+ say(f'Update reservation may still be active. Instructions: {self.job / "RECOVERY.md"}')
424
+ raise
425
+
426
+ def recovery_notes(self):
427
+ import shlex
428
+ restart = shlex.join(self.compose('up', '-d', '--no-deps', '--wait', 'kobo'))
429
+ (self.job / 'RECOVERY.md').write_text(f'''# Kōbō update recovery
430
+
431
+ Private log: `{self.job / 'update.log'}`. Durable phase: `state.json`.
432
+ Previous configuration: `compose.before.yaml`.
433
+ Previous immutable image: `{self.old_image}`.
434
+ Original volume: `{self.volume}`.
435
+ The archive `home.tar.gz` is validated only when backup_complete=true.
436
+ new_version_start_attempted indicates whether a new-version launch was attempted.
437
+ Projects outside /root are NOT included in this archive; back them up separately.
438
+ Old images, volumes and backups are never automatically deleted.
439
+
440
+ ## Interrupted admission reservation
441
+ The runtime reservation blocks agent starts and defers scheduled deliveries until
442
+ the container stops or the owner explicitly releases it. It does not expire.
443
+ If phase is reserving/stopping/recovery-required, inspect whether the old process
444
+ is still running. Before any new-version launch, stop it with Docker and confirm
445
+ it has exited before restarting the previous configuration below; this clears
446
+ the in-memory reservation. Do not release admission while a Docker stop may still
447
+ be in progress. The reservation identifier is state.json's maintenance_owner.
448
+
449
+ ## Before a new-version launch
450
+ If no new-version launch was attempted, restore compose.before.yaml to
451
+ `{self.compose_file}` and run:
452
+
453
+ ```sh
454
+ {restart}
455
+ ```
456
+
457
+ Verify service health before setting state.json phase to aborted to allow a retry.
458
+
459
+ ## After a new-version launch (launching/recovery-required)
460
+ Do not run an older image against a potentially migrated database.
461
+ Inspect the private log and Compose logs (which may contain credentials).
462
+ A proxy error can leave a working backend: check for new messages and resumed
463
+ work before considering a restore.
464
+ To roll back fully, stop Kōbō, back up the current volume too, extract home.tar.gz
465
+ into a NEW empty Docker volume and adapt the previous Compose configuration to
466
+ use that restored volume and the previous immutable image above. Keep the current
467
+ volume. Database rollback does not undo external agent or project changes.
468
+ After recovery and health verification, set state.json phase to complete or aborted.
469
+ ''')
470
+
471
+
472
+ def main():
473
+ parser = argparse.ArgumentParser(description='Update a Kōbō VPS Compose stack (latest stable release by default).')
474
+ parser.add_argument('version', nargs='?', default='latest', help='latest, 2.1.16 or v2.1.16')
475
+ modes = parser.add_mutually_exclusive_group()
476
+ modes.add_argument('--check', action='store_true', help='check without building or interrupting the service')
477
+ modes.add_argument('--prepare', action='store_true', help='build and test the image without deploying')
478
+ parser.add_argument('--directory', type=Path, default=Path.cwd(),
479
+ help='deployment directory containing compose.yaml and .env (default: current directory)')
480
+ args = parser.parse_args()
481
+ os.umask(0o077)
482
+ for sig in (signal.SIGTERM, signal.SIGHUP):
483
+ signal.signal(sig, lambda *_: (_ for _ in ()).throw(KeyboardInterrupt()))
484
+ try:
485
+ with (args.directory / '.update.lock').open('a') as lock:
486
+ try:
487
+ fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
488
+ except BlockingIOError:
489
+ raise UpdateError('Another update is already running.')
490
+ Updater(args.directory).execute(args.version, args.check, args.prepare)
491
+ except (Exception, KeyboardInterrupt) as e:
492
+ message = str(e) if isinstance(e, UpdateError) else type(e).__name__
493
+ say(f'Stopped: {message}.')
494
+ return 1
495
+ return 0
496
+
497
+
498
+ if __name__ == '__main__':
499
+ sys.exit(main())
@@ -0,0 +1,2 @@
1
+ import{M as e,S as t,dt as n,lt as r,p as i,pt as a,v as o}from"./reactivity.esm-bundler-DY1JkAEA.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-Bqg71Vll.js";import{$ as ne,K as re,M as E,f as ie,z as ae}from"./dom-BtYTvLeN.js";import{l as oe,t as D,u as se}from"./QBtn-BUSdPaf4.js";import{Kt as ce,O as le,Qt as ue,Ut as O,_t as de,at as fe,cn as pe,dn as k,j as me,m as he,mt as ge,pt as _e,st as ve,tn as A,v as ye}from"./index-CbyHfF8I.js";import{t as be}from"./QSpinnerDots-xdWyaE1i.js";import{t as xe}from"./QList-Cx1UfaUm.js";import{t as Se}from"./QMarkupTable-UhP7fVUp.js";import{c as j,l as M,s as N}from"./QSelect-bEq3g_io.js";import{t as P}from"./QItemSection-MdJ1_ZK7.js";import{t as Ce}from"./QExpansionItem-BhFHiyq9.js";import{a as F,c as we,r as I,t as L}from"./ToolCallItem-CdzEME4w.js";import{t as Te}from"./QScrollArea-BsNY6slB.js";import{a as R}from"./use-tours-IVbXTmz2.js";import{f as Ee,g as z,m as De,p as Oe,u as ke}from"./use-worktree-restore-B3s5xDGR.js";import{t as B}from"./QTooltip-xYZ6KNu2.js";import{n as V}from"./render-chat-markdown-CafzCcmu.js";import{n as H}from"./WorkspacePage-CK5XCuG_.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=ae({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,E.passive))}function C(){i!==void 0&&(i.removeEventListener(`scroll`,m,E.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)),ie(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)||le(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:n}=ue(),r=x(()=>{let e=`agent.error.${t.item.category}`;return n(e)?e:`agent.error.other`});return(t,n)=>(u(),S(`div`,ze,[g(oe,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),w(`div`,Be,[w(`div`,Ve,a(t.$t(r.value)),1),w(`div`,G,a(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,n=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,a(e.$t(n.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=O(e,{signal:n.signal}).then(async e=>{if(!e.ok)throw await ce(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}=ue(),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}=ue(),d=fe(),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:le(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 re=x(()=>T(s.turn.ts)),E=x(()=>s.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),ie=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}),ae=x(()=>{let e=s.turn.ts,t=ie.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)}),se=x(()=>ae.value!==``);function ce(e){return we(e.origin,Object.values(d.subagents[s.workspaceId]??{}),e.type===`tool`?e.toolCallId:void 0)}function O(e){return e||l(`chat.subagentActivity`)}function de(e){return(e.type===`text`||e.type===`thinking`||e.type===`tool`)&&e.origin?.kind===`subagent`}let pe=x(()=>{let e=[],t=new Map;for(let n of s.turn.items){if(!de(n)){e.push({type:`item`,key:W(n),item:n});continue}let r=ce(n),i=r?.toolUseId??n.origin?.toolCallId??n.origin?.threadId??W(n),a=t.get(i);if(a){a.items.push(n);continue}let o={type:`subagent`,key:`subagent:${W(n)}`,items:[n],subagentKey:i,subagentName:r?.description||null};t.set(i,o),e.push(o)}return e});function k(e){e.origin&&f?.({origin:e.origin})}let me=x(()=>s.turn.items.filter(e=>e.type===`tool`).length);return(i,o)=>(u(),S(`div`,{ref_key:`cardEl`,ref:p,class:r([`turn-card`,{"turn-card--user":t.turn.speaker===`user`,"turn-card--highlighted":t.highlighted}]),"data-event-ids":E.value,style:n({"--turn-accent":b.value.accent})},[w(`div`,X,[w(`span`,{class:r([`turn-badge`,b.value.badgeClass])},a(b.value.label),3),re.value?(u(),S(`span`,Ze,a(re.value),1)):v(``,!0),se.value?(u(),S(C,{key:1},[g(oe,{name:`arrow_forward`,size:`10px`,color:`kobo-3`,class:`turn-time-arrow`}),w(`span`,Qe,[y(a(ae.value)+` `,1),g(B,null,{default:h(()=>[y(a(e(l)(`chat.lastUpdatedAt`,{time:ae.value})),1)]),_:1})])],64)):v(``,!0),me.value>0?(u(),S(`span`,$e,` · `+a(e(l)(`chat.nActions`,{n:me.value})),1)):v(``,!0)]),w(`div`,et,[(u(!0),S(C,null,te(pe.value,n=>(u(),S(C,{key:n.key},[n.type===`subagent`?(u(),_(Ce,{key:0,class:`turn-subagent-activity`,dense:``,label:O(n.subagentName),caption:e(l)(`chat.nActions`,{n:n.items.length}),"default-opened":t.highlighted&&n.items.some(e=>e.eventIds?.some(e=>E.value.includes(e)))},{header:h(()=>[g(P,null,{default:h(()=>[g(N,{class:`row items-center no-wrap`,title:n.subagentName??void 0},{default:h(()=>[g(oe,{name:`hub`,size:`16px`,class:`q-mr-xs`},{default:h(()=>[g(B,null,{default:h(()=>[y(a(e(l)(`chat.subagentActivity`)),1)]),_:1})]),_:1}),w(`span`,tt,a(O(n.subagentName)),1)]),_:2},1032,[`title`]),g(N,{caption:``},{default:h(()=>[y(a(e(l)(`chat.nActions`,{n:n.items.length})),1)]),_:2},1024)]),_:2},1024),g(P,{side:``},{default:h(()=>[g(D,{flat:``,round:``,dense:``,size:`sm`,icon:`open_in_new`,"aria-label":e(l)(`chat.openSubagentActivity`),onClick:ne(e=>k(n.items[0]),[`stop`])},{default:h(()=>[g(B,null,{default:h(()=>[y(a(e(l)(`chat.openSubagentActivity`)),1)]),_:1})]),_:1},8,[`aria-label`,`onClick`])]),_:2},1024)]),default:h(()=>[w(`div`,nt,[(u(!0),S(C,null,te(n.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},[n.item.type===`text`?(u(),_(I,{key:0,item:n.item},null,8,[`item`])):n.item.type===`thinking`?(u(),_(H,{key:1,item:n.item},null,8,[`item`])):n.item.type===`tool`?(u(),_(L,{key:2,item:n.item},null,8,[`item`])):n.item.type===`user`?(u(),_(Y,{key:3,item:n.item,"workspace-id":t.workspaceId},null,8,[`item`,`workspace-id`])):n.item.type===`session`?(u(),_(We,{key:4,item:n.item},null,8,[`item`])):n.item.type===`error`?(u(),_(He,{key:5,item:n.item},null,8,[`item`])):v(``,!0)],64))],64))),128))]),t.turn.items.length>4?(u(),S(`div`,rt,[g(D,{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(a(e(l)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):v(``,!0)],14,Xe))}}),[[`__scopeId`,`data-v-13bebb3d`]]),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:n}=ue(),r=t,i=de(),s=me(),d=fe(),m=he(),ee=x(()=>m.isCompacting(r.workspaceId)),b=x(()=>d.selectedSessionId),T=x(()=>d.sessions.find(e=>e.id===b.value)?.engineSessionId??null),ne=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:ne.value:!0}let ie=x(()=>(d.activityFeeds[r.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&E(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,source:ye(e.meta?.source),ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),ae=x(()=>{let e=d.workspaces.find(e=>e.id===r.workspaceId);return ve(e?.status)}),le=ke(),pe=``,k=x(()=>{let e=i.eventsFor(r.workspaceId),t=i.timestampsFor(r.workspaceId),n=i.sessionIdsFor(r.workspaceId),a=i.eventIdsFor(r.workspaceId),o=[],c=[],l=[];for(let r=0;r<e.length;r++)E(n[r])&&(o.push(e[r]),c.push(t[r]),l.push(a[r]??null));let u=`${r.workspaceId}::${b.value??`*`}`;u!==pe&&(le=ke(),pe=u);let d=Ee(le,o,c,ae.value,l);return Fe(De(d,ie.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?i.eventsFor(r.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 r=t[e];if(r){if(r.type===`text`&&!r.streaming)return{key:`text:${r.messageId}`,text:r.text};if(r.type===`tool`)return r.result?{key:`tool:${r.toolCallId}:end`,text:r.result.isError?n(`activity.a11y.toolFailed`,{name:r.name}):n(`activity.a11y.toolFinished`,{name:r.name})}:{key:`tool:${r.toolCallId}:start`,text:n(`activity.a11y.toolStarted`,{name:r.name})}}}}return null}),Se=o(``);l(xe,(e,t)=>{if(!e||e.key===t?.key)return;let r=e.key.startsWith(`text:`)?n(`activity.a11y.agentSaid`,{text:e.text.slice(0,bt)}):e.text;Se.value=r});let j=o(null),M=o(!0),N=o(!1),P=o(``),F=_e(),we=_e(),I=_e(),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=r.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[r.workspaceId]||m.isSyncPending(r.workspaceId)||b.value!==null&&V.value.has(W(r.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(r.workspaceId,e))??!0:i.hasMoreOlderFor(r.workspaceId)}function Be(e,t,n){je.value.set(W(e,t),n)}function Ve(e){if(!b.value)return i.oldestIdFor(e);let t=i.eventIdsFor(e),n=i.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(()=>[r.workspaceId,b.value],Ue,{flush:`sync`});async function K(){let e=r.workspaceId,t=b.value,n=Ve(e);if(!n)return;let a=F.begin();P.value=``,N.value=!0;try{let r=j.value;if(await f(),!F.isCurrent(a))return;let o=r?.getScroll().verticalSize??0,s=r?.getScroll().verticalPosition??0,c=new URLSearchParams({before:n,limit:`200`});t&&c.set(`session`,t);let l=await O(`/api/workspaces/${e}/events?${c.toString()}`,{signal:a});if(!l.ok)throw await ce(l);let u=await l.json();if(!F.isCurrent(a))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),i.prepend(e,g,_,{oldestId:ee,hasMoreOlder:t?i.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:ye(n.source)}})}if(await f(),r&&F.isCurrent(a)){let e=r.getScroll().verticalSize-o;if(e>0){let t=Math.max(s+e,250);r.setScrollPosition(`vertical`,t,0)}}}catch(e){if(ge(e)||!F.isCurrent(a))return;P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] failed to load older events:`,e)}finally{F.isCurrent(a)&&(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!==r.workspaceId||!t.eventId)return;Q(),M.value=!1;let n=I.begin(),a=r.workspaceId;try{let e=new URLSearchParams({around:t.eventId,limit:`200`});t.sessionId&&e.set(`session`,t.sessionId);let o=await O(`/api/workspaces/${a}/events?${e}`,{signal:n});if(!o.ok)throw await ce(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===r.workspaceId),u=c.filter(e=>e.type===`user:message`&&e.workspaceId===r.workspaceId);i.reset(r.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(r.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`,source:ye(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(ge(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=r.workspaceId;try{let t=Le(k.value,X.value);if(t<0)for(let n=0;n<15;n++){if(e!==r.workspaceId)return;if(!ze())break;let n=k.value.length;if(await He(),await f(),e!==r.workspaceId)return;if(X.value+=k.value.length-n,t=Le(k.value,X.value),t>=0)break}t>=0&&e===r.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=i.sessionIdsFor(r.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:r.workspaceId,sessionId:b.value,eventId:e}})),Je())}),l([Nt,()=>ie.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(()=>i.liveAppendCountFor(r.workspaceId,E)),$=()=>`${r.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([()=>r.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=r.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 a=we.begin();H.set(n,a),P.value=``;try{let n=await O(`/api/workspaces/${e}/events?session=${encodeURIComponent(t)}&limit=500`,{signal:a});if(!n.ok)throw await ce(n);let r=await n.json();if(!we.isCurrent(a))return;let o=r.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,r.hasMore),l.length>0&&i.prepend(e,l,u,{oldestId:o[0].id,hasMoreOlder:i.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:ye(n.source)}})}}catch(e){if(ge(e)||!we.isCurrent(a)){(!H.has(n)||H.get(n)===a)&&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)===a&&(H.delete(n),V.value.delete(n))}}let Lt=x(()=>ie.value.filter(e=>e.sender===`user`).length),Rt=x(()=>(d.activityFeeds[r.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,n)=>Ae.value?(u(),S(`div`,at,[g(be,{size:`40px`,color:`primary`})])):(u(),S(`div`,ot,[e(d).sessionsErrors[r.workspaceId]?(u(),S(`div`,st,[y(a(t.$t(`activity.sessionsError`,{message:e(d).sessionsErrors[r.workspaceId]}))+` `,1),g(D,{flat:``,dense:``,label:t.$t(`common.retry`),onClick:n[0]||=t=>e(d).fetchSessions(r.workspaceId)},null,8,[`label`])])):v(``,!0),P.value?(u(),S(`div`,ct,[y(a(t.$t(`activity.historyError`,{message:P.value}))+` `,1),g(D,{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`)},a(Se.value),9,lt),g(re,{name:`fade`},{default:h(()=>[ee.value?(u(),S(`div`,ut,[g(be,{size:`18px`,color:`primary`}),w(`span`,dt,a(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(se,{size:`sm`}),y(` `+a(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":r.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(oe,{name:`forum`,size:`28px`}),w(`div`,mt,a(t.$t(`activity.empty`)),1),w(`div`,ht,a(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`},a(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(oe,{name:`person`,size:`16px`}),w(`span`,vt,a(rt.value.content),1)],8,_t)):v(``,!0),w(`div`,yt,[M.value?v(``,!0):(u(),_(D,{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(D,{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};
@@ -1 +1 @@
1
- import{M as e,pt as t,v as n}from"./reactivity.esm-bundler-DY1JkAEA.js";import{J as r,L as i,Y as a,_ as o,d as s,f as c,l,p as u,r as d,u as f,y as p}from"./runtime-core.esm-bundler-Bqg71Vll.js";import{t as m}from"./QBtn-BUSdPaf4.js";import{At as h,Zt as g,_n as _,en as v,f as y,hn as b,j as x,jt as S,l as C}from"./index-C5fAw9ll.js";import{t as w}from"./QSelect-Cb_s_nox.js";import{a as T}from"./use-tours-BHNasCvg.js";import{i as E,t as D}from"./permissionModes-DLWiBfdb.js";import{t as O}from"./efforts-nmxfTVQD.js";function k(e,t){return e.engine!==t.engine||e.model!==t.model||e.reasoningEffort!==t.reasoningEffort||e.agentPermissionMode!==t.agentPermissionMode}var A={class:`text-h6`},ee={class:`text-body2 text-kobo-2 q-mt-xs`},te={class:`row q-col-gutter-sm q-mb-md`},ne={class:`col-12 col-sm-6`},j={class:`col-12 col-sm-6`},M={class:`col-12 col-sm-6`},N={class:`col-12 col-sm-6`},P={key:0,class:`text-caption text-kobo-3 q-mt-xs`},F={class:`text-caption text-kobo-3 q-mt-xs`},I={class:`text-caption text-kobo-3 q-mt-xs`},L=T(p({__name:`StartReviewDialog`,props:{modelValue:{type:Boolean},loading:{type:Boolean},workspace:{},canReturnToSession:{type:Boolean},currentSession:{},mode:{},scheduledConfiguration:{}},emits:[`update:modelValue`,`submit`],setup(e,{emit:p}){let T=e,L=p,{t:R}=g(),z=x(),B=l(()=>T.mode===`auto-loop`),V=n(``),H=n(!1),U=n(!1),W=n(`claude-code`),G=n(`auto`),K=n(`auto`),q=n(`bypass`),J=l(()=>B.value||U.value?`plan`:q.value),Y=l(()=>({engine:W.value,model:G.value,reasoningEffort:K.value,agentPermissionMode:J.value})),X=l(()=>{let e=T.currentSession;return B.value||U.value||!!T.workspace&&k(Y.value,T.workspace)||!!e?.engine&&e.engine!==W.value||G.value!==`auto`&&!!e?.model&&e.model!==G.value}),Z=l({get:()=>H.value||X.value,set:e=>{H.value=e}}),re=l(()=>[{value:`claude-code`,label:R(`workspacePage.engineClaude`)},{value:`codex`,label:R(`workspacePage.engineCodex`)}]),Q=l(()=>{let e=(E[W.value]??[]).map(e=>({value:e.value,label:R(e.i18nLabelKey)}));T.workspace?.engine===W.value&&!e.some(e=>e.value===T.workspace?.model)&&e.push({value:T.workspace.model,label:T.workspace.model});let t=T.scheduledConfiguration;return t?.engine===W.value&&!e.some(e=>e.value===t.model)&&e.push({value:t.model,label:t.model}),e}),ie=l(()=>(O[W.value]??[]).map(e=>({value:e.value,label:R(e.i18nLabelKey)}))),$=l(()=>(D[W.value]??[]).map(e=>({value:e,label:R(`agentPermissionMode.${e}`)})));r(W,()=>{let e=T.workspace,t=z.global.defaultModelByEngine[W.value];G.value=e?.engine===W.value?e.model:Q.value.some(e=>e.value===t)?t:`auto`,K.value=e?.engine===W.value?e.reasoningEffort:`auto`,$.value.some(e=>e.value===q.value)||(q.value=`plan`)},{flush:`sync`}),r([()=>T.modelValue,()=>T.workspace?.id],([e])=>{if(!e)return;let t=B.value?T.scheduledConfiguration:null;V.value=t?.additionalInstructions??``,H.value=!1,U.value=B.value,T.workspace&&(W.value=T.workspace.engine,G.value=T.workspace.model,K.value=T.workspace.reasoningEffort,q.value=T.workspace.agentPermissionMode),t&&(W.value=t.engine,G.value=t.model,K.value=t.reasoningEffort)},{immediate:!0}),r(()=>T.canReturnToSession,e=>{!e&&!B.value&&(U.value=!1)});function ae(){T.loading||L(`update:modelValue`,!1)}function oe(){!T.loading&&T.workspace&&L(`submit`,{...Y.value,additionalInstructions:V.value,newSession:Z.value,returnToSession:B.value||U.value&&T.canReturnToSession})}return(n,r)=>(i(),s(v,{"model-value":e.modelValue,persistent:e.loading,"onUpdate:modelValue":r[7]||=e=>L(`update:modelValue`,e)},{default:a(()=>[o(b,{class:`review-card text-kobo-1`},{default:a(()=>[o(_,null,{default:a(()=>[f(`div`,A,t(n.$t(B.value?`autoLoop.finalReview.title`:`review.title`)),1),f(`div`,ee,t(n.$t(B.value?`autoLoop.finalReview.description`:`review.subtitle`)),1)]),_:1}),o(y,{dark:``}),o(_,{class:`q-pt-md`},{default:a(()=>[f(`div`,te,[f(`div`,ne,[o(w,{modelValue:W.value,"onUpdate:modelValue":r[0]||=e=>W.value=e,options:re.value,label:n.$t(`engine.select`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,j,[o(w,{modelValue:G.value,"onUpdate:modelValue":r[1]||=e=>G.value=e,options:Q.value,label:n.$t(`engine.model`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,M,[o(w,{modelValue:K.value,"onUpdate:modelValue":r[2]||=e=>K.value=e,options:ie.value,label:n.$t(`engine.effort`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,N,[o(w,{"model-value":J.value,options:$.value,label:n.$t(`agentPermissionMode.label`),disable:e.loading||U.value,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``,"onUpdate:modelValue":r[3]||=e=>q.value=e},null,8,[`model-value`,`options`,`label`,`disable`]),U.value?(i(),u(`div`,P,t(n.$t(`review.readOnlyForced`)),1)):c(``,!0)])]),o(S,{modelValue:V.value,"onUpdate:modelValue":r[4]||=e=>V.value=e,type:`textarea`,disable:e.loading,label:n.$t(`review.additionalInstructions`),placeholder:n.$t(`review.additionalInstructionsPlaceholder`),rows:4,outlined:``,dark:``,color:`primary`},null,8,[`modelValue`,`disable`,`label`,`placeholder`])]),_:1}),B.value?c(``,!0):(i(),s(_,{key:0,class:`q-pt-none`},{default:a(()=>[o(C,{modelValue:Z.value,"onUpdate:modelValue":r[5]||=e=>Z.value=e,disable:e.loading||X.value,label:n.$t(`review.newSession`),color:`primary`,dark:``},null,8,[`modelValue`,`disable`,`label`]),f(`div`,F,t(n.$t(X.value?`review.newSessionRequired`:`review.newSessionHint`)),1),Z.value?(i(),u(d,{key:0},[o(C,{modelValue:U.value,"onUpdate:modelValue":r[6]||=e=>U.value=e,disable:e.loading||!e.canReturnToSession,label:n.$t(`review.returnToSession`),color:`primary`,dark:``,class:`q-mt-md`},null,8,[`modelValue`,`disable`,`label`]),f(`div`,I,t(n.$t(e.canReturnToSession?`review.returnToSessionHint`:`review.returnUnavailable`)),1)],64)):c(``,!0)]),_:1})),o(h,{align:`right`,class:`q-pa-md`},{default:a(()=>[o(m,{flat:``,"no-caps":``,label:n.$t(`review.cancel`),color:`kobo-2`,disable:e.loading,onClick:ae},null,8,[`label`,`disable`]),o(m,{"no-caps":``,color:`primary`,label:n.$t(B.value?`autoLoop.finalReview.save`:`review.start`),loading:e.loading,disable:!e.workspace||e.loading,onClick:oe},null,8,[`label`,`loading`,`disable`])]),_:1})]),_:1})]),_:1},8,[`model-value`,`persistent`]))}}),[[`__scopeId`,`data-v-a4419350`]]),R={class:`final-review-settings`},z={class:`text-caption text-kobo-3`},B=T(p({__name:`AutoLoopFinalReviewSettings`,props:{modelValue:{},workspace:{},disabled:{type:Boolean},loading:{type:Boolean}},emits:[`update:modelValue`],setup(a,{emit:l}){let d=a,p=l,{t:h}=g(),_=n(!1),v=n(null);r(()=>d.modelValue,e=>{JSON.stringify(e)===JSON.stringify(v.value)&&(v.value=null)}),r(()=>d.workspace.id,()=>{_.value=!1,v.value=null});function y(e){d.disabled||d.loading||(e?_.value=!0:p(`update:modelValue`,null))}function b(e){!d.disabled&&!d.loading&&e.engine&&e.model&&e.reasoningEffort&&(v.value={engine:e.engine,model:e.model,reasoningEffort:e.reasoningEffort,additionalInstructions:e.additionalInstructions??``},p(`update:modelValue`,v.value),_.value=!1)}return(n,r)=>(i(),u(`div`,R,[o(C,{"model-value":!!a.modelValue,disable:a.disabled||a.loading,color:`primary`,label:e(h)(`autoLoop.finalReview.enabled`),"onUpdate:modelValue":y},null,8,[`model-value`,`disable`,`label`]),f(`p`,z,t(e(h)(`autoLoop.finalReview.hint`)),1),a.modelValue?(i(),s(m,{key:0,flat:``,dense:``,"no-caps":``,disable:a.disabled||a.loading,label:e(h)(`autoLoop.finalReview.configure`),onClick:r[0]||=e=>_.value=!0},null,8,[`disable`,`label`])):c(``,!0),o(L,{modelValue:_.value,"onUpdate:modelValue":r[1]||=e=>_.value=e,mode:`auto-loop`,loading:a.loading??!1,workspace:a.workspace,"scheduled-configuration":v.value??a.modelValue,"can-return-to-session":!0,onSubmit:b},null,8,[`modelValue`,`loading`,`workspace`,`scheduled-configuration`])]))}}),[[`__scopeId`,`data-v-b7f36f83`]]);export{L as n,B as t};
1
+ import{M as e,pt as t,v as n}from"./reactivity.esm-bundler-DY1JkAEA.js";import{J as r,L as i,Y as a,_ as o,d as s,f as c,l,p as u,r as d,u as f,y as p}from"./runtime-core.esm-bundler-Bqg71Vll.js";import{t as m}from"./QBtn-BUSdPaf4.js";import{Mt as h,Qt as g,f as _,gn as v,j as y,jt as b,l as x,tn as S,vn as C}from"./index-CbyHfF8I.js";import{t as w}from"./QSelect-bEq3g_io.js";import{a as T}from"./use-tours-IVbXTmz2.js";import{i as E,t as D}from"./permissionModes-DLWiBfdb.js";import{t as O}from"./efforts-y7Wf_Rgd.js";function k(e,t){return e.engine!==t.engine||e.model!==t.model||e.reasoningEffort!==t.reasoningEffort||e.agentPermissionMode!==t.agentPermissionMode}var A={class:`text-h6`},j={class:`text-body2 text-kobo-2 q-mt-xs`},ee={class:`row q-col-gutter-sm q-mb-md`},te={class:`col-12 col-sm-6`},ne={class:`col-12 col-sm-6`},M={class:`col-12 col-sm-6`},N={class:`col-12 col-sm-6`},P={key:0,class:`text-caption text-kobo-3 q-mt-xs`},F={class:`text-caption text-kobo-3 q-mt-xs`},I={class:`text-caption text-kobo-3 q-mt-xs`},L=T(p({__name:`StartReviewDialog`,props:{modelValue:{type:Boolean},loading:{type:Boolean},workspace:{},canReturnToSession:{type:Boolean},currentSession:{},mode:{},scheduledConfiguration:{}},emits:[`update:modelValue`,`submit`],setup(e,{emit:p}){let T=e,L=p,{t:R}=g(),z=y(),B=l(()=>T.mode===`auto-loop`),V=n(``),H=n(!1),U=n(!1),W=n(`claude-code`),G=n(`auto`),K=n(`auto`),q=n(`bypass`),J=l(()=>B.value||U.value?`plan`:q.value),Y=l(()=>({engine:W.value,model:G.value,reasoningEffort:K.value,agentPermissionMode:J.value})),X=l(()=>{let e=T.currentSession;return B.value||U.value||!!T.workspace&&k(Y.value,T.workspace)||!!e?.engine&&e.engine!==W.value||G.value!==`auto`&&!!e?.model&&e.model!==G.value}),Z=l({get:()=>H.value||X.value,set:e=>{H.value=e}}),re=l(()=>[{value:`claude-code`,label:R(`workspacePage.engineClaude`)},{value:`codex`,label:R(`workspacePage.engineCodex`)}]),Q=l(()=>{let e=(E[W.value]??[]).map(e=>({value:e.value,label:R(e.i18nLabelKey)}));T.workspace?.engine===W.value&&!e.some(e=>e.value===T.workspace?.model)&&e.push({value:T.workspace.model,label:T.workspace.model});let t=T.scheduledConfiguration;return t?.engine===W.value&&!e.some(e=>e.value===t.model)&&e.push({value:t.model,label:t.model}),e}),ie=l(()=>(O[W.value]??[]).map(e=>({value:e.value,label:R(e.i18nLabelKey)}))),$=l(()=>(D[W.value]??[]).map(e=>({value:e,label:R(`agentPermissionMode.${e}`)})));r(W,()=>{let e=T.workspace,t=z.global.defaultModelByEngine[W.value];G.value=e?.engine===W.value?e.model:Q.value.some(e=>e.value===t)?t:`auto`,K.value=e?.engine===W.value?e.reasoningEffort:`auto`,$.value.some(e=>e.value===q.value)||(q.value=`plan`)},{flush:`sync`}),r([()=>T.modelValue,()=>T.workspace?.id],([e])=>{if(!e)return;let t=B.value?T.scheduledConfiguration:null;V.value=t?.additionalInstructions??``,H.value=!1,U.value=B.value,T.workspace&&(W.value=T.workspace.engine,G.value=T.workspace.model,K.value=T.workspace.reasoningEffort,q.value=T.workspace.agentPermissionMode),t&&(W.value=t.engine,G.value=t.model,K.value=t.reasoningEffort)},{immediate:!0}),r(()=>T.canReturnToSession,e=>{!e&&!B.value&&(U.value=!1)});function ae(){T.loading||L(`update:modelValue`,!1)}function oe(){!T.loading&&T.workspace&&L(`submit`,{...Y.value,additionalInstructions:V.value,newSession:Z.value,returnToSession:B.value||U.value&&T.canReturnToSession})}return(n,r)=>(i(),s(S,{"model-value":e.modelValue,persistent:e.loading,"onUpdate:modelValue":r[7]||=e=>L(`update:modelValue`,e)},{default:a(()=>[o(v,{class:`review-card text-kobo-1`},{default:a(()=>[o(C,null,{default:a(()=>[f(`div`,A,t(n.$t(B.value?`autoLoop.finalReview.title`:`review.title`)),1),f(`div`,j,t(n.$t(B.value?`autoLoop.finalReview.description`:`review.subtitle`)),1)]),_:1}),o(_,{dark:``}),o(C,{class:`q-pt-md`},{default:a(()=>[f(`div`,ee,[f(`div`,te,[o(w,{modelValue:W.value,"onUpdate:modelValue":r[0]||=e=>W.value=e,options:re.value,label:n.$t(`engine.select`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,ne,[o(w,{modelValue:G.value,"onUpdate:modelValue":r[1]||=e=>G.value=e,options:Q.value,label:n.$t(`engine.model`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,M,[o(w,{modelValue:K.value,"onUpdate:modelValue":r[2]||=e=>K.value=e,options:ie.value,label:n.$t(`engine.effort`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,N,[o(w,{"model-value":J.value,options:$.value,label:n.$t(`agentPermissionMode.label`),disable:e.loading||U.value,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``,"onUpdate:modelValue":r[3]||=e=>q.value=e},null,8,[`model-value`,`options`,`label`,`disable`]),U.value?(i(),u(`div`,P,t(n.$t(`review.readOnlyForced`)),1)):c(``,!0)])]),o(h,{modelValue:V.value,"onUpdate:modelValue":r[4]||=e=>V.value=e,type:`textarea`,disable:e.loading,label:n.$t(`review.additionalInstructions`),placeholder:n.$t(`review.additionalInstructionsPlaceholder`),rows:4,outlined:``,dark:``,color:`primary`},null,8,[`modelValue`,`disable`,`label`,`placeholder`])]),_:1}),B.value?c(``,!0):(i(),s(C,{key:0,class:`q-pt-none`},{default:a(()=>[o(x,{modelValue:Z.value,"onUpdate:modelValue":r[5]||=e=>Z.value=e,disable:e.loading||X.value,label:n.$t(`review.newSession`),color:`primary`,dark:``},null,8,[`modelValue`,`disable`,`label`]),f(`div`,F,t(n.$t(X.value?`review.newSessionRequired`:`review.newSessionHint`)),1),Z.value?(i(),u(d,{key:0},[o(x,{modelValue:U.value,"onUpdate:modelValue":r[6]||=e=>U.value=e,disable:e.loading||!e.canReturnToSession,label:n.$t(`review.returnToSession`),color:`primary`,dark:``,class:`q-mt-md`},null,8,[`modelValue`,`disable`,`label`]),f(`div`,I,t(n.$t(e.canReturnToSession?`review.returnToSessionHint`:`review.returnUnavailable`)),1)],64)):c(``,!0)]),_:1})),o(b,{align:`right`,class:`q-pa-md`},{default:a(()=>[o(m,{flat:``,"no-caps":``,label:n.$t(`review.cancel`),color:`kobo-2`,disable:e.loading,onClick:ae},null,8,[`label`,`disable`]),o(m,{"no-caps":``,color:`primary`,label:n.$t(B.value?`autoLoop.finalReview.save`:`review.start`),loading:e.loading,disable:!e.workspace||e.loading,onClick:oe},null,8,[`label`,`loading`,`disable`])]),_:1})]),_:1})]),_:1},8,[`model-value`,`persistent`]))}}),[[`__scopeId`,`data-v-a4419350`]]),R={class:`final-review-settings`},z={class:`text-caption text-kobo-3`},B=T(p({__name:`AutoLoopFinalReviewSettings`,props:{modelValue:{},workspace:{},disabled:{type:Boolean},loading:{type:Boolean}},emits:[`update:modelValue`],setup(a,{emit:l}){let d=a,p=l,{t:h}=g(),_=n(!1),v=n(null);r(()=>d.modelValue,e=>{JSON.stringify(e)===JSON.stringify(v.value)&&(v.value=null)}),r(()=>d.workspace.id,()=>{_.value=!1,v.value=null});function y(e){d.disabled||d.loading||(e?_.value=!0:p(`update:modelValue`,null))}function b(e){!d.disabled&&!d.loading&&e.engine&&e.model&&e.reasoningEffort&&(v.value={engine:e.engine,model:e.model,reasoningEffort:e.reasoningEffort,additionalInstructions:e.additionalInstructions??``},p(`update:modelValue`,v.value),_.value=!1)}return(n,r)=>(i(),u(`div`,R,[o(x,{"model-value":!!a.modelValue,disable:a.disabled||a.loading,color:`primary`,label:e(h)(`autoLoop.finalReview.enabled`),"onUpdate:modelValue":y},null,8,[`model-value`,`disable`,`label`]),f(`p`,z,t(e(h)(`autoLoop.finalReview.hint`)),1),a.modelValue?(i(),s(m,{key:0,flat:``,dense:``,"no-caps":``,disable:a.disabled||a.loading,label:e(h)(`autoLoop.finalReview.configure`),onClick:r[0]||=e=>_.value=!0},null,8,[`disable`,`label`])):c(``,!0),o(L,{modelValue:_.value,"onUpdate:modelValue":r[1]||=e=>_.value=e,mode:`auto-loop`,loading:a.loading??!1,workspace:a.workspace,"scheduled-configuration":v.value??a.modelValue,"can-return-to-session":!0,onSubmit:b},null,8,[`modelValue`,`loading`,`workspace`,`scheduled-configuration`])]))}}),[[`__scopeId`,`data-v-b7f36f83`]]);export{L as n,B as t};
@@ -1 +1 @@
1
- import{M as e,pt as t,v as n}from"./reactivity.esm-bundler-DY1JkAEA.js";import{L as r,P as i,Y as a,_ as o,d as s,f as c,p as l,r as u,u as d,y as f,z as p}from"./runtime-core.esm-bundler-Bqg71Vll.js";import{t as m}from"./QBtn-BUSdPaf4.js";import{Gt as h,Ht as g,_n as _,a as v,hn as y}from"./index-C5fAw9ll.js";import{a as b,i as x,n as S}from"./use-tours-BHNasCvg.js";import{n as C}from"./render-chat-markdown-CafzCcmu.js";import{t as w}from"./QChip-Be16M4Ig.js";import{t as T}from"./QPage-CqkGHokx.js";import{t as E}from"./DrawerToggleButton-DG1Qjqk1.js";import{t as D}from"./TourReplayButton-DQwS2xdD.js";var O={class:`row items-center q-mb-md`},k={class:`text-h6 q-ml-sm`},A={key:0,class:`text-kobo-3 text-center q-pa-lg`},j={key:1,class:`text-negative text-center q-pa-lg`},M={key:2,class:`text-kobo-3 text-center q-pa-lg`},N={key:3,class:`column q-gutter-md`,"data-tour":`changelog-list`},P={key:0,class:`text-caption text-kobo-3`},F={class:`row items-center q-mb-sm`},I={class:`text-subtitle1 text-kobo-2`,style:{"font-family":`var(--kobo-font-mono, monospace)`}},L=[`innerHTML`],R=b(f({__name:`ChangelogPage`,setup(f){let b=v(),{scheduleAutoRun:R}=S(),z=n([]),B=n(``),V=n(!1),H=n(null);function U(e){return C(e)}async function W(){V.value=!0,H.value=null;try{let e=await g(`/api/changelog`);if(!e.ok)throw await h(e);let t=await e.json();B.value=t.currentVersion??``,z.value=t.versions??[]}catch(e){H.value=e instanceof Error?e.message:String(e)}finally{V.value=!1}}return i(()=>{W(),R(`changelog`)}),(n,i)=>(r(),s(T,{class:`q-pa-md`,style:{"max-width":`900px`,margin:`0 auto`}},{default:a(()=>[d(`div`,O,[o(E,{class:`q-mr-xs`}),o(m,{flat:``,dense:``,round:``,icon:`arrow_back`,onClick:i[0]||=t=>e(b).back()}),d(`div`,k,t(n.$t(`changelog.title`)),1),o(x),o(m,{flat:``,dense:``,icon:`refresh`,loading:V.value,label:n.$t(`common.refresh`),onClick:W},null,8,[`loading`,`label`]),o(D,{"tour-id":`changelog`,class:`q-ml-xs`})]),V.value&&z.value.length===0?(r(),l(`div`,A,t(n.$t(`common.loading`)),1)):H.value?(r(),l(`div`,j,t(H.value),1)):z.value.length===0?(r(),l(`div`,M,t(n.$t(`changelog.empty`)),1)):(r(),l(`div`,N,[B.value?(r(),l(`div`,P,t(n.$t(`changelog.currentVersion`,{version:B.value})),1)):c(``,!0),(r(!0),l(u,null,p(z.value,e=>(r(),s(y,{key:e.version,dark:``,flat:``,bordered:``},{default:a(()=>[o(_,null,{default:a(()=>[d(`div`,F,[d(`div`,I,` v`+t(e.version),1),e.version===B.value?(r(),s(w,{key:0,dense:``,size:`sm`,color:`primary`,"text-color":`kobo-1`,label:n.$t(`changelog.current`),class:`q-ml-sm`},null,8,[`label`])):c(``,!0)]),d(`div`,{class:`changelog-notes`,innerHTML:U(e.notes)},null,8,L)]),_:2},1024)]),_:2},1024))),128))]))]),_:1}))}}),[[`__scopeId`,`data-v-4032778f`]]);export{R as default};
1
+ import{M as e,pt as t,v as n}from"./reactivity.esm-bundler-DY1JkAEA.js";import{L as r,P as i,Y as a,_ as o,d as s,f as c,p as l,r as u,u as d,y as f,z as p}from"./runtime-core.esm-bundler-Bqg71Vll.js";import{t as m}from"./QBtn-BUSdPaf4.js";import{Kt as h,Ut as g,a as _,gn as v,vn as y}from"./index-CbyHfF8I.js";import{a as b,i as x,n as S}from"./use-tours-IVbXTmz2.js";import{n as C}from"./render-chat-markdown-CafzCcmu.js";import{t as w}from"./QChip-Be16M4Ig.js";import{t as T}from"./QPage-CqkGHokx.js";import{t as E}from"./DrawerToggleButton-pwBSstQP.js";import{t as D}from"./TourReplayButton-B0j6AZwU.js";var O={class:`row items-center q-mb-md`},k={class:`text-h6 q-ml-sm`},A={key:0,class:`text-kobo-3 text-center q-pa-lg`},j={key:1,class:`text-negative text-center q-pa-lg`},M={key:2,class:`text-kobo-3 text-center q-pa-lg`},N={key:3,class:`column q-gutter-md`,"data-tour":`changelog-list`},P={key:0,class:`text-caption text-kobo-3`},F={class:`row items-center q-mb-sm`},I={class:`text-subtitle1 text-kobo-2`,style:{"font-family":`var(--kobo-font-mono, monospace)`}},L=[`innerHTML`],R=b(f({__name:`ChangelogPage`,setup(f){let b=_(),{scheduleAutoRun:R}=S(),z=n([]),B=n(``),V=n(!1),H=n(null);function U(e){return C(e)}async function W(){V.value=!0,H.value=null;try{let e=await g(`/api/changelog`);if(!e.ok)throw await h(e);let t=await e.json();B.value=t.currentVersion??``,z.value=t.versions??[]}catch(e){H.value=e instanceof Error?e.message:String(e)}finally{V.value=!1}}return i(()=>{W(),R(`changelog`)}),(n,i)=>(r(),s(T,{class:`q-pa-md`,style:{"max-width":`900px`,margin:`0 auto`}},{default:a(()=>[d(`div`,O,[o(E,{class:`q-mr-xs`}),o(m,{flat:``,dense:``,round:``,icon:`arrow_back`,onClick:i[0]||=t=>e(b).back()}),d(`div`,k,t(n.$t(`changelog.title`)),1),o(x),o(m,{flat:``,dense:``,icon:`refresh`,loading:V.value,label:n.$t(`common.refresh`),onClick:W},null,8,[`loading`,`label`]),o(D,{"tour-id":`changelog`,class:`q-ml-xs`})]),V.value&&z.value.length===0?(r(),l(`div`,A,t(n.$t(`common.loading`)),1)):H.value?(r(),l(`div`,j,t(H.value),1)):z.value.length===0?(r(),l(`div`,M,t(n.$t(`changelog.empty`)),1)):(r(),l(`div`,N,[B.value?(r(),l(`div`,P,t(n.$t(`changelog.currentVersion`,{version:B.value})),1)):c(``,!0),(r(!0),l(u,null,p(z.value,e=>(r(),s(v,{key:e.version,dark:``,flat:``,bordered:``},{default:a(()=>[o(y,null,{default:a(()=>[d(`div`,F,[d(`div`,I,` v`+t(e.version),1),e.version===B.value?(r(),s(w,{key:0,dense:``,size:`sm`,color:`primary`,"text-color":`kobo-1`,label:n.$t(`changelog.current`),class:`q-ml-sm`},null,8,[`label`])):c(``,!0)]),d(`div`,{class:`changelog-notes`,innerHTML:U(e.notes)},null,8,L)]),_:2},1024)]),_:2},1024))),128))]))]),_:1}))}}),[[`__scopeId`,`data-v-4032778f`]]);export{R as default};