@loicngr/kobo 2.1.2 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/server/routes/workspaces.js +39 -0
- package/dist/server/services/agent/engines/claude-code/engine.js +87 -20
- package/dist/server/services/agent/engines/claude-code/event-mapper.js +88 -22
- package/dist/server/services/agent/engines/claude-code/options-builder.js +1 -0
- package/dist/server/services/agent/engines/codex/engine.js +27 -5
- package/dist/server/services/agent/engines/codex/options-builder.js +1 -0
- package/dist/server/services/agent/orchestrator.js +35 -0
- package/dist/server/services/agent/session-controller.js +12 -0
- package/package.json +8 -8
- package/src/client/dist/pwa/assets/ActivityFeed-DdGARu8o.js +8 -0
- package/src/client/dist/pwa/assets/{ChangelogPage-DNRcPRtW.js → ChangelogPage-CGenjiI5.js} +1 -1
- package/src/client/dist/pwa/assets/{ClosePopup-BikuzYle.js → ClosePopup-C1a82pL9.js} +1 -1
- package/src/client/dist/pwa/assets/{ComparisonPanel-DZoDqgJI.js → ComparisonPanel-aXgjEulK.js} +2 -2
- package/src/client/dist/pwa/assets/{CreatePage-wNH35uTG.js → CreatePage-BaB_30xJ.js} +2 -2
- package/src/client/dist/pwa/assets/{DashboardPage-DUyy4SzS.js → DashboardPage-DsbwmO2W.js} +1 -1
- package/src/client/dist/pwa/assets/{DiffViewer-CZ5QwXqi.js → DiffViewer--dUMf7b3.js} +4 -4
- package/src/client/dist/pwa/assets/{DocumentsPanel-sBatgKA-.js → DocumentsPanel-DTjEUYUd.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-Dfvkaro0.js → DrawerToggleButton-Bh4FXdtJ.js} +1 -1
- package/src/client/dist/pwa/assets/GitPanel-PUPLj0M8.js +3 -0
- package/src/client/dist/pwa/assets/{HealthPage-BwXYpczh.js → HealthPage-BHgucifz.js} +1 -1
- package/src/client/dist/pwa/assets/{MainLayout-gHENJEFQ.css → MainLayout-CNmwCnDD.css} +1 -1
- package/src/client/dist/pwa/assets/MainLayout-CQVLnTX3.js +2 -0
- package/src/client/dist/pwa/assets/QBadge-rGILsUYl.js +1 -0
- package/src/client/dist/pwa/assets/QBanner-XE0EMe9L.js +1 -0
- package/src/client/dist/pwa/assets/QBtn-b_baQtAt.js +1 -0
- package/src/client/dist/pwa/assets/QBtnDropdown-DVbxGTNb.js +1 -0
- package/src/client/dist/pwa/assets/QBtnGroup-CUmAgBV8.js +1 -0
- package/src/client/dist/pwa/assets/QBtnToggle-COL46j6W.js +1 -0
- package/src/client/dist/pwa/assets/QChip-Bt_JvnHb.js +1 -0
- package/src/client/dist/pwa/assets/QExpansionItem-CxEBY3jY.js +1 -0
- package/src/client/dist/pwa/assets/QItemSection-Cf6OZEH4.js +1 -0
- package/src/client/dist/pwa/assets/QList-Z90BspT4.js +1 -0
- package/src/client/dist/pwa/assets/QMarkupTable-Bwjknbmw.js +1 -0
- package/src/client/dist/pwa/assets/QPage-BCDXjfVj.js +1 -0
- package/src/client/dist/pwa/assets/QSelect-CMzJDhi-.js +1 -0
- package/src/client/dist/pwa/assets/QSpinnerDots-BsKmccpX.js +1 -0
- package/src/client/dist/pwa/assets/QTooltip-BLhkNT4s.js +1 -0
- package/src/client/dist/pwa/assets/{SchedulePanel-D6LTiRFg.js → SchedulePanel-DTVwe7sX.js} +1 -1
- package/src/client/dist/pwa/assets/{SearchPage-Dj487BdP.js → SearchPage-CtrR7dNr.js} +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-Bl_RNTGi.js +9 -0
- package/src/client/dist/pwa/assets/SplitWorkspacePage-Bac_Pzmm.js +1 -0
- package/src/client/dist/pwa/assets/{TerminalPanel-B9LDe7uG.js → TerminalPanel-D2j5AA8S.js} +1 -1
- package/src/client/dist/pwa/assets/TouchPan-C0EqFcrr.js +1 -0
- package/src/client/dist/pwa/assets/{TourReplayButton-Dtv_UDaY.js → TourReplayButton-DlMvPblb.js} +1 -1
- package/src/client/dist/pwa/assets/WorkspaceAttentionLabels-BAk5l4Ik.js +1 -0
- package/src/client/dist/pwa/assets/WorkspacePage-FScR5YAD.js +4 -0
- package/src/client/dist/pwa/assets/{browser-B15GGwtW.js → browser-DNEbLF-4.js} +1 -1
- package/src/client/dist/pwa/assets/build-path-tree-C0b61nxI.js +1 -0
- package/src/client/dist/pwa/assets/custom-sounds-DE4JLdGG.js +1 -0
- package/src/client/dist/pwa/assets/{de-B_Y4EArT.js → de-DgZ9jKq_.js} +1 -1
- package/src/client/dist/pwa/assets/documents-CALwmao-.js +1 -0
- package/src/client/dist/pwa/assets/{editor.api-DhaLhNl-.js → editor.api-PENN0io6.js} +1 -1
- package/src/client/dist/pwa/assets/efforts-Detdm-FH.js +9 -0
- package/src/client/dist/pwa/assets/{es-DtsTTShP.js → es-zvlgzw9H.js} +1 -1
- package/src/client/dist/pwa/assets/expand-template-ZIVLPdW2.js +1 -0
- package/src/client/dist/pwa/assets/{format-2JisC3s1.js → format-CVI7mr7G.js} +1 -1
- package/src/client/dist/pwa/assets/{formatters-ZVNs2VOl.js → formatters-C_zcfJBt.js} +1 -1
- package/src/client/dist/pwa/assets/{fr-C82MIVrA.js → fr-BICzq59I.js} +1 -1
- package/src/client/dist/pwa/assets/{freemarker2-PYrDqU04.js → freemarker2-BxN_Aq6C.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-BuiY0fFf.js → handlebars-C9UHXtO1.js} +1 -1
- package/src/client/dist/pwa/assets/{html-Du3cjdug.js → html-CTH9wSJr.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-DG_vChZ3.js +1 -0
- package/src/client/dist/pwa/assets/index-C8zAZ-k-.css +1 -0
- package/src/client/dist/pwa/assets/index-fuLr-ZVa.js +20 -0
- package/src/client/dist/pwa/assets/{it-BJNuTcj2.js → it-BIGArQrS.js} +1 -1
- package/src/client/dist/pwa/assets/{javascript-Bb48hPli.js → javascript-WT4BS8p4.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-Be77iQe3.js → liquid-D6dfe-gU.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-CfohNJja.js → mdx-B8pNXNeC.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-CyM4wsIg.js → monaco-contributions-COLfslyi.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco.contribution-BcIfN6Mi.js → monaco.contribution-CDhuQbzi.js} +2 -2
- package/src/client/dist/pwa/assets/{network-auth-B1xAaHmx.js → network-auth-QouPCx6A.js} +1 -1
- package/src/client/dist/pwa/assets/{notify-theme-DvvaSyg1.js → notify-theme-7MsG-MyN.js} +1 -1
- package/src/client/dist/pwa/assets/pending-diff-open-CX24u9gW.js +1 -0
- package/src/client/dist/pwa/assets/{pinia-CTJ3OfP3.js → pinia-BdFjQEqa.js} +1 -1
- package/src/client/dist/pwa/assets/{private.use-form-Bk8ti264.js → private.use-form-vxvbh_VN.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-Be0t12-n.js → project-color-Bo6iEcqN.js} +1 -1
- package/src/client/dist/pwa/assets/{python-BNemmtx8.js → python-D2gb6Jjz.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-3NM8iFVf.js → razor-dksrCQF2.js} +1 -1
- package/src/client/dist/pwa/assets/{reactivity.esm-bundler-Bo9anJjg.js → reactivity.esm-bundler-tuEvQ47f.js} +1 -1
- package/src/client/dist/pwa/assets/{render-chat-markdown-BWJNIDv3.js → render-chat-markdown-px_6WDIX.js} +1 -1
- package/src/client/dist/pwa/assets/runtime-core.esm-bundler-DOiZcasU.js +1 -0
- package/src/client/dist/pwa/assets/{typescript-DwyZ4CF7.js → typescript-DTPyRTrW.js} +1 -1
- package/src/client/dist/pwa/assets/{use-checkbox-2xzX4yYj.js → use-checkbox-Bh9EQhkV.js} +1 -1
- package/src/client/dist/pwa/assets/use-id-b4A2I8LB.js +1 -0
- package/src/client/dist/pwa/assets/use-is-mobile-DLIZxEo9.js +1 -0
- package/src/client/dist/pwa/assets/{use-search-index-status-Bluf9L99.js → use-search-index-status-_nCyACc7.js} +1 -1
- package/src/client/dist/pwa/assets/{use-slide-transition-CiaDm-tR.js → use-slide-transition-C5eDGOmu.js} +1 -1
- package/src/client/dist/pwa/assets/{use-tours-CjASQcG5.js → use-tours-b00RruX_.js} +1 -1
- package/src/client/dist/pwa/assets/use-worktree-restore-BDOZc9fk.js +1 -0
- package/src/client/dist/pwa/assets/{workspace-templates-BxYxJt3S.js → workspace-templates-DV9Njz0T.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-B1UujXdS.js → xml-mxUZCM4r.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-mHN8kNgU.js → yaml-BuN8sUWC.js} +1 -1
- package/src/client/dist/pwa/index.html +9 -9
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-CO5rOaa5.js +0 -8
- package/src/client/dist/pwa/assets/GitPanel-B8e1olfm.js +0 -3
- package/src/client/dist/pwa/assets/MainLayout-DFXIS_wv.js +0 -2
- package/src/client/dist/pwa/assets/QBadge-CQF1smL-.js +0 -1
- package/src/client/dist/pwa/assets/QBanner-bdDA-A5g.js +0 -1
- package/src/client/dist/pwa/assets/QBtn-CcQQrYA_.js +0 -1
- package/src/client/dist/pwa/assets/QBtnDropdown-C-1EhO9J.js +0 -1
- package/src/client/dist/pwa/assets/QBtnGroup-wd5lKRXu.js +0 -1
- package/src/client/dist/pwa/assets/QBtnToggle-BnC8rTql.js +0 -1
- package/src/client/dist/pwa/assets/QChip-D75nKi-L.js +0 -1
- package/src/client/dist/pwa/assets/QExpansionItem-RpMPvfxX.js +0 -1
- package/src/client/dist/pwa/assets/QItemSection-Bt9H35JJ.js +0 -1
- package/src/client/dist/pwa/assets/QList-D5msNbSZ.js +0 -1
- package/src/client/dist/pwa/assets/QMarkupTable-D3REW6RC.js +0 -1
- package/src/client/dist/pwa/assets/QPage-HAaeQ-cv.js +0 -1
- package/src/client/dist/pwa/assets/QSelect-Dpru_T81.js +0 -1
- package/src/client/dist/pwa/assets/QSpinnerDots-DDsPDUH9.js +0 -1
- package/src/client/dist/pwa/assets/QTooltip-C31vXtse.js +0 -1
- package/src/client/dist/pwa/assets/SettingsPage-CJmgFiAa.js +0 -9
- package/src/client/dist/pwa/assets/SplitWorkspacePage-BGSDgGIO.js +0 -1
- package/src/client/dist/pwa/assets/TouchPan-DJqqxDBF.js +0 -1
- package/src/client/dist/pwa/assets/WorkspaceAttentionLabels-VlJCIkwU.js +0 -1
- package/src/client/dist/pwa/assets/WorkspacePage-B_SlX8YB.js +0 -4
- package/src/client/dist/pwa/assets/build-path-tree-CU6uXunR.js +0 -1
- package/src/client/dist/pwa/assets/custom-sounds-CDJsgChY.js +0 -1
- package/src/client/dist/pwa/assets/documents-Cf6nmu8Z.js +0 -1
- package/src/client/dist/pwa/assets/efforts-DoYZi0AZ.js +0 -9
- package/src/client/dist/pwa/assets/engineFeatures-C9XiCfvv.js +0 -1
- package/src/client/dist/pwa/assets/expand-template-LDLM-6iN.js +0 -1
- package/src/client/dist/pwa/assets/i18n-BumeGGx_.js +0 -1
- package/src/client/dist/pwa/assets/index-DolbyIM4.js +0 -20
- package/src/client/dist/pwa/assets/index-VMFLfvp2.css +0 -1
- package/src/client/dist/pwa/assets/pending-diff-open-B6N0vI5n.js +0 -1
- package/src/client/dist/pwa/assets/runtime-core.esm-bundler-CNFKm4ij.js +0 -1
- package/src/client/dist/pwa/assets/use-id-vOr8PRTG.js +0 -1
- package/src/client/dist/pwa/assets/use-is-mobile-CJfSpTHx.js +0 -1
- /package/src/client/dist/pwa/assets/{engineFeatures-B-9jHocY.css → use-worktree-restore-B-9jHocY.css} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ 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.4
|
|
8
|
+
|
|
9
|
+
- chore(deps): update server client and pwa packages
|
|
10
|
+
|
|
11
|
+
## 2.1.3
|
|
12
|
+
|
|
13
|
+
- fix(subagents): reliable lifecycle tracking and a stop button
|
|
14
|
+
|
|
7
15
|
## 2.1.2
|
|
8
16
|
|
|
9
17
|
- fix: read-only returning reviews, resumed-turn stdin, feed and tag filter
|
|
@@ -4621,4 +4621,43 @@ app.post('/:id/interrupt', migrationGuard, async (c) => {
|
|
|
4621
4621
|
return c.json({ error: message }, workspaceErrorStatus(err));
|
|
4622
4622
|
}
|
|
4623
4623
|
});
|
|
4624
|
+
// POST /api/workspaces/:id/subagents/stop - stop one running sub-agent (body
|
|
4625
|
+
// `{ id }`, its canonical card id) or all of them, without interrupting the turn
|
|
4626
|
+
app.post('/:id/subagents/stop', migrationGuard, async (c) => {
|
|
4627
|
+
try {
|
|
4628
|
+
const id = c.req.param('id');
|
|
4629
|
+
const workspace = workspaceService.getWorkspace(id);
|
|
4630
|
+
if (!workspace) {
|
|
4631
|
+
return c.json({ error: `Workspace '${id}' not found` }, 404);
|
|
4632
|
+
}
|
|
4633
|
+
const rawBody = await c.req.text();
|
|
4634
|
+
let body = {};
|
|
4635
|
+
if (rawBody.trim().length > 0) {
|
|
4636
|
+
let parsed;
|
|
4637
|
+
try {
|
|
4638
|
+
parsed = JSON.parse(rawBody);
|
|
4639
|
+
}
|
|
4640
|
+
catch {
|
|
4641
|
+
return c.json({ error: 'Request body must be valid JSON' }, 400);
|
|
4642
|
+
}
|
|
4643
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
4644
|
+
return c.json({ error: 'Request body must be a JSON object' }, 400);
|
|
4645
|
+
}
|
|
4646
|
+
body = parsed;
|
|
4647
|
+
}
|
|
4648
|
+
if (body.id !== undefined && (typeof body.id !== 'string' || body.id.trim().length === 0)) {
|
|
4649
|
+
return c.json({ error: 'id must be a non-empty string when provided' }, 400);
|
|
4650
|
+
}
|
|
4651
|
+
const stopped = agentManager.stopSubagents(id, body.id);
|
|
4652
|
+
return c.json({ stopped });
|
|
4653
|
+
}
|
|
4654
|
+
catch (err) {
|
|
4655
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4656
|
+
if (err instanceof agentManager.StopSubagentsError) {
|
|
4657
|
+
const status = err.code === 'subagent_not_running' ? 404 : 409;
|
|
4658
|
+
return c.json({ error: message, code: err.code }, status);
|
|
4659
|
+
}
|
|
4660
|
+
return c.json({ error: message }, workspaceErrorStatus(err));
|
|
4661
|
+
}
|
|
4662
|
+
});
|
|
4624
4663
|
export default app;
|
|
@@ -124,9 +124,52 @@ export function createClaudeCodeEngine() {
|
|
|
124
124
|
async start(options, onEvent) {
|
|
125
125
|
const abortController = new AbortController();
|
|
126
126
|
const mapperState = createMapperState();
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
|
|
127
|
+
// Active sub-agents keyed by their canonical id (SDK task id when known,
|
|
128
|
+
// else the tool call id), which is also the id passed to `q.stopTask`.
|
|
129
|
+
// One task can surface under both ids (relaunch, kill/restart), so
|
|
130
|
+
// running and terminal events must resolve to the same entry.
|
|
131
|
+
const activeSubagentTaskIds = new Set();
|
|
132
|
+
// tool call id -> SDK task id, learned from events carrying both.
|
|
133
|
+
const subagentTaskIdByToolCallId = new Map();
|
|
134
|
+
// Ambient tasks (SDK `ambient`, e.g. Monitor watchers) are not activity:
|
|
135
|
+
// they never enter `activeSubagentTaskIds`, so they never block
|
|
136
|
+
// turn:completed, keep the input open or arm the stall watchdog. They are
|
|
137
|
+
// tracked apart only so a user interrupt still stops them.
|
|
138
|
+
const ambientSubagentTaskIds = new Set();
|
|
139
|
+
// Canonical ids whose last lifecycle event was terminal: a late
|
|
140
|
+
// `progress` for them is ignored, only a `started` relaunch revives them.
|
|
141
|
+
const terminalSubagentTaskIds = new Set();
|
|
142
|
+
const trackSubagentProgress = (ev) => {
|
|
143
|
+
if (ev.taskId) {
|
|
144
|
+
subagentTaskIdByToolCallId.set(ev.toolCallId, ev.taskId);
|
|
145
|
+
// Merge an entry first tracked under its tool call id alone.
|
|
146
|
+
if (ev.toolCallId !== ev.taskId) {
|
|
147
|
+
activeSubagentTaskIds.delete(ev.toolCallId);
|
|
148
|
+
ambientSubagentTaskIds.delete(ev.toolCallId);
|
|
149
|
+
if (terminalSubagentTaskIds.delete(ev.toolCallId))
|
|
150
|
+
terminalSubagentTaskIds.add(ev.taskId);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const taskId = ev.taskId ?? subagentTaskIdByToolCallId.get(ev.toolCallId);
|
|
154
|
+
const key = taskId ?? ev.toolCallId;
|
|
155
|
+
if (ev.status !== 'running') {
|
|
156
|
+
activeSubagentTaskIds.delete(key);
|
|
157
|
+
ambientSubagentTaskIds.delete(key);
|
|
158
|
+
terminalSubagentTaskIds.add(key);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (ev.phase === 'progress' && terminalSubagentTaskIds.has(key))
|
|
162
|
+
return;
|
|
163
|
+
terminalSubagentTaskIds.delete(key);
|
|
164
|
+
if (ev.ambient) {
|
|
165
|
+
activeSubagentTaskIds.delete(key);
|
|
166
|
+
ambientSubagentTaskIds.add(key);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
ambientSubagentTaskIds.delete(key);
|
|
170
|
+
activeSubagentTaskIds.add(key);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
130
173
|
// Foreground tool calls currently awaiting their tool_result. Cleared on
|
|
131
174
|
// every `result` message: a turn cannot end with a tool still in flight.
|
|
132
175
|
const pendingToolCallIds = new Set();
|
|
@@ -260,6 +303,17 @@ export function createClaudeCodeEngine() {
|
|
|
260
303
|
sdkOptions.pathToClaudeCodeExecutable = explicitBinary;
|
|
261
304
|
const inputStream = new ClaudeInputStream(effectivePrompt);
|
|
262
305
|
const q = query({ prompt: inputStream, options: sdkOptions });
|
|
306
|
+
// Best-effort SDK stop of one background task; failures are ignored.
|
|
307
|
+
const requestStopTask = (taskId) => {
|
|
308
|
+
try {
|
|
309
|
+
void q.stopTask(taskId).catch(() => {
|
|
310
|
+
/* best-effort */
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
/* best-effort */
|
|
315
|
+
}
|
|
316
|
+
};
|
|
263
317
|
let resolveReady;
|
|
264
318
|
let rejectReady;
|
|
265
319
|
const ready = new Promise((resolve, reject) => {
|
|
@@ -507,15 +561,14 @@ export function createClaudeCodeEngine() {
|
|
|
507
561
|
clearResultDrainWatchdog();
|
|
508
562
|
const events = mapSdkMessage(msg, mapperState);
|
|
509
563
|
for (const ev of events) {
|
|
510
|
-
if (ev.kind
|
|
511
|
-
|
|
512
|
-
if (ev.status === 'running')
|
|
513
|
-
activeSubagentTaskIds.set(ev.toolCallId, ev.taskId ?? ev.toolCallId);
|
|
514
|
-
else
|
|
515
|
-
activeSubagentTaskIds.delete(ev.toolCallId);
|
|
564
|
+
if (ev.kind === 'subagent:progress')
|
|
565
|
+
trackSubagentProgress(ev);
|
|
516
566
|
}
|
|
517
567
|
if (activeSubagentTaskIds.size > 0 &&
|
|
518
|
-
(events.some((ev) =>
|
|
568
|
+
(events.some((ev) =>
|
|
569
|
+
// An ambient watcher's progress is not proof that a real
|
|
570
|
+
// sub-agent advanced: it must not reset the stall bound.
|
|
571
|
+
(ev.kind === 'subagent:progress' && !ev.ambient) ||
|
|
519
572
|
ev.kind === 'message:text' ||
|
|
520
573
|
ev.kind === 'message:thinking' ||
|
|
521
574
|
ev.kind === 'tool:call' ||
|
|
@@ -689,6 +742,28 @@ export function createClaudeCodeEngine() {
|
|
|
689
742
|
armSubagentStallWatchdog();
|
|
690
743
|
return true;
|
|
691
744
|
},
|
|
745
|
+
stopSubagents(ids) {
|
|
746
|
+
const isRunning = (taskId) => activeSubagentTaskIds.has(taskId) || ambientSubagentTaskIds.has(taskId);
|
|
747
|
+
const targets = new Set();
|
|
748
|
+
if (ids === undefined) {
|
|
749
|
+
for (const taskId of [...activeSubagentTaskIds, ...ambientSubagentTaskIds])
|
|
750
|
+
targets.add(taskId);
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
for (const id of ids) {
|
|
754
|
+
// A card id is the SDK task id, or the tool call id when the
|
|
755
|
+
// task id was not known yet: resolve both to the tracked entry.
|
|
756
|
+
const taskId = isRunning(id) ? id : subagentTaskIdByToolCallId.get(id);
|
|
757
|
+
if (taskId && isRunning(taskId))
|
|
758
|
+
targets.add(taskId);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
// The main turn keeps running: only the targeted tasks are stopped.
|
|
762
|
+
// Their terminal notification clears the tracking as usual.
|
|
763
|
+
for (const taskId of targets)
|
|
764
|
+
requestStopTask(taskId);
|
|
765
|
+
return targets.size;
|
|
766
|
+
},
|
|
692
767
|
interrupt() {
|
|
693
768
|
userInterrupted = true;
|
|
694
769
|
// The SDK ends an interrupted run by emitting a `result` with
|
|
@@ -699,16 +774,8 @@ export function createClaudeCodeEngine() {
|
|
|
699
774
|
// background subagent task keeps running unless told to stop via
|
|
700
775
|
// its own API, which would otherwise leave the Stop button
|
|
701
776
|
// appearing to do nothing for up to SUBAGENT_STALL_TIMEOUT_MS.
|
|
702
|
-
for (const taskId of activeSubagentTaskIds
|
|
703
|
-
|
|
704
|
-
void q.stopTask(taskId).catch(() => {
|
|
705
|
-
/* best-effort */
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
catch {
|
|
709
|
-
/* best-effort */
|
|
710
|
-
}
|
|
711
|
-
}
|
|
777
|
+
for (const taskId of [...activeSubagentTaskIds, ...ambientSubagentTaskIds])
|
|
778
|
+
requestStopTask(taskId);
|
|
712
779
|
const qq = q;
|
|
713
780
|
if (typeof qq.interrupt === 'function') {
|
|
714
781
|
try {
|
|
@@ -77,6 +77,21 @@ export const QUOTA_PATTERN = /\b429\b|out of extra usage|rate[_ ]limit|usage lim
|
|
|
77
77
|
* of a previous attempt, a sub-agent failure, or application HTTP handling.
|
|
78
78
|
*/
|
|
79
79
|
const ASSISTANT_QUOTA_NOTICE = /^(?:you(?:'|’)re out of extra usage|you(?:'|’)ve hit your (?:session|weekly|usage) limit|claude ai usage limit reached|usage limit reached|quota exceeded|rate limit exceeded)(?:[.!]?(?:[ \t]*[·—-]?[ \t]*resets?\b[^\r\n]*)?)?[.!]?$/i;
|
|
80
|
+
/** SDKTaskNotificationMessage.status -> subagent:progress terminal status. */
|
|
81
|
+
const TASK_NOTIFICATION_STATUS = {
|
|
82
|
+
completed: 'done',
|
|
83
|
+
failed: 'failed',
|
|
84
|
+
stopped: 'stopped',
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* SDKTaskUpdatedMessage.patch.status values that end a task. `pending`,
|
|
88
|
+
* `running` and `paused` are not lifecycle edges and are ignored.
|
|
89
|
+
*/
|
|
90
|
+
const TASK_UPDATED_TERMINAL_STATUS = {
|
|
91
|
+
completed: 'done',
|
|
92
|
+
failed: 'failed',
|
|
93
|
+
killed: 'stopped',
|
|
94
|
+
};
|
|
80
95
|
export function createMapperState() {
|
|
81
96
|
return {
|
|
82
97
|
sessionStartedEmitted: false,
|
|
@@ -84,6 +99,8 @@ export function createMapperState() {
|
|
|
84
99
|
sawErrorResult: false,
|
|
85
100
|
quotaErrorEmitted: false,
|
|
86
101
|
userInterrupted: false,
|
|
102
|
+
taskToolUseIds: new Map(),
|
|
103
|
+
taskFlags: new Map(),
|
|
87
104
|
};
|
|
88
105
|
}
|
|
89
106
|
/** Known SDK `result` subtypes that indicate the run failed. */
|
|
@@ -191,30 +208,79 @@ export function mapSdkMessage(msg, state) {
|
|
|
191
208
|
events.push({ kind: 'session:compacting', active: sdkStatus === 'compacting' });
|
|
192
209
|
return events;
|
|
193
210
|
}
|
|
194
|
-
if (subtype === 'task_started' ||
|
|
211
|
+
if (subtype === 'task_started' ||
|
|
212
|
+
subtype === 'task_progress' ||
|
|
213
|
+
subtype === 'task_notification' ||
|
|
214
|
+
subtype === 'task_updated') {
|
|
195
215
|
const taskId = typeof parsed.task_id === 'string' ? parsed.task_id : undefined;
|
|
196
|
-
const
|
|
197
|
-
if (
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
216
|
+
const toolUseId = typeof parsed.tool_use_id === 'string' ? parsed.tool_use_id : undefined;
|
|
217
|
+
if (taskId && toolUseId)
|
|
218
|
+
state.taskToolUseIds.set(taskId, toolUseId);
|
|
219
|
+
const toolCallId = toolUseId ?? (taskId ? (state.taskToolUseIds.get(taskId) ?? taskId) : undefined);
|
|
220
|
+
if (!toolCallId)
|
|
221
|
+
return events;
|
|
222
|
+
let status;
|
|
223
|
+
let phase;
|
|
224
|
+
if (subtype === 'task_updated') {
|
|
225
|
+
// Only a terminal patch status is a lifecycle signal; other patches
|
|
226
|
+
// (pending/running/paused, description, is_backgrounded...) are ignored.
|
|
227
|
+
const patch = parsed.patch;
|
|
228
|
+
const patchStatus = TASK_UPDATED_TERMINAL_STATUS[String(patch?.status)];
|
|
229
|
+
if (!patchStatus)
|
|
230
|
+
return events;
|
|
231
|
+
status = patchStatus;
|
|
232
|
+
}
|
|
233
|
+
else if (subtype === 'task_notification') {
|
|
234
|
+
// task_notification is only ever emitted once a task settles: an
|
|
235
|
+
// unknown status value is still terminal (`done`), so a future SDK
|
|
236
|
+
// value never leaves the sub-agent stuck 'running'.
|
|
237
|
+
status = TASK_NOTIFICATION_STATUS[String(parsed.status)] ?? 'done';
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
status = 'running';
|
|
241
|
+
phase = subtype === 'task_started' ? 'started' : 'progress';
|
|
242
|
+
}
|
|
243
|
+
if (taskId) {
|
|
244
|
+
const hasAmbient = typeof parsed.ambient === 'boolean';
|
|
245
|
+
const hasSkip = typeof parsed.skip_transcript === 'boolean';
|
|
246
|
+
if (subtype === 'task_started') {
|
|
247
|
+
// A (re)launch defines the task's classification afresh.
|
|
248
|
+
state.taskFlags.set(taskId, {
|
|
249
|
+
...(hasAmbient ? { ambient: parsed.ambient } : {}),
|
|
250
|
+
...(hasSkip ? { skipTranscript: parsed.skip_transcript } : {}),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
else if (hasAmbient || hasSkip) {
|
|
254
|
+
state.taskFlags.set(taskId, {
|
|
255
|
+
...state.taskFlags.get(taskId),
|
|
256
|
+
...(hasAmbient ? { ambient: parsed.ambient } : {}),
|
|
257
|
+
...(hasSkip ? { skipTranscript: parsed.skip_transcript } : {}),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
217
260
|
}
|
|
261
|
+
const flags = taskId ? state.taskFlags.get(taskId) : undefined;
|
|
262
|
+
const usage = parsed.usage;
|
|
263
|
+
const description = typeof parsed.description === 'string'
|
|
264
|
+
? parsed.description
|
|
265
|
+
: subtype === 'task_updated' &&
|
|
266
|
+
typeof parsed.patch?.description === 'string'
|
|
267
|
+
? parsed.patch.description
|
|
268
|
+
: undefined;
|
|
269
|
+
events.push({
|
|
270
|
+
kind: 'subagent:progress',
|
|
271
|
+
toolCallId,
|
|
272
|
+
...(taskId ? { taskId } : {}),
|
|
273
|
+
status,
|
|
274
|
+
...(phase ? { phase } : {}),
|
|
275
|
+
...(flags?.ambient ? { ambient: true } : {}),
|
|
276
|
+
...(flags?.skipTranscript ? { skipTranscript: true } : {}),
|
|
277
|
+
description,
|
|
278
|
+
taskType: typeof parsed.task_type === 'string' ? parsed.task_type : undefined,
|
|
279
|
+
lastToolName: typeof parsed.last_tool_name === 'string' ? parsed.last_tool_name : undefined,
|
|
280
|
+
totalTokens: typeof usage?.total_tokens === 'number' ? usage.total_tokens : undefined,
|
|
281
|
+
toolUses: typeof usage?.tool_uses === 'number' ? usage.tool_uses : undefined,
|
|
282
|
+
durationMs: typeof usage?.duration_ms === 'number' ? usage.duration_ms : undefined,
|
|
283
|
+
});
|
|
218
284
|
return events;
|
|
219
285
|
}
|
|
220
286
|
if (subtype === 'init') {
|
|
@@ -14,6 +14,7 @@ const KOBO_MCP_BRIEF = [
|
|
|
14
14
|
'• `kobo__set_workspace_status` when the mission is done or irrecoverably blocked. Never set `idle` while this session is active; use AskUserQuestion for clarification, or end the turn.',
|
|
15
15
|
'• `kobo__schedule_wakeup` / `kobo__cancel_wakeup` to schedule (or cancel) a follow-up session — prefer these over the built-in `ScheduleWakeup` tool.',
|
|
16
16
|
'Each tool carries its own "WHEN to use" guidance in its description — follow it.',
|
|
17
|
+
'Only one agent edits the worktree at a time: do not run background subagents that modify files while you or another subagent modify files too. Parallel read-only subagents (search, analysis, review) are fine. Wait for a file-editing subagent to finish before editing or running tests yourself, and remember that changes appearing in the worktree may come from your own subagents.',
|
|
17
18
|
].join('\n');
|
|
18
19
|
/**
|
|
19
20
|
* Appended to the Claude Code preset system prompt so the rule is always in
|
|
@@ -340,13 +340,35 @@ export function createCodexEngine() {
|
|
|
340
340
|
if (n.item.type === 'fileChange')
|
|
341
341
|
fileChanges.set(itemKey(n.threadId, n.turnId, n.item.id), n.item);
|
|
342
342
|
const events = mapTurnEvents(n, (state) => handleItemCompleted(n.item, state));
|
|
343
|
+
const collabItem = n.item.type === 'collabAgentToolCall' ? n.item : undefined;
|
|
344
|
+
const isActiveAgent = (status) => status === 'pendingInit' || status === 'running';
|
|
345
|
+
const progressIndex = events.findIndex((event) => event.kind === 'subagent:progress');
|
|
346
|
+
if (collabItem && progressIndex >= 0) {
|
|
347
|
+
// The card that first tracked a thread (normally its spawnAgent)
|
|
348
|
+
// owns it until it finishes. A later wait/sendInput/resumeAgent
|
|
349
|
+
// on an already-owned thread (e.g. a wait that timed out) must
|
|
350
|
+
// not steal it, or the owner card is orphaned in `running`; such
|
|
351
|
+
// a card is done as soon as its own call completes.
|
|
352
|
+
const progress = events[progressIndex];
|
|
353
|
+
const ownsActiveThread = Object.entries(collabItem.agentsStates).some(([threadId, agentState]) => {
|
|
354
|
+
if (!isActiveAgent(agentState.status))
|
|
355
|
+
return false;
|
|
356
|
+
const owner = activeSubagentThreads.get(threadId);
|
|
357
|
+
return !owner || owner.toolCallId === progress.toolCallId;
|
|
358
|
+
});
|
|
359
|
+
if (progress.status === 'running' && !ownsActiveThread) {
|
|
360
|
+
events[progressIndex] = { ...progress, status: 'done' };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
343
363
|
for (const ev of events)
|
|
344
364
|
safeEmit(ev);
|
|
345
|
-
if (
|
|
346
|
-
const progress =
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
365
|
+
if (collabItem) {
|
|
366
|
+
const progress = progressIndex >= 0
|
|
367
|
+
? events[progressIndex]
|
|
368
|
+
: undefined;
|
|
369
|
+
for (const [threadId, agentState] of Object.entries(collabItem.agentsStates)) {
|
|
370
|
+
if (isActiveAgent(agentState.status)) {
|
|
371
|
+
if (progress && !activeSubagentThreads.has(threadId)) {
|
|
350
372
|
activeSubagentThreads.set(threadId, {
|
|
351
373
|
toolCallId: progress.toolCallId,
|
|
352
374
|
description: progress.description,
|
|
@@ -11,6 +11,7 @@ const CODEX_KOBO_MCP_BRIEF = [
|
|
|
11
11
|
'• `mcp__kobo-tasks__set_workspace_status` when the mission is done or irrecoverably blocked. Never set `idle` while this session is active; ask the user or end the turn instead.',
|
|
12
12
|
'• `mcp__kobo-tasks__schedule_wakeup` / `mcp__kobo-tasks__cancel_wakeup` to schedule (or cancel) a follow-up session.',
|
|
13
13
|
'Each tool carries its own "WHEN to use" guidance in its description — follow it.',
|
|
14
|
+
'Only one agent edits the worktree at a time: do not run background subagents that modify files while you or another subagent modify files too. Parallel read-only subagents (search, analysis, review) are fine. Wait for a file-editing subagent to finish before editing or running tests yourself, and remember that changes appearing in the worktree may come from your own subagents.',
|
|
14
15
|
].join('\n');
|
|
15
16
|
export function buildCodexOptions(input) {
|
|
16
17
|
const isResume = input.resumeFromEngineSessionId !== undefined;
|
|
@@ -34,6 +34,14 @@ export class InterruptAgentError extends Error {
|
|
|
34
34
|
this.name = 'InterruptAgentError';
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
export class StopSubagentsError extends Error {
|
|
38
|
+
code;
|
|
39
|
+
constructor(message, code) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.name = 'StopSubagentsError';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
37
45
|
// ── State ──────────────────────────────────────────────────────────────────────
|
|
38
46
|
/** Actual bound port of the running backend — set at startup via setBackendPort() */
|
|
39
47
|
let backendPort = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 3000;
|
|
@@ -1494,6 +1502,33 @@ export function interruptAgent(workspaceId, options = {}) {
|
|
|
1494
1502
|
autoLoopService.disable(workspaceId, 'user-action');
|
|
1495
1503
|
}
|
|
1496
1504
|
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Stop one running sub-agent (by its canonical id: task id or tool call id)
|
|
1507
|
+
* or, without id, every running sub-agent. The main turn keeps running.
|
|
1508
|
+
* Returns the number of sub-agents a stop was requested for.
|
|
1509
|
+
*/
|
|
1510
|
+
export function stopSubagents(workspaceId, id) {
|
|
1511
|
+
const ctrl = controllers.get(workspaceId);
|
|
1512
|
+
if (!ctrl) {
|
|
1513
|
+
throw new StopSubagentsError(`No agent running for workspace '${workspaceId}'`, 'no_agent_running');
|
|
1514
|
+
}
|
|
1515
|
+
let stopped;
|
|
1516
|
+
try {
|
|
1517
|
+
stopped = ctrl.stopSubagents(id === undefined ? undefined : [id]);
|
|
1518
|
+
}
|
|
1519
|
+
catch (err) {
|
|
1520
|
+
// A controller whose engine has not started yet has nothing to stop.
|
|
1521
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1522
|
+
throw new StopSubagentsError(`No agent running for workspace '${workspaceId}': ${message}`, 'no_agent_running');
|
|
1523
|
+
}
|
|
1524
|
+
if (stopped === undefined) {
|
|
1525
|
+
throw new StopSubagentsError(`The agent engine of workspace '${workspaceId}' cannot stop a single sub-agent`, 'unsupported');
|
|
1526
|
+
}
|
|
1527
|
+
if (id !== undefined && stopped === 0) {
|
|
1528
|
+
throw new StopSubagentsError(`Sub-agent '${id}' is not running for workspace '${workspaceId}'`, 'subagent_not_running');
|
|
1529
|
+
}
|
|
1530
|
+
return stopped;
|
|
1531
|
+
}
|
|
1497
1532
|
/** Bound the caller's wait, retaining ownership until actual engine shutdown. */
|
|
1498
1533
|
export const STOP_AGENT_TIMEOUT_MS = 15_000;
|
|
1499
1534
|
async function stopController(workspaceId, ctrl, cause = 'user') {
|
|
@@ -121,6 +121,18 @@ export class SessionController {
|
|
|
121
121
|
throw new Error('SessionController not started');
|
|
122
122
|
this._engineProcess.interrupt();
|
|
123
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Stop running sub-agents without interrupting the main turn. Returns the
|
|
126
|
+
* number of sub-agents a stop was requested for, or `undefined` when the
|
|
127
|
+
* engine cannot stop a single sub-agent.
|
|
128
|
+
*/
|
|
129
|
+
stopSubagents(ids) {
|
|
130
|
+
if (!this._engineProcess)
|
|
131
|
+
throw new Error('SessionController not started');
|
|
132
|
+
if (typeof this._engineProcess.stopSubagents !== 'function')
|
|
133
|
+
return undefined;
|
|
134
|
+
return this._engineProcess.stopSubagents(ids);
|
|
135
|
+
}
|
|
124
136
|
stop() {
|
|
125
137
|
if (!this._stopPromise) {
|
|
126
138
|
this._stopPromise = this.stopEngine().catch((err) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
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,15 +81,15 @@
|
|
|
81
81
|
"security:scan": "node scripts/scan-secrets.mjs"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
84
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.285",
|
|
85
85
|
"@emnapi/core": "^1.11.3",
|
|
86
86
|
"@emnapi/runtime": "^1.11.3",
|
|
87
|
-
"@hono/node-server": "^2.1.
|
|
88
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
89
|
-
"@openai/codex": "^0.
|
|
87
|
+
"@hono/node-server": "^2.1.3",
|
|
88
|
+
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
89
|
+
"@openai/codex": "^0.159.2",
|
|
90
90
|
"better-sqlite3": "^13.0.3",
|
|
91
91
|
"cron-parser": "^5.10.1",
|
|
92
|
-
"hono": "^4.13.
|
|
92
|
+
"hono": "^4.13.12",
|
|
93
93
|
"nanoid": "^6.0.1",
|
|
94
94
|
"node-pty": "^1.1.0",
|
|
95
95
|
"ws": "^8.22.0"
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"@biomejs/biome": "2.5.14",
|
|
99
99
|
"@types/better-sqlite3": "^9.6.0",
|
|
100
100
|
"@types/node": "^26.6.3",
|
|
101
|
-
"@types/ws": "^8.18.
|
|
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",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"vitest": "^5.0.2"
|
|
107
107
|
},
|
|
108
108
|
"overrides": {
|
|
109
|
-
"@hono/node-server": "^2.1.
|
|
109
|
+
"@hono/node-server": "^2.1.3",
|
|
110
110
|
"shell-quote": "^1.10.0"
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
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,Y as m,_ as h,d as g,f as _,g as v,j as y,k as b,l as x,p as S,r as C,u as w,y as T,z as ee}from"./runtime-core.esm-bundler-DOiZcasU.js";import{T as E,Y as D,b as te,l as O,lt as k,mt as A,nt as j,t as ne,x as re}from"./QBtn-b_baQtAt.js";import{Bt as ie,O as ae,Rt as M,T as oe,Xt as N,g as se,j as P,nt as ce,p as le,qt as ue,rt as de,ut as fe}from"./index-fuLr-ZVa.js";import{t as pe}from"./QSpinnerDots-BsKmccpX.js";import{t as me}from"./QList-Z90BspT4.js";import{t as F}from"./QMarkupTable-Bwjknbmw.js";import{c as I,l as L}from"./QSelect-CMzJDhi-.js";import{t as he}from"./QExpansionItem-CxEBY3jY.js";import{t as R}from"./documents-CALwmao-.js";import{r as ge,t as z}from"./pending-diff-open-CX24u9gW.js";import{a as B}from"./use-tours-b00RruX_.js";import{t as V}from"./QTooltip-BLhkNT4s.js";import{i as _e,n as ve,o as H,r as ye,t as be}from"./render-chat-markdown-px_6WDIX.js";import{a as xe,i as Se,n as Ce,o as we,r as Te,s as U}from"./WorkspacePage-FScR5YAD.js";function Ee(e,t){return s(`div`,e,[s(`table`,{class:`q-table`},t)])}var De={list:me,table:F},W=[`list`,`table`,`__qtable`],Oe=j({name:`QVirtualScroll`,props:{...L,type:{type:String,default:`list`,validator:e=>W.includes(e)},items:{type:Array,default:()=>[]},itemsFn:Function,itemsSize:Number,scrollTarget:N},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}=I({virtualScrollLength:o,getVirtualScrollTarget:S,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,()=>{w(),C()});function v(){let e=a.value;return e===null?null:e.$el||e}function S(){return i}function C(){let t=v();t!==null&&(i=ue(t,e.scrollTarget),i.addEventListener(`scroll`,m,D.passive))}function w(){i!==void 0&&(i.removeEventListener(`scroll`,m,D.passive),i=void 0)}function T(){let t=f(e.type===`list`?`div`:`tbody`,h.value.map(n.default));return n.before!==void 0&&(t=n.before().concat(t)),re(n.after,t)}return u(),p(()=>{C()}),b(()=>{C()}),d(()=>{w()}),y(()=>{w()}),()=>{if(n.default===void 0){console.error(`QVirtualScroll: default scoped slot is required for rendering`);return}return e.type===`__qtable`?Ee({ref:a,class:`q-table__middle `+g.value,tabindex:0},T()):s(De[e.type],{...r,ref:a,class:[r.class,g.value],..._.value},T)}}}),G=new Set([`setup`,`cleanup`,`archive`]);function ke(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:G.has(e.sender)||oe(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function Ae(e){let t=[],n=null,r=null;for(let i of e){let e=ke(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 je(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 Me(e){let t=e.items[0];return`${e.speaker}:${t?je(t):`empty`}`}function Ne(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 Pe(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 Fe={class:`agent-error-item`},Ie={class:`agent-error-item__body`},Le={class:`agent-error-item__title`},Re={class:`agent-error-item__message`},ze=B(T({__name:`AgentErrorItem`,props:{item:{}},setup(e){let t=e,{te:r}=M(),i=x(()=>{let e=`agent.error.${t.item.category}`;return r(e)?e:`agent.error.other`});return(t,r)=>(u(),S(`div`,Fe,[h(O,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),w(`div`,Ie,[w(`div`,Le,n(t.$t(i.value)),1),w(`div`,Re,n(e.item.message),1)])]))}}),[[`__scopeId`,`data-v-e98c3c02`]]),K={class:`text-caption text-kobo-3`},Be=T({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,r=x(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return U(t.item.detail);case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(u(),S(`span`,K,n(e.$t(r.value)),1))}});function q(e,t){if(t.length===0||e.length===0)return e;let n=[...t].sort((e,t)=>t.length-e.length),r=new DOMParser().parseFromString(`<div>${e}</div>`,`text/html`),i=r.body.firstChild;if(!i)return e;function a(e){if(e.nodeType===Node.TEXT_NODE){Ve(e,n,r);return}if(e.nodeName===`A`)return;let t=Array.from(e.childNodes);for(let e of t)a(e)}return a(i),i.innerHTML}function Ve(e,t,n){let r=e.textContent??``;if(!t.some(e=>r.includes(e)))return;let i=n.createDocumentFragment(),a=0;for(;a<r.length;){let e=J(r,a,t);if(!e){i.appendChild(n.createTextNode(r.slice(a)));break}e.index>a&&i.appendChild(n.createTextNode(r.slice(a,e.index)));let o=n.createElement(`a`);o.className=`document-link`,o.setAttribute(`data-document-path`,e.path),o.setAttribute(`href`,`#`),o.textContent=e.path,i.appendChild(o),a=e.index+e.path.length}e.parentNode?.replaceChild(i,e)}function J(e,t,n){let r=null;for(let i of n){let n=e.indexOf(i,t);n<0||(!r||n<r.index||n===r.index&&i.length>r.path.length)&&(r={index:n,path:i})}return r}var He=[`innerHTML`],Y=B(T({__name:`TextMessageItem`,props:{item:{}},setup(e){let t=e,n=R(),r=ce(),i=te(),{t:a}=M(),o=x(()=>{let e=r.selectedWorkspaceId;return e?n.documentsFor(e).map(e=>e.path):[]}),s=x(()=>{let e=t.item;if(e.streaming)return be(e.text);let n=o.value;return ye(`${e.messageId}|${n.join(`,`)}`,e.text,e=>{let t=q(H.parse(e,{async:!1,breaks:!0,gfm:!0}),n);return _e(t,{addAttr:[`data-document-path`]})})});function c(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let o=t.getAttribute(`data-document-path`),s=r.selectedWorkspaceId;o&&s&&n.openDocumentByPath(s,o).catch(e=>{console.error(`[TextMessageItem] openDocumentByPath failed:`,e),i.notify({type:`negative`,message:a(`documents.loadFailed`),position:`top`})})}return(t,n)=>(u(),S(`div`,{class:`markdown-message`,onClick:c},[w(`div`,{innerHTML:s.value},null,8,He),e.item.streaming?(u(),g(E,{key:0,size:`xs`,class:`q-ml-xs`})):_(``,!0)]))}}),[[`__scopeId`,`data-v-99371035`]]);function Ue(e,t){let n=e.split(`
|
|
2
|
+
`),r=t.split(`
|
|
3
|
+
`),i=0;for(;i<n.length&&i<r.length&&n[i]===r[i];)i++;let a=n.length,o=r.length;for(;a>i&&o>i&&n[a-1]===r[o-1];)a--,o--;let s=n.slice(0,i).map(e=>({type:`context`,content:e})),c=n.slice(a).map(e=>({type:`context`,content:e})),l=n.slice(i,a),u=r.slice(i,o);return l.length>800||u.length>800?[...s,...l.map(e=>({type:`del`,content:e})),...u.map(e=>({type:`add`,content:e})),...c]:[...s,...X(l,u),...c]}function X(e,t){let n=e.length,r=t.length;if(n===0&&r===0)return[];if(n===0)return t.map(e=>({type:`add`,content:e}));if(r===0)return e.map(e=>({type:`del`,content:e}));let i=r+1,a=new Int32Array((n+1)*i);for(let o=n-1;o>=0;o--)for(let n=r-1;n>=0;n--)a[o*i+n]=e[o]===t[n]?a[(o+1)*i+(n+1)]+1:Math.max(a[(o+1)*i+n],a[o*i+(n+1)]);let o=[],s=0,c=0;for(;s<n&&c<r;)e[s]===t[c]?(o.push({type:`context`,content:e[s]}),s++,c++):a[(s+1)*i+c]>=a[s*i+(c+1)]?(o.push({type:`del`,content:e[s]}),s++):(o.push({type:`add`,content:t[c]}),c++);for(;s<n;)o.push({type:`del`,content:e[s++]});for(;c<r;)o.push({type:`add`,content:t[c++]});return o}function We(e){let t=e.split(`
|
|
4
|
+
`),n=[],r=!1;for(let e of t){if(e.startsWith(`@@`)){r=!0;continue}(r||!e.startsWith(`+++`)&&!e.startsWith(`---`))&&(e.startsWith(`+`)?n.push({type:`add`,content:e.slice(1)}):e.startsWith(`-`)?n.push({type:`del`,content:e.slice(1)}):e.startsWith(` `)?n.push({type:`context`,content:e.slice(1)}):e.length>0&&n.push({type:`context`,content:e}))}return n}function Ge(e,t){if(!t||typeof t!=`object`)return null;let n=t;if(e===`Edit`){let e=n.file_path;if(!e)return null;let t=n.old_string??``,r=n.new_string??``,i=typeof n.diff==`string`?n.diff:``;if(!t&&!r&&i.length>0){let t=We(i);return{toolName:`Edit`,filePath:e,additions:t.filter(e=>e.type===`add`).length,deletions:t.filter(e=>e.type===`del`).length,diffLines:t}}return{toolName:`Edit`,filePath:e,oldString:t,newString:r,replaceAll:n.replace_all??!1,additions:r?r.split(`
|
|
5
|
+
`).length:0,deletions:t?t.split(`
|
|
6
|
+
`).length:0}}if(e===`Write`){let e=n.file_path;if(!e)return null;let t=n.content??``;return{toolName:`Write`,filePath:e,content:t,additions:t?t.split(`
|
|
7
|
+
`).length:0,deletions:0}}if(e===`Bash`){let e=(n.command??``).match(/^\s*rm\s+(?:-[a-zA-Z]*\s+)*(.+)/);if(e)return{toolName:`Bash:rm`,filePath:e[1].trim().replace(/["']/g,``),additions:0,deletions:1}}return null}function Z(e,t){if(!e||!t?.projectPath)return e;let n=t.worktreePath;if(n){let t=Q(e,n);if(t!==e)return t}let r=Q(e,`${t.projectPath}/${P}/${t.workingBranch}`);return r===e?Q(e,t.projectPath):r}function Q(e,t){if(!t)return e;let n=Ke(t);return n?e.replace(RegExp(`${n}[\\\\/]+`,`g`),``).replace(RegExp(`${n}(?=\\s|$|["'\`])`,`g`),`.`):e}function Ke(e){let t=e.replace(/[\\/]+$/,``);return t?`${/^[\\/]+/.test(t)?`[\\\\/]+`:``}${t.split(/[\\/]+/).filter(Boolean).map(qe).join(`[\\\\/]+`)}`:``}function qe(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}var Je={class:`tool-name`},Ye=[`title`],Xe={key:0,class:`tool-stat-add`},Ze={key:1,class:`tool-stat-del`},Qe={class:`diff-sign`},$e={class:`tool-name`},et=[`title`],tt=B(T({__name:`ToolCallItem`,props:{item:{}},setup(t){let r=t,i=o(!1),s=ce(),{t:c}=M(),d=x(()=>Ge(r.item.name,r.item.input)),f=x(()=>d.value?Z(d.value.filePath,s.selectedWorkspace):``),p={Bash:`terminal`,Read:`description`,Edit:`edit`,Write:`edit_note`,MultiEdit:`edit`,Glob:`folder_open`,Grep:`manage_search`,LS:`list`,Skill:`auto_awesome`,Task:`hub`,Agent:`hub`,TodoWrite:`checklist`,TodoRead:`checklist`,TaskCreate:`add_task`,TaskUpdate:`checklist`,TaskGet:`checklist`,TaskList:`checklist`,ToolSearch:`search`,WebFetch:`public`,WebSearch:`travel_explore`,NotebookRead:`book`,NotebookEdit:`edit_note`,SendMessage:`send`,ExitPlanMode:`check_circle_outline`,KillShell:`stop_circle`,BashOutput:`terminal`},y=x(()=>p[r.item.name]??`build`),b=x(()=>{if(d.value)return``;let e=r.item.input,t=T(e);return t?Z(t,s.selectedWorkspace):``});function T(e){if(!e||typeof e!=`object`)return typeof e==`string`?e:``;let t=e;for(let e of[`file_path`,`path`,`command`,`pattern`,`query`,`url`,`skill`,`description`,`subject`,`prompt`]){let n=t[e];if(typeof n==`string`&&n.length>0)return n}for(let e of Object.values(t))if(typeof e==`string`&&e.length>0)return e;return``}let E=x(()=>{let e=d.value;return e?e.diffLines?e.diffLines:e.toolName===`Edit`&&e.oldString!==void 0&&e.newString!==void 0?Ue(e.oldString,e.newString):e.toolName===`Write`&&e.content!==void 0?e.content.split(`
|
|
8
|
+
`).map(e=>({type:`add`,content:e})):e.toolName===`Bash:rm`?[{type:`del`,content:`File deleted`}]:null:null}),D=x(()=>{let e=r.item.result;if(!e)return``;if(typeof e.output==`string`)return e.output;try{return JSON.stringify(e.output)}catch{return String(e.output)}}),te=new Set([`Read`]),k=x(()=>!!(r.item.result&&D.value)&&(!te.has(r.item.name)||r.item.result?.isError===!0));function j(){i.value=!i.value}function re(){let e=d.value?.filePath,t=s.selectedWorkspace,n=t?.id,r=e?Z(e,t):``;r&&!/^(?:[A-Za-z]:)?[\\/]/.test(r)&&n&&(z(n,r),window.dispatchEvent(new CustomEvent(`kobo:open-diff`,{detail:{workspaceId:n,path:r}})))}return l(()=>r.item.result?.isError===!0,e=>{e&&(i.value=!0)},{immediate:!0}),(r,o)=>d.value?(u(),S(`div`,{key:0,class:a([`tool-row`,{"tool-row-expanded":i.value}])},[w(`div`,{class:`tool-header`,onClick:j},[h(O,{name:y.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),w(`span`,Je,n(d.value.toolName===`Bash:rm`?`Bash`:d.value.toolName),1),w(`span`,{class:`tool-path`,title:d.value.filePath},n(f.value),9,Ye),h(ne,{flat:``,dense:``,round:``,size:`xs`,icon:`open_in_new`,class:`tool-open-diff`,onClick:A(re,[`stop`])},{default:m(()=>[h(V,null,{default:m(()=>[v(n(e(c)(`chat.openInGitDiff`)),1)]),_:1})]),_:1}),d.value.additions>0?(u(),S(`span`,Xe,`+`+n(d.value.additions),1)):_(``,!0),d.value.deletions>0?(u(),S(`span`,Ze,`-`+n(d.value.deletions),1)):_(``,!0),t.item.result?.isError?(u(),g(O,{key:2,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-xs`})):t.item.result?(u(),g(O,{key:3,name:`check`,color:`positive`,size:`xs`,class:`q-ml-xs`})):_(``,!0),h(O,{name:i.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-auto text-kobo-3`},null,8,[`name`])]),i.value&&E.value?(u(),S(`div`,{key:0,class:`tool-diff`,onClick:o[0]||=A(()=>{},[`stop`])},[(u(!0),S(C,null,ee(E.value,(e,t)=>(u(),S(`div`,{key:t,class:a([`diff-line`,{"diff-del":e.type===`del`,"diff-add":e.type===`add`,"diff-context":e.type===`context`}])},[w(`span`,Qe,n(e.type===`del`?`-`:e.type===`add`?`+`:` `),1),v(n(e.content),1)],2))),128))])):_(``,!0)],2)):(u(),S(`div`,{key:1,class:a([`tool-row tool-row-generic`,{"tool-row-expanded":i.value,"tool-row--toggleable":k.value}])},[w(`div`,{class:`tool-header`,onClick:o[1]||=e=>k.value&&j()},[h(O,{name:y.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),w(`span`,$e,n(t.item.name),1),b.value?(u(),S(`span`,{key:0,class:`tool-arg`,title:T(t.item.input)||b.value},n(b.value),9,et)):_(``,!0),t.item.result?.isError?(u(),g(O,{key:1,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-auto`})):t.item.result?(u(),g(O,{key:2,name:`check`,color:`positive`,size:`xs`,class:`q-ml-auto`})):_(``,!0),k.value?(u(),g(O,{key:3,name:i.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-xs text-kobo-3`},null,8,[`name`])):_(``,!0)]),i.value&&k.value?(u(),S(`div`,{key:0,class:`tool-output`,onClick:o[2]||=A(()=>{},[`stop`])},n(D.value),1)):_(``,!0)],2))}}),[[`__scopeId`,`data-v-2482adfe`]]),$=new Map;function nt(){for(let e of $.values())e.controller.abort(),e.blobUrl&&URL.revokeObjectURL(e.blobUrl);$.clear()}typeof window<`u`&&(window.addEventListener(`kobo:auth-token-changed`,nt),window.addEventListener(`storage`,e=>{(e.key===`kobo:network-token`||e.key===null)&&nt()}));function rt(e){if(!/^\/api\/workspaces\/[^/]+\/images\/file\?path=/.test(e))throw Error(`Invalid workspace image URL`);let t=$.get(e);if(!t){let n=new AbortController,r={references:0,controller:n,ready:Promise.resolve(``)},i=setTimeout(()=>n.abort(),3e4);r.ready=fetch(e,{signal:n.signal}).then(async e=>{if(!e.ok)throw Error(`HTTP ${e.status}`);let t=await e.blob();if(n.signal.aborted)throw new DOMException(`Aborted`,`AbortError`);return r.blobUrl=URL.createObjectURL(t),r.blobUrl}).finally(()=>clearTimeout(i)),t=r,$.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),$.get(e)===r&&$.delete(e)))}}}function it(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=rt(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 at(e,t){return t?e.replace(/\[image:\s+([^\]]+)\]/g,(e,n)=>{let r=String(n).trim();return/^(\.ai\/images\/|images\/)/.test(r)?`}/images/file?path=${encodeURIComponent(r)}`})`:e}):e}var ot=[`innerHTML`],st=[`innerHTML`],ct=[`src`],lt=B(T({__name:`UserMessageItem`,props:{item:{},workspaceId:{}},setup(t){let n=t,{t:r}=M(),a=x(()=>n.item.sender===`system-prompt`),s=it(x(()=>{let e=at(n.item.content,n.workspaceId);return ve(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 v=e=>{(e.key===`kobo:network-token`||e.key===null)&&p()};window.addEventListener(`storage`,v),window.addEventListener(`kobo:auth-token-changed`,p),i(()=>{p(),window.removeEventListener(`kobo:auth-token-changed`,p),window.removeEventListener(`storage`,v)});function y(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(p(),n.dataset.koboImageUrl&&(f=rt(n.dataset.koboImageUrl),f.ready.catch(p)),c.value=n.src,d.value=!0)}return(t,n)=>(u(),S(C,null,[a.value?(u(),g(he,{key:0,dense:``,"dense-toggle":``,label:t.$t(`chat.systemPrompt`),"header-class":`text-kobo-2 text-caption`},{default:m(()=>[w(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:e(s)},null,8,ot)]),_:1},8,[`label`])):(u(),S(`div`,{key:1,class:`markdown-message`,onClick:y},[w(`div`,{innerHTML:e(s)},null,8,st)])),h(ie,{modelValue:d.value,"onUpdate:modelValue":n[1]||=e=>d.value=e},{default:m(()=>[c.value?(u(),S(`img`,{key:0,src:c.value,alt:``,class:`image-lightbox-img`,onClick:n[0]||=e=>d.value=!1},null,8,ct)):_(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-db2fc622`]]),ut=[`data-event-ids`],dt={class:`turn-header`},ft={key:0,class:`turn-time`},pt={class:`turn-time turn-time-updated`},mt={key:2,class:`turn-actions`},ht={class:`turn-body`},gt={key:0,class:`turn-scroll-top`},_t=B(T({__name:`TurnCard`,props:{turn:{},workspaceId:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(t,{emit:i}){let s=t,c=i,{t:l}=M(),d=o(null);function f(){let e=d.value;if(!e)return;let t=e.closest(`.q-scrollarea`)?.querySelector(`.q-scrollarea__content`);if(!t){e.scrollIntoView({behavior:`smooth`,block:`start`});return}let n=e.getBoundingClientRect().top-t.getBoundingClientRect().top;c(`scrollTo`,Math.max(0,n-8))}let p=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:oe(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 y(e,t=!1){if(!e)return``;let n=new Date(e);return Number.isNaN(n.getTime())?``:n.toLocaleTimeString(void 0,t?{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}:{hour:`2-digit`,minute:`2-digit`})}let b=x(()=>y(s.turn.ts)),T=x(()=>s.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),E=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=E.value;if(!t||!e||t===e)return``;let n=new Date(e).getTime(),r=new Date(t).getTime();return Number.isNaN(n)||Number.isNaN(r)||r<=n?``:y(t,r-n<6e4)}),te=x(()=>D.value!==``),k=x(()=>s.turn.items.filter(e=>e.type===`tool`).length);return(i,o)=>(u(),S(`div`,{ref_key:`cardEl`,ref:d,class:a([`turn-card`,{"turn-card--user":t.turn.speaker===`user`,"turn-card--highlighted":t.highlighted}]),"data-event-ids":T.value,style:r({"--turn-accent":p.value.accent})},[w(`div`,dt,[w(`span`,{class:a([`turn-badge`,p.value.badgeClass])},n(p.value.label),3),b.value?(u(),S(`span`,ft,n(b.value),1)):_(``,!0),te.value?(u(),S(C,{key:1},[h(O,{name:`arrow_forward`,size:`10px`,color:`kobo-3`,class:`turn-time-arrow`}),w(`span`,pt,[v(n(D.value)+` `,1),h(V,null,{default:m(()=>[v(n(e(l)(`chat.lastUpdatedAt`,{time:D.value})),1)]),_:1})])],64)):_(``,!0),k.value>0?(u(),S(`span`,mt,` · `+n(e(l)(`chat.nActions`,{n:k.value})),1)):_(``,!0)]),w(`div`,ht,[(u(!0),S(C,null,ee(t.turn.items,n=>(u(),S(C,{key:e(je)(n)},[n.type===`text`?(u(),g(Y,{key:0,item:n},null,8,[`item`])):n.type===`thinking`?(u(),g(Ce,{key:1,item:n},null,8,[`item`])):n.type===`tool`?(u(),g(tt,{key:2,item:n},null,8,[`item`])):n.type===`user`?(u(),g(lt,{key:3,item:n,"workspace-id":t.workspaceId},null,8,[`item`,`workspace-id`])):n.type===`session`?(u(),g(Be,{key:4,item:n},null,8,[`item`])):n.type===`error`?(u(),g(ze,{key:5,item:n},null,8,[`item`])):_(``,!0)],64))),128))]),t.turn.items.length>4?(u(),S(`div`,gt,[h(ne,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`kobo-3`,class:`turn-scroll-top-btn`,onClick:f},{default:m(()=>[h(V,null,{default:m(()=>[v(n(e(l)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):_(``,!0)],14,ut))}}),[[`__scopeId`,`data-v-c0e24cbd`]]),vt={key:0,class:`activity-feed-switching`},yt={key:1,class:`activity-feed-wrap`,"data-tour":`ws-chat`},bt=[`aria-label`],xt={key:0,class:`activity-feed-compacting`},St={class:`text-caption text-kobo-2`},Ct={key:0,class:`text-center q-py-sm text-caption text-kobo-3`},wt={key:1,class:`activity-feed-empty q-pa-md`},Tt={class:`activity-feed-empty__title`},Et={class:`activity-feed-empty__hint`},Dt={key:2,class:`q-px-md q-pb-md`},Ot={class:`activity-feed-nav-cluster`},kt=300,At=60,jt=200,Mt=180,Nt=3,Pt=60,Ft=B(T({__name:`ActivityFeed`,props:{workspaceId:{}},setup(t){let{t:r}=M(),i=t,a=fe(),s=ae(),d=ce(),y=le(),b=x(()=>y.isCompacting(i.workspaceId)),T=x(()=>d.selectedSessionId),D=x(()=>d.sessions.find(e=>e.id===T.value)?.engineSessionId??null),te=x(()=>{let e=d.sessions;return e.length!==0&&T.value===e[e.length-1].id});function A(e){return T.value?e?e===T.value||e===D.value:te.value:!0}let j=x(()=>(d.activityFeeds[i.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&A(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,source:se(e.meta?.source),ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),re=x(()=>{let e=d.workspaces.find(e=>e.id===i.workspaceId);return de(e?.status)}),ie=Te(),oe=``,N=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++)A(n[i])&&(o.push(e[i]),c.push(t[i]),l.push(r[i]??null));let u=`${i.workspaceId}::${T.value??`*`}`;u!==oe&&(ie=Te(),oe=u);let d=Se(ie,o,c,re.value,l);return Ae(we(d,j.value).filter(e=>e.type===`thinking`?!1:e.type===`session`?e.kind===`ended`?s.showVerboseSystemMessages||!xe(e.detail):s.showVerboseSystemMessages:!0))}),P=x(()=>s.showVerboseSystemMessages?a.eventsFor(i.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),ue=x(()=>{for(let e=N.value.length-1;e>=0;e--){let t=N.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}),me=o(``);l(ue,(e,t)=>{if(!e||e.key===t?.key)return;let n=e.key.startsWith(`text:`)?r(`activity.a11y.agentSaid`,{text:e.text.slice(0,kt)}):e.text;me.value=n});let F=o(null),I=o(!0),L=o(!1),R=o(null),z=!1,B=o(new Set),V=x(()=>{if(N.value.length>0||P.value.length>0)return!1;let e=i.workspaceId,t=T.value;return!!d.loadingSessions[e]||y.isSyncPending(e)||t!==null&&B.value.has(H(e,t))}),_e=o(new Map);function ve(e){let t=e.verticalSize-e.verticalPosition-e.verticalContainerSize;I.value=t<=At,z&&e.verticalPosition<=jt&&!L.value&&ye()&&Ee()}function H(e,t){return`${e}:${t}`}function ye(){let e=T.value;return e?_e.value.get(H(i.workspaceId,e))??!0:a.hasMoreOlderFor(i.workspaceId)}function be(e,t,n){_e.value.set(H(e,t),n)}function Ce(e){if(!T.value)return a.oldestIdFor(e);let t=a.eventIdsFor(e),n=a.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!A(n[e]))continue;let r=t[e];if(r)return r}}let U=null;function Ee(){return U||(U=De().finally(()=>{U=null}),U)}async function De(){let e=i.workspaceId,t=T.value,n=Ce(e);if(n){L.value=!0;try{let r=F.value;await f();let i=r?.getScroll().verticalSize??0,o=r?.getScroll().verticalPosition??0,s=new URLSearchParams({before:n,limit:`200`});t&&s.set(`session`,t);let c=await fetch(`/api/workspaces/${e}/events?${s.toString()}`);if(!c.ok){t?be(e,t,!1):a.prepend(e,[],[],{oldestId:n,hasMoreOlder:!1});return}let l=await c.json(),u=l.events??[],p=u.filter(t=>t.type===`agent:event`&&t.workspaceId===e),m=u.filter(t=>t.type===`user:message`&&t.workspaceId===e),h=p.map(e=>e.payload),g=p.map(e=>e.createdAt),_=p.map(e=>e.sessionId??null),v=p.map(e=>e.id),y=u.length>0?u[0].id:n;t&&be(e,t,l.hasMore),a.prepend(e,h,g,{oldestId:y,hasMoreOlder:t?a.hasMoreOlderFor(e):l.hasMore,sessionIds:_,eventIds:v});for(let t of m){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:se(n.source)}})}if(await f(),r){let e=r.getScroll().verticalSize-i;if(e>0){let t=Math.max(o+e,250);r.setScrollPosition(`vertical`,t,0)}}}catch(e){console.error(`[ActivityFeed] failed to load older events:`,e)}finally{L.value=!1}}}async function W(e=0){await f();let t=F.value;if(!t)return;let n=t.getScroll();t.setScrollPosition(`vertical`,n.verticalSize,e)}let G=null,ke=0;function je(){if(G!=null)return;let e=performance.now()-ke<Mt;G=requestAnimationFrame(()=>{G=null,ke=performance.now(),W(e?0:180)})}function Fe(e){let t=F.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function Ie(){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 Le(e){let t=e.detail;if(t?.workspaceId===i.workspaceId&&t.eventId)try{let e=new URLSearchParams({around:t.eventId,limit:`200`});t.sessionId&&e.set(`session`,t.sessionId);let n=await fetch(`/api/workspaces/${i.workspaceId}/events?${e}`);if(!n.ok)return;let r=await n.json(),o=r.events??[],s=o.filter(e=>e.type===`agent:event`&&e.workspaceId===i.workspaceId),c=o.filter(e=>e.type===`user:message`&&e.workspaceId===i.workspaceId);a.reset(i.workspaceId,s.map(e=>e.payload),s.map(e=>e.createdAt),{oldestId:o[0]?.id,hasMoreOlder:r.hasMore??!1,sessionIds:s.map(e=>e.sessionId),eventIds:s.map(e=>e.id)});for(let e of c){let t=e.payload;typeof t.content==`string`&&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:se(t.source)}})}await Pe(V,e=>!e,5e3),await f(),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e())));let l=N.value.findIndex(e=>e.items.some(e=>e.eventIds?.includes(t.eventId)??!1));if(l<0)return;R.value=t.eventId,Re.value?.scrollTo(l,`center`),K.value=l,window.setTimeout(()=>{R.value===t.eventId&&(R.value=null)},1800)}catch(e){console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Re=o(null),ze=o(null),K=o(0);function Be(e){K.value=e.index}let q=o(!1);async function Ve(){if(q.value)return;q.value=!0;let e=i.workspaceId;try{let t=Ne(N.value,K.value);if(t<0)for(let n=0;n<15;n++){if(e!==i.workspaceId)return;if(!ye())break;let n=N.value.length;if(await Ee(),await f(),e!==i.workspaceId)return;if(K.value+=N.value.length-n,t=Ne(N.value,K.value),t>=0)break}t>=0&&e===i.workspaceId&&(Re.value?.scrollTo(t,`start`),K.value=t)}finally{q.value=!1}}let J=0;function He(){return new Promise(e=>requestAnimationFrame(()=>e()))}async function Y(){z=!1;let e=++J;await f();let t=-1,n=0;for(let r=0;r<Pt&&n<Nt;r++){if(e!==J)return;await W(0),await He();let r=F.value?.getScroll().verticalSize??0;n=r===t?n+1:0,t=r}e===J&&(z=!0)}function Ue(){z||=(J++,!0)}let X=x(()=>{let e=a.sessionIdsFor(i.workspaceId);if(!T.value)return e.length;let t=0;for(let n of e)A(n)&&t++;return t});l(V,async e=>{!e&&X.value>0&&await Y(),e||f(()=>{ze.value=F.value?.getScrollTarget()??null})}),p(()=>{window.addEventListener(`kobo:focus-history-event`,Le),f(()=>{ze.value=F.value?.getScrollTarget()??null}),X.value>0&&Y(),T.value&&Ke();let e=new URLSearchParams(window.location.hash.split(`?`)[1]??``).get(`eventId`);e&&(Le(new CustomEvent(`kobo:focus-history-event`,{detail:{workspaceId:i.workspaceId,sessionId:T.value,eventId:e}})),Ie())});let We=X.value>0;l(X,async e=>{L.value||!We&&e>0&&(We=!0,await Y())});let Ge=x(()=>a.liveAppendCountFor(i.workspaceId,A)),Z=()=>`${i.workspaceId}\u0000${d.selectedSessionId??``}`;l([Ge,Z],([e,t],[n,r])=>{t===r&&e>n&&I.value&&!L.value&&je()}),c(()=>{window.removeEventListener(`kobo:focus-history-event`,Le),G!=null&&(cancelAnimationFrame(G),G=null)}),l(()=>i.workspaceId,()=>{I.value=!0,We=X.value>0,z=!1,Ke(),X.value>0&&Y()}),l(()=>d.selectedSessionId,async()=>{I.value=!0,z=!1,Ke(),await Y()});let Q=new Set;async function Ke(){let e=i.workspaceId,t=T.value;if(!t||X.value>0)return;let n=H(e,t);if(!Q.has(n)){Q.add(n),B.value.add(n);try{let r=await fetch(`/api/workspaces/${e}/events?session=${encodeURIComponent(t)}&limit=500`);if(!r.ok){Q.delete(n);return}let o=await r.json(),s=o.events??[];if(s.length===0)return;let c=s.filter(t=>t.type===`agent:event`&&t.workspaceId===e),l=s.filter(t=>t.type===`user:message`&&t.workspaceId===e),u=c.map(e=>e.payload),p=c.map(e=>e.createdAt),m=c.map(e=>e.sessionId??null),h=c.map(e=>e.id);be(e,t,o.hasMore),u.length>0&&a.prepend(e,u,p,{oldestId:s[0].id,hasMoreOlder:a.hasMoreOlderFor(e),sessionIds:m,eventIds:h});for(let t of l){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:se(n.source)}})}await f(),i.workspaceId===e&&T.value===t&&await W(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Q.delete(n)}finally{B.value.delete(n)}}}let qe=x(()=>j.value.filter(e=>e.sender!==`system-prompt`).length);l([qe,Z],async([e,t],[n,r])=>{t===r&&e>n&&(I.value=!0,await W(180))});async function Je(){I.value=!0,await W(250)}return(t,r)=>V.value?(u(),S(`div`,vt,[h(pe,{size:`40px`,color:`primary`})])):(u(),S(`div`,yt,[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(me.value),9,bt),h(k,{name:`fade`},{default:m(()=>[b.value?(u(),S(`div`,xt,[h(pe,{size:`18px`,color:`primary`}),w(`span`,St,n(t.$t(`activity.compacting`)),1)])):_(``,!0)]),_:1}),h(ge,{ref_key:`scrollRef`,ref:F,class:`activity-feed-scroll`,onScroll:ve,onWheelPassive:Ue,onTouchstartPassive:Ue,onKeydown:Ue},{default:m(()=>[L.value?(u(),S(`div`,Ct,[h(E,{size:`sm`}),v(` `+n(t.$t(`activity.loading_older`)),1)])):_(``,!0),h(Oe,{ref_key:`virtualScrollRef`,ref:Re,class:`q-pa-md`,items:N.value,"scroll-target":ze.value??void 0,"virtual-scroll-item-size":160,"virtual-scroll-slice-size":20,onVirtualScroll:Be},{default:m(({item:t,index:n})=>[(u(),g(_t,{key:e(Me)(t),turn:t,"workspace-id":i.workspaceId,"data-turn-index":n,highlighted:t.items.some(e=>e.eventIds?.includes(R.value??``)??!1),onScrollTo:Fe},null,8,[`turn`,`workspace-id`,`data-turn-index`,`highlighted`]))]),_:1},8,[`items`,`scroll-target`]),N.value.length===0&&P.value.length===0&&!L.value?(u(),S(`div`,wt,[h(O,{name:`forum`,size:`28px`}),w(`div`,Tt,n(t.$t(`activity.empty`)),1),w(`div`,Et,n(t.$t(`activity.emptyHint`)),1)])):_(``,!0),P.value.length?(u(),S(`div`,Dt,[h(he,{label:t.$t(`activity.raw_lines`,{n:P.value.length}),dense:``},{default:m(()=>[(u(!0),S(C,null,ee(P.value,(e,t)=>(u(),S(`div`,{key:t,class:`text-caption text-kobo-3 q-pa-xs`},n(e),1))),128))]),_:1},8,[`label`])])):_(``,!0)]),_:1},512),w(`div`,Ot,[I.value?_(``,!0):(u(),g(ne,{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:Je},null,8,[`title`,`aria-label`])),h(ne,{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:q.value,disable:q.value,onClick:Ve},null,8,[`title`,`aria-label`,`loading`,`disable`])])]))}}),[[`__scopeId`,`data-v-028e8af5`]]);export{Ft as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{M as e,dt as t,v as n}from"./reactivity.esm-bundler-tuEvQ47f.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-DOiZcasU.js";import{t as m}from"./QBtn-b_baQtAt.js";import{i as h,rn as g,tn as _}from"./index-fuLr-ZVa.js";import{a as v,i as y,n as b}from"./use-tours-b00RruX_.js";import{n as x}from"./render-chat-markdown-px_6WDIX.js";import{t as S}from"./QChip-Bt_JvnHb.js";import{t as C}from"./QPage-BCDXjfVj.js";import{t as w}from"./DrawerToggleButton-Bh4FXdtJ.js";import{t as T}from"./TourReplayButton-DlMvPblb.js";var E={class:`row items-center q-mb-md`},D={class:`text-h6 q-ml-sm`},O={key:0,class:`text-kobo-3 text-center q-pa-lg`},k={key:1,class:`text-negative text-center q-pa-lg`},A={key:2,class:`text-kobo-3 text-center q-pa-lg`},j={key:3,class:`column q-gutter-md`,"data-tour":`changelog-list`},M={key:0,class:`text-caption text-kobo-3`},N={class:`row items-center q-mb-sm`},P={class:`text-subtitle1 text-kobo-2`,style:{"font-family":`var(--kobo-font-mono, monospace)`}},F=[`innerHTML`],I=v(f({__name:`ChangelogPage`,setup(f){let v=h(),{scheduleAutoRun:I}=b(),L=n([]),R=n(``),z=n(!1),B=n(null);function V(e){return x(e)}async function H(){z.value=!0,B.value=null;try{let e=await fetch(`/api/changelog`);if(!e.ok)throw Error(`HTTP ${e.status}`);let t=await e.json();R.value=t.currentVersion??``,L.value=t.versions??[]}catch(e){B.value=e instanceof Error?e.message:String(e)}finally{z.value=!1}}return i(()=>{H(),I(`changelog`)}),(n,i)=>(r(),s(C,{class:`q-pa-md`,style:{"max-width":`900px`,margin:`0 auto`}},{default:a(()=>[d(`div`,E,[o(w,{class:`q-mr-xs`}),o(m,{flat:``,dense:``,round:``,icon:`arrow_back`,onClick:i[0]||=t=>e(v).back()}),d(`div`,D,t(n.$t(`changelog.title`)),1),o(y),o(m,{flat:``,dense:``,icon:`refresh`,loading:z.value,label:n.$t(`common.refresh`),onClick:H},null,8,[`loading`,`label`]),o(T,{"tour-id":`changelog`,class:`q-ml-xs`})]),z.value&&L.value.length===0?(r(),l(`div`,O,t(n.$t(`common.loading`)),1)):B.value?(r(),l(`div`,k,t(B.value),1)):L.value.length===0?(r(),l(`div`,A,t(n.$t(`changelog.empty`)),1)):(r(),l(`div`,j,[R.value?(r(),l(`div`,M,t(n.$t(`changelog.currentVersion`,{version:R.value})),1)):c(``,!0),(r(!0),l(u,null,p(L.value,e=>(r(),s(_,{key:e.version,dark:``,flat:``,bordered:``},{default:a(()=>[o(g,null,{default:a(()=>[d(`div`,N,[d(`div`,P,` v`+t(e.version),1),e.version===R.value?(r(),s(S,{key:0,dense:``,size:`sm`,color:`primary`,"text-color":`kobo-1`,label:n.$t(`changelog.current`),class:`q-ml-sm`},null,8,[`label`])):c(``,!0)]),d(`div`,{class:`changelog-notes`,innerHTML:V(e.notes)},null,8,F)]),_:2},1024)]),_:2},1024))),128))]))]),_:1}))}}),[[`__scopeId`,`data-v-6137cb7c`]]);export{I as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{H as e,rt as t}from"./QBtn-b_baQtAt.js";import{g as n,h as r}from"./use-id-b4A2I8LB.js";function i(e){return e===!1?0:e===!0||e===void 0?1:Number.parseInt(e,10)||0}function a(e){let t=e.currentTarget,i=t.__qclosepopup;i!==0&&setTimeout(()=>{let a=n(t);a!==void 0&&r(a,e,i)},0)}function o(t){e(t,13)&&a(t)}var s=t({name:`close-popup`,beforeMount(e,{value:t}){e.__qclosepopup=i(t),e.addEventListener(`click`,a),e.addEventListener(`keyup`,o)},updated(e,{value:t,oldValue:n}){t!==n&&e.__qclosepopup!==void 0&&(e.__qclosepopup=i(t))},beforeUnmount(e){e.__qclosepopup!==void 0&&(e.removeEventListener(`click`,a),e.removeEventListener(`keyup`,o),e.__qclosepopup=void 0)}});export{s as t};
|