@loicngr/kobo 1.11.1 → 1.11.2
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 +32 -0
- package/README.md +5 -5
- package/dist/server/routes/workspaces.js +69 -1
- package/dist/server/services/agent/engines/claude-code/event-mapper.js +1 -1
- package/dist/server/services/agent/engines/codex/event-mapper.js +1 -1
- package/dist/server/services/agent/orchestrator.js +56 -2
- package/dist/server/services/forge/github/provider.js +10 -1
- package/dist/server/services/forge/gitlab/provider.js +10 -1
- package/dist/server/services/forge/none.js +10 -1
- package/dist/server/services/settings-service.js +53 -11
- package/dist/shared/project-colors.js +7 -0
- package/package.json +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-BNhy8oDx.js +8 -0
- package/src/client/dist/pwa/assets/{ChangelogPage-CppcIG5K.js → ChangelogPage-eRWLuQLR.js} +1 -1
- package/src/client/dist/pwa/assets/{ClosePopup-B0-qi7Jo.js → ClosePopup-Cc2YwliF.js} +1 -1
- package/src/client/dist/pwa/assets/CreatePage-B9Mxi7zD.js +2 -0
- package/src/client/dist/pwa/assets/CreatePage-B_JcmRfU.css +1 -0
- package/src/client/dist/pwa/assets/{DiffViewer-COD5Mdp_.js → DiffViewer-DglPAzaZ.js} +3 -3
- package/src/client/dist/pwa/assets/{HealthPage-Bnad2BVm.js → HealthPage-sS-N-_9T.js} +1 -1
- package/src/client/dist/pwa/assets/{MainLayout-nn4YIcy8.js → MainLayout-BelFb6SW.js} +17 -17
- package/src/client/dist/pwa/assets/{MainLayout-Bwulksar.css → MainLayout-BoZsUoGd.css} +1 -1
- package/src/client/dist/pwa/assets/{QBtnToggle-CZXgD3Hf.js → QBtnToggle-eC2_l1xJ.js} +1 -1
- package/src/client/dist/pwa/assets/{QChip-BJH-iJCV.js → QChip-BT8qyt_p.js} +1 -1
- package/src/client/dist/pwa/assets/QSelect-Bj-wqU82.js +36 -0
- package/src/client/dist/pwa/assets/QTooltip-CCNfQ3sa.js +1 -0
- package/src/client/dist/pwa/assets/{SearchPage-BwD6K8qD.js → SearchPage-6CSIDDcM.js} +1 -1
- package/src/client/dist/pwa/assets/{SettingsPage-DJEH2ahl.css → SettingsPage-CENUP7ua.css} +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-jGIZfB7N.js +16 -0
- package/src/client/dist/pwa/assets/{ThinkingItem-DBWuhjgW.js → ThinkingItem-kP-lU251.js} +1 -1
- package/src/client/dist/pwa/assets/{TouchPan-txugr_lm.js → TouchPan-DVggdTB9.js} +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-BWRpD_9V.js +4 -0
- package/src/client/dist/pwa/assets/{WorkspacePage-Cv04uh_0.css → WorkspacePage-D-1HoeQg.css} +1 -1
- package/src/client/dist/pwa/assets/{build-path-tree-BUkEDxlT.js → build-path-tree-FNTYv-td.js} +1 -1
- package/src/client/dist/pwa/assets/{cssMode-BDLIXvw9.js → cssMode-DHVn7kI3.js} +1 -1
- package/src/client/dist/pwa/assets/documents-DoEDCa8X.js +1 -0
- package/src/client/dist/pwa/assets/{editor.api2-PhaZVUXg.js → editor.api2-DO15WOFV.js} +1 -1
- package/src/client/dist/pwa/assets/{editor.main-2nNvSQc1.js → editor.main-Ce_TPxw1.js} +2 -2
- package/src/client/dist/pwa/assets/{expand-template-HgUyAUBK.js → expand-template-DnNNlpvQ.js} +1 -1
- package/src/client/dist/pwa/assets/{formatters-Chk4XGyr.js → formatters-BBT0Ye5P.js} +1 -1
- package/src/client/dist/pwa/assets/{freemarker2-Dy02xxKp.js → freemarker2-B5mFQ9MW.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-Do59Qec5.js → handlebars-TGXWWwrU.js} +1 -1
- package/src/client/dist/pwa/assets/{html-MJ9-LLjS.js → html-BcoyXTgh.js} +1 -1
- package/src/client/dist/pwa/assets/{htmlMode-AsZ-SMzZ.js → htmlMode-Dhuwig6w.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-BHXcyfLT.js +1 -0
- package/src/client/dist/pwa/assets/{index-BpTtR9lB.js → index-CGnQPjwo.js} +12 -12
- package/src/client/dist/pwa/assets/{javascript-DoZFpstd.js → javascript-v_uXBoLN.js} +1 -1
- package/src/client/dist/pwa/assets/{jsonMode-CMeFz2Y2.js → jsonMode-CErvRpqG.js} +1 -1
- package/src/client/dist/pwa/assets/{kobo-commands-DmJq1I6k.js → kobo-commands-DXJ85QAa.js} +1 -1
- package/src/client/dist/pwa/assets/{layout-BSJkgTMQ.js → layout-Di93C8KP.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-Bc70ZdlV.js → liquid-ZappZiY7.js} +1 -1
- package/src/client/dist/pwa/assets/{lspLanguageFeatures-Co4WnPYT.js → lspLanguageFeatures-ur5VVJZY.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-BsXN2I7z.js → mdx-BKL2jNxT.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco.contribution-BpmQVvpQ.js → monaco.contribution-WfNiFCn_.js} +2 -2
- package/src/client/dist/pwa/assets/{network-auth-B0RCBQ5K.js → network-auth-O2hz04zH.js} +1 -1
- package/src/client/dist/pwa/assets/{pending-diff-open-BiozufWe.js → pending-diff-open-CysqNMDL.js} +1 -1
- package/src/client/dist/pwa/assets/{permissionModes-kRWgzgvi.js → permissionModes-Crg3_WdF.js} +1 -1
- package/src/client/dist/pwa/assets/project-color-DVafgHFe.js +1 -0
- package/src/client/dist/pwa/assets/{python-CV5Zt8gJ.js → python-DXpnDgWC.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-4Ev2x3ig.js → razor-2pTtVGVn.js} +1 -1
- package/src/client/dist/pwa/assets/{render-chat-markdown-Cjt11WnX.js → render-chat-markdown-BbmsqP4r.js} +1 -1
- package/src/client/dist/pwa/assets/{tsMode-BDysTsn1.js → tsMode-t5L5YTTB.js} +1 -1
- package/src/client/dist/pwa/assets/{typescript-DM1CefPN.js → typescript-C2FUrxc2.js} +1 -1
- package/src/client/dist/pwa/assets/{use-onboarding-D2BGKiP_.js → use-onboarding-CxQRTetV.js} +1 -1
- package/src/client/dist/pwa/assets/{use-quasar-Cps11O9Y.js → use-quasar-CUBX9V4F.js} +1 -1
- package/src/client/dist/pwa/assets/{workers-BuJZJ6j6.js → workers-ri7So4GA.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-DzARVQ9o.js → xml-CvWFj3be.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-P0ZYSZvj.js → yaml-DsSH3Hc4.js} +1 -1
- package/src/client/dist/pwa/index.html +1 -1
- package/src/client/dist/pwa/sounds/basic-notification.mp3 +0 -0
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-ByJjMo0k.js +0 -8
- package/src/client/dist/pwa/assets/CreatePage-D0xvdJAQ.js +0 -2
- package/src/client/dist/pwa/assets/CreatePage-qMgdzdb-.css +0 -1
- package/src/client/dist/pwa/assets/QSelect-tsFTetLi.js +0 -36
- package/src/client/dist/pwa/assets/QTooltip-CA_Iyybc.js +0 -1
- package/src/client/dist/pwa/assets/SettingsPage-CF-xI5PO.js +0 -16
- package/src/client/dist/pwa/assets/WorkspacePage-DgucXHjd.js +0 -4
- package/src/client/dist/pwa/assets/documents-ClS4k_bT.js +0 -1
- package/src/client/dist/pwa/assets/i18n-Djv0PzK4.js +0 -1
- package/src/client/dist/pwa/assets/project-color-D35ZMh9_.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,38 @@ 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
|
+
## 1.11.2
|
|
8
|
+
|
|
9
|
+
- feat(integrations): let Notion and Sentry be enabled independently, keep
|
|
10
|
+
their settings isolated, hide inactive project overrides, and add detailed
|
|
11
|
+
**Test connection** feedback.
|
|
12
|
+
- feat(chat): stream Claude output token-by-token like Codex, consolidate agent
|
|
13
|
+
reasoning into one live panel above the composer, and add a visibility
|
|
14
|
+
preference for reasoning and verbose system messages.
|
|
15
|
+
- feat(chat): add `Ctrl+J` for a new line, `Ctrl+Enter` to force a queued
|
|
16
|
+
message while an agent is busy, and keep the shortcuts visible below chat.
|
|
17
|
+
- feat(chat): add session-aware `Ctrl+F` workspace-history search, scrolling to
|
|
18
|
+
the selected result; global search now opens the same exact message.
|
|
19
|
+
- feat(chat): add a `Ctrl+K` workspace command palette for common actions.
|
|
20
|
+
- feat(chat): make worktree-relative paths and Edit/Write cards open the
|
|
21
|
+
corresponding file in the Git diff viewer.
|
|
22
|
+
- feat(git): add workspace commits with either a user-written message or an
|
|
23
|
+
agent request; add confirmed PR/MR merging and an optional remote-branch
|
|
24
|
+
deletion after merge.
|
|
25
|
+
- feat(auto-loop): retry quota, rate-limit, and temporary upstream failures;
|
|
26
|
+
add a configurable maximum retry count.
|
|
27
|
+
- feat(notifications): add `basic-notification.mp3`, event-level sound
|
|
28
|
+
inheritance, an agent-error sound, browser-permission diagnostics, and more
|
|
29
|
+
reliable playback while Kobo is in a background tab.
|
|
30
|
+
- feat(settings): extend the project color palette and improve save-state
|
|
31
|
+
handling for activity-feed preferences.
|
|
32
|
+
- feat(observability): enrich the session timeline with tools, tokens, and
|
|
33
|
+
errors; keep it fresh during an active run and retain diagnostic export.
|
|
34
|
+
- feat(pwa): show install, offline, update, and WebSocket-reconnection states;
|
|
35
|
+
preserve the visible chat area when the status banner is displayed.
|
|
36
|
+
- fix(websocket): avoid reconnecting after an explicit disconnect and resume
|
|
37
|
+
safely after network connectivity returns.
|
|
38
|
+
|
|
7
39
|
## 1.11.1
|
|
8
40
|
|
|
9
41
|
- feat(workspace): improve workspace actions
|
package/README.md
CHANGED
|
@@ -17,11 +17,11 @@ Kōbō runs multiple coding agents in parallel, each isolated in its own git wor
|
|
|
17
17
|
|
|
18
18
|
- **Isolated worktrees**: each workspace is a dedicated git worktree on its own branch, so parallel sessions never collide.
|
|
19
19
|
- **Two agent engines**: Claude Code (via `@anthropic-ai/claude-agent-sdk`) and OpenAI Codex (via `codex app-server`), chosen per workspace.
|
|
20
|
-
- **Live chat**: streaming text, inline Edit/Write diffs, per-turn cards, a compaction-in-progress indicator, infinite scrollback, and a reasoning panel immediately above the message input while Claude or Codex is actively thinking. `/` autocompletes skills and commands, `@` fuzzy-autocompletes worktree file paths, and `Ctrl+F` searches readable messages in the current workspace: selecting a result loads its session and scrolls to it. The global search page deep-links to the same location. Messages queued while an agent is busy stay scoped to their workspace and session. **Send now** delivers a queued message to an active Claude Code stream or steers the active Codex turn; it remains queued until the engine confirms acceptance. Use `Enter` to send, `Ctrl+Enter` to force delivery while an agent is busy, and `Shift+Enter` or `Ctrl+J` for a new line.
|
|
20
|
+
- **Live chat**: streaming text, inline Edit/Write diffs, per-turn cards, a compaction-in-progress indicator, infinite scrollback, and a reasoning panel immediately above the message input while Claude or Codex is actively thinking. `/` autocompletes skills and commands, `@` fuzzy-autocompletes worktree file paths, and `Ctrl+F` searches readable messages in the current workspace: selecting a result loads its session and scrolls to it. `Ctrl+K` opens a command palette for common workspace actions. The global search page deep-links to the same location. Messages queued while an agent is busy stay scoped to their workspace and session. **Send now** delivers a queued message to an active Claude Code stream or steers the active Codex turn; it remains queued until the engine confirms acceptance. Use `Enter` to send, `Ctrl+Enter` to force delivery while an agent is busy, and `Shift+Enter` or `Ctrl+J` for a new line.
|
|
21
21
|
- **Full MCP toolset (`kobo-tasks`)**: a per-workspace MCP server the agent uses for far more than tasks — task/acceptance-criteria CRUD, starting/stopping the dev server and reading its logs, a unified `get_ticket` (Notion or Sentry), searching past conversations across every workspace, per-session token/cost usage, and a `.ai/thoughts` decision log. Native Claude Code Task tools complement it for lightweight sub-agent coordination. See [`AGENTS.md`](./AGENTS.md) for the full tool list.
|
|
22
22
|
|
|
23
23
|

|
|
24
|
-
- **Git panel**: a Monaco-based diff viewer with **inline file editing** (edit the right-hand panel directly, save with `Ctrl/Cmd+S`, conflict-guarded via sha precondition), inline conflict resolution, and `Sync` / `Push` / `Commit` / `Open PR` / `Merge ready PR` / `Change PR base` / `Change source branch`. You can write the commit message yourself or ask the active engine to prepare the commit; PR/MR merging always requires a confirmation. Edit/Write cards in chat open their exact changed file in the diff viewer. Multi-forge: GitHub (`gh`), GitLab (`glab`), or no forge, auto-detected from the remote and overridable per project.
|
|
24
|
+
- **Git panel**: a Monaco-based diff viewer with **inline file editing** (edit the right-hand panel directly, save with `Ctrl/Cmd+S`, conflict-guarded via sha precondition), inline conflict resolution, and `Sync` / `Push` / `Commit` / `Open PR` / `Merge ready PR` / `Change PR base` / `Change source branch`. You can write the commit message yourself or ask the active engine to prepare the commit; PR/MR merging always requires a confirmation, then Kōbō can offer to delete the merged remote branch. Edit/Write cards in chat open their exact changed file in the diff viewer. Multi-forge: GitHub (`gh`), GitLab (`glab`), or no forge, auto-detected from the remote and overridable per project.
|
|
25
25
|
|
|
26
26
|

|
|
27
27
|
- **Dev server panel**: start, stop, and tail logs for a workspace's dev server (Docker or npm) straight from the Tools panel — no need to leave the UI. When a project has no configured server, the panel and MCP tool report that explicitly instead of implying one is running.
|
|
@@ -29,15 +29,15 @@ Kōbō runs multiple coding agents in parallel, each isolated in its own git wor
|
|
|
29
29
|
- **Interactive Q&A**: an agent can pause mid-session to ask a clarifying question through the UI; the workspace surfaces under "Needs Attention" until you answer. Choosing **Other** opens an inline free-form field, and option previews are available on hover. Codex native interactive questions require its `plan` permission mode.
|
|
30
30
|
|
|
31
31
|

|
|
32
|
-
- **Auto-loop**: an opt-in mode that walks the task list, spawning a fresh session per task and stopping once there's nothing left to do, progress stalls, an error occurs, or the agent needs input from you. Optionally run the initial brainstorming session on a stronger model and every task after that on a cheaper one, without leaving the engine you picked.
|
|
32
|
+
- **Auto-loop**: an opt-in mode that walks the task list, spawning a fresh session per task and stopping once there's nothing left to do, progress stalls, an error occurs, or the agent needs input from you. Rate-limit and temporary upstream failures retry on the quota backoff ladder; Settings controls the maximum number of automatic retries. Optionally run the initial brainstorming session on a stronger model and every task after that on a cheaper one, without leaving the engine you picked.
|
|
33
33
|
- **Quota-aware**: 5-hour / 7-day Claude usage and Codex rate-limit buckets sit in the footer, and sessions auto-resume after a rate-limit reset.
|
|
34
34
|
- **Scheduled wakeups**: the agent schedules a one-shot wake-up via the `schedule_wakeup` MCP tool. Kōbō persists it across restarts, shows a live countdown, and re-invokes the agent with the stored prompt at the chosen time.
|
|
35
35
|
- **Cron schedules**: recurring per-workspace triggers the agent registers through MCP tools (`cron_create` / `cron_delete` / `cron_list`). Each tick resumes the workspace session (skipped if already active), and schedules are re-armed at boot with skip-missed semantics.
|
|
36
36
|
- **Lifecycle scripts**: shell scripts run automatically at key moments — **setup** (worktree created), **cleanup** (session ended), **archive** (workspace archived). Configure them globally or per project, with their output streamed into the chat.
|
|
37
37
|
- **Disk-space purge**: free a merged workspace's disk space without losing its chat history — see [below](#disk-space-purge).
|
|
38
|
-
- **Observability**: a compact session timeline in the right panel shows session duration and
|
|
38
|
+
- **Observability**: a compact session timeline in the right panel shows each session’s duration, tools, tokens, and errors; download a redacted workspace diagnostic JSON for troubleshooting.
|
|
39
39
|
- **Optional integrations**: independently enable or disable Notion (import missions) and Sentry (fix from issue URL) in Settings, with a **Test connection** action for each; local voice transcription uses whisper.cpp.
|
|
40
|
-
- **Installable PWA**: production builds work as a Progressive Web App. Install Kōbō from your browser on desktop or mobile for an app-like window; it signals offline state and offers an explicit reload when an update is ready. Live agent activity still requires a connection to the local Kōbō server.
|
|
40
|
+
- **Installable PWA**: production builds work as a Progressive Web App. Install Kōbō from your browser on desktop or mobile for an app-like window; it signals offline state, WebSocket reconnection, and offers an explicit reload when an update is ready. Live agent activity still requires a connection to the local Kōbō server.
|
|
41
41
|
|
|
42
42
|
## Quick start
|
|
43
43
|
|
|
@@ -1559,6 +1559,48 @@ function readableHistorySearchResult(type, rawPayload, query) {
|
|
|
1559
1559
|
snippet: `${start > 0 ? '…' : ''}${text.slice(start, end).replace(/\s+/g, ' ').trim()}${end < text.length ? '…' : ''}`,
|
|
1560
1560
|
};
|
|
1561
1561
|
}
|
|
1562
|
+
// GET /api/workspaces/:id/session-metrics — compact observability data for the timeline.
|
|
1563
|
+
app.get('/:id/session-metrics', (c) => {
|
|
1564
|
+
try {
|
|
1565
|
+
const id = c.req.param('id');
|
|
1566
|
+
if (!workspaceService.getWorkspace(id))
|
|
1567
|
+
return c.json({ error: `Workspace '${id}' not found` }, 404);
|
|
1568
|
+
const db = getDb();
|
|
1569
|
+
const sessions = workspaceService.listSessions(id);
|
|
1570
|
+
const metrics = new Map(sessions.map((session) => [
|
|
1571
|
+
session.id,
|
|
1572
|
+
{ sessionId: session.id, toolCalls: 0, errors: 0, inputTokens: 0, outputTokens: 0 },
|
|
1573
|
+
]));
|
|
1574
|
+
const events = db
|
|
1575
|
+
.prepare("SELECT session_id, payload FROM ws_events WHERE workspace_id = ? AND type = 'agent:event' AND session_id IS NOT NULL")
|
|
1576
|
+
.all(id);
|
|
1577
|
+
for (const row of events) {
|
|
1578
|
+
const metric = metrics.get(row.session_id);
|
|
1579
|
+
if (!metric)
|
|
1580
|
+
continue;
|
|
1581
|
+
try {
|
|
1582
|
+
const event = JSON.parse(row.payload);
|
|
1583
|
+
if (event.kind === 'tool:call')
|
|
1584
|
+
metric.toolCalls++;
|
|
1585
|
+
if (event.kind === 'error' || (event.kind === 'tool:result' && event.isError === true))
|
|
1586
|
+
metric.errors++;
|
|
1587
|
+
if (event.kind === 'usage') {
|
|
1588
|
+
if (typeof event.inputTokens === 'number')
|
|
1589
|
+
metric.inputTokens = Math.max(metric.inputTokens, event.inputTokens);
|
|
1590
|
+
if (typeof event.outputTokens === 'number')
|
|
1591
|
+
metric.outputTokens = Math.max(metric.outputTokens, event.outputTokens);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
catch {
|
|
1595
|
+
// Corrupt historical events are intentionally ignored in diagnostics.
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return c.json({ sessions, metrics: [...metrics.values()] });
|
|
1599
|
+
}
|
|
1600
|
+
catch (err) {
|
|
1601
|
+
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
1602
|
+
}
|
|
1603
|
+
});
|
|
1562
1604
|
// GET /api/workspaces/:id/diagnostic.json — portable, redacted-by-design event diagnostic.
|
|
1563
1605
|
app.get('/:id/diagnostic.json', (c) => {
|
|
1564
1606
|
try {
|
|
@@ -3186,7 +3228,8 @@ app.post('/:id/merge-pr', async (c) => {
|
|
|
3186
3228
|
return c.json({ error: 'This PR is not ready to merge', code: 'pr_not_ready' }, 409);
|
|
3187
3229
|
}
|
|
3188
3230
|
await provider.mergeRequest(workspace.worktreePath, snapshot.number);
|
|
3189
|
-
|
|
3231
|
+
const merged = await provider.getPrStatus(workspace.worktreePath, workspace.workingBranch);
|
|
3232
|
+
return c.json({ success: true, merged: merged?.state === 'MERGED', branch: workspace.workingBranch });
|
|
3190
3233
|
}
|
|
3191
3234
|
catch (err) {
|
|
3192
3235
|
if (err instanceof ForgeUnavailableError) {
|
|
@@ -3196,6 +3239,31 @@ app.post('/:id/merge-pr', async (c) => {
|
|
|
3196
3239
|
return c.json({ error: message }, 500);
|
|
3197
3240
|
}
|
|
3198
3241
|
});
|
|
3242
|
+
/** Delete the workspace branch from the remote after its PR/MR was merged. */
|
|
3243
|
+
app.post('/:id/delete-remote-branch', async (c) => {
|
|
3244
|
+
try {
|
|
3245
|
+
const id = c.req.param('id');
|
|
3246
|
+
const workspace = workspaceService.getWorkspace(id);
|
|
3247
|
+
if (!workspace)
|
|
3248
|
+
return c.json({ error: `Workspace '${id}' not found` }, 404);
|
|
3249
|
+
const provider = getForgeProvider(resolveForge(workspace.projectPath));
|
|
3250
|
+
if (!provider.capabilities.canDeleteRemoteBranch) {
|
|
3251
|
+
return c.json({ error: 'This project cannot delete remote branches', code: 'forge_unsupported' }, 409);
|
|
3252
|
+
}
|
|
3253
|
+
const availability = await provider.isAvailable(workspace.worktreePath);
|
|
3254
|
+
if (!availability.available)
|
|
3255
|
+
return c.json({ error: 'Forge CLI unavailable', code: 'forge_unavailable' }, 409);
|
|
3256
|
+
const snapshot = await provider.getPrStatus(workspace.worktreePath, workspace.workingBranch);
|
|
3257
|
+
if (snapshot?.state !== 'MERGED')
|
|
3258
|
+
return c.json({ error: 'The PR is not merged yet', code: 'pr_not_merged' }, 409);
|
|
3259
|
+
await provider.deleteRemoteBranch(workspace.worktreePath, workspace.workingBranch);
|
|
3260
|
+
return c.json({ success: true });
|
|
3261
|
+
}
|
|
3262
|
+
catch (err) {
|
|
3263
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3264
|
+
return c.json({ error: message }, 500);
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3199
3267
|
/** Re-target a workspace onto a new source branch (metadata + worktree + PR base). */
|
|
3200
3268
|
app.post('/:id/change-source-branch', async (c) => {
|
|
3201
3269
|
const id = c.req.param('id');
|
|
@@ -76,7 +76,7 @@ function normalizeRateLimitInfo(info) {
|
|
|
76
76
|
* Anthropic's surfaces (`rate_limit_exceeded`, `Claude AI usage limit
|
|
77
77
|
* reached`, `You're out of extra usage`, `quota exceeded`).
|
|
78
78
|
*/
|
|
79
|
-
export const QUOTA_PATTERN =
|
|
79
|
+
export const QUOTA_PATTERN = /\b429\b|out of extra usage|rate[_ ]limit|usage limit|quota exceeded/i;
|
|
80
80
|
export function createMapperState() {
|
|
81
81
|
return {
|
|
82
82
|
sessionStartedEmitted: false,
|
|
@@ -7,7 +7,7 @@ import { nanoid } from 'nanoid';
|
|
|
7
7
|
// - "quota exceeded" / "insufficient_quota" / "insufficient quota"
|
|
8
8
|
// - "out of extra usage" / "out of usage"
|
|
9
9
|
// - "usage limit exceeded"
|
|
10
|
-
export const QUOTA_PATTERN =
|
|
10
|
+
export const QUOTA_PATTERN = /\b429\b|rate[_ ]limit|quota|usage limit|insufficient[_ ]quota|out of (extra )?usage/i;
|
|
11
11
|
export function createMapperState(opts) {
|
|
12
12
|
return {
|
|
13
13
|
sessionStartedEmitted: false,
|
|
@@ -7,7 +7,7 @@ import * as autoLoopService from '../auto-loop-service.js';
|
|
|
7
7
|
import * as cleanupScriptService from '../cleanup-script-service.js';
|
|
8
8
|
import * as cronService from '../cron-service.js';
|
|
9
9
|
import * as quotaBackoffService from '../quota-backoff-service.js';
|
|
10
|
-
import { getEffectiveSettings } from '../settings-service.js';
|
|
10
|
+
import { getEffectiveSettings, getGlobalSettings } from '../settings-service.js';
|
|
11
11
|
import { refreshNow } from '../usage/poller.js';
|
|
12
12
|
import * as wakeupService from '../wakeup-service.js';
|
|
13
13
|
import { emit, emitEphemeral } from '../websocket-service.js';
|
|
@@ -540,6 +540,12 @@ function handleEvent(workspaceId, agentSessionId, ev) {
|
|
|
540
540
|
if (ev.kind === 'error' && ev.category === 'quota') {
|
|
541
541
|
void handleQuota(workspaceId, agentSessionId);
|
|
542
542
|
}
|
|
543
|
+
if (ev.kind === 'error' &&
|
|
544
|
+
ev.category === 'other' &&
|
|
545
|
+
TRANSIENT_SERVER_ERROR_PATTERN.test(ev.message) &&
|
|
546
|
+
getWs(workspaceId)?.autoLoop) {
|
|
547
|
+
void handleTransientAutoLoopFailure(workspaceId);
|
|
548
|
+
}
|
|
543
549
|
if (ev.kind === 'error' && ev.category === 'resume_failed') {
|
|
544
550
|
resumeFailedSet.add(workspaceId);
|
|
545
551
|
clearStaleEngineSessionId(workspaceId);
|
|
@@ -1195,6 +1201,8 @@ function clearStaleEngineSessionId(workspaceId) {
|
|
|
1195
1201
|
console.error('[orchestrator] Failed to clear stale engine session ID:', err);
|
|
1196
1202
|
}
|
|
1197
1203
|
}
|
|
1204
|
+
/** Retryable upstream failures for an auto-loop iteration (not coding errors). */
|
|
1205
|
+
export const TRANSIENT_SERVER_ERROR_PATTERN = /\b(?:http\s*)?500\b|internal server error|service unavailable|temporarily unavailable|overloaded/i;
|
|
1198
1206
|
const QUOTA_SAFETY_MARGIN_MS = 30_000;
|
|
1199
1207
|
const QUOTA_MAX_BACKOFF_MS = 24 * 60 * 60 * 1000;
|
|
1200
1208
|
const QUOTA_SATURATION_THRESHOLD_PCT = 95;
|
|
@@ -1214,7 +1222,11 @@ const QUOTA_FALLBACK_LADDER_MINUTES = [15, 30, 60, 180, 300];
|
|
|
1214
1222
|
* finally (2) a fixed ladder (15 → 30 → 60 → 180 → 300 min) whenever neither
|
|
1215
1223
|
* source is usable.
|
|
1216
1224
|
*/
|
|
1217
|
-
export async function computeQuotaBackoffMs(workspaceId, retryCount) {
|
|
1225
|
+
export async function computeQuotaBackoffMs(workspaceId, retryCount, preferQuotaReset = true) {
|
|
1226
|
+
if (!preferQuotaReset) {
|
|
1227
|
+
const idx = Math.min(Math.max(0, retryCount), QUOTA_FALLBACK_LADDER_MINUTES.length - 1);
|
|
1228
|
+
return { delayMs: QUOTA_FALLBACK_LADDER_MINUTES[idx] * 60 * 1000, source: 'fallback_ladder' };
|
|
1229
|
+
}
|
|
1218
1230
|
// 1. Prefer the rate_limit event from the agent stream — most recent + most precise.
|
|
1219
1231
|
const info = latestRateLimitInfo.get(workspaceId);
|
|
1220
1232
|
if (info?.buckets?.length) {
|
|
@@ -1270,14 +1282,56 @@ async function handleQuota(workspaceId, _agentSessionId) {
|
|
|
1270
1282
|
// May fail if transition is not valid
|
|
1271
1283
|
}
|
|
1272
1284
|
const retryCount = retryCounts.get(workspaceId) ?? 0;
|
|
1285
|
+
const autoLoopEnabled = getWs(workspaceId)?.autoLoop === true;
|
|
1286
|
+
const maxRetries = autoLoopEnabled ? (getGlobalSettings().autoLoopMaxRetries ?? 5) : 5;
|
|
1287
|
+
if (autoLoopEnabled && retryCount >= maxRetries) {
|
|
1288
|
+
autoLoopService.disable(workspaceId, 'error');
|
|
1289
|
+
try {
|
|
1290
|
+
updateWorkspaceStatus(workspaceId, 'error');
|
|
1291
|
+
}
|
|
1292
|
+
catch {
|
|
1293
|
+
// The loop is disabled even if an already-terminal status rejects this transition.
|
|
1294
|
+
}
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1273
1297
|
const { delayMs, resetsAt, source } = await computeQuotaBackoffMs(workspaceId, retryCount);
|
|
1274
1298
|
retryCounts.set(workspaceId, retryCount + 1);
|
|
1275
1299
|
// The quotaBackoffService owns the timer + the persistent row + the
|
|
1276
1300
|
// 'agent:quota-backoff' WS emit. Hand off everything to it.
|
|
1277
1301
|
quotaBackoffService.arm(workspaceId, delayMs, { resetsAt: resetsAt ?? null, source });
|
|
1278
1302
|
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Use the same persisted retry path for temporary upstream failures (HTTP 500
|
|
1305
|
+
* and overloads) while deliberately ignoring quota-reset data: a server error
|
|
1306
|
+
* is unrelated to the user's quota window and follows the fallback ladder.
|
|
1307
|
+
*/
|
|
1308
|
+
async function handleTransientAutoLoopFailure(workspaceId) {
|
|
1309
|
+
try {
|
|
1310
|
+
updateWorkspaceStatus(workspaceId, 'quota');
|
|
1311
|
+
}
|
|
1312
|
+
catch {
|
|
1313
|
+
// May fail if transition is not valid.
|
|
1314
|
+
}
|
|
1315
|
+
const retryCount = retryCounts.get(workspaceId) ?? 0;
|
|
1316
|
+
const maxRetries = getGlobalSettings().autoLoopMaxRetries ?? 5;
|
|
1317
|
+
if (retryCount >= maxRetries) {
|
|
1318
|
+
autoLoopService.disable(workspaceId, 'error');
|
|
1319
|
+
try {
|
|
1320
|
+
updateWorkspaceStatus(workspaceId, 'error');
|
|
1321
|
+
}
|
|
1322
|
+
catch {
|
|
1323
|
+
// The loop is disabled even if an already-terminal status rejects this transition.
|
|
1324
|
+
}
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
const { delayMs, resetsAt, source } = await computeQuotaBackoffMs(workspaceId, retryCount, false);
|
|
1328
|
+
retryCounts.set(workspaceId, retryCount + 1);
|
|
1329
|
+
quotaBackoffService.arm(workspaceId, delayMs, { resetsAt: resetsAt ?? null, source });
|
|
1330
|
+
}
|
|
1279
1331
|
/** @internal test-only — re-export of `handleQuota` for direct testing. */
|
|
1280
1332
|
export const _handleQuota = handleQuota;
|
|
1333
|
+
/** @internal test-only — re-export of the transient auto-loop retry handler. */
|
|
1334
|
+
export const _handleTransientAutoLoopFailure = handleTransientAutoLoopFailure;
|
|
1281
1335
|
/**
|
|
1282
1336
|
* Rebuild the in-memory `retryCounts` map from the persisted `pending_quota_backoffs`
|
|
1283
1337
|
* rows. Called from `index.ts` at boot, before `quotaBackoffService.restoreOnBoot`.
|
|
@@ -92,7 +92,13 @@ function availabilityFromError(err) {
|
|
|
92
92
|
}
|
|
93
93
|
export const githubProvider = {
|
|
94
94
|
id: 'github',
|
|
95
|
-
capabilities: {
|
|
95
|
+
capabilities: {
|
|
96
|
+
canCreatePr: true,
|
|
97
|
+
canChangePrBase: true,
|
|
98
|
+
canMergeRequest: true,
|
|
99
|
+
canDeleteRemoteBranch: true,
|
|
100
|
+
requestTermShort: 'PR',
|
|
101
|
+
},
|
|
96
102
|
async isAvailable(repoPath) {
|
|
97
103
|
try {
|
|
98
104
|
await execFileAsync('gh', ['auth', 'status'], { cwd: repoPath, encoding: 'utf-8' });
|
|
@@ -130,4 +136,7 @@ export const githubProvider = {
|
|
|
130
136
|
async mergeRequest(repoPath, number) {
|
|
131
137
|
await execFileAsync('gh', ['pr', 'merge', String(number), '--merge'], { cwd: repoPath, encoding: 'utf-8' });
|
|
132
138
|
},
|
|
139
|
+
async deleteRemoteBranch(repoPath, branch) {
|
|
140
|
+
await execFileAsync('git', ['push', 'origin', '--delete', branch], { cwd: repoPath, encoding: 'utf-8' });
|
|
141
|
+
},
|
|
133
142
|
};
|
|
@@ -129,7 +129,13 @@ function availabilityFromError(err) {
|
|
|
129
129
|
}
|
|
130
130
|
export const gitlabProvider = {
|
|
131
131
|
id: 'gitlab',
|
|
132
|
-
capabilities: {
|
|
132
|
+
capabilities: {
|
|
133
|
+
canCreatePr: true,
|
|
134
|
+
canChangePrBase: true,
|
|
135
|
+
canMergeRequest: true,
|
|
136
|
+
canDeleteRemoteBranch: true,
|
|
137
|
+
requestTermShort: 'MR',
|
|
138
|
+
},
|
|
133
139
|
async isAvailable(repoPath) {
|
|
134
140
|
try {
|
|
135
141
|
await execFileAsync('glab', ['auth', 'status'], { cwd: repoPath, encoding: 'utf-8' });
|
|
@@ -182,4 +188,7 @@ export const gitlabProvider = {
|
|
|
182
188
|
async mergeRequest(repoPath, number) {
|
|
183
189
|
await execFileAsync('glab', ['mr', 'merge', String(number), '--yes'], { cwd: repoPath, encoding: 'utf-8' });
|
|
184
190
|
},
|
|
191
|
+
async deleteRemoteBranch(repoPath, branch) {
|
|
192
|
+
await execFileAsync('git', ['push', 'origin', '--delete', branch], { cwd: repoPath, encoding: 'utf-8' });
|
|
193
|
+
},
|
|
185
194
|
};
|
|
@@ -7,7 +7,13 @@ import { ForgeUnavailableError } from './types.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export const noneProvider = {
|
|
9
9
|
id: 'none',
|
|
10
|
-
capabilities: {
|
|
10
|
+
capabilities: {
|
|
11
|
+
canCreatePr: false,
|
|
12
|
+
canChangePrBase: false,
|
|
13
|
+
canMergeRequest: false,
|
|
14
|
+
canDeleteRemoteBranch: false,
|
|
15
|
+
requestTermShort: 'PR',
|
|
16
|
+
},
|
|
11
17
|
async isAvailable() {
|
|
12
18
|
return { available: false };
|
|
13
19
|
},
|
|
@@ -23,4 +29,7 @@ export const noneProvider = {
|
|
|
23
29
|
async mergeRequest() {
|
|
24
30
|
throw new ForgeUnavailableError('This project has no supported forge configured');
|
|
25
31
|
},
|
|
32
|
+
async deleteRemoteBranch() {
|
|
33
|
+
throw new ForgeUnavailableError('This project has no supported forge configured');
|
|
34
|
+
},
|
|
26
35
|
};
|
|
@@ -768,6 +768,31 @@ const settingsMigrations = [
|
|
|
768
768
|
global.showThinkingBlocks = true;
|
|
769
769
|
},
|
|
770
770
|
},
|
|
771
|
+
{
|
|
772
|
+
version: 48,
|
|
773
|
+
name: 'add-agent-error-notification-sound',
|
|
774
|
+
migrate: ({ global }) => {
|
|
775
|
+
if (typeof global.audioAgentErrorNotifications !== 'boolean')
|
|
776
|
+
global.audioAgentErrorNotifications = false;
|
|
777
|
+
if (typeof global.audioAgentErrorSound !== 'string' || global.audioAgentErrorSound.length === 0) {
|
|
778
|
+
global.audioAgentErrorSound = 'inherit';
|
|
779
|
+
}
|
|
780
|
+
if (typeof global.audioAgentErrorVolume !== 'number' || !Number.isFinite(global.audioAgentErrorVolume)) {
|
|
781
|
+
global.audioAgentErrorVolume = 1;
|
|
782
|
+
}
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
version: 49,
|
|
787
|
+
name: 'add-auto-loop-max-retries',
|
|
788
|
+
migrate: ({ global }) => {
|
|
789
|
+
if (typeof global.autoLoopMaxRetries !== 'number' ||
|
|
790
|
+
!Number.isInteger(global.autoLoopMaxRetries) ||
|
|
791
|
+
global.autoLoopMaxRetries < 1) {
|
|
792
|
+
global.autoLoopMaxRetries = 5;
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
},
|
|
771
796
|
];
|
|
772
797
|
/** Current settings schema version — always equals the highest migration version. */
|
|
773
798
|
export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
|
|
@@ -814,40 +839,44 @@ function defaultSettings() {
|
|
|
814
839
|
fileManagerCommand: '',
|
|
815
840
|
terminalCommand: '',
|
|
816
841
|
autoPurgeOnPrMerged: false,
|
|
842
|
+
autoLoopMaxRetries: 5,
|
|
817
843
|
networkAccessEnabled: false,
|
|
818
844
|
networkAccessToken: '',
|
|
819
845
|
networkAccessBehindProxy: false,
|
|
820
846
|
browserNotifications: true,
|
|
821
847
|
audioNotifications: true,
|
|
822
|
-
audioQuestionNotifications:
|
|
823
|
-
audioWorkspaceCreatedNotifications:
|
|
848
|
+
audioQuestionNotifications: false,
|
|
849
|
+
audioWorkspaceCreatedNotifications: false,
|
|
850
|
+
audioAgentErrorNotifications: false,
|
|
824
851
|
audioNotificationSound: 'hey.mp3',
|
|
825
|
-
audioQuestionSound: '
|
|
826
|
-
audioWorkspaceCreatedSound: '
|
|
852
|
+
audioQuestionSound: 'inherit',
|
|
853
|
+
audioWorkspaceCreatedSound: 'inherit',
|
|
854
|
+
audioAgentErrorSound: 'inherit',
|
|
827
855
|
audioPrCiFailedSound: 'inherit',
|
|
828
|
-
audioPrCiFailedEnabled:
|
|
856
|
+
audioPrCiFailedEnabled: false,
|
|
829
857
|
audioPrCiFailedVolume: 1,
|
|
830
858
|
audioPrCiRecoveredSound: 'inherit',
|
|
831
|
-
audioPrCiRecoveredEnabled:
|
|
859
|
+
audioPrCiRecoveredEnabled: false,
|
|
832
860
|
audioPrCiRecoveredVolume: 1,
|
|
833
861
|
audioPrChangesRequestedSound: 'inherit',
|
|
834
|
-
audioPrChangesRequestedEnabled:
|
|
862
|
+
audioPrChangesRequestedEnabled: false,
|
|
835
863
|
audioPrChangesRequestedVolume: 1,
|
|
836
864
|
audioPrApprovedSound: 'inherit',
|
|
837
|
-
audioPrApprovedEnabled:
|
|
865
|
+
audioPrApprovedEnabled: false,
|
|
838
866
|
audioPrApprovedVolume: 1,
|
|
839
867
|
audioPrMergeConflictSound: 'inherit',
|
|
840
|
-
audioPrMergeConflictEnabled:
|
|
868
|
+
audioPrMergeConflictEnabled: false,
|
|
841
869
|
audioPrMergeConflictVolume: 1,
|
|
842
870
|
audioPrReadyToMergeSound: 'inherit',
|
|
843
|
-
audioPrReadyToMergeEnabled:
|
|
871
|
+
audioPrReadyToMergeEnabled: false,
|
|
844
872
|
audioPrReadyToMergeVolume: 1,
|
|
845
873
|
audioPrMergedSound: 'inherit',
|
|
846
|
-
audioPrMergedEnabled:
|
|
874
|
+
audioPrMergedEnabled: false,
|
|
847
875
|
audioPrMergedVolume: 1,
|
|
848
876
|
audioNotificationVolume: 1,
|
|
849
877
|
audioQuestionVolume: 1,
|
|
850
878
|
audioWorkspaceCreatedVolume: 1,
|
|
879
|
+
audioAgentErrorVolume: 1,
|
|
851
880
|
notionStatusProperty: '',
|
|
852
881
|
notionInProgressStatus: '',
|
|
853
882
|
notionAssigneeProperty: '',
|
|
@@ -1182,6 +1211,15 @@ export function updateGlobalSettings(data) {
|
|
|
1182
1211
|
delete data.cleanupScriptMode;
|
|
1183
1212
|
}
|
|
1184
1213
|
}
|
|
1214
|
+
if ('autoLoopMaxRetries' in data) {
|
|
1215
|
+
if (typeof data.autoLoopMaxRetries !== 'number' ||
|
|
1216
|
+
!Number.isInteger(data.autoLoopMaxRetries) ||
|
|
1217
|
+
data.autoLoopMaxRetries < 1 ||
|
|
1218
|
+
data.autoLoopMaxRetries > 20) {
|
|
1219
|
+
console.warn(`[settings] Invalid autoLoopMaxRetries value rejected: ${data.autoLoopMaxRetries}`);
|
|
1220
|
+
delete data.autoLoopMaxRetries;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1185
1223
|
const allowedGlobalKeys = [
|
|
1186
1224
|
'defaultModelByEngine',
|
|
1187
1225
|
'dangerouslySkipPermissions',
|
|
@@ -1202,13 +1240,16 @@ export function updateGlobalSettings(data) {
|
|
|
1202
1240
|
'fileManagerCommand',
|
|
1203
1241
|
'terminalCommand',
|
|
1204
1242
|
'autoPurgeOnPrMerged',
|
|
1243
|
+
'autoLoopMaxRetries',
|
|
1205
1244
|
'browserNotifications',
|
|
1206
1245
|
'audioNotifications',
|
|
1207
1246
|
'audioQuestionNotifications',
|
|
1208
1247
|
'audioWorkspaceCreatedNotifications',
|
|
1248
|
+
'audioAgentErrorNotifications',
|
|
1209
1249
|
'audioNotificationSound',
|
|
1210
1250
|
'audioQuestionSound',
|
|
1211
1251
|
'audioWorkspaceCreatedSound',
|
|
1252
|
+
'audioAgentErrorSound',
|
|
1212
1253
|
'audioPrCiFailedSound',
|
|
1213
1254
|
'audioPrCiFailedEnabled',
|
|
1214
1255
|
'audioPrCiFailedVolume',
|
|
@@ -1233,6 +1274,7 @@ export function updateGlobalSettings(data) {
|
|
|
1233
1274
|
'audioNotificationVolume',
|
|
1234
1275
|
'audioQuestionVolume',
|
|
1235
1276
|
'audioWorkspaceCreatedVolume',
|
|
1277
|
+
'audioAgentErrorVolume',
|
|
1236
1278
|
'notionStatusProperty',
|
|
1237
1279
|
'notionInProgressStatus',
|
|
1238
1280
|
'notionAssigneeProperty',
|
|
@@ -17,6 +17,13 @@ export const PROJECT_COLOR_PALETTE = [
|
|
|
17
17
|
'amber-5',
|
|
18
18
|
'orange-5',
|
|
19
19
|
'brown-5',
|
|
20
|
+
'light-blue-5',
|
|
21
|
+
'light-green-5',
|
|
22
|
+
'lime-5',
|
|
23
|
+
'yellow-6',
|
|
24
|
+
'deep-orange-5',
|
|
25
|
+
'blue-grey-5',
|
|
26
|
+
'grey-6',
|
|
20
27
|
];
|
|
21
28
|
export function isValidProjectColor(value) {
|
|
22
29
|
return typeof value === 'string' && PROJECT_COLOR_PALETTE.includes(value);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.2",
|
|
4
4
|
"description": "Kōbō — multi-workspace agent manager for Claude Code. Orchestrates isolated git worktrees with dev servers, Notion integration, and MCP tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{$ as e,B as t,F as n,I as r,R as i,S as a,U as o,W as s,_ as c,at as l,b as u,c as d,ct as f,g as p,h as m,it as h,k as g,m as _,n as v,p as y,s as b,st as x,y as S}from"./vue.runtime.esm-bundler-JZnIeD9D.js";import{l as C,t as w}from"./QIcon-C6E-oEjI.js";import{G as T,K as E,d as D,k as O,p as k,v as A,y as j,z as ee}from"./index-CGnQPjwo.js";import{t as M}from"./QSpinnerDots-DIV68-cV.js";import{t as N}from"./QTooltip-CCNfQ3sa.js";import{a as te}from"./permissionModes-Crg3_WdF.js";import{r as P,t as ne}from"./pending-diff-open-CysqNMDL.js";import{t as F}from"./documents-DoEDCa8X.js";import{i as I,n as re,t as L}from"./render-chat-markdown-BbmsqP4r.js";import{t as R}from"./_plugin-vue_export-helper-BDNMzG2s.js";import{t as z}from"./ThinkingItem-kP-lU251.js";function ie(e,t,n=!0,r){let i=[],a=new Map,o=new Map;for(let n=0;n<e.length;n++){let s=e[n],c=t?.[n],l=r?.[n]??void 0;switch(s.kind){case`message:text`:{let e=a.get(s.messageId);if(e)e.text+=s.text,e.streaming=s.streaming,l&&e.eventIds?.push(l);else{let e={type:`text`,messageId:s.messageId,text:s.text,streaming:s.streaming,ts:c,eventIds:l?[l]:void 0};a.set(s.messageId,e),i.push(e)}break}case`message:end`:{let e=a.get(s.messageId);e&&(e.streaming=!1);break}case`message:thinking`:i.push({type:`thinking`,messageId:s.messageId,text:s.text,ts:c,eventIds:l?[l]:void 0});break;case`tool:call`:{let e={type:`tool`,toolCallId:s.toolCallId,name:s.name,input:s.input,ts:c,eventIds:l?[l]:void 0};o.set(s.toolCallId,e),i.push(e);break}case`tool:result`:{let e=o.get(s.toolCallId);e&&(e.result={output:s.output,isError:s.isError},l&&e.eventIds?.push(l));break}case`session:started`:i.push({type:`session`,kind:`started`,detail:{engineSessionId:s.engineSessionId,model:s.model},ts:c,eventIds:l?[l]:void 0});break;case`session:ended`:i.push({type:`session`,kind:`ended`,detail:{reason:s.reason,exitCode:s.exitCode},ts:c,eventIds:l?[l]:void 0});break;case`session:compacted`:i.push({type:`session`,kind:`compacted`,ts:c,eventIds:l?[l]:void 0});break;case`session:compacting`:case`session:brainstorm-complete`:case`session:user-input-requested`:case`mcp:status`:case`message:raw`:case`skills:discovered`:case`usage`:case`rate_limit`:case`subagent:progress`:case`error`:break;default:}}let s=null;for(let e of i)e.type===`text`&&e.streaming&&(s&&(s.streaming=!1),s=e);return s&&!n&&(s.streaming=!1),i}function ae(e,t){if(t.length===0)return e;let n=t.map(e=>({type:`user`,content:e.content,sender:e.sender,ts:e.ts,eventIds:e.eventIds})),r=[...e,...n];r.sort((e,t)=>{let n=e.ts??``,r=t.ts??``;return n===r?0:n?r?n<r?-1:1:-1:1});let i;for(let e of r)e.type===`user`&&e.sender!==`system-prompt`&&e.ts&&(!i||e.ts>i)&&(i=e.ts);if(i)for(let e of r)e.type===`text`&&e.streaming&&(!e.ts||e.ts<i)&&(e.streaming=!1);return r}var B=new Set([`setup`,`cleanup`,`archive`]);function V(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:B.has(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function oe(e){let t=[],n=null,r=null;for(let i of e){let e=V(i),a=e===`session`||e===`system-prompt`,o=e===`script`&&i.type===`user`?`script:${i.sender}`:e;!n||r!==o||a?(n={speaker:e,ts:i.ts,items:[i]},r=o,t.push(n),a&&(n=null)):n.items.push(i)}return t}var H={class:`text-caption text-grey-6`},se=a({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,n=y(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return`session.ended`;case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(i(),c(`span`,H,f(e.$t(n.value)),1))}});function ce(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){le(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 le(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=ue(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 ue(e,t,n){let r=null;for(let i of n){let n=e.indexOf(i,t);n<0||(!r||n<r.index||n===r.index&&i.length>r.path.length)&&(r={index:n,path:i})}return r}var U=[`innerHTML`],de=R(a({__name:`TextMessageItem`,props:{item:{}},setup(e){let t=e,n=F(),r=D(),a=y(()=>{let e=r.selectedWorkspaceId;return e?n.documentsFor(e).map(e=>e.path):[]}),o=y(()=>re(ce(I.parse(t.item.text,{async:!1,breaks:!0,gfm:!0}),a.value),{addAttr:[`data-document-path`]}));function s(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let i=t.getAttribute(`data-document-path`),a=r.selectedWorkspaceId;!i||!a||n.openDocumentByPath(a,i)}return(t,n)=>(i(),c(`div`,{class:`markdown-message`,onClick:s},[_(`div`,{innerHTML:o.value},null,8,U),e.item.streaming?(i(),m(C,{key:0,size:`xs`,class:`q-ml-xs`})):p(``,!0)]))}}),[[`__scopeId`,`data-v-1b7bd8ca`]]);function fe(e,t){let n=e.split(`
|
|
2
|
+
`),r=t.split(`
|
|
3
|
+
`),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array(a+1).fill(0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)n[e]===r[t]?o[e][t]=o[e+1][t+1]+1:o[e][t]=Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push({type:`context`,content:n[c]}),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push({type:`del`,content:n[c]}),c++):(s.push({type:`add`,content:r[l]}),l++);for(;c<i;)s.push({type:`del`,content:n[c++]});for(;l<a;)s.push({type:`add`,content:r[l++]});return s}function W(e){let t=e.split(`
|
|
4
|
+
`),n=[];for(let e of t)e.startsWith(`@@`)||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 G(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=W(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 K(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}/${j}/${t.workingBranch}`);return r===e?q(e,t.projectPath):r}function q(e,t){if(!t)return e;let n=pe(t);return n?e.replace(RegExp(`${n}[\\\\/]+`,`g`),``).replace(RegExp(`${n}(?=\\s|$|["'\`])`,`g`),`.`):e}function pe(e){let t=e.replace(/[\\/]+$/,``);return t?`${/^[\\/]+/.test(t)?`[\\\\/]+`:``}${t.split(/[\\/]+/).filter(Boolean).map(me).join(`[\\\\/]+`)}`:``}function me(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}var J={class:`tool-name`},Y=[`title`],X={key:0,class:`tool-stat-add`},he={key:1,class:`tool-stat-del`},ge={class:`diff-sign`},_e={class:`tool-name`},Z=[`title`],Q=R(a({__name:`ToolCallItem`,props:{item:{}},setup(n){let r=n,a=e(!1),g=D(),{t:v}=T(),x=y(()=>G(r.item.name,r.item.input)),C=y(()=>x.value?K(x.value.filePath,g.selectedWorkspace):``),E={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`},O=y(()=>E[r.item.name]??`build`),k=y(()=>{if(x.value)return``;let e=r.item.input,t=A(e);return t?K(t,g.selectedWorkspace):``});function A(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 j=y(()=>{let e=x.value;return e?e.diffLines?e.diffLines:e.toolName===`Edit`&&e.oldString!==void 0&&e.newString!==void 0?fe(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}),M=y(()=>{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`]),P=y(()=>!!(r.item.result&&M.value)&&(!te.has(r.item.name)||r.item.result?.isError===!0));function F(){a.value=!a.value}function I(){let e=x.value?.filePath,t=g.selectedWorkspace,n=t?.id,r=e?K(e,t):``;!r||/^(?:[A-Za-z]:)?[\\/]/.test(r)||!n||(ne(n,r),window.dispatchEvent(new CustomEvent(`kobo:open-diff`,{detail:{workspaceId:n,path:r}})))}return o(()=>r.item.result?.isError===!0,e=>{e&&(a.value=!0)},{immediate:!0}),(e,r)=>x.value?(i(),c(`div`,{key:0,class:l([`tool-row`,{"tool-row-expanded":a.value}])},[_(`div`,{class:`tool-header`,onClick:F},[u(w,{name:O.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,J,f(x.value.toolName===`Bash:rm`?`Bash`:x.value.toolName),1),_(`span`,{class:`tool-path`,title:x.value.filePath},f(C.value),9,Y),u(ee,{flat:``,dense:``,round:``,size:`xs`,icon:`open_in_new`,class:`tool-open-diff`,onClick:b(I,[`stop`])},{default:s(()=>[u(N,null,{default:s(()=>[S(f(h(v)(`chat.openInGitDiff`)),1)]),_:1})]),_:1}),x.value.additions>0?(i(),c(`span`,X,`+`+f(x.value.additions),1)):p(``,!0),x.value.deletions>0?(i(),c(`span`,he,`-`+f(x.value.deletions),1)):p(``,!0),n.item.result?.isError?(i(),m(w,{key:2,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-xs`})):n.item.result?(i(),m(w,{key:3,name:`check`,color:`positive`,size:`xs`,class:`q-ml-xs`})):p(``,!0),u(w,{name:a.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-auto text-grey-6`},null,8,[`name`])]),a.value&&j.value?(i(),c(`div`,{key:0,class:`tool-diff`,onClick:r[0]||=b(()=>{},[`stop`])},[(i(!0),c(d,null,t(j.value,(e,t)=>(i(),c(`div`,{key:t,class:l([`diff-line`,{"diff-del":e.type===`del`,"diff-add":e.type===`add`,"diff-context":e.type===`context`}])},[_(`span`,ge,f(e.type===`del`?`-`:e.type===`add`?`+`:` `),1),S(f(e.content),1)],2))),128))])):p(``,!0)],2)):(i(),c(`div`,{key:1,class:l([`tool-row tool-row-generic`,{"tool-row-expanded":a.value,"tool-row--toggleable":P.value}])},[_(`div`,{class:`tool-header`,onClick:r[1]||=e=>P.value&&F()},[u(w,{name:O.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,_e,f(n.item.name),1),k.value?(i(),c(`span`,{key:0,class:`tool-arg`,title:A(n.item.input)||k.value},f(k.value),9,Z)):p(``,!0),n.item.result?.isError?(i(),m(w,{key:1,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-auto`})):n.item.result?(i(),m(w,{key:2,name:`check`,color:`positive`,size:`xs`,class:`q-ml-auto`})):p(``,!0),P.value?(i(),m(w,{key:3,name:a.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-xs text-grey-6`},null,8,[`name`])):p(``,!0)]),a.value&&P.value?(i(),c(`div`,{key:0,class:`tool-output`,onClick:r[2]||=b(()=>{},[`stop`])},f(M.value),1)):p(``,!0)],2))}}),[[`__scopeId`,`data-v-fc912366`]]);function ve(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 ye=[`innerHTML`],$=[`innerHTML`],be=[`src`],xe=R(a({__name:`UserMessageItem`,props:{item:{}},setup(t){let n=t,r=D(),a=y(()=>n.item.sender===`system-prompt`),o=y(()=>L(ve(n.item.content,r.selectedWorkspaceId??``))),l=e(null),f=e(!1);function h(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(l.value=n.src,f.value=!0)}return(e,t)=>(i(),c(d,null,[a.value?(i(),m(te,{key:0,dense:``,"dense-toggle":``,label:e.$t(`chat.systemPrompt`),"header-class":`text-grey-5 text-caption`},{default:s(()=>[_(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:o.value},null,8,ye)]),_:1},8,[`label`])):(i(),c(`div`,{key:1,class:`markdown-message`,onClick:h},[_(`div`,{innerHTML:o.value},null,8,$)])),u(E,{modelValue:f.value,"onUpdate:modelValue":t[1]||=e=>f.value=e},{default:s(()=>[l.value?(i(),c(`img`,{key:0,src:l.value,alt:``,class:`image-lightbox-img`,onClick:t[0]||=e=>f.value=!1},null,8,be)):p(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-f34be4c5`]]),Se=[`data-event-ids`],Ce={class:`turn-header`},we={key:0,class:`turn-time`},Te={class:`turn-time turn-time-updated`},Ee={key:2,class:`turn-actions`},De={class:`turn-body`},Oe={key:0,class:`turn-scroll-top`},ke=R(a({__name:`TurnCard`,props:{turn:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(n,{emit:r}){let a=n,o=r,{t:g}=T(),v=e(null);function b(){let e=v.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;o(`scrollTo`,Math.max(0,n-8))}let C=y(()=>{switch(a.turn.speaker){case`user`:return{label:g(`chat.you`),accent:`#ce93d8`,badgeClass:`turn-badge-user`};case`agent`:return{label:g(`chat.agent`),accent:`#7986cb`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:g(`chat.systemPrompt`),accent:`#757575`,badgeClass:`turn-badge-system`};case`session`:return{label:g(`chat.session`),accent:`#616161`,badgeClass:`turn-badge-session`};case`script`:{let e=a.turn.items[0],t=e?.type===`user`?e.sender:``;return{label:g(t===`archive`?`chat.archiveScript`:t===`setup`?`chat.setupScript`:`chat.cleanupScript`),accent:`#4db6ac`,badgeClass:`turn-badge-script`}}}});function E(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 D=y(()=>E(a.turn.ts)),O=y(()=>a.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),k=y(()=>{let e=a.turn.items;if(e.length===0)return null;for(let t=e.length-1;t>=0;t--){let n=e[t].ts;if(n)return n}return null}),A=y(()=>{let e=a.turn.ts,t=k.value;if(!t||!e||t===e)return``;let n=new Date(e).getTime(),r=new Date(t).getTime();return Number.isNaN(n)||Number.isNaN(r)||r<=n?``:E(t,r-n<6e4)}),j=y(()=>A.value!==``),M=y(()=>a.turn.items.filter(e=>e.type===`tool`).length);return(e,r)=>(i(),c(`div`,{ref_key:`cardEl`,ref:v,class:l([`turn-card`,{"turn-card--user":n.turn.speaker===`user`,"turn-card--highlighted":n.highlighted}]),"data-event-ids":O.value,style:x({"--turn-accent":C.value.accent})},[_(`div`,Ce,[_(`span`,{class:l([`turn-badge`,C.value.badgeClass])},f(C.value.label),3),D.value?(i(),c(`span`,we,f(D.value),1)):p(``,!0),j.value?(i(),c(d,{key:1},[u(w,{name:`arrow_forward`,size:`10px`,color:`grey-7`,class:`turn-time-arrow`}),_(`span`,Te,[S(f(A.value)+` `,1),u(N,null,{default:s(()=>[S(f(h(g)(`chat.lastUpdatedAt`,{time:A.value})),1)]),_:1})])],64)):p(``,!0),M.value>0?(i(),c(`span`,Ee,` · `+f(h(g)(`chat.nActions`,{n:M.value})),1)):p(``,!0)]),_(`div`,De,[(i(!0),c(d,null,t(n.turn.items,(e,t)=>(i(),c(d,{key:t},[e.type===`text`?(i(),m(de,{key:0,item:e},null,8,[`item`])):e.type===`thinking`?(i(),m(z,{key:1,item:e},null,8,[`item`])):e.type===`tool`?(i(),m(Q,{key:2,item:e},null,8,[`item`])):e.type===`user`?(i(),m(xe,{key:3,item:e},null,8,[`item`])):e.type===`session`?(i(),m(se,{key:4,item:e},null,8,[`item`])):p(``,!0)],64))),128))]),n.turn.items.length>4?(i(),c(`div`,Oe,[u(ee,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`grey-6`,class:`turn-scroll-top-btn`,onClick:b},{default:s(()=>[u(N,null,{default:s(()=>[S(f(h(g)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):p(``,!0)],14,Se))}}),[[`__scopeId`,`data-v-6b8d8e3e`]]),Ae={key:0,class:`activity-feed-switching`},je={key:1,class:`activity-feed-wrap`},Me={key:0,class:`activity-feed-compacting`},Ne={class:`text-caption text-grey-4`},Pe={key:0,class:`text-center q-py-sm text-caption text-grey-6`},Fe={class:`q-pa-md`},Ie={key:1,class:`q-px-md q-pb-md`},Le={class:`activity-feed-nav-cluster`},Re=60,ze=200,Be=200,Ve=400,He=200,Ue=180,We=R(a({__name:`ActivityFeed`,props:{workspaceId:{}},setup(a){let l=a,h=O(),b=A(),x=D(),w=y(()=>h.isCompacting(l.workspaceId)),T=y(()=>x.selectedSessionId),E=y(()=>x.sessions.find(e=>e.id===T.value)?.engineSessionId??null),j=y(()=>{let e=x.sessions;return e.length===0?!1:T.value===e[e.length-1].id});function N(e){return T.value?e?e===T.value||e===E.value:j.value:!0}let ne=y(()=>(x.activityFeeds[l.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&N(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),F=y(()=>k(x.workspaces.find(e=>e.id===l.workspaceId)?.status)),I=y(()=>{let e=h.eventsFor(l.workspaceId),t=h.timestampsFor(l.workspaceId),n=h.sessionIdsFor(l.workspaceId),r=h.eventIdsFor(l.workspaceId),i=[],a=[],o=[];for(let s=0;s<e.length;s++)N(n[s])&&(i.push(e[s]),a.push(t[s]),o.push(r[s]??null));return oe(ae(ie(i,a,F.value,o),ne.value).filter(e=>e.type!==`thinking`&&(b.showVerboseSystemMessages||e.type!==`session`)))}),re=y(()=>b.showVerboseSystemMessages?h.eventsFor(l.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),L=e(null),R=e(!0),z=e(!1),B=e(null),V=!1,H=e(!0),se=e(new Map);function ce(e){R.value=e.verticalSize-e.verticalPosition-e.verticalContainerSize<=Re,V&&e.verticalPosition<=ze&&!z.value&&ue()&&fe()}function le(e,t){return`${e}:${t}`}function ue(){let e=T.value;return e?se.value.get(le(l.workspaceId,e))??!0:h.hasMoreOlderFor(l.workspaceId)}function U(e,t,n){se.value.set(le(e,t),n)}function de(e){if(!T.value)return h.oldestIdFor(e);let t=h.eventIdsFor(e),n=h.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!N(n[e]))continue;let r=t[e];if(r)return r}}async function fe(){let e=l.workspaceId,t=T.value,n=de(e);if(!n)return;z.value=!0;let r=Date.now();try{let r=L.value;await g();let i=r?.getScroll().verticalSize??0,a=r?.getScroll().verticalPosition??0,o=new URLSearchParams({before:n,limit:`200`});t&&o.set(`session`,t);let s=fetch(`/api/workspaces/${e}/events?${o.toString()}`),c=new Promise(e=>setTimeout(e,Be)),[l]=await Promise.all([s,c]);if(!l.ok){t?U(e,t,!1):h.prepend(e,[],[],{oldestId:n,hasMoreOlder:!1});return}let u=await l.json(),d=u.events??[],f=d.filter(t=>t.type===`agent:event`&&t.workspaceId===e),p=d.filter(t=>t.type===`user:message`&&t.workspaceId===e),m=f.map(e=>e.payload),_=f.map(e=>e.createdAt),v=f.map(e=>e.sessionId??null),y=f.map(e=>e.id),b=d.length>0?d[0].id:n;t&&U(e,t,u.hasMore),h.prepend(e,m,_,{oldestId:b,hasMoreOlder:t?h.hasMoreOlderFor(e):u.hasMore,sessionIds:v,eventIds:y});for(let t of p){let n=t.payload;typeof n.content==`string`&&x.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`}})}if(await g(),r){let e=r.getScroll().verticalSize-i;if(e>0){let t=Math.max(a+e,250);r.setScrollPosition(`vertical`,t,0)}}}catch(e){console.error(`[ActivityFeed] failed to load older events:`,e)}finally{let e=Date.now()-r,t=Math.max(0,Be-e);await new Promise(e=>setTimeout(e,t+Ve)),z.value=!1}}async function W(e=0){await g();let t=L.value;if(!t)return;let n=t.getScroll();t.setScrollPosition(`vertical`,n.verticalSize,e)}let G=null,K=0;function q(){if(G!=null)return;let e=performance.now()-K<Ue;G=requestAnimationFrame(()=>{G=null,K=performance.now(),W(e?0:180)})}function pe(e){let t=L.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function me(){let e=window.location.hash,t=e.indexOf(`?`);if(t===-1||!new URLSearchParams(e.slice(t+1)).has(`eventId`))return;let n=e.slice(0,t);window.history.replaceState(window.history.state,``,`${window.location.pathname}${window.location.search}${n}`)}async function J(e){let t=e.detail;if(!(t?.workspaceId!==l.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/${l.workspaceId}/events?${e}`);if(!n.ok)return;let r=(await n.json()).events??[],i=r.filter(e=>e.type===`agent:event`&&e.workspaceId===l.workspaceId),a=r.filter(e=>e.type===`user:message`&&e.workspaceId===l.workspaceId);h.reset(l.workspaceId,i.map(e=>e.payload),i.map(e=>e.createdAt),{oldestId:r[0]?.id,hasMoreOlder:!1,sessionIds:i.map(e=>e.sessionId),eventIds:i.map(e=>e.id)});for(let e of a){let t=e.payload;typeof t.content==`string`&&x.addActivityItem(l.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}let o=Date.now()+5e3;for(;H.value&&Date.now()<o;)await new Promise(e=>window.setTimeout(e,20));await g(),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e())));let s=I.value.findIndex(e=>e.items.some(e=>e.eventIds?.includes(t.eventId)??!1)),c=Y.value[s]?.$el,u=L.value,d=X.value;if(!c||!u||!d)return;let f=c.getBoundingClientRect().top-d.getBoundingClientRect().top;B.value=t.eventId,u.setScrollPosition(`vertical`,Math.max(0,f-16),250),window.setTimeout(()=>{B.value===t.eventId&&(B.value=null)},1800)}catch(e){console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Y=e([]),X=e(null);function he(){let e=I.value,t=Y.value,n=[];if(t.length===e.length){for(let r=0;r<e.length;r++){if(e[r].speaker!==`user`)continue;let i=t[r]?.$el;i&&n.push(i)}if(n.length>0)return n}let r=X.value?.parentElement;if(r){let e=r.querySelectorAll(`.turn-card--user`);for(let t of e)n.push(t)}return n}function ge(){let e=L.value;if(!e)return null;let t=X.value;if(!t)return null;let n=e.getScroll().verticalPosition,r=t.getBoundingClientRect().top,i=null;for(let e of he()){let t=e.getBoundingClientRect().top-r;if(t<n-40)i=t;else break}return i}async function _e(){let e=L.value;if(!e)return;let t=ge();if(t===null)for(let e=0;e<15&&ue();e++){for(;z.value;)await new Promise(e=>setTimeout(e,50));if(await fe(),await g(),t=ge(),t!==null)break}t!==null&&e.setScrollPosition(`vertical`,Math.max(0,t-12),250)}async function Z(){V=!1,await g(),await W(0),requestAnimationFrame(()=>{requestAnimationFrame(()=>{V=!0})})}let Q=y(()=>{let e=h.sessionIdsFor(l.workspaceId);if(!T.value)return e.length;let t=0;for(let n of e)N(n)&&t++;return t}),ve=y(()=>h.eventsFor(l.workspaceId).length);async function ye(){H.value=!0;let e=Date.now();await new Promise(e=>setTimeout(e,He));let t=e+5e3;for(;ve.value===0&&Date.now()<t;)await new Promise(e=>setTimeout(e,50));H.value=!1}o(H,async e=>{!e&&Q.value>0&&await Z(),!e&&Q.value===0&&T.value&&xe()}),n(()=>{window.addEventListener(`kobo:focus-history-event`,J),ye(),Q.value>0&&Z(),T.value&&xe();let e=new URLSearchParams(window.location.hash.split(`?`)[1]??``).get(`eventId`);e&&(J(new CustomEvent(`kobo:focus-history-event`,{detail:{workspaceId:l.workspaceId,sessionId:T.value,eventId:e}})),me())});let $=!1;o(Q,async(e,t)=>{if(!$&&e>0){$=!0,await Z();return}e>t&&R.value&&!z.value&&q()}),r(()=>{window.removeEventListener(`kobo:focus-history-event`,J),G!=null&&(cancelAnimationFrame(G),G=null)}),o(()=>l.workspaceId,()=>{R.value=!0,$=!1,V=!1,ye(),Q.value>0&&Z()}),o(()=>x.selectedSessionId,async()=>{R.value=!0,V=!1,await Z(),xe()});let be=new Set;async function xe(){let e=T.value;if(!e||Q.value>0)return;let t=le(l.workspaceId,e);if(!be.has(t)){be.add(t);try{let t=await fetch(`/api/workspaces/${l.workspaceId}/events?session=${encodeURIComponent(e)}&limit=500`);if(!t.ok)return;let n=await t.json(),r=n.events??[];if(r.length===0)return;let i=r.filter(e=>e.type===`agent:event`&&e.workspaceId===l.workspaceId),a=r.filter(e=>e.type===`user:message`&&e.workspaceId===l.workspaceId),o=i.map(e=>e.payload),s=i.map(e=>e.createdAt),c=i.map(e=>e.sessionId??null),u=i.map(e=>e.id);U(l.workspaceId,e,n.hasMore),o.length>0&&h.prepend(l.workspaceId,o,s,{oldestId:r[0].id,hasMoreOlder:h.hasMoreOlderFor(l.workspaceId),sessionIds:c,eventIds:u});for(let e of a){let t=e.payload;typeof t.content==`string`&&x.addActivityItem(l.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await g(),await W(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),be.delete(t)}}}o(y(()=>ne.value.filter(e=>e.sender!==`system-prompt`).length),async(e,t)=>{e>t&&(R.value=!0,await W(180))});async function Se(){R.value=!0,await W(250)}return(e,n)=>H.value?(i(),c(`div`,Ae,[u(M,{size:`40px`,color:`indigo-4`})])):(i(),c(`div`,je,[u(v,{name:`fade`},{default:s(()=>[w.value?(i(),c(`div`,Me,[u(M,{size:`18px`,color:`indigo-4`}),_(`span`,Ne,f(e.$t(`activity.compacting`)),1)])):p(``,!0)]),_:1}),u(P,{ref_key:`scrollRef`,ref:L,class:`activity-feed-scroll`,onScroll:ce},{default:s(()=>[_(`div`,{ref_key:`contentOriginRef`,ref:X,class:`content-origin-marker`},null,512),z.value?(i(),c(`div`,Pe,[u(C,{size:`sm`}),S(` `+f(e.$t(`activity.loading_older`)),1)])):p(``,!0),_(`div`,Fe,[(i(!0),c(d,null,t(I.value,(e,t)=>(i(),m(ke,{key:t,ref_for:!0,ref_key:`turnRefs`,ref:Y,turn:e,highlighted:e.items.some(e=>e.eventIds?.includes(B.value??``)??!1),onScrollTo:pe},null,8,[`turn`,`highlighted`]))),128))]),re.value.length?(i(),c(`div`,Ie,[u(te,{label:e.$t(`activity.raw_lines`,{n:re.value.length}),dense:``},{default:s(()=>[(i(!0),c(d,null,t(re.value,(e,t)=>(i(),c(`div`,{key:t,class:`text-caption text-grey q-pa-xs`},f(e),1))),128))]),_:1},8,[`label`])])):p(``,!0)]),_:1},512),_(`div`,Le,[R.value?p(``,!0):(i(),m(ee,{key:0,round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.scroll_to_bottom`),onClick:Se},null,8,[`title`])),u(ee,{round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.prev_user_message`),onClick:_e},null,8,[`title`])])]))}}),[[`__scopeId`,`data-v-0cdc51af`]]);export{We as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as e,B as t,F as n,R as r,S as i,W as a,_ as o,b as s,c,ct as l,g as u,h as d,it as f,m as p}from"./vue.runtime.esm-bundler-JZnIeD9D.js";import{
|
|
1
|
+
import{$ as e,B as t,F as n,R as r,S as i,W as a,_ as o,b as s,c,ct as l,g as u,h as d,it as f,m as p}from"./vue.runtime.esm-bundler-JZnIeD9D.js";import{St as m,bt as h,s as g,z as _}from"./index-CGnQPjwo.js";import{t as v}from"./render-chat-markdown-BbmsqP4r.js";import{t as y}from"./_plugin-vue_export-helper-BDNMzG2s.js";import{t as b}from"./QSpace-ezNrZkuj.js";import{t as x}from"./QChip-BT8qyt_p.js";import{t as S}from"./QPage-D3rjwA1l.js";var C={class:`row items-center q-mb-md`},w={class:`text-h6 q-ml-sm`},T={key:0,class:`text-grey-6 text-center q-pa-lg`},E={key:1,class:`text-negative text-center q-pa-lg`},D={key:2,class:`text-grey-6 text-center q-pa-lg`},O={key:3,class:`column q-gutter-md`},k={key:0,class:`text-caption text-grey-6`},A={class:`row items-center q-mb-sm`},j={class:`text-subtitle1 text-indigo-3`,style:{"font-family":`var(--kobo-font-mono, monospace)`}},M=[`innerHTML`],N=y(i({__name:`ChangelogPage`,setup(i){let y=g(),N=e([]),P=e(``),F=e(!1),I=e(null);function L(e){return v(e)}async function R(){F.value=!0,I.value=null;try{let e=await fetch(`/api/changelog`);if(!e.ok)throw Error(`HTTP ${e.status}`);let t=await e.json();P.value=t.currentVersion??``,N.value=t.versions??[]}catch(e){I.value=e instanceof Error?e.message:String(e)}finally{F.value=!1}}return n(R),(e,n)=>(r(),d(S,{class:`q-pa-md`,style:{"max-width":`900px`,margin:`0 auto`}},{default:a(()=>[p(`div`,C,[s(_,{flat:``,dense:``,round:``,icon:`arrow_back`,onClick:n[0]||=e=>f(y).back()}),p(`div`,w,l(e.$t(`changelog.title`)),1),s(b),s(_,{flat:``,dense:``,icon:`refresh`,loading:F.value,label:e.$t(`common.refresh`),onClick:R},null,8,[`loading`,`label`])]),F.value&&N.value.length===0?(r(),o(`div`,T,l(e.$t(`common.loading`)),1)):I.value?(r(),o(`div`,E,l(I.value),1)):N.value.length===0?(r(),o(`div`,D,l(e.$t(`changelog.empty`)),1)):(r(),o(`div`,O,[P.value?(r(),o(`div`,k,l(e.$t(`changelog.currentVersion`,{version:P.value})),1)):u(``,!0),(r(!0),o(c,null,t(N.value,t=>(r(),d(h,{key:t.version,dark:``,flat:``,bordered:``},{default:a(()=>[s(m,null,{default:a(()=>[p(`div`,A,[p(`div`,j,` v`+l(t.version),1),t.version===P.value?(r(),d(x,{key:0,dense:``,size:`sm`,color:`indigo-7`,"text-color":`grey-2`,label:e.$t(`changelog.current`),class:`q-ml-sm`},null,8,[`label`])):u(``,!0)]),p(`div`,{class:`changelog-notes`,innerHTML:L(t.notes)},null,8,M)]),_:2},1024)]),_:2},1024))),128))]))]),_:1}))}}),[[`__scopeId`,`data-v-ed73d661`]]);export{N as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{P as e,b as t}from"./QIcon-C6E-oEjI.js";import{
|
|
1
|
+
import{P as e,b as t}from"./QIcon-C6E-oEjI.js";import{ct as n,lt as r}from"./index-CGnQPjwo.js";var i={left:!0,right:!0,up:!0,down:!0,horizontal:!0,vertical:!0},a=Object.keys(i);i.all=!0;function o(e){let t={};for(let n of a)e[n]&&(t[n]=!0);return Object.keys(t).length===0?i:(t.horizontal?t.left=t.right=!0:t.left&&t.right&&(t.horizontal=!0),t.vertical?t.up=t.down=!0:t.up&&t.down&&(t.vertical=!0),t.horizontal&&t.vertical&&(t.all=!0),t)}var s=[`INPUT`,`TEXTAREA`];function c(e,t){return t.event===void 0&&e.target!==void 0&&!e.target.draggable&&typeof t.handler==`function`&&!s.includes(e.target.nodeName.toUpperCase())&&(e.qClonedBy===void 0||!e.qClonedBy.includes(t.uid))}function l(e){return e===!1?0:e===!0||e===void 0?1:Number.parseInt(e,10)||0}var u=e({name:`close-popup`,beforeMount(e,{value:i}){let a={depth:l(i),handler(t){a.depth!==0&&setTimeout(()=>{let i=r(e);i!==void 0&&n(i,t,a.depth)},0)},handlerKey(e){t(e,13)&&a.handler(e)}};e.__qclosepopup=a,e.addEventListener(`click`,a.handler),e.addEventListener(`keyup`,a.handlerKey)},updated(e,{value:t,oldValue:n}){t!==n&&(e.__qclosepopup.depth=l(t))},beforeUnmount(e){let t=e.__qclosepopup;e.removeEventListener(`click`,t.handler),e.removeEventListener(`keyup`,t.handlerKey),delete e.__qclosepopup}});export{o as n,c as r,u as t};
|