@loicngr/kobo 2.1.25 → 2.1.26
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/AGENTS.md +24 -0
- package/CHANGELOG.md +4 -0
- package/CONFIGURATION.md +59 -0
- package/README.md +2 -1
- package/dist/server/routes/usage.js +24 -0
- package/dist/server/services/provider-accounts-service.js +202 -0
- package/dist/server/services/usage/poller.js +44 -3
- package/dist/shared/provider-accounts.js +1 -0
- package/package.json +1 -1
- package/src/client/dist/pwa/assets/{ActivityFeed-xJMPnXKA.js → ActivityFeed-DfYrVNEh.js} +1 -1
- package/src/client/dist/pwa/assets/{AutoLoopFinalReviewSettings-Dafi7Tue.js → AutoLoopFinalReviewSettings-DOr41E2Y.js} +1 -1
- package/src/client/dist/pwa/assets/{ChangelogPage-DH-zUtlf.js → ChangelogPage-DOCgZMpn.js} +1 -1
- package/src/client/dist/pwa/assets/{ComparisonPanel-DwaQyEgb.js → ComparisonPanel-b5nTBjK6.js} +1 -1
- package/src/client/dist/pwa/assets/{CreatePage-DyF_efbs.js → CreatePage-B7VPHEqm.js} +1 -1
- package/src/client/dist/pwa/assets/{DashboardPage-B8gTooRi.js → DashboardPage-dBpJaQSb.js} +1 -1
- package/src/client/dist/pwa/assets/{DiffViewer-BivWNITj.js → DiffViewer-BLQz2Qyx.js} +3 -3
- package/src/client/dist/pwa/assets/{DocumentsPanel-DXjjhGUg.js → DocumentsPanel-AqLx0MzB.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-CiVNMTbq.js → DrawerToggleButton-BLWaFgh0.js} +1 -1
- package/src/client/dist/pwa/assets/{GitPanel-DQjgGtoN.js → GitPanel-Ba7ltCsM.js} +3 -3
- package/src/client/dist/pwa/assets/{HealthPage-lc2Cuuks.js → HealthPage-DeryVLyz.js} +1 -1
- package/src/client/dist/pwa/assets/{MainLayout-D3pGcTx8.js → MainLayout-B2XvjdWi.js} +2 -2
- package/src/client/dist/pwa/assets/{MemoryPanel-SrR94UVW.js → MemoryPanel-B34DENtz.js} +1 -1
- package/src/client/dist/pwa/assets/{MemoryProposalList-B5uVf3KU.js → MemoryProposalList-BpN_OU8J.js} +1 -1
- package/src/client/dist/pwa/assets/{QBtnDropdown-Cxr3lBPB.js → QBtnDropdown-BPcK_rVs.js} +1 -1
- package/src/client/dist/pwa/assets/{QExpansionItem-xIXDevKz.js → QExpansionItem-CpwQ8L_F.js} +1 -1
- package/src/client/dist/pwa/assets/{QScrollArea-DeczMHVs.js → QScrollArea-BkNSVl7s.js} +1 -1
- package/src/client/dist/pwa/assets/{QSelect-GQAsf7pB.js → QSelect-DrLpbvcr.js} +1 -1
- package/src/client/dist/pwa/assets/{QTooltip-be9mqhmB.js → QTooltip-Dp8fitHV.js} +1 -1
- package/src/client/dist/pwa/assets/{SchedulePanel-R6H-D-m5.js → SchedulePanel-Ck8LKDCa.js} +1 -1
- package/src/client/dist/pwa/assets/{SearchPage-CaeYoIDw.js → SearchPage-BAUZg1zq.js} +1 -1
- package/src/client/dist/pwa/assets/{SettingsPage-BCWdQHS5.js → SettingsPage-Cj6kSVT5.js} +1 -1
- package/src/client/dist/pwa/assets/{SplitWorkspacePage-Dw1qUIFU.js → SplitWorkspacePage-CH8OHmtn.js} +1 -1
- package/src/client/dist/pwa/assets/{TerminalPanel-Dqw88QqH.js → TerminalPanel-CBmZ0IzQ.js} +1 -1
- package/src/client/dist/pwa/assets/{ToolCallItem-BMn7eela.js → ToolCallItem-CLtGTV6w.js} +1 -1
- package/src/client/dist/pwa/assets/{TouchPan-BwCpe_Oh.js → TouchPan-9SH2dx6m.js} +1 -1
- package/src/client/dist/pwa/assets/{TourReplayButton-yeGYf6xT.js → TourReplayButton-Dw4sGf9B.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-BJPw65vU.js → WorkspaceAttentionLabels-wd_sObJa.js} +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-BEBwwr81.css +1 -0
- package/src/client/dist/pwa/assets/WorkspacePage-D4ODPOG-.js +4 -0
- package/src/client/dist/pwa/assets/{browser-C9U-mSDm.js → browser-CC4W8r76.js} +1 -1
- package/src/client/dist/pwa/assets/{build-path-tree-DYdRW2T4.js → build-path-tree-BBuyB-my.js} +1 -1
- package/src/client/dist/pwa/assets/{custom-sounds-DPsfYErA.js → custom-sounds-BbPNRzAs.js} +1 -1
- package/src/client/dist/pwa/assets/{de-Bqil7RJE.js → de-D9VzPDGF.js} +1 -1
- package/src/client/dist/pwa/assets/{editor.api-iu5YkEl3.js → editor.api-BwfxvQ88.js} +1 -1
- package/src/client/dist/pwa/assets/{efforts-ja6uJadn.js → efforts-B3I7w5bs.js} +1 -1
- package/src/client/dist/pwa/assets/{es-CNcMm1Zz.js → es-3R4MJuTt.js} +1 -1
- package/src/client/dist/pwa/assets/{expand-template-BbgnJ8YH.js → expand-template-Ci9UjnxZ.js} +1 -1
- package/src/client/dist/pwa/assets/{format-CLXLy6Ov.js → format-CJIzi3Vf.js} +1 -1
- package/src/client/dist/pwa/assets/{formatters-DeKgxYJe.js → formatters-DQ8cP7nO.js} +1 -1
- package/src/client/dist/pwa/assets/{fr-BObHEohg.js → fr-DXwDVnJ4.js} +1 -1
- package/src/client/dist/pwa/assets/{freemarker2-2IrnDDvu.js → freemarker2-DbpVO-cL.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-CeLN7FCP.js → handlebars-BHve0PpA.js} +1 -1
- package/src/client/dist/pwa/assets/{html-Bn3rhcp3.js → html-BhUkdFPz.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-CGLQAADf.js +1 -0
- package/src/client/dist/pwa/assets/{index-CH4JpI2a.js → index-CNcXBpzz.js} +3 -3
- package/src/client/dist/pwa/assets/{it-DDMC7Emk.js → it-T9AjnG42.js} +1 -1
- package/src/client/dist/pwa/assets/{javascript-BMvON8oT.js → javascript-BLAorPa3.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-D_AQLvQl.js → liquid-CJGF-h-f.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-DDJzMB8Q.js → mdx-Cm0oNz_l.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-DFKwR-_X.js → monaco-contributions-nBg6wf05.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco.contribution-4AwFLnjV.js → monaco.contribution-hr9s26Ud.js} +2 -2
- package/src/client/dist/pwa/assets/{network-auth-CMERg4Qk.js → network-auth-C850jGfI.js} +1 -1
- package/src/client/dist/pwa/assets/{notify-theme-CMzdjL91.js → notify-theme-EcbxuH_8.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-C5mEKdlH.js → project-color-vDuTaTJn.js} +1 -1
- package/src/client/dist/pwa/assets/{python-BOqcp9xc.js → python-BEjbzFAQ.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-BrGwBzd7.js → razor-BJPT7Wxx.js} +1 -1
- package/src/client/dist/pwa/assets/{typescript-mOGf86uY.js → typescript-DPqM46Ef.js} +1 -1
- package/src/client/dist/pwa/assets/{use-search-index-status-P7O7EaV_.js → use-search-index-status-BwwrB410.js} +1 -1
- package/src/client/dist/pwa/assets/{use-tours-CMFPIW6f.js → use-tours-B0KtCS9w.js} +1 -1
- package/src/client/dist/pwa/assets/{use-worktree-restore-DF6P7h8g.js → use-worktree-restore-tzd2FESU.js} +1 -1
- package/src/client/dist/pwa/assets/{workspace-templates-C_bgIQvs.js → workspace-templates-CubzsZUU.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-YcMBzYAm.js → xml-C6SVzw5_.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-BP_67Sds.js → yaml-OpCqnv4g.js} +1 -1
- package/src/client/dist/pwa/index.html +1 -1
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-BHO1Q8PG.js +0 -4
- package/src/client/dist/pwa/assets/WorkspacePage-BnklZAYZ.css +0 -1
- package/src/client/dist/pwa/assets/i18n-Df_y96N2.js +0 -1
package/AGENTS.md
CHANGED
|
@@ -149,6 +149,30 @@ src/
|
|
|
149
149
|
|
|
150
150
|
Tasks have `role` (`work | finalization`) and structured `verification`. MCP and HTTP share transactional task mutations. An auto-loop task cannot become done without passing reported checks. Finalization runs after all other tasks/criteria, is invalidated by scope changes, and is mandatory before completion. The proof is agent-reported evidence, not server-side execution of arbitrary commands.
|
|
151
151
|
|
|
152
|
+
## Provider account switching
|
|
153
|
+
|
|
154
|
+
The usage popover integrates optional `ccswap` for Claude Code and Codex. Select
|
|
155
|
+
accounts for the viewed session's engine, falling back to the workspace engine.
|
|
156
|
+
Missing executable hides account controls; it must not hide existing Claude
|
|
157
|
+
usage. Codex account support is separate from quota snapshot support.
|
|
158
|
+
|
|
159
|
+
`provider-accounts-service.ts` owns subprocess invocation and sanitization. Use
|
|
160
|
+
provider allowlists, numeric slots and bounded `execFile`, never shell text. Keep
|
|
161
|
+
credential mutations serialized with their validation. Return only display
|
|
162
|
+
metadata; do not forward raw CLI JSON, stderr, tokens or credential paths. Inherit
|
|
163
|
+
the Kōbō process user, HOME, PATH and credential-directory environment: in Docker,
|
|
164
|
+
the CLI must run inside that container, not on the host. Do not install tools or
|
|
165
|
+
perform real switches in tests.
|
|
166
|
+
|
|
167
|
+
A switch changes the provider-wide credential store, not a workspace identity.
|
|
168
|
+
Do not automatically stop agents, migrate conversations, copy settings/skills or
|
|
169
|
+
promise that an existing process hot-reloads authentication. Invalidate Claude
|
|
170
|
+
quota data around switches and reject results from earlier in-flight polls.
|
|
171
|
+
Keep frontend requests scoped across provider/session changes and handle missing,
|
|
172
|
+
empty, disabled, unrecognized and failed account states without automatic retries.
|
|
173
|
+
See [configuration](./CONFIGURATION.md#switch-claude-code-and-codex-accounts-with-ccswap)
|
|
174
|
+
and [VPS setup](./docs/vps-projects-and-skills.md#switch-engine-accounts-inside-the-container).
|
|
175
|
+
|
|
152
176
|
## Persistent memory
|
|
153
177
|
|
|
154
178
|
Kōbō memory is independent of provider chat history/native memory and shared by Claude Code and Codex. `memoryMode` is a global setting defaulting to `hybrid`: Manual permits reads only; Hybrid applies workspace writes and proposes project/global writes; Automatic applies writes to the explicitly targeted scope. Mode is checked per operation, so a setting change affects existing agent sessions; pending/rejected proposals are never automatically approved or revived. Plan/review/report-only capabilities cannot write.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,10 @@ 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.26
|
|
8
|
+
|
|
9
|
+
- feat: switch Claude and Codex accounts with ccswap
|
|
10
|
+
|
|
7
11
|
## 2.1.25
|
|
8
12
|
|
|
9
13
|
- feat: improve merge options, diff controls and progress reporting
|
package/CONFIGURATION.md
CHANGED
|
@@ -1549,6 +1549,65 @@ When a Codex session is already working, **Send now** sends a queued chat messag
|
|
|
1549
1549
|
|
|
1550
1550
|
The selector reads [`src/shared/codex-models.ts`](./src/shared/codex-models.ts). This checkout lists GPT-6 Astra, GPT-5.6 Sol/Terra/Luna, GPT-5.5 and older variants; availability depends on your account and authentication. Kōbō exposes effort values `auto`, `none`, `low`, `medium`, `high`, `xhigh`, and `max`, as declared in the engine's capabilities. The runtime must support the selected model/effort combination. Both selectors switch when you change the engine.
|
|
1551
1551
|
|
|
1552
|
+
### Switch Claude Code and Codex accounts with ccswap
|
|
1553
|
+
|
|
1554
|
+
The usage popover in the chat footer can show the current **managed account**
|
|
1555
|
+
and switch to another account registered in [ccswap](https://github.com/errhythm/ccswap).
|
|
1556
|
+
It follows the selected session's engine, falling back to the workspace engine.
|
|
1557
|
+
Choose an account and explicitly click **Switch account**. Claude Code and Codex
|
|
1558
|
+
have separate account lists; switching one does not switch the other.
|
|
1559
|
+
|
|
1560
|
+
ccswap is optional and is not bundled with Kōbō. Without its executable, Kōbō
|
|
1561
|
+
shows no account selector or installation notice; existing Claude usage indicators
|
|
1562
|
+
continue to work. Codex account controls do not imply that Claude-style quota
|
|
1563
|
+
buckets are available for Codex.
|
|
1564
|
+
|
|
1565
|
+
Install and register accounts **as the same operating-system user and in the
|
|
1566
|
+
same environment as the Kōbō server**. The integration uses ccswap's JSON CLI
|
|
1567
|
+
(tested with 0.37.0). With `uv` already installed:
|
|
1568
|
+
|
|
1569
|
+
```bash
|
|
1570
|
+
uv tool install ccswap
|
|
1571
|
+
# After logging into Claude Code with each account:
|
|
1572
|
+
ccswap add
|
|
1573
|
+
# After `codex login` with each Codex account:
|
|
1574
|
+
ccswap codex add
|
|
1575
|
+
ccswap list --provider claude
|
|
1576
|
+
ccswap codex list
|
|
1577
|
+
```
|
|
1578
|
+
|
|
1579
|
+
Register the current login before switching so ccswap can preserve it. Account
|
|
1580
|
+
registration, login renewal, aliases and removal remain CLI operations. A disabled
|
|
1581
|
+
account cannot be selected in Kōbō. If no managed account matches the current
|
|
1582
|
+
login, the panel reports an unknown account rather than guessing from a slot.
|
|
1583
|
+
|
|
1584
|
+
**Scope:** switching changes that provider's active credentials for the server
|
|
1585
|
+
user, shared by all Kōbō workspaces and other tools using that same login store.
|
|
1586
|
+
It is not a per-workspace account assignment. Kōbō keeps its worktrees, conversation
|
|
1587
|
+
history, settings and skills; it does not copy profiles or automatically restart
|
|
1588
|
+
agents. The displayed account identifies the active credential store, not proof
|
|
1589
|
+
that an already-running process has adopted it. Stop and resume the affected
|
|
1590
|
+
sessions if they still use the previous account, especially with Codex. Existing
|
|
1591
|
+
API-key or custom-provider authentication can take precedence over a saved login.
|
|
1592
|
+
|
|
1593
|
+
The adapter inherits absolute `CLAUDE_CONFIG_DIR` on Linux and `CODEX_HOME`.
|
|
1594
|
+
Empty or relative authentication-directory overrides are refused: server and workspace
|
|
1595
|
+
processes can have different working directories. Claude switching is also refused
|
|
1596
|
+
with `CLAUDE_SECURESTORAGE_CONFIG_DIR` set, or with any nonempty `CLAUDE_CONFIG_DIR`
|
|
1597
|
+
on macOS (even the explicit default path), because the corresponding Keychain
|
|
1598
|
+
write is not verified by this integration. Use the native login workflow for
|
|
1599
|
+
those configurations.
|
|
1600
|
+
|
|
1601
|
+
Claude usage is invalidated and refreshed after a switch. A failed refresh is
|
|
1602
|
+
not shown as the new account's old quota. Switching failures do not trigger an
|
|
1603
|
+
automatic retry: refresh the account list to check the actual state before trying
|
|
1604
|
+
again. The server exposes display metadata only, never ccswap's credential files
|
|
1605
|
+
or raw command output.
|
|
1606
|
+
|
|
1607
|
+
For Docker/VPS, installing ccswap on your PC or on the VPS host is insufficient:
|
|
1608
|
+
it must be reachable on **Kōbō's container PATH**, with the same runtime home and
|
|
1609
|
+
credential-directory environment. See [VPS account setup](./docs/vps-projects-and-skills.md#switch-engine-accounts-inside-the-container).
|
|
1610
|
+
|
|
1552
1611
|
### Permission modes
|
|
1553
1612
|
|
|
1554
1613
|
Claude Code uses SDK permission modes; Codex uses sandbox and approval flags. These controls differ by engine. A Git worktree itself is not a security boundary.
|
package/README.md
CHANGED
|
@@ -64,7 +64,8 @@ Kōbō's production build is an installable PWA — use your browser's **Install
|
|
|
64
64
|
- **Interactive Q&A**: an agent can pause mid-session to ask you a question through the UI instead of guessing.
|
|
65
65
|
|
|
66
66
|

|
|
67
|
-
- **Quota-aware**: 5-hour / 7-day Claude usage
|
|
67
|
+
- **Quota-aware**: 5-hour / 7-day Claude usage is shown in the footer; engine-reported rate limits guide quota waits; auto-loops can resume after a reset. Explicit cron/wakeup schedules can also resume work; a manual workspace does not restart solely because its quota resets.
|
|
68
|
+
- **Account switching**: with optional [ccswap](https://github.com/errhythm/ccswap), view and switch the active Claude Code or Codex account from the usage popover. Credentials are shared across workspaces using that engine; [setup and session behavior](./CONFIGURATION.md#switch-claude-code-and-codex-accounts-with-ccswap).
|
|
68
69
|
- **Disk-space purge**: reclaim a merged workspace's `node_modules`/`vendor` weight without losing its chat history — see [`CONFIGURATION.md`](./CONFIGURATION.md#auto-purge-worktree-on-pr-merged).
|
|
69
70
|
- **Lifecycle scripts**: shell scripts run on setup, cleanup, archive, session end, PR merge, or auto-loop stop, with output streamed into the chat.
|
|
70
71
|
- **Observability**: a per-session timeline (duration, tools, tokens, errors) and a downloadable redacted diagnostic JSON.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { getDb } from '../db/index.js';
|
|
3
|
+
import { listProviderAccounts, ProviderAccountsError, switchProviderAccount, validateAccountId, } from '../services/provider-accounts-service.js';
|
|
3
4
|
import { refreshNow } from '../services/usage/poller.js';
|
|
4
5
|
import { clearReliabilityReset, computeEngineReliability, getReliabilityResetAt, resetReliability, } from '../services/usage/reliability.js';
|
|
5
6
|
const app = new Hono();
|
|
@@ -39,6 +40,29 @@ app.delete('/reliability/reset', (c) => {
|
|
|
39
40
|
return c.json({ error: message }, 500);
|
|
40
41
|
}
|
|
41
42
|
});
|
|
43
|
+
function accountError(error) {
|
|
44
|
+
return error instanceof ProviderAccountsError ? error : new ProviderAccountsError('accounts_command_failed');
|
|
45
|
+
}
|
|
46
|
+
app.get('/:providerId/accounts', async (c) => {
|
|
47
|
+
try {
|
|
48
|
+
return c.json(await listProviderAccounts(c.req.param('providerId')));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const safe = accountError(error);
|
|
52
|
+
return c.json({ error: safe.message, code: safe.code }, safe.status);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
app.post('/:providerId/accounts/switch', async (c) => {
|
|
56
|
+
try {
|
|
57
|
+
const body = await c.req.json().catch(() => null);
|
|
58
|
+
const accountId = validateAccountId(typeof body === 'object' && body !== null && 'accountId' in body ? body.accountId : undefined);
|
|
59
|
+
return c.json(await switchProviderAccount(c.req.param('providerId'), accountId));
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const safe = accountError(error);
|
|
63
|
+
return c.json({ error: safe.message, code: safe.code }, safe.status);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
42
66
|
app.post('/:providerId/refresh', async (c) => {
|
|
43
67
|
const providerId = c.req.param('providerId');
|
|
44
68
|
try {
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { withUsageAccountSwitch } from './usage/poller.js';
|
|
5
|
+
const ERRORS = {
|
|
6
|
+
accounts_unknown_provider: [404, 'Unknown account provider.'],
|
|
7
|
+
accounts_invalid_account: [400, 'Invalid account selection.'],
|
|
8
|
+
accounts_not_found: [404, 'Account is no longer available.'],
|
|
9
|
+
accounts_disabled: [409, 'Account is disabled.'],
|
|
10
|
+
accounts_unavailable: [503, 'Account switching is unavailable.'],
|
|
11
|
+
accounts_unsupported_environment: [409, 'Account switching is unsupported in this runtime environment.'],
|
|
12
|
+
accounts_command_failed: [502, 'Account operation failed.'],
|
|
13
|
+
accounts_busy: [429, 'Account operations are busy.'],
|
|
14
|
+
};
|
|
15
|
+
export class ProviderAccountsError extends Error {
|
|
16
|
+
code;
|
|
17
|
+
status;
|
|
18
|
+
constructor(code) {
|
|
19
|
+
super(ERRORS[code][1]);
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.status = ERRORS[code][0];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// ccswap shares backup/config state across providers. Keep validation and the
|
|
25
|
+
// post-switch read in the same queue, not just the mutating child process.
|
|
26
|
+
let operations = Promise.resolve();
|
|
27
|
+
let queued = 0;
|
|
28
|
+
function serialize(operation) {
|
|
29
|
+
if (queued >= 32)
|
|
30
|
+
return Promise.reject(new ProviderAccountsError('accounts_busy'));
|
|
31
|
+
queued++;
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
let expired = false;
|
|
34
|
+
const timeout = setTimeout(() => {
|
|
35
|
+
expired = true;
|
|
36
|
+
reject(new ProviderAccountsError('accounts_busy'));
|
|
37
|
+
}, 5_000);
|
|
38
|
+
const result = operations
|
|
39
|
+
.then(async () => {
|
|
40
|
+
clearTimeout(timeout);
|
|
41
|
+
// The caller has already received busy; never perform a delayed switch.
|
|
42
|
+
if (expired)
|
|
43
|
+
return;
|
|
44
|
+
resolve(await operation());
|
|
45
|
+
})
|
|
46
|
+
.catch(reject);
|
|
47
|
+
operations = result.finally(() => {
|
|
48
|
+
queued--;
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function providerId(value) {
|
|
53
|
+
if (value !== 'claude-code' && value !== 'codex')
|
|
54
|
+
throw new ProviderAccountsError('accounts_unknown_provider');
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
function assertRuntime(provider) {
|
|
58
|
+
if (provider !== 'claude-code')
|
|
59
|
+
return;
|
|
60
|
+
// ccswap 0.37 writes the regular active store, not Claude's separate secure
|
|
61
|
+
// store override or macOS's hashed Keychain service for ANY nonempty
|
|
62
|
+
// CLAUDE_CONFIG_DIR, even when it explicitly names the default directory.
|
|
63
|
+
const config = process.env.CLAUDE_CONFIG_DIR;
|
|
64
|
+
if (process.env.CLAUDE_SECURESTORAGE_CONFIG_DIR !== undefined || (process.platform === 'darwin' && config)) {
|
|
65
|
+
throw new ProviderAccountsError('accounts_unsupported_environment');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function run(args, allowEmpty = false) {
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
// Inherit the actual server/container identity and homes. Never invoke a
|
|
71
|
+
// shell, docker, SSH, or resolve a binary relative to a workspace checkout.
|
|
72
|
+
execFile('ccswap', args, {
|
|
73
|
+
encoding: 'utf8',
|
|
74
|
+
shell: false,
|
|
75
|
+
killSignal: 'SIGKILL',
|
|
76
|
+
timeout: 30_000,
|
|
77
|
+
maxBuffer: 1024 * 1024,
|
|
78
|
+
cwd: os.homedir(),
|
|
79
|
+
env: process.env,
|
|
80
|
+
}, (error, stdout) => {
|
|
81
|
+
if (error) {
|
|
82
|
+
reject(new ProviderAccountsError(error.code === 'ENOENT' ? 'accounts_unavailable' : 'accounts_command_failed'));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (allowEmpty && !stdout.trim()) {
|
|
86
|
+
resolve(null);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
resolve(JSON.parse(stdout));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
reject(new ProviderAccountsError('accounts_command_failed'));
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function object(value) {
|
|
99
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
100
|
+
}
|
|
101
|
+
function display(value) {
|
|
102
|
+
return typeof value === 'string'
|
|
103
|
+
? value
|
|
104
|
+
.replace(/[\p{Cc}\p{Cf}]/gu, '')
|
|
105
|
+
.trim()
|
|
106
|
+
.slice(0, 320)
|
|
107
|
+
: '';
|
|
108
|
+
}
|
|
109
|
+
async function list(provider) {
|
|
110
|
+
const home = provider === 'codex' ? process.env.CODEX_HOME : process.env.CLAUDE_CONFIG_DIR;
|
|
111
|
+
// Agent subprocesses run in their worktrees, the quota provider in the server
|
|
112
|
+
// cwd, and ccswap in HOME. Relative (including empty) overrides cannot name
|
|
113
|
+
// the same credential store reliably. Require an absolute path or no override.
|
|
114
|
+
if (home !== undefined && !path.isAbsolute(home)) {
|
|
115
|
+
throw new ProviderAccountsError('accounts_unsupported_environment');
|
|
116
|
+
}
|
|
117
|
+
const raw = await run(provider === 'codex' ? ['codex', 'list', '--json'] : ['list', '--provider', 'claude', '--json']);
|
|
118
|
+
if (!object(raw) ||
|
|
119
|
+
!Array.isArray(raw.accounts) ||
|
|
120
|
+
raw.accounts.length > 1000 ||
|
|
121
|
+
(provider === 'claude-code' ? raw.schemaVersion !== 1 : raw.provider !== 'codex')) {
|
|
122
|
+
throw new ProviderAccountsError('accounts_command_failed');
|
|
123
|
+
}
|
|
124
|
+
const seen = new Set();
|
|
125
|
+
const accounts = raw.accounts.map((row) => {
|
|
126
|
+
if (!object(row) ||
|
|
127
|
+
typeof row.number !== 'number' ||
|
|
128
|
+
!Number.isSafeInteger(row.number) ||
|
|
129
|
+
row.number < 1 ||
|
|
130
|
+
typeof row.active !== 'boolean' ||
|
|
131
|
+
(row.disabled !== undefined && typeof row.disabled !== 'boolean')) {
|
|
132
|
+
throw new ProviderAccountsError('accounts_command_failed');
|
|
133
|
+
}
|
|
134
|
+
const id = String(row.number);
|
|
135
|
+
if (seen.has(id))
|
|
136
|
+
throw new ProviderAccountsError('accounts_command_failed');
|
|
137
|
+
seen.add(id);
|
|
138
|
+
return {
|
|
139
|
+
id,
|
|
140
|
+
email: display(row.email),
|
|
141
|
+
label: display(provider === 'codex' ? row.label : row.alias) || display(row.organizationName),
|
|
142
|
+
active: row.active,
|
|
143
|
+
disabled: row.disabled === true,
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
return { available: true, accounts };
|
|
147
|
+
}
|
|
148
|
+
export async function listProviderAccounts(value) {
|
|
149
|
+
const provider = providerId(value);
|
|
150
|
+
return serialize(async () => {
|
|
151
|
+
try {
|
|
152
|
+
return await list(provider);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
if (error instanceof ProviderAccountsError && error.code === 'accounts_unavailable')
|
|
156
|
+
return { available: false, accounts: [] };
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
export function validateAccountId(accountId) {
|
|
162
|
+
if (typeof accountId !== 'string' || !/^[1-9]\d{0,15}$/.test(accountId) || !Number.isSafeInteger(Number(accountId))) {
|
|
163
|
+
throw new ProviderAccountsError('accounts_invalid_account');
|
|
164
|
+
}
|
|
165
|
+
return accountId;
|
|
166
|
+
}
|
|
167
|
+
export async function switchProviderAccount(value, accountId) {
|
|
168
|
+
const provider = providerId(value);
|
|
169
|
+
const selectedId = validateAccountId(accountId);
|
|
170
|
+
return serialize(async () => {
|
|
171
|
+
assertRuntime(provider);
|
|
172
|
+
const current = await list(provider);
|
|
173
|
+
const selected = current.accounts.find((account) => account.id === selectedId);
|
|
174
|
+
if (!selected)
|
|
175
|
+
throw new ProviderAccountsError('accounts_not_found');
|
|
176
|
+
if (selected.disabled)
|
|
177
|
+
throw new ProviderAccountsError('accounts_disabled');
|
|
178
|
+
const change = async () => {
|
|
179
|
+
const raw = await run(provider === 'codex' ? ['codex', 'switch', selectedId, '--json'] : ['switch', selectedId, '--json'], provider === 'codex');
|
|
180
|
+
// ccswap 0.37's Codex dispatcher discards the switch return value.
|
|
181
|
+
// Empty stdout is accepted only here; the following list must confirm it.
|
|
182
|
+
if (provider === 'codex' && raw === null)
|
|
183
|
+
return;
|
|
184
|
+
if (!object(raw) ||
|
|
185
|
+
typeof raw.switched !== 'boolean' ||
|
|
186
|
+
!object(raw.to) ||
|
|
187
|
+
raw.to.number !== Number(selectedId) ||
|
|
188
|
+
(provider === 'claude-code' && raw.schemaVersion !== 1)) {
|
|
189
|
+
throw new ProviderAccountsError('accounts_command_failed');
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
if (provider === 'claude-code')
|
|
193
|
+
await withUsageAccountSwitch(provider, change);
|
|
194
|
+
else
|
|
195
|
+
await change();
|
|
196
|
+
const updated = await list(provider);
|
|
197
|
+
if (!updated.accounts.some((account) => account.id === selectedId && account.active)) {
|
|
198
|
+
throw new ProviderAccountsError('accounts_command_failed');
|
|
199
|
+
}
|
|
200
|
+
return updated;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
@@ -8,17 +8,23 @@ export const POLL_INTERVAL_MS = 5 * 60_000;
|
|
|
8
8
|
const DEFAULT_PROVIDERS = [createClaudeCodeProvider()];
|
|
9
9
|
let providers = DEFAULT_PROVIDERS;
|
|
10
10
|
let intervalHandle = null;
|
|
11
|
+
const generations = new Map();
|
|
12
|
+
const switchingProviders = new Set();
|
|
13
|
+
const latest = new Map();
|
|
11
14
|
function persistAndBroadcast(snap) {
|
|
12
15
|
upsertUsageSnapshot(snap);
|
|
16
|
+
latest.set(snap.providerId, snap);
|
|
13
17
|
broadcastAll('usage:snapshot', { providerId: snap.providerId, snapshot: snap });
|
|
14
18
|
}
|
|
15
19
|
async function tick() {
|
|
16
20
|
for (const provider of providers) {
|
|
17
21
|
try {
|
|
18
|
-
|
|
22
|
+
const generation = generations.get(provider.id) ?? 0;
|
|
23
|
+
if (switchingProviders.has(provider.id) || !(await provider.isAvailable()))
|
|
19
24
|
continue;
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
if (generation !== (generations.get(provider.id) ?? 0))
|
|
26
|
+
continue;
|
|
27
|
+
await refreshNow(provider.id);
|
|
22
28
|
}
|
|
23
29
|
catch (err) {
|
|
24
30
|
console.error('[usage-poller] tick failed for provider', provider.id, err);
|
|
@@ -45,11 +51,46 @@ export async function refreshNow(providerId) {
|
|
|
45
51
|
const provider = providers.find((p) => p.id === providerId);
|
|
46
52
|
if (!provider)
|
|
47
53
|
return null;
|
|
54
|
+
if (switchingProviders.has(providerId))
|
|
55
|
+
return latest.get(providerId) ?? null;
|
|
56
|
+
const generation = generations.get(providerId) ?? 0;
|
|
48
57
|
const snap = await provider.fetchSnapshot();
|
|
58
|
+
// A request started with old credentials must not restore them in SQLite,
|
|
59
|
+
// WebSocket clients, or its own delayed HTTP refresh response.
|
|
60
|
+
if (generation !== (generations.get(providerId) ?? 0))
|
|
61
|
+
return latest.get(providerId) ?? null;
|
|
49
62
|
persistAndBroadcast(snap);
|
|
50
63
|
return snap;
|
|
51
64
|
}
|
|
65
|
+
/** Invalidate before changing credentials, including uncertain CLI failures. */
|
|
66
|
+
export async function withUsageAccountSwitch(providerId, operation) {
|
|
67
|
+
generations.set(providerId, (generations.get(providerId) ?? 0) + 1);
|
|
68
|
+
switchingProviders.add(providerId);
|
|
69
|
+
try {
|
|
70
|
+
persistAndBroadcast({
|
|
71
|
+
providerId,
|
|
72
|
+
status: 'error',
|
|
73
|
+
buckets: [],
|
|
74
|
+
fetchedAt: new Date().toISOString(),
|
|
75
|
+
errorMessage: 'Account credentials are changing. Refresh usage after the switch.',
|
|
76
|
+
});
|
|
77
|
+
return await operation();
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
switchingProviders.delete(providerId);
|
|
81
|
+
try {
|
|
82
|
+
await refreshNow(providerId);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Quota refresh failure must not turn a successful account mutation into
|
|
86
|
+
// a failed switch or replace the original command error. Keep empty data.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
52
90
|
// Test seam — pass `null` to restore the default provider list.
|
|
53
91
|
export function _setProvidersForTest(list) {
|
|
54
92
|
providers = list ?? DEFAULT_PROVIDERS;
|
|
93
|
+
generations.clear();
|
|
94
|
+
switchingProviders.clear();
|
|
95
|
+
latest.clear();
|
|
55
96
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.26",
|
|
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",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{M as e,S as t,dt as n,lt as r,p as i,pt as a,v as o}from"./reactivity.esm-bundler-DY1JkAEA.js";import{C as s,F as c,J as l,L as u,N as d,O as f,P as p,T as m,Y as h,_ as g,d as _,f as v,g as y,j as ee,k as b,l as x,p as S,r as C,u as w,y as T,z as te}from"./runtime-core.esm-bundler-Bqg71Vll.js";import{$ as ne,K as re,M as E,f as ie,z as ae}from"./dom-BtYTvLeN.js";import{l as oe,t as D,u as se}from"./QBtn-BUSdPaf4.js";import{Kt as ce,O as le,Qt as ue,Ut as O,_t as de,at as fe,cn as pe,dn as k,j as me,m as he,mt as ge,pt as _e,st as ve,tn as A,v as ye}from"./index-CH4JpI2a.js";import{t as be}from"./QSpinnerDots-xdWyaE1i.js";import{t as xe}from"./QList-Cx1UfaUm.js";import{t as Se}from"./QMarkupTable-UhP7fVUp.js";import{c as j,l as M,s as N}from"./QSelect-GQAsf7pB.js";import{t as P}from"./QItemSection-MdJ1_ZK7.js";import{t as Ce}from"./QExpansionItem-xIXDevKz.js";import{a as F,c as we,r as I,t as L}from"./ToolCallItem-BMn7eela.js";import{t as Te}from"./QScrollArea-DeczMHVs.js";import{a as R}from"./use-tours-CMFPIW6f.js";import{f as Ee,g as z,m as De,p as Oe,u as ke}from"./use-worktree-restore-DF6P7h8g.js";import{t as B}from"./QTooltip-be9mqhmB.js";import{n as V}from"./render-chat-markdown-CafzCcmu.js";import{n as H}from"./WorkspacePage-BHO1Q8PG.js";function Ae(e,t){return s(`div`,e,[s(`table`,{class:`q-table`},t)])}var je={list:xe,table:Se},U=[`list`,`table`,`__qtable`],Me=ae({name:`QVirtualScroll`,props:{...M,type:{type:String,default:`list`,validator:e=>U.includes(e)},items:{type:Array,default:()=>[]},itemsFn:Function,itemsSize:Number,scrollTarget:k},setup(e,{slots:n,attrs:r}){let i,a=t(null),o=x(()=>e.itemsSize>=0&&e.itemsFn!==void 0?Number.parseInt(e.itemsSize,10):Array.isArray(e.items)?e.items.length:0),{virtualScrollSliceRange:c,localResetVirtualScroll:u,padVirtualScroll:f,onVirtualScrollEvt:m}=j({virtualScrollLength:o,getVirtualScrollTarget:y,getVirtualScrollEl:v}),h=x(()=>{if(o.value===0)return[];let t=(e,t)=>({index:c.value.from+t,item:e});return e.itemsFn===void 0?e.items.slice(c.value.from,c.value.to).map(t):e.itemsFn(c.value.from,c.value.to-c.value.from).map(t)}),g=x(()=>`q-virtual-scroll q-virtual-scroll`+(e.virtualScrollHorizontal?`--horizontal`:`--vertical`)+(e.scrollTarget===void 0?` scroll`:``)),_=x(()=>e.scrollTarget===void 0?{tabindex:0}:{});l(o,()=>{u()}),l(()=>e.scrollTarget,()=>{C(),S()});function v(){let e=a.value;return e===null?null:e.$el||e}function y(){return i}function S(){let t=v();t!==null&&(i=pe(t,e.scrollTarget),i.addEventListener(`scroll`,m,E.passive))}function C(){i!==void 0&&(i.removeEventListener(`scroll`,m,E.passive),i=void 0)}function w(){let t=f(e.type===`list`?`div`:`tbody`,h.value.map(n.default));return n.before!==void 0&&(t=n.before().concat(t)),ie(n.after,t)}return u(),p(()=>{S()}),b(()=>{S()}),d(()=>{C()}),ee(()=>{C()}),()=>{if(n.default===void 0){console.error(`QVirtualScroll: default scoped slot is required for rendering`);return}return e.type===`__qtable`?Ae({ref:a,class:`q-table__middle `+g.value,tabindex:0},w()):s(je[e.type],{...r,ref:a,class:[r.class,g.value],..._.value},w)}}}),Ne=new Set([`setup`,`cleanup`,`archive`]);function Pe(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:Ne.has(e.sender)||le(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function Fe(e){let t=[],n=null,r=null;for(let i of e){let e=Pe(i),a=e===`session`||e===`system-prompt`,o=e===`script`&&i.type===`user`?`script:${i.sender}`:e===`user`&&i.type===`user`&&i.source?JSON.stringify([`mcp`,i.source.transport,i.source.clientName]):e;!n||r!==o||a?(n={speaker:e,ts:i.ts,items:[i]},r=o,t.push(n),a&&(n=null)):n.items.push(i)}return t}function W(e){switch(e.type){case`text`:return`text:${e.messageId}`;case`thinking`:return`thinking:${e.messageId}:${e.eventIds?.[0]??e.ts??``}`;case`tool`:return`tool:${e.toolCallId}`;case`session`:return`session:${e.kind}:${e.eventIds?.[0]??e.ts??``}`;case`user`:return`user:${e.eventIds?.[0]??`${e.sender}:${e.ts??``}`}`;case`error`:return`error:${e.eventIds?.[0]??`${e.category}:${e.ts??``}`}`}}function Ie(e){let t=e.items[0];return`${e.speaker}:${t?W(t):`empty`}`}function Le(e,t){let n=Math.min(t,e.length)-1;for(let t=n;t>=0;t--)if(e[t]?.speaker===`user`)return t;return-1}function Re(e,t,n){return new Promise(r=>{let i=!1,a=null,o=null,s=e=>{i||(i=!0,a!==null&&clearTimeout(a),a=null,o?.(),r(e))};if(o=l(e,e=>{t(e)&&s(!0)},{immediate:!0}),i){o();return}a=setTimeout(()=>s(!1),n)})}var ze={class:`agent-error-item`},Be={class:`agent-error-item__body`},Ve={class:`agent-error-item__title`},G={class:`agent-error-item__message`},He=R(T({__name:`AgentErrorItem`,props:{item:{}},setup(e){let t=e,{te:n}=ue(),r=x(()=>{let e=`agent.error.${t.item.category}`;return n(e)?e:`agent.error.other`});return(t,n)=>(u(),S(`div`,ze,[g(oe,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),w(`div`,Be,[w(`div`,Ve,a(t.$t(r.value)),1),w(`div`,G,a(e.item.message),1)])]))}}),[[`__scopeId`,`data-v-e98c3c02`]]),Ue={class:`text-caption text-kobo-3`},We=T({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,n=x(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return z(t.item.detail);case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(u(),S(`span`,Ue,a(e.$t(n.value)),1))}}),K=new Map;function q(){for(let e of K.values())e.controller.abort(),e.blobUrl&&URL.revokeObjectURL(e.blobUrl);K.clear()}typeof window<`u`&&(window.addEventListener(`kobo:auth-token-changed`,q),window.addEventListener(`storage`,e=>{(e.key===`kobo:network-token`||e.key===null)&&q()}));function J(e){if(!/^\/api\/workspaces\/[^/]+\/images\/file\?path=/.test(e))throw Error(`Invalid workspace image URL`);let t=K.get(e);if(!t){let n=new AbortController,r={references:0,controller:n,ready:Promise.resolve(``)};r.ready=O(e,{signal:n.signal}).then(async e=>{if(!e.ok)throw await ce(e);let t=await e.blob();if(n.signal.aborted)throw new DOMException(`Aborted`,`AbortError`);return r.blobUrl=URL.createObjectURL(t),r.blobUrl}),t=r,K.set(e,t)}t.references++;let n=!1,r=t;return{ready:r.ready,release(){n||(n=!0,!(--r.references>0)&&(r.controller.abort(),r.blobUrl&&URL.revokeObjectURL(r.blobUrl),K.get(e)===r&&K.delete(e)))}}}function Ge(e,t){let n=o(``),r=o(0),a=()=>{r.value++},s=e=>{(e.key===`kobo:network-token`||e.key===null)&&a()};return window.addEventListener(`kobo:auth-token-changed`,a),window.addEventListener(`storage`,s),i(()=>{window.removeEventListener(`kobo:auth-token-changed`,a),window.removeEventListener(`storage`,s)}),l([e,r],([e],r,i)=>{let a=!0,o=[],s=document.createElement(`template`);s.innerHTML=e;for(let e of s.content.querySelectorAll(`img`)){let r=e.getAttribute(`src`)??``;if(e.removeAttribute(`data-kobo-image-url`),!/^\/api\/workspaces\/[^/]+\/images\/file\?path=/.test(r))continue;e.removeAttribute(`src`);let i=J(r);o.push(i),i.ready.then(t=>{a&&(e.setAttribute(`src`,t),e.setAttribute(`data-kobo-image-url`,r),n.value=s.innerHTML)}).catch(()=>{a&&(e.setAttribute(`title`,t()),n.value=s.innerHTML)})}n.value=s.innerHTML,i(()=>{a=!1;for(let e of o)e.release()})},{immediate:!0}),n}function Ke(e,t){return t?e.replace(/\[image:\s+([^\]]+)\]/g,(e,n)=>{let r=String(n).trim();return/^(\.ai\/images\/|images\/)/.test(r)?`}/images/file?path=${encodeURIComponent(r)}`})`:e}):e}var qe=[`innerHTML`],Je=[`innerHTML`],Ye=[`src`],Y=R(T({__name:`UserMessageItem`,props:{item:{},workspaceId:{}},setup(t){let n=t,{t:r}=ue(),a=x(()=>n.item.sender===`system-prompt`),s=Ge(x(()=>{let e=Ke(n.item.content,n.workspaceId);return V(e)}),()=>r(`network.login.unreachable`)),c=o(null),d=o(!1),f;function p(){d.value=!1,c.value=null,f?.release(),f=void 0}l(d,e=>{e||p()}),l(()=>n.workspaceId,p);let m=e=>{(e.key===`kobo:network-token`||e.key===null)&&p()};window.addEventListener(`storage`,m),window.addEventListener(`kobo:auth-token-changed`,p),i(()=>{p(),window.removeEventListener(`kobo:auth-token-changed`,p),window.removeEventListener(`storage`,m)});function y(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(p(),n.dataset.koboImageUrl&&(f=J(n.dataset.koboImageUrl),f.ready.catch(p)),c.value=n.src,d.value=!0)}return(t,n)=>(u(),S(C,null,[a.value?(u(),_(Ce,{key:0,dense:``,"dense-toggle":``,label:t.$t(`chat.systemPrompt`),"header-class":`text-kobo-2 text-caption`},{default:h(()=>[w(`div`,{class:`q-py-xs markdown-message markdown-user-prompt`,innerHTML:e(s)},null,8,qe)]),_:1},8,[`label`])):(u(),S(`div`,{key:1,class:`markdown-message`,onClick:y},[w(`div`,{innerHTML:e(s)},null,8,Je)])),g(A,{modelValue:d.value,"onUpdate:modelValue":n[1]||=e=>d.value=e},{default:h(()=>[c.value?(u(),S(`img`,{key:0,src:c.value,alt:``,class:`image-lightbox-img`,onClick:n[0]||=e=>d.value=!1},null,8,Ye)):v(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-965e6297`]]),Xe=[`data-event-ids`],X={class:`turn-header`},Ze={key:0,class:`turn-time`},Qe={class:`turn-time turn-time-updated`},$e={key:2,class:`turn-actions`},et={class:`turn-body`},tt={class:`ellipsis`},nt={class:`q-px-sm q-pb-sm`},rt={key:0,class:`turn-scroll-top`},it=R(T({__name:`TurnCard`,props:{turn:{},workspaceId:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(t,{emit:i}){let s=t,c=i,{t:l}=ue(),d=fe(),f=m(F),p=o(null);function ee(){let e=p.value;if(!e)return;let t=e.closest(`.q-scrollarea`)?.querySelector(`.q-scrollarea__content`);if(!t){e.scrollIntoView({behavior:`smooth`,block:`start`});return}let n=e.getBoundingClientRect().top-t.getBoundingClientRect().top;c(`scrollTo`,Math.max(0,n-8))}let b=x(()=>{switch(s.turn.speaker){case`user`:{let e=s.turn.items[0],t=e?.type===`user`?e.source:void 0;return{label:t?l(`chat.externalLlm`,{name:t.clientName}):l(`chat.you`),accent:`var(--kobo-turn-user)`,badgeClass:`turn-badge-user`}}case`agent`:return{label:l(`chat.agent`),accent:`var(--kobo-turn-agent)`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:l(`chat.systemPrompt`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-system`};case`session`:return{label:l(`chat.session`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-session`};case`script`:{let e=s.turn.items[0],t=e?.type===`user`?e.sender:``;return{label:le(t)?l(`chat.hookScript`,{event:t.slice(5)}):l(t===`archive`?`chat.archiveScript`:t===`setup`?`chat.setupScript`:`chat.cleanupScript`),accent:`var(--kobo-success)`,badgeClass:`turn-badge-script`}}}});function T(e,t=!1){if(!e)return``;let n=new Date(e);return Number.isNaN(n.getTime())?``:n.toLocaleTimeString(void 0,t?{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}:{hour:`2-digit`,minute:`2-digit`})}let re=x(()=>T(s.turn.ts)),E=x(()=>s.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),ie=x(()=>{let e=s.turn.items;if(e.length===0)return null;for(let t=e.length-1;t>=0;t--){let n=e[t].ts;if(n)return n}return null}),ae=x(()=>{let e=s.turn.ts,t=ie.value;if(!t||!e||t===e)return``;let n=new Date(e).getTime(),r=new Date(t).getTime();return Number.isNaN(n)||Number.isNaN(r)||r<=n?``:T(t,r-n<6e4)}),se=x(()=>ae.value!==``);function ce(e){return we(e.origin,Object.values(d.subagents[s.workspaceId]??{}),e.type===`tool`?e.toolCallId:void 0)}function O(e){return e||l(`chat.subagentActivity`)}function de(e){return(e.type===`text`||e.type===`thinking`||e.type===`tool`)&&e.origin?.kind===`subagent`}let pe=x(()=>{let e=[],t=new Map;for(let n of s.turn.items){if(!de(n)){e.push({type:`item`,key:W(n),item:n});continue}let r=ce(n),i=r?.toolUseId??n.origin?.toolCallId??n.origin?.threadId??W(n),a=t.get(i);if(a){a.items.push(n);continue}let o={type:`subagent`,key:`subagent:${W(n)}`,items:[n],subagentKey:i,subagentName:r?.description||null};t.set(i,o),e.push(o)}return e});function k(e){e.origin&&f?.({origin:e.origin})}let me=x(()=>s.turn.items.filter(e=>e.type===`tool`).length);return(i,o)=>(u(),S(`div`,{ref_key:`cardEl`,ref:p,class:r([`turn-card`,{"turn-card--user":t.turn.speaker===`user`,"turn-card--highlighted":t.highlighted}]),"data-event-ids":E.value,style:n({"--turn-accent":b.value.accent})},[w(`div`,X,[w(`span`,{class:r([`turn-badge`,b.value.badgeClass])},a(b.value.label),3),re.value?(u(),S(`span`,Ze,a(re.value),1)):v(``,!0),se.value?(u(),S(C,{key:1},[g(oe,{name:`arrow_forward`,size:`10px`,color:`kobo-3`,class:`turn-time-arrow`}),w(`span`,Qe,[y(a(ae.value)+` `,1),g(B,null,{default:h(()=>[y(a(e(l)(`chat.lastUpdatedAt`,{time:ae.value})),1)]),_:1})])],64)):v(``,!0),me.value>0?(u(),S(`span`,$e,` · `+a(e(l)(`chat.nActions`,{n:me.value})),1)):v(``,!0)]),w(`div`,et,[(u(!0),S(C,null,te(pe.value,n=>(u(),S(C,{key:n.key},[n.type===`subagent`?(u(),_(Ce,{key:0,class:`turn-subagent-activity`,dense:``,label:O(n.subagentName),caption:e(l)(`chat.nActions`,{n:n.items.length}),"default-opened":t.highlighted&&n.items.some(e=>e.eventIds?.some(e=>E.value.includes(e)))},{header:h(()=>[g(P,null,{default:h(()=>[g(N,{class:`row items-center no-wrap`,title:n.subagentName??void 0},{default:h(()=>[g(oe,{name:`hub`,size:`16px`,class:`q-mr-xs`},{default:h(()=>[g(B,null,{default:h(()=>[y(a(e(l)(`chat.subagentActivity`)),1)]),_:1})]),_:1}),w(`span`,tt,a(O(n.subagentName)),1)]),_:2},1032,[`title`]),g(N,{caption:``},{default:h(()=>[y(a(e(l)(`chat.nActions`,{n:n.items.length})),1)]),_:2},1024)]),_:2},1024),g(P,{side:``},{default:h(()=>[g(D,{flat:``,round:``,dense:``,size:`sm`,icon:`open_in_new`,"aria-label":e(l)(`chat.openSubagentActivity`),onClick:ne(e=>k(n.items[0]),[`stop`])},{default:h(()=>[g(B,null,{default:h(()=>[y(a(e(l)(`chat.openSubagentActivity`)),1)]),_:1})]),_:1},8,[`aria-label`,`onClick`])]),_:2},1024)]),default:h(()=>[w(`div`,nt,[(u(!0),S(C,null,te(n.items,t=>(u(),S(C,{key:e(W)(t)},[t.type===`text`?(u(),_(I,{key:0,item:t},null,8,[`item`])):t.type===`thinking`?(u(),_(H,{key:1,item:t},null,8,[`item`])):t.type===`tool`?(u(),_(L,{key:2,item:t},null,8,[`item`])):v(``,!0)],64))),128))])]),_:2},1032,[`label`,`caption`,`default-opened`])):(u(),S(C,{key:1},[n.item.type===`text`?(u(),_(I,{key:0,item:n.item},null,8,[`item`])):n.item.type===`thinking`?(u(),_(H,{key:1,item:n.item},null,8,[`item`])):n.item.type===`tool`?(u(),_(L,{key:2,item:n.item},null,8,[`item`])):n.item.type===`user`?(u(),_(Y,{key:3,item:n.item,"workspace-id":t.workspaceId},null,8,[`item`,`workspace-id`])):n.item.type===`session`?(u(),_(We,{key:4,item:n.item},null,8,[`item`])):n.item.type===`error`?(u(),_(He,{key:5,item:n.item},null,8,[`item`])):v(``,!0)],64))],64))),128))]),t.turn.items.length>4?(u(),S(`div`,rt,[g(D,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`kobo-3`,class:`turn-scroll-top-btn`,onClick:ee},{default:h(()=>[g(B,null,{default:h(()=>[y(a(e(l)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):v(``,!0)],14,Xe))}}),[[`__scopeId`,`data-v-13bebb3d`]]),at={key:0,class:`activity-feed-switching`},ot={key:1,class:`activity-feed-wrap`,"data-tour":`ws-chat`},st={key:0,role:`alert`,class:`q-pa-sm text-negative`},ct={key:1,"data-testid":`history-load-error`,role:`alert`,class:`q-pa-sm text-negative`},lt=[`aria-label`],ut={key:0,class:`activity-feed-compacting`},dt={class:`text-caption text-kobo-2`},ft={key:0,class:`text-center q-py-sm text-caption text-kobo-3`},pt={key:1,class:`activity-feed-empty q-pa-md`},mt={class:`activity-feed-empty__title`},ht={class:`activity-feed-empty__hint`},gt={key:2,class:`q-px-md q-pb-md`},_t=[`title`],vt={class:`ellipsis`},yt={class:`activity-feed-nav-cluster`},bt=300,xt=60,St=200,Ct=180,wt=255,Tt=3,Et=60,Dt=R(T({__name:`ActivityFeed`,props:{workspaceId:{}},setup(t){let{t:n}=ue(),r=t,i=de(),s=me(),d=fe(),m=he(),ee=x(()=>m.isCompacting(r.workspaceId)),b=x(()=>d.selectedSessionId),T=x(()=>d.sessions.find(e=>e.id===b.value)?.engineSessionId??null),ne=x(()=>{let e=d.sessions;return e.length!==0&&b.value===e[e.length-1].id});function E(e){return b.value?e?e===b.value||e===T.value:ne.value:!0}let ie=x(()=>(d.activityFeeds[r.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&E(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,source:ye(e.meta?.source),ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),ae=x(()=>{let e=d.workspaces.find(e=>e.id===r.workspaceId);return ve(e?.status)}),le=ke(),pe=``,k=x(()=>{let e=i.eventsFor(r.workspaceId),t=i.timestampsFor(r.workspaceId),n=i.sessionIdsFor(r.workspaceId),a=i.eventIdsFor(r.workspaceId),o=[],c=[],l=[];for(let r=0;r<e.length;r++)E(n[r])&&(o.push(e[r]),c.push(t[r]),l.push(a[r]??null));let u=`${r.workspaceId}::${b.value??`*`}`;u!==pe&&(le=ke(),pe=u);let d=Ee(le,o,c,ae.value,l);return Fe(De(d,ie.value).filter(e=>e.type===`thinking`?!1:e.type===`session`?e.kind===`ended`?s.showVerboseSystemMessages||!Oe(e.detail):s.showVerboseSystemMessages:!0))}),A=x(()=>s.showVerboseSystemMessages?i.eventsFor(r.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),xe=x(()=>{for(let e=k.value.length-1;e>=0;e--){let t=k.value[e]?.items??[];for(let e=t.length-1;e>=0;e--){let r=t[e];if(r){if(r.type===`text`&&!r.streaming)return{key:`text:${r.messageId}`,text:r.text};if(r.type===`tool`)return r.result?{key:`tool:${r.toolCallId}:end`,text:r.result.isError?n(`activity.a11y.toolFailed`,{name:r.name}):n(`activity.a11y.toolFinished`,{name:r.name})}:{key:`tool:${r.toolCallId}:start`,text:n(`activity.a11y.toolStarted`,{name:r.name})}}}}return null}),Se=o(``);l(xe,(e,t)=>{if(!e||e.key===t?.key)return;let r=e.key.startsWith(`text:`)?n(`activity.a11y.agentSaid`,{text:e.text.slice(0,bt)}):e.text;Se.value=r});let j=o(null),M=o(!0),N=o(!1),P=o(``),F=_e(),we=_e(),I=_e(),L=o(null),R=!1,z=!0,B=null,V=o(new Set),H=new Map,Ae=x(()=>{if(k.value.length>0||A.value.length>0)return!1;let e=r.workspaceId,t=b.value;return!!d.loadingSessions[e]||m.isSyncPending(e)||t!==null&&V.value.has(W(e,t))}),je=o(new Map),U=x(()=>k.value.length>0||A.value.length>0),Ne=x(()=>!!d.loadingSessions[r.workspaceId]||m.isSyncPending(r.workspaceId)||b.value!==null&&V.value.has(W(r.workspaceId,b.value)));function Pe(e){tt();let t=B;if(B=e.verticalSize,t!==null&&e.verticalSize>t&&M.value&&!N.value){q(0);return}let n=e.verticalSize-e.verticalPosition-e.verticalContainerSize;M.value=n<=xt,R&&e.verticalPosition<=St&&!N.value&&!P.value&&ze()&&He()}function W(e,t){return`${e}:${t}`}function ze(){let e=b.value;return e?je.value.get(W(r.workspaceId,e))??!0:i.hasMoreOlderFor(r.workspaceId)}function Be(e,t,n){je.value.set(W(e,t),n)}function Ve(e){if(!b.value)return i.oldestIdFor(e);let t=i.eventIdsFor(e),n=i.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!E(n[e]))continue;let r=t[e];if(r)return r}}let G=null;function He(){if(G)return G;let e=K().finally(()=>{G===e&&(G=null)});return G=e,G}function Ue(){F.abort(),we.abort(),I.abort(),N.value=!1,G=null;for(let e of V.value)Ft.delete(e);H.clear(),V.value.clear(),P.value=``}function We(){P.value=``,Nt.value===0&&b.value?It():He()}l(()=>[r.workspaceId,b.value],Ue,{flush:`sync`});async function K(){let e=r.workspaceId,t=b.value,n=Ve(e);if(!n)return;let a=F.begin();P.value=``,N.value=!0;try{let r=j.value;if(await f(),!F.isCurrent(a))return;let o=r?.getScroll().verticalSize??0,s=r?.getScroll().verticalPosition??0,c=new URLSearchParams({before:n,limit:`200`});t&&c.set(`session`,t);let l=await O(`/api/workspaces/${e}/events?${c.toString()}`,{signal:a});if(!l.ok)throw await ce(l);let u=await l.json();if(!F.isCurrent(a))return;let p=u.events??[],m=p.filter(t=>t.type===`agent:event`&&t.workspaceId===e),h=p.filter(t=>t.type===`user:message`&&t.workspaceId===e),g=m.map(e=>e.payload),_=m.map(e=>e.createdAt),v=m.map(e=>e.sessionId??null),y=m.map(e=>e.id),ee=p.length>0?p[0].id:n;t&&Be(e,t,u.hasMore),i.prepend(e,g,_,{oldestId:ee,hasMoreOlder:t?i.hasMoreOlderFor(e):u.hasMore,sessionIds:v,eventIds:y});for(let t of h){let n=t.payload;typeof n.content==`string`&&d.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`,source:ye(n.source)}})}if(await f(),r&&F.isCurrent(a)){let e=r.getScroll().verticalSize-o;if(e>0){let t=Math.max(s+e,250);r.setScrollPosition(`vertical`,t,0)}}}catch(e){if(ge(e)||!F.isCurrent(a))return;P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] failed to load older events:`,e)}finally{F.isCurrent(a)&&(N.value=!1)}}async function q(e=0){let t=$();if(await f(),t!==$())return;let n=j.value;if(!n)return;let r=n.getScroll();n.setScrollPosition(`vertical`,r.verticalSize,e)}let J=null,Ge=0;function Ke(){if(J!=null)return;let e=performance.now()-Ge<Ct,t=$();J=requestAnimationFrame(()=>{J=null,t===$()&&(Ge=performance.now(),q(e?0:180))})}function qe(e){Q(),M.value=!1;let t=j.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function Je(){let e=window.location.hash,t=e.indexOf(`?`);if(t===-1||!new URLSearchParams(e.slice(t+1)).has(`eventId`))return;let n=e.slice(0,t);window.history.replaceState(window.history.state,``,`${window.location.pathname}${window.location.search}${n}`)}async function Ye(e){let t=e.detail;if(t?.workspaceId!==r.workspaceId||!t.eventId)return;Q(),M.value=!1;let n=I.begin(),a=r.workspaceId;try{let e=new URLSearchParams({around:t.eventId,limit:`200`});t.sessionId&&e.set(`session`,t.sessionId);let o=await O(`/api/workspaces/${a}/events?${e}`,{signal:n});if(!o.ok)throw await ce(o);let s=await o.json();if(!I.isCurrent(n))return;let c=s.events??[],l=c.filter(e=>e.type===`agent:event`&&e.workspaceId===r.workspaceId),u=c.filter(e=>e.type===`user:message`&&e.workspaceId===r.workspaceId);i.reset(r.workspaceId,l.map(e=>e.payload),l.map(e=>e.createdAt),{oldestId:c[0]?.id,hasMoreOlder:s.hasMore??!1,sessionIds:l.map(e=>e.sessionId),eventIds:l.map(e=>e.id)});for(let e of u){let t=e.payload;typeof t.content==`string`&&d.addActivityItem(r.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`,source:ye(t.source)}})}if(await Re(Ae,e=>!e,5e3),await f(),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e()))),!I.isCurrent(n))return;let p=k.value.findIndex(e=>e.items.some(e=>e.eventIds?.includes(t.eventId)??!1));if(p<0)return;L.value=t.eventId,Y.value?.scrollTo(p,`center`),X.value=p,window.setTimeout(()=>{L.value===t.eventId&&(L.value=null)},1800)}catch(e){if(ge(e)||!I.isCurrent(n))return;P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Y=o(null),Xe=o(null),X=o(0),Ze=o(!1),Qe=x(()=>{for(let e=k.value.length-1;e>=0;e--)if(k.value[e]?.speaker===`user`)return e;return-1}),$e=null;function et(){let e=Qe.value,t=j.value?.getScrollTarget(),n=t?.querySelector(`[data-turn-index="${e}"]`);if(!n||!t){Ze.value=!1;return}let r=t.getBoundingClientRect(),i=n.getBoundingClientRect();Ze.value=i.bottom>r.top&&i.top<r.bottom}function tt(){$e===null&&($e=requestAnimationFrame(()=>{$e=null,et()}))}function nt(e){return e.length<=wt?e:`${e.slice(0,252)}...`}let rt=x(()=>{let e=Qe.value,t=k.value[e];if(!t||X.value<=e||Ze.value)return null;let n=t.items.filter(e=>e.type===`user`).map(e=>e.content).join(`
|
|
1
|
+
import{M as e,S as t,dt as n,lt as r,p as i,pt as a,v as o}from"./reactivity.esm-bundler-DY1JkAEA.js";import{C as s,F as c,J as l,L as u,N as d,O as f,P as p,T as m,Y as h,_ as g,d as _,f as v,g as y,j as ee,k as b,l as x,p as S,r as C,u as w,y as T,z as te}from"./runtime-core.esm-bundler-Bqg71Vll.js";import{$ as ne,K as re,M as E,f as ie,z as ae}from"./dom-BtYTvLeN.js";import{l as oe,t as D,u as se}from"./QBtn-BUSdPaf4.js";import{Kt as ce,O as le,Qt as ue,Ut as O,_t as de,at as fe,cn as pe,dn as k,j as me,m as he,mt as ge,pt as _e,st as ve,tn as A,v as ye}from"./index-CNcXBpzz.js";import{t as be}from"./QSpinnerDots-xdWyaE1i.js";import{t as xe}from"./QList-Cx1UfaUm.js";import{t as Se}from"./QMarkupTable-UhP7fVUp.js";import{c as j,l as M,s as N}from"./QSelect-DrLpbvcr.js";import{t as P}from"./QItemSection-MdJ1_ZK7.js";import{t as Ce}from"./QExpansionItem-CpwQ8L_F.js";import{a as F,c as we,r as I,t as L}from"./ToolCallItem-CLtGTV6w.js";import{t as Te}from"./QScrollArea-BkNSVl7s.js";import{a as R}from"./use-tours-B0KtCS9w.js";import{f as Ee,g as z,m as De,p as Oe,u as ke}from"./use-worktree-restore-tzd2FESU.js";import{t as B}from"./QTooltip-Dp8fitHV.js";import{n as V}from"./render-chat-markdown-CafzCcmu.js";import{n as H}from"./WorkspacePage-D4ODPOG-.js";function Ae(e,t){return s(`div`,e,[s(`table`,{class:`q-table`},t)])}var je={list:xe,table:Se},U=[`list`,`table`,`__qtable`],Me=ae({name:`QVirtualScroll`,props:{...M,type:{type:String,default:`list`,validator:e=>U.includes(e)},items:{type:Array,default:()=>[]},itemsFn:Function,itemsSize:Number,scrollTarget:k},setup(e,{slots:n,attrs:r}){let i,a=t(null),o=x(()=>e.itemsSize>=0&&e.itemsFn!==void 0?Number.parseInt(e.itemsSize,10):Array.isArray(e.items)?e.items.length:0),{virtualScrollSliceRange:c,localResetVirtualScroll:u,padVirtualScroll:f,onVirtualScrollEvt:m}=j({virtualScrollLength:o,getVirtualScrollTarget:y,getVirtualScrollEl:v}),h=x(()=>{if(o.value===0)return[];let t=(e,t)=>({index:c.value.from+t,item:e});return e.itemsFn===void 0?e.items.slice(c.value.from,c.value.to).map(t):e.itemsFn(c.value.from,c.value.to-c.value.from).map(t)}),g=x(()=>`q-virtual-scroll q-virtual-scroll`+(e.virtualScrollHorizontal?`--horizontal`:`--vertical`)+(e.scrollTarget===void 0?` scroll`:``)),_=x(()=>e.scrollTarget===void 0?{tabindex:0}:{});l(o,()=>{u()}),l(()=>e.scrollTarget,()=>{C(),S()});function v(){let e=a.value;return e===null?null:e.$el||e}function y(){return i}function S(){let t=v();t!==null&&(i=pe(t,e.scrollTarget),i.addEventListener(`scroll`,m,E.passive))}function C(){i!==void 0&&(i.removeEventListener(`scroll`,m,E.passive),i=void 0)}function w(){let t=f(e.type===`list`?`div`:`tbody`,h.value.map(n.default));return n.before!==void 0&&(t=n.before().concat(t)),ie(n.after,t)}return u(),p(()=>{S()}),b(()=>{S()}),d(()=>{C()}),ee(()=>{C()}),()=>{if(n.default===void 0){console.error(`QVirtualScroll: default scoped slot is required for rendering`);return}return e.type===`__qtable`?Ae({ref:a,class:`q-table__middle `+g.value,tabindex:0},w()):s(je[e.type],{...r,ref:a,class:[r.class,g.value],..._.value},w)}}}),Ne=new Set([`setup`,`cleanup`,`archive`]);function Pe(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:Ne.has(e.sender)||le(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function Fe(e){let t=[],n=null,r=null;for(let i of e){let e=Pe(i),a=e===`session`||e===`system-prompt`,o=e===`script`&&i.type===`user`?`script:${i.sender}`:e===`user`&&i.type===`user`&&i.source?JSON.stringify([`mcp`,i.source.transport,i.source.clientName]):e;!n||r!==o||a?(n={speaker:e,ts:i.ts,items:[i]},r=o,t.push(n),a&&(n=null)):n.items.push(i)}return t}function W(e){switch(e.type){case`text`:return`text:${e.messageId}`;case`thinking`:return`thinking:${e.messageId}:${e.eventIds?.[0]??e.ts??``}`;case`tool`:return`tool:${e.toolCallId}`;case`session`:return`session:${e.kind}:${e.eventIds?.[0]??e.ts??``}`;case`user`:return`user:${e.eventIds?.[0]??`${e.sender}:${e.ts??``}`}`;case`error`:return`error:${e.eventIds?.[0]??`${e.category}:${e.ts??``}`}`}}function Ie(e){let t=e.items[0];return`${e.speaker}:${t?W(t):`empty`}`}function Le(e,t){let n=Math.min(t,e.length)-1;for(let t=n;t>=0;t--)if(e[t]?.speaker===`user`)return t;return-1}function Re(e,t,n){return new Promise(r=>{let i=!1,a=null,o=null,s=e=>{i||(i=!0,a!==null&&clearTimeout(a),a=null,o?.(),r(e))};if(o=l(e,e=>{t(e)&&s(!0)},{immediate:!0}),i){o();return}a=setTimeout(()=>s(!1),n)})}var ze={class:`agent-error-item`},Be={class:`agent-error-item__body`},Ve={class:`agent-error-item__title`},G={class:`agent-error-item__message`},He=R(T({__name:`AgentErrorItem`,props:{item:{}},setup(e){let t=e,{te:n}=ue(),r=x(()=>{let e=`agent.error.${t.item.category}`;return n(e)?e:`agent.error.other`});return(t,n)=>(u(),S(`div`,ze,[g(oe,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),w(`div`,Be,[w(`div`,Ve,a(t.$t(r.value)),1),w(`div`,G,a(e.item.message),1)])]))}}),[[`__scopeId`,`data-v-e98c3c02`]]),Ue={class:`text-caption text-kobo-3`},We=T({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,n=x(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return z(t.item.detail);case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(u(),S(`span`,Ue,a(e.$t(n.value)),1))}}),K=new Map;function q(){for(let e of K.values())e.controller.abort(),e.blobUrl&&URL.revokeObjectURL(e.blobUrl);K.clear()}typeof window<`u`&&(window.addEventListener(`kobo:auth-token-changed`,q),window.addEventListener(`storage`,e=>{(e.key===`kobo:network-token`||e.key===null)&&q()}));function J(e){if(!/^\/api\/workspaces\/[^/]+\/images\/file\?path=/.test(e))throw Error(`Invalid workspace image URL`);let t=K.get(e);if(!t){let n=new AbortController,r={references:0,controller:n,ready:Promise.resolve(``)};r.ready=O(e,{signal:n.signal}).then(async e=>{if(!e.ok)throw await ce(e);let t=await e.blob();if(n.signal.aborted)throw new DOMException(`Aborted`,`AbortError`);return r.blobUrl=URL.createObjectURL(t),r.blobUrl}),t=r,K.set(e,t)}t.references++;let n=!1,r=t;return{ready:r.ready,release(){n||(n=!0,!(--r.references>0)&&(r.controller.abort(),r.blobUrl&&URL.revokeObjectURL(r.blobUrl),K.get(e)===r&&K.delete(e)))}}}function Ge(e,t){let n=o(``),r=o(0),a=()=>{r.value++},s=e=>{(e.key===`kobo:network-token`||e.key===null)&&a()};return window.addEventListener(`kobo:auth-token-changed`,a),window.addEventListener(`storage`,s),i(()=>{window.removeEventListener(`kobo:auth-token-changed`,a),window.removeEventListener(`storage`,s)}),l([e,r],([e],r,i)=>{let a=!0,o=[],s=document.createElement(`template`);s.innerHTML=e;for(let e of s.content.querySelectorAll(`img`)){let r=e.getAttribute(`src`)??``;if(e.removeAttribute(`data-kobo-image-url`),!/^\/api\/workspaces\/[^/]+\/images\/file\?path=/.test(r))continue;e.removeAttribute(`src`);let i=J(r);o.push(i),i.ready.then(t=>{a&&(e.setAttribute(`src`,t),e.setAttribute(`data-kobo-image-url`,r),n.value=s.innerHTML)}).catch(()=>{a&&(e.setAttribute(`title`,t()),n.value=s.innerHTML)})}n.value=s.innerHTML,i(()=>{a=!1;for(let e of o)e.release()})},{immediate:!0}),n}function Ke(e,t){return t?e.replace(/\[image:\s+([^\]]+)\]/g,(e,n)=>{let r=String(n).trim();return/^(\.ai\/images\/|images\/)/.test(r)?`}/images/file?path=${encodeURIComponent(r)}`})`:e}):e}var qe=[`innerHTML`],Je=[`innerHTML`],Ye=[`src`],Y=R(T({__name:`UserMessageItem`,props:{item:{},workspaceId:{}},setup(t){let n=t,{t:r}=ue(),a=x(()=>n.item.sender===`system-prompt`),s=Ge(x(()=>{let e=Ke(n.item.content,n.workspaceId);return V(e)}),()=>r(`network.login.unreachable`)),c=o(null),d=o(!1),f;function p(){d.value=!1,c.value=null,f?.release(),f=void 0}l(d,e=>{e||p()}),l(()=>n.workspaceId,p);let m=e=>{(e.key===`kobo:network-token`||e.key===null)&&p()};window.addEventListener(`storage`,m),window.addEventListener(`kobo:auth-token-changed`,p),i(()=>{p(),window.removeEventListener(`kobo:auth-token-changed`,p),window.removeEventListener(`storage`,m)});function y(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(p(),n.dataset.koboImageUrl&&(f=J(n.dataset.koboImageUrl),f.ready.catch(p)),c.value=n.src,d.value=!0)}return(t,n)=>(u(),S(C,null,[a.value?(u(),_(Ce,{key:0,dense:``,"dense-toggle":``,label:t.$t(`chat.systemPrompt`),"header-class":`text-kobo-2 text-caption`},{default:h(()=>[w(`div`,{class:`q-py-xs markdown-message markdown-user-prompt`,innerHTML:e(s)},null,8,qe)]),_:1},8,[`label`])):(u(),S(`div`,{key:1,class:`markdown-message`,onClick:y},[w(`div`,{innerHTML:e(s)},null,8,Je)])),g(A,{modelValue:d.value,"onUpdate:modelValue":n[1]||=e=>d.value=e},{default:h(()=>[c.value?(u(),S(`img`,{key:0,src:c.value,alt:``,class:`image-lightbox-img`,onClick:n[0]||=e=>d.value=!1},null,8,Ye)):v(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-965e6297`]]),Xe=[`data-event-ids`],X={class:`turn-header`},Ze={key:0,class:`turn-time`},Qe={class:`turn-time turn-time-updated`},$e={key:2,class:`turn-actions`},et={class:`turn-body`},tt={class:`ellipsis`},nt={class:`q-px-sm q-pb-sm`},rt={key:0,class:`turn-scroll-top`},it=R(T({__name:`TurnCard`,props:{turn:{},workspaceId:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(t,{emit:i}){let s=t,c=i,{t:l}=ue(),d=fe(),f=m(F),p=o(null);function ee(){let e=p.value;if(!e)return;let t=e.closest(`.q-scrollarea`)?.querySelector(`.q-scrollarea__content`);if(!t){e.scrollIntoView({behavior:`smooth`,block:`start`});return}let n=e.getBoundingClientRect().top-t.getBoundingClientRect().top;c(`scrollTo`,Math.max(0,n-8))}let b=x(()=>{switch(s.turn.speaker){case`user`:{let e=s.turn.items[0],t=e?.type===`user`?e.source:void 0;return{label:t?l(`chat.externalLlm`,{name:t.clientName}):l(`chat.you`),accent:`var(--kobo-turn-user)`,badgeClass:`turn-badge-user`}}case`agent`:return{label:l(`chat.agent`),accent:`var(--kobo-turn-agent)`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:l(`chat.systemPrompt`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-system`};case`session`:return{label:l(`chat.session`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-session`};case`script`:{let e=s.turn.items[0],t=e?.type===`user`?e.sender:``;return{label:le(t)?l(`chat.hookScript`,{event:t.slice(5)}):l(t===`archive`?`chat.archiveScript`:t===`setup`?`chat.setupScript`:`chat.cleanupScript`),accent:`var(--kobo-success)`,badgeClass:`turn-badge-script`}}}});function T(e,t=!1){if(!e)return``;let n=new Date(e);return Number.isNaN(n.getTime())?``:n.toLocaleTimeString(void 0,t?{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}:{hour:`2-digit`,minute:`2-digit`})}let re=x(()=>T(s.turn.ts)),E=x(()=>s.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),ie=x(()=>{let e=s.turn.items;if(e.length===0)return null;for(let t=e.length-1;t>=0;t--){let n=e[t].ts;if(n)return n}return null}),ae=x(()=>{let e=s.turn.ts,t=ie.value;if(!t||!e||t===e)return``;let n=new Date(e).getTime(),r=new Date(t).getTime();return Number.isNaN(n)||Number.isNaN(r)||r<=n?``:T(t,r-n<6e4)}),se=x(()=>ae.value!==``);function ce(e){return we(e.origin,Object.values(d.subagents[s.workspaceId]??{}),e.type===`tool`?e.toolCallId:void 0)}function O(e){return e||l(`chat.subagentActivity`)}function de(e){return(e.type===`text`||e.type===`thinking`||e.type===`tool`)&&e.origin?.kind===`subagent`}let pe=x(()=>{let e=[],t=new Map;for(let n of s.turn.items){if(!de(n)){e.push({type:`item`,key:W(n),item:n});continue}let r=ce(n),i=r?.toolUseId??n.origin?.toolCallId??n.origin?.threadId??W(n),a=t.get(i);if(a){a.items.push(n);continue}let o={type:`subagent`,key:`subagent:${W(n)}`,items:[n],subagentKey:i,subagentName:r?.description||null};t.set(i,o),e.push(o)}return e});function k(e){e.origin&&f?.({origin:e.origin})}let me=x(()=>s.turn.items.filter(e=>e.type===`tool`).length);return(i,o)=>(u(),S(`div`,{ref_key:`cardEl`,ref:p,class:r([`turn-card`,{"turn-card--user":t.turn.speaker===`user`,"turn-card--highlighted":t.highlighted}]),"data-event-ids":E.value,style:n({"--turn-accent":b.value.accent})},[w(`div`,X,[w(`span`,{class:r([`turn-badge`,b.value.badgeClass])},a(b.value.label),3),re.value?(u(),S(`span`,Ze,a(re.value),1)):v(``,!0),se.value?(u(),S(C,{key:1},[g(oe,{name:`arrow_forward`,size:`10px`,color:`kobo-3`,class:`turn-time-arrow`}),w(`span`,Qe,[y(a(ae.value)+` `,1),g(B,null,{default:h(()=>[y(a(e(l)(`chat.lastUpdatedAt`,{time:ae.value})),1)]),_:1})])],64)):v(``,!0),me.value>0?(u(),S(`span`,$e,` · `+a(e(l)(`chat.nActions`,{n:me.value})),1)):v(``,!0)]),w(`div`,et,[(u(!0),S(C,null,te(pe.value,n=>(u(),S(C,{key:n.key},[n.type===`subagent`?(u(),_(Ce,{key:0,class:`turn-subagent-activity`,dense:``,label:O(n.subagentName),caption:e(l)(`chat.nActions`,{n:n.items.length}),"default-opened":t.highlighted&&n.items.some(e=>e.eventIds?.some(e=>E.value.includes(e)))},{header:h(()=>[g(P,null,{default:h(()=>[g(N,{class:`row items-center no-wrap`,title:n.subagentName??void 0},{default:h(()=>[g(oe,{name:`hub`,size:`16px`,class:`q-mr-xs`},{default:h(()=>[g(B,null,{default:h(()=>[y(a(e(l)(`chat.subagentActivity`)),1)]),_:1})]),_:1}),w(`span`,tt,a(O(n.subagentName)),1)]),_:2},1032,[`title`]),g(N,{caption:``},{default:h(()=>[y(a(e(l)(`chat.nActions`,{n:n.items.length})),1)]),_:2},1024)]),_:2},1024),g(P,{side:``},{default:h(()=>[g(D,{flat:``,round:``,dense:``,size:`sm`,icon:`open_in_new`,"aria-label":e(l)(`chat.openSubagentActivity`),onClick:ne(e=>k(n.items[0]),[`stop`])},{default:h(()=>[g(B,null,{default:h(()=>[y(a(e(l)(`chat.openSubagentActivity`)),1)]),_:1})]),_:1},8,[`aria-label`,`onClick`])]),_:2},1024)]),default:h(()=>[w(`div`,nt,[(u(!0),S(C,null,te(n.items,t=>(u(),S(C,{key:e(W)(t)},[t.type===`text`?(u(),_(I,{key:0,item:t},null,8,[`item`])):t.type===`thinking`?(u(),_(H,{key:1,item:t},null,8,[`item`])):t.type===`tool`?(u(),_(L,{key:2,item:t},null,8,[`item`])):v(``,!0)],64))),128))])]),_:2},1032,[`label`,`caption`,`default-opened`])):(u(),S(C,{key:1},[n.item.type===`text`?(u(),_(I,{key:0,item:n.item},null,8,[`item`])):n.item.type===`thinking`?(u(),_(H,{key:1,item:n.item},null,8,[`item`])):n.item.type===`tool`?(u(),_(L,{key:2,item:n.item},null,8,[`item`])):n.item.type===`user`?(u(),_(Y,{key:3,item:n.item,"workspace-id":t.workspaceId},null,8,[`item`,`workspace-id`])):n.item.type===`session`?(u(),_(We,{key:4,item:n.item},null,8,[`item`])):n.item.type===`error`?(u(),_(He,{key:5,item:n.item},null,8,[`item`])):v(``,!0)],64))],64))),128))]),t.turn.items.length>4?(u(),S(`div`,rt,[g(D,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`kobo-3`,class:`turn-scroll-top-btn`,onClick:ee},{default:h(()=>[g(B,null,{default:h(()=>[y(a(e(l)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):v(``,!0)],14,Xe))}}),[[`__scopeId`,`data-v-13bebb3d`]]),at={key:0,class:`activity-feed-switching`},ot={key:1,class:`activity-feed-wrap`,"data-tour":`ws-chat`},st={key:0,role:`alert`,class:`q-pa-sm text-negative`},ct={key:1,"data-testid":`history-load-error`,role:`alert`,class:`q-pa-sm text-negative`},lt=[`aria-label`],ut={key:0,class:`activity-feed-compacting`},dt={class:`text-caption text-kobo-2`},ft={key:0,class:`text-center q-py-sm text-caption text-kobo-3`},pt={key:1,class:`activity-feed-empty q-pa-md`},mt={class:`activity-feed-empty__title`},ht={class:`activity-feed-empty__hint`},gt={key:2,class:`q-px-md q-pb-md`},_t=[`title`],vt={class:`ellipsis`},yt={class:`activity-feed-nav-cluster`},bt=300,xt=60,St=200,Ct=180,wt=255,Tt=3,Et=60,Dt=R(T({__name:`ActivityFeed`,props:{workspaceId:{}},setup(t){let{t:n}=ue(),r=t,i=de(),s=me(),d=fe(),m=he(),ee=x(()=>m.isCompacting(r.workspaceId)),b=x(()=>d.selectedSessionId),T=x(()=>d.sessions.find(e=>e.id===b.value)?.engineSessionId??null),ne=x(()=>{let e=d.sessions;return e.length!==0&&b.value===e[e.length-1].id});function E(e){return b.value?e?e===b.value||e===T.value:ne.value:!0}let ie=x(()=>(d.activityFeeds[r.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&E(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,source:ye(e.meta?.source),ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),ae=x(()=>{let e=d.workspaces.find(e=>e.id===r.workspaceId);return ve(e?.status)}),le=ke(),pe=``,k=x(()=>{let e=i.eventsFor(r.workspaceId),t=i.timestampsFor(r.workspaceId),n=i.sessionIdsFor(r.workspaceId),a=i.eventIdsFor(r.workspaceId),o=[],c=[],l=[];for(let r=0;r<e.length;r++)E(n[r])&&(o.push(e[r]),c.push(t[r]),l.push(a[r]??null));let u=`${r.workspaceId}::${b.value??`*`}`;u!==pe&&(le=ke(),pe=u);let d=Ee(le,o,c,ae.value,l);return Fe(De(d,ie.value).filter(e=>e.type===`thinking`?!1:e.type===`session`?e.kind===`ended`?s.showVerboseSystemMessages||!Oe(e.detail):s.showVerboseSystemMessages:!0))}),A=x(()=>s.showVerboseSystemMessages?i.eventsFor(r.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),xe=x(()=>{for(let e=k.value.length-1;e>=0;e--){let t=k.value[e]?.items??[];for(let e=t.length-1;e>=0;e--){let r=t[e];if(r){if(r.type===`text`&&!r.streaming)return{key:`text:${r.messageId}`,text:r.text};if(r.type===`tool`)return r.result?{key:`tool:${r.toolCallId}:end`,text:r.result.isError?n(`activity.a11y.toolFailed`,{name:r.name}):n(`activity.a11y.toolFinished`,{name:r.name})}:{key:`tool:${r.toolCallId}:start`,text:n(`activity.a11y.toolStarted`,{name:r.name})}}}}return null}),Se=o(``);l(xe,(e,t)=>{if(!e||e.key===t?.key)return;let r=e.key.startsWith(`text:`)?n(`activity.a11y.agentSaid`,{text:e.text.slice(0,bt)}):e.text;Se.value=r});let j=o(null),M=o(!0),N=o(!1),P=o(``),F=_e(),we=_e(),I=_e(),L=o(null),R=!1,z=!0,B=null,V=o(new Set),H=new Map,Ae=x(()=>{if(k.value.length>0||A.value.length>0)return!1;let e=r.workspaceId,t=b.value;return!!d.loadingSessions[e]||m.isSyncPending(e)||t!==null&&V.value.has(W(e,t))}),je=o(new Map),U=x(()=>k.value.length>0||A.value.length>0),Ne=x(()=>!!d.loadingSessions[r.workspaceId]||m.isSyncPending(r.workspaceId)||b.value!==null&&V.value.has(W(r.workspaceId,b.value)));function Pe(e){tt();let t=B;if(B=e.verticalSize,t!==null&&e.verticalSize>t&&M.value&&!N.value){q(0);return}let n=e.verticalSize-e.verticalPosition-e.verticalContainerSize;M.value=n<=xt,R&&e.verticalPosition<=St&&!N.value&&!P.value&&ze()&&He()}function W(e,t){return`${e}:${t}`}function ze(){let e=b.value;return e?je.value.get(W(r.workspaceId,e))??!0:i.hasMoreOlderFor(r.workspaceId)}function Be(e,t,n){je.value.set(W(e,t),n)}function Ve(e){if(!b.value)return i.oldestIdFor(e);let t=i.eventIdsFor(e),n=i.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!E(n[e]))continue;let r=t[e];if(r)return r}}let G=null;function He(){if(G)return G;let e=K().finally(()=>{G===e&&(G=null)});return G=e,G}function Ue(){F.abort(),we.abort(),I.abort(),N.value=!1,G=null;for(let e of V.value)Ft.delete(e);H.clear(),V.value.clear(),P.value=``}function We(){P.value=``,Nt.value===0&&b.value?It():He()}l(()=>[r.workspaceId,b.value],Ue,{flush:`sync`});async function K(){let e=r.workspaceId,t=b.value,n=Ve(e);if(!n)return;let a=F.begin();P.value=``,N.value=!0;try{let r=j.value;if(await f(),!F.isCurrent(a))return;let o=r?.getScroll().verticalSize??0,s=r?.getScroll().verticalPosition??0,c=new URLSearchParams({before:n,limit:`200`});t&&c.set(`session`,t);let l=await O(`/api/workspaces/${e}/events?${c.toString()}`,{signal:a});if(!l.ok)throw await ce(l);let u=await l.json();if(!F.isCurrent(a))return;let p=u.events??[],m=p.filter(t=>t.type===`agent:event`&&t.workspaceId===e),h=p.filter(t=>t.type===`user:message`&&t.workspaceId===e),g=m.map(e=>e.payload),_=m.map(e=>e.createdAt),v=m.map(e=>e.sessionId??null),y=m.map(e=>e.id),ee=p.length>0?p[0].id:n;t&&Be(e,t,u.hasMore),i.prepend(e,g,_,{oldestId:ee,hasMoreOlder:t?i.hasMoreOlderFor(e):u.hasMore,sessionIds:v,eventIds:y});for(let t of h){let n=t.payload;typeof n.content==`string`&&d.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`,source:ye(n.source)}})}if(await f(),r&&F.isCurrent(a)){let e=r.getScroll().verticalSize-o;if(e>0){let t=Math.max(s+e,250);r.setScrollPosition(`vertical`,t,0)}}}catch(e){if(ge(e)||!F.isCurrent(a))return;P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] failed to load older events:`,e)}finally{F.isCurrent(a)&&(N.value=!1)}}async function q(e=0){let t=$();if(await f(),t!==$())return;let n=j.value;if(!n)return;let r=n.getScroll();n.setScrollPosition(`vertical`,r.verticalSize,e)}let J=null,Ge=0;function Ke(){if(J!=null)return;let e=performance.now()-Ge<Ct,t=$();J=requestAnimationFrame(()=>{J=null,t===$()&&(Ge=performance.now(),q(e?0:180))})}function qe(e){Q(),M.value=!1;let t=j.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function Je(){let e=window.location.hash,t=e.indexOf(`?`);if(t===-1||!new URLSearchParams(e.slice(t+1)).has(`eventId`))return;let n=e.slice(0,t);window.history.replaceState(window.history.state,``,`${window.location.pathname}${window.location.search}${n}`)}async function Ye(e){let t=e.detail;if(t?.workspaceId!==r.workspaceId||!t.eventId)return;Q(),M.value=!1;let n=I.begin(),a=r.workspaceId;try{let e=new URLSearchParams({around:t.eventId,limit:`200`});t.sessionId&&e.set(`session`,t.sessionId);let o=await O(`/api/workspaces/${a}/events?${e}`,{signal:n});if(!o.ok)throw await ce(o);let s=await o.json();if(!I.isCurrent(n))return;let c=s.events??[],l=c.filter(e=>e.type===`agent:event`&&e.workspaceId===r.workspaceId),u=c.filter(e=>e.type===`user:message`&&e.workspaceId===r.workspaceId);i.reset(r.workspaceId,l.map(e=>e.payload),l.map(e=>e.createdAt),{oldestId:c[0]?.id,hasMoreOlder:s.hasMore??!1,sessionIds:l.map(e=>e.sessionId),eventIds:l.map(e=>e.id)});for(let e of u){let t=e.payload;typeof t.content==`string`&&d.addActivityItem(r.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`,source:ye(t.source)}})}if(await Re(Ae,e=>!e,5e3),await f(),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e()))),!I.isCurrent(n))return;let p=k.value.findIndex(e=>e.items.some(e=>e.eventIds?.includes(t.eventId)??!1));if(p<0)return;L.value=t.eventId,Y.value?.scrollTo(p,`center`),X.value=p,window.setTimeout(()=>{L.value===t.eventId&&(L.value=null)},1800)}catch(e){if(ge(e)||!I.isCurrent(n))return;P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Y=o(null),Xe=o(null),X=o(0),Ze=o(!1),Qe=x(()=>{for(let e=k.value.length-1;e>=0;e--)if(k.value[e]?.speaker===`user`)return e;return-1}),$e=null;function et(){let e=Qe.value,t=j.value?.getScrollTarget(),n=t?.querySelector(`[data-turn-index="${e}"]`);if(!n||!t){Ze.value=!1;return}let r=t.getBoundingClientRect(),i=n.getBoundingClientRect();Ze.value=i.bottom>r.top&&i.top<r.bottom}function tt(){$e===null&&($e=requestAnimationFrame(()=>{$e=null,et()}))}function nt(e){return e.length<=wt?e:`${e.slice(0,252)}...`}let rt=x(()=>{let e=Qe.value,t=k.value[e];if(!t||X.value<=e||Ze.value)return null;let n=t.items.filter(e=>e.type===`user`).map(e=>e.content).join(`
|
|
2
2
|
`).trim();return n?{index:e,content:nt(n)}:null});function Dt(e){X.value=e.index,tt()}function Ot(){let e=rt.value;e&&(Q(),M.value=!1,Y.value?.scrollTo(e.index,`start`),X.value=e.index)}let kt=o(!1);async function At(){if(kt.value)return;Q(),M.value=!1,kt.value=!0;let e=r.workspaceId;try{let t=Le(k.value,X.value);if(t<0)for(let n=0;n<15;n++){if(e!==r.workspaceId)return;if(!ze())break;let n=k.value.length;if(await He(),await f(),e!==r.workspaceId)return;if(X.value+=k.value.length-n,t=Le(k.value,X.value),t>=0)break}t>=0&&e===r.workspaceId&&(Y.value?.scrollTo(t,`start`),X.value=t)}finally{kt.value=!1}}let Z=0;function jt(){return new Promise(e=>requestAnimationFrame(()=>e()))}async function Mt(){if(!z||!U.value||Ae.value)return;R=!1;let e=++Z;if(await f(),e!==Z||!z||(Xe.value=j.value?.getScrollTarget()??null,await f(),e!==Z||!z))return;k.value.length>0&&Y.value?.scrollTo(k.value.length-1,`end-force`),await jt();let t=-1,n=0;for(let r=0;r<Et&&n<Tt;r++){if(e!==Z||!z)return;await q(0),await jt();let r=j.value?.getScroll().verticalSize??0;B=r,n=r===t?n+1:0,t=r}e===Z&&!Ne.value&&(R=!0,z=!1)}function Q(){z=!1,!R&&(Z++,R=!0)}let Nt=x(()=>{let e=i.sessionIdsFor(r.workspaceId);if(!b.value)return e.length;let t=0;for(let n of e)E(n)&&t++;return t});l(Ae,async e=>{!e&&U.value&&await Mt(),e||f(()=>{Xe.value=j.value?.getScrollTarget()??null,tt()})}),l(Ne,(e,t)=>{!e&&t&&z&&U.value&&Mt()}),p(()=>{window.addEventListener(`kobo:focus-history-event`,Ye),f(()=>{Xe.value=j.value?.getScrollTarget()??null,tt()}),U.value&&Mt(),b.value&&It();let e=new URLSearchParams(window.location.hash.split(`?`)[1]??``).get(`eventId`);e&&(Ye(new CustomEvent(`kobo:focus-history-event`,{detail:{workspaceId:r.workspaceId,sessionId:b.value,eventId:e}})),Je())}),l([Nt,()=>ie.value.length,()=>A.value.length],async([e,t,n])=>{N.value||z&&e+t+n>0&&await Mt()}),l([Qe,()=>k.value.length],()=>{f(tt)});let Pt=x(()=>i.liveAppendCountFor(r.workspaceId,E)),$=()=>`${r.workspaceId}\u0000${d.selectedSessionId??``}`;l([Pt,$],([e,t],[n,r])=>{t===r&&e>n&&M.value&&!N.value&&Ke()}),c(()=>{Z++,z=!1,Ue(),window.removeEventListener(`kobo:focus-history-event`,Ye),J!=null&&(cancelAnimationFrame(J),J=null),$e!==null&&cancelAnimationFrame($e)}),l([()=>r.workspaceId,()=>d.selectedSessionId],()=>{Z++,z=!0,B=null,M.value=!0,R=!1,It(),U.value&&Mt()});let Ft=new Set;async function It(){let e=r.workspaceId,t=b.value;if(!t||Nt.value>0)return;let n=W(e,t);if(Ft.has(n))return;Ft.add(n),V.value.add(n);let a=we.begin();H.set(n,a),P.value=``;try{let n=await O(`/api/workspaces/${e}/events?session=${encodeURIComponent(t)}&limit=500`,{signal:a});if(!n.ok)throw await ce(n);let r=await n.json();if(!we.isCurrent(a))return;let o=r.events??[];if(o.length===0)return;let s=o.filter(t=>t.type===`agent:event`&&t.workspaceId===e),c=o.filter(t=>t.type===`user:message`&&t.workspaceId===e),l=s.map(e=>e.payload),u=s.map(e=>e.createdAt),f=s.map(e=>e.sessionId??null),p=s.map(e=>e.id);Be(e,t,r.hasMore),l.length>0&&i.prepend(e,l,u,{oldestId:o[0].id,hasMoreOlder:i.hasMoreOlderFor(e),sessionIds:f,eventIds:p});for(let t of c){let n=t.payload;typeof n.content==`string`&&d.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`,source:ye(n.source)}})}}catch(e){if(ge(e)||!we.isCurrent(a)){(!H.has(n)||H.get(n)===a)&&Ft.delete(n);return}P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Ft.delete(n)}finally{H.get(n)===a&&(H.delete(n),V.value.delete(n))}}let Lt=x(()=>ie.value.filter(e=>e.sender===`user`).length),Rt=x(()=>(d.activityFeeds[r.workspaceId]??[]).filter(e=>e.meta?.sender===`user`&&e.meta.pending&&E(e.sessionId)).length);l([Lt,Rt,$],async([e,t,n],[r,i,a])=>{let o=t>i;n===a&&(o||e>r&&M.value&&!N.value&&!Ne.value)&&(M.value=!0,await q(o?180:0))});async function zt(){M.value=!0,await q(250)}return(t,n)=>Ae.value?(u(),S(`div`,at,[g(be,{size:`40px`,color:`primary`})])):(u(),S(`div`,ot,[e(d).sessionsErrors[r.workspaceId]?(u(),S(`div`,st,[y(a(t.$t(`activity.sessionsError`,{message:e(d).sessionsErrors[r.workspaceId]}))+` `,1),g(D,{flat:``,dense:``,label:t.$t(`common.retry`),onClick:n[0]||=t=>e(d).fetchSessions(r.workspaceId)},null,8,[`label`])])):v(``,!0),P.value?(u(),S(`div`,ct,[y(a(t.$t(`activity.historyError`,{message:P.value}))+` `,1),g(D,{flat:``,dense:``,label:t.$t(`common.retry`),onClick:We},null,8,[`label`])])):v(``,!0),w(`div`,{"data-testid":`activity-live-region`,class:`sr-only`,role:`log`,"aria-live":`polite`,"aria-atomic":`true`,"aria-label":t.$t(`activity.a11y.region`)},a(Se.value),9,lt),g(re,{name:`fade`},{default:h(()=>[ee.value?(u(),S(`div`,ut,[g(be,{size:`18px`,color:`primary`}),w(`span`,dt,a(t.$t(`activity.compacting`)),1)])):v(``,!0)]),_:1}),g(Te,{ref_key:`scrollRef`,ref:j,class:`activity-feed-scroll`,onScroll:Pe,onWheelPassive:Q,onTouchstartPassive:Q,onKeydown:Q},{default:h(()=>[N.value?(u(),S(`div`,ft,[g(se,{size:`sm`}),y(` `+a(t.$t(`activity.loading_older`)),1)])):v(``,!0),g(Me,{ref_key:`virtualScrollRef`,ref:Y,class:`q-pa-md`,items:k.value,"scroll-target":Xe.value??void 0,"virtual-scroll-item-size":160,"virtual-scroll-slice-size":20,onVirtualScroll:Dt},{default:h(({item:t,index:n})=>[(u(),_(it,{key:e(Ie)(t),turn:t,"workspace-id":r.workspaceId,"data-turn-index":n,highlighted:t.items.some(e=>e.eventIds?.includes(L.value??``)??!1),onScrollTo:qe},null,8,[`turn`,`workspace-id`,`data-turn-index`,`highlighted`]))]),_:1},8,[`items`,`scroll-target`]),k.value.length===0&&A.value.length===0&&!N.value?(u(),S(`div`,pt,[g(oe,{name:`forum`,size:`28px`}),w(`div`,mt,a(t.$t(`activity.empty`)),1),w(`div`,ht,a(t.$t(`activity.emptyHint`)),1)])):v(``,!0),A.value.length?(u(),S(`div`,gt,[g(Ce,{label:t.$t(`activity.raw_lines`,{n:A.value.length}),dense:``},{default:h(()=>[(u(!0),S(C,null,te(A.value,(e,t)=>(u(),S(`div`,{key:t,class:`text-caption text-kobo-3 q-pa-xs`},a(e),1))),128))]),_:1},8,[`label`])])):v(``,!0)]),_:1},512),rt.value?(u(),S(`button`,{key:2,type:`button`,class:`activity-feed-last-user`,"data-testid":`latest-user-message`,title:rt.value.content,onClick:Ot},[g(oe,{name:`person`,size:`16px`}),w(`span`,vt,a(rt.value.content),1)],8,_t)):v(``,!0),w(`div`,yt,[M.value?v(``,!0):(u(),_(D,{key:0,round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:t.$t(`activity.scroll_to_bottom`),"aria-label":t.$t(`activity.scroll_to_bottom`),onClick:zt},null,8,[`title`,`aria-label`])),g(D,{round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:t.$t(`activity.prev_user_message`),"aria-label":t.$t(`activity.prev_user_message`),loading:kt.value,disable:kt.value,onClick:At},null,8,[`title`,`aria-label`,`loading`,`disable`])])]))}}),[[`__scopeId`,`data-v-58324159`]]);export{Dt as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{M as e,pt as t,v as n}from"./reactivity.esm-bundler-DY1JkAEA.js";import{J as r,L as i,Y as a,_ as o,d as s,f as c,l,p as u,r as d,u as f,y as p}from"./runtime-core.esm-bundler-Bqg71Vll.js";import{t as m}from"./QBtn-BUSdPaf4.js";import{Mt as h,Qt as g,f as _,gn as v,j as y,jt as b,l as x,tn as S,vn as C}from"./index-
|
|
1
|
+
import{M as e,pt as t,v as n}from"./reactivity.esm-bundler-DY1JkAEA.js";import{J as r,L as i,Y as a,_ as o,d as s,f as c,l,p as u,r as d,u as f,y as p}from"./runtime-core.esm-bundler-Bqg71Vll.js";import{t as m}from"./QBtn-BUSdPaf4.js";import{Mt as h,Qt as g,f as _,gn as v,j as y,jt as b,l as x,tn as S,vn as C}from"./index-CNcXBpzz.js";import{t as w}from"./QSelect-DrLpbvcr.js";import{a as T}from"./use-tours-B0KtCS9w.js";import{i as E,t as D}from"./permissionModes-DLWiBfdb.js";import{t as O}from"./efforts-B3I7w5bs.js";function k(e,t){return e.engine!==t.engine||e.model!==t.model||e.reasoningEffort!==t.reasoningEffort||e.agentPermissionMode!==t.agentPermissionMode}var A={class:`text-h6`},j={class:`text-body2 text-kobo-2 q-mt-xs`},ee={class:`row q-col-gutter-sm q-mb-md`},te={class:`col-12 col-sm-6`},ne={class:`col-12 col-sm-6`},M={class:`col-12 col-sm-6`},N={class:`col-12 col-sm-6`},P={key:0,class:`text-caption text-kobo-3 q-mt-xs`},F={class:`text-caption text-kobo-3 q-mt-xs`},I={class:`text-caption text-kobo-3 q-mt-xs`},L=T(p({__name:`StartReviewDialog`,props:{modelValue:{type:Boolean},loading:{type:Boolean},workspace:{},canReturnToSession:{type:Boolean},currentSession:{},mode:{},scheduledConfiguration:{}},emits:[`update:modelValue`,`submit`],setup(e,{emit:p}){let T=e,L=p,{t:R}=g(),z=y(),B=l(()=>T.mode===`auto-loop`),V=n(``),H=n(!1),U=n(!1),W=n(`claude-code`),G=n(`auto`),K=n(`auto`),q=n(`bypass`),J=l(()=>B.value||U.value?`plan`:q.value),Y=l(()=>({engine:W.value,model:G.value,reasoningEffort:K.value,agentPermissionMode:J.value})),X=l(()=>{let e=T.currentSession;return B.value||U.value||!!T.workspace&&k(Y.value,T.workspace)||!!e?.engine&&e.engine!==W.value||G.value!==`auto`&&!!e?.model&&e.model!==G.value}),Z=l({get:()=>H.value||X.value,set:e=>{H.value=e}}),re=l(()=>[{value:`claude-code`,label:R(`workspacePage.engineClaude`)},{value:`codex`,label:R(`workspacePage.engineCodex`)}]),Q=l(()=>{let e=(E[W.value]??[]).map(e=>({value:e.value,label:R(e.i18nLabelKey)}));T.workspace?.engine===W.value&&!e.some(e=>e.value===T.workspace?.model)&&e.push({value:T.workspace.model,label:T.workspace.model});let t=T.scheduledConfiguration;return t?.engine===W.value&&!e.some(e=>e.value===t.model)&&e.push({value:t.model,label:t.model}),e}),ie=l(()=>(O[W.value]??[]).map(e=>({value:e.value,label:R(e.i18nLabelKey)}))),$=l(()=>(D[W.value]??[]).map(e=>({value:e,label:R(`agentPermissionMode.${e}`)})));r(W,()=>{let e=T.workspace,t=z.global.defaultModelByEngine[W.value];G.value=e?.engine===W.value?e.model:Q.value.some(e=>e.value===t)?t:`auto`,K.value=e?.engine===W.value?e.reasoningEffort:`auto`,$.value.some(e=>e.value===q.value)||(q.value=`plan`)},{flush:`sync`}),r([()=>T.modelValue,()=>T.workspace?.id],([e])=>{if(!e)return;let t=B.value?T.scheduledConfiguration:null;V.value=t?.additionalInstructions??``,H.value=!1,U.value=B.value,T.workspace&&(W.value=T.workspace.engine,G.value=T.workspace.model,K.value=T.workspace.reasoningEffort,q.value=T.workspace.agentPermissionMode),t&&(W.value=t.engine,G.value=t.model,K.value=t.reasoningEffort)},{immediate:!0}),r(()=>T.canReturnToSession,e=>{!e&&!B.value&&(U.value=!1)});function ae(){T.loading||L(`update:modelValue`,!1)}function oe(){!T.loading&&T.workspace&&L(`submit`,{...Y.value,additionalInstructions:V.value,newSession:Z.value,returnToSession:B.value||U.value&&T.canReturnToSession})}return(n,r)=>(i(),s(S,{"model-value":e.modelValue,persistent:e.loading,"onUpdate:modelValue":r[7]||=e=>L(`update:modelValue`,e)},{default:a(()=>[o(v,{class:`review-card text-kobo-1`},{default:a(()=>[o(C,null,{default:a(()=>[f(`div`,A,t(n.$t(B.value?`autoLoop.finalReview.title`:`review.title`)),1),f(`div`,j,t(n.$t(B.value?`autoLoop.finalReview.description`:`review.subtitle`)),1)]),_:1}),o(_,{dark:``}),o(C,{class:`q-pt-md`},{default:a(()=>[f(`div`,ee,[f(`div`,te,[o(w,{modelValue:W.value,"onUpdate:modelValue":r[0]||=e=>W.value=e,options:re.value,label:n.$t(`engine.select`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,ne,[o(w,{modelValue:G.value,"onUpdate:modelValue":r[1]||=e=>G.value=e,options:Q.value,label:n.$t(`engine.model`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,M,[o(w,{modelValue:K.value,"onUpdate:modelValue":r[2]||=e=>K.value=e,options:ie.value,label:n.$t(`engine.effort`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,N,[o(w,{"model-value":J.value,options:$.value,label:n.$t(`agentPermissionMode.label`),disable:e.loading||U.value,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``,"onUpdate:modelValue":r[3]||=e=>q.value=e},null,8,[`model-value`,`options`,`label`,`disable`]),U.value?(i(),u(`div`,P,t(n.$t(`review.readOnlyForced`)),1)):c(``,!0)])]),o(h,{modelValue:V.value,"onUpdate:modelValue":r[4]||=e=>V.value=e,type:`textarea`,disable:e.loading,label:n.$t(`review.additionalInstructions`),placeholder:n.$t(`review.additionalInstructionsPlaceholder`),rows:4,outlined:``,dark:``,color:`primary`},null,8,[`modelValue`,`disable`,`label`,`placeholder`])]),_:1}),B.value?c(``,!0):(i(),s(C,{key:0,class:`q-pt-none`},{default:a(()=>[o(x,{modelValue:Z.value,"onUpdate:modelValue":r[5]||=e=>Z.value=e,disable:e.loading||X.value,label:n.$t(`review.newSession`),color:`primary`,dark:``},null,8,[`modelValue`,`disable`,`label`]),f(`div`,F,t(n.$t(X.value?`review.newSessionRequired`:`review.newSessionHint`)),1),Z.value?(i(),u(d,{key:0},[o(x,{modelValue:U.value,"onUpdate:modelValue":r[6]||=e=>U.value=e,disable:e.loading||!e.canReturnToSession,label:n.$t(`review.returnToSession`),color:`primary`,dark:``,class:`q-mt-md`},null,8,[`modelValue`,`disable`,`label`]),f(`div`,I,t(n.$t(e.canReturnToSession?`review.returnToSessionHint`:`review.returnUnavailable`)),1)],64)):c(``,!0)]),_:1})),o(b,{align:`right`,class:`q-pa-md`},{default:a(()=>[o(m,{flat:``,"no-caps":``,label:n.$t(`review.cancel`),color:`kobo-2`,disable:e.loading,onClick:ae},null,8,[`label`,`disable`]),o(m,{"no-caps":``,color:`primary`,label:n.$t(B.value?`autoLoop.finalReview.save`:`review.start`),loading:e.loading,disable:!e.workspace||e.loading,onClick:oe},null,8,[`label`,`loading`,`disable`])]),_:1})]),_:1})]),_:1},8,[`model-value`,`persistent`]))}}),[[`__scopeId`,`data-v-a4419350`]]),R={class:`final-review-settings`},z={class:`text-caption text-kobo-3`},B=T(p({__name:`AutoLoopFinalReviewSettings`,props:{modelValue:{},workspace:{},disabled:{type:Boolean},loading:{type:Boolean}},emits:[`update:modelValue`],setup(a,{emit:l}){let d=a,p=l,{t:h}=g(),_=n(!1),v=n(null);r(()=>d.modelValue,e=>{JSON.stringify(e)===JSON.stringify(v.value)&&(v.value=null)}),r(()=>d.workspace.id,()=>{_.value=!1,v.value=null});function y(e){d.disabled||d.loading||(e?_.value=!0:p(`update:modelValue`,null))}function b(e){!d.disabled&&!d.loading&&e.engine&&e.model&&e.reasoningEffort&&(v.value={engine:e.engine,model:e.model,reasoningEffort:e.reasoningEffort,additionalInstructions:e.additionalInstructions??``},p(`update:modelValue`,v.value),_.value=!1)}return(n,r)=>(i(),u(`div`,R,[o(x,{"model-value":!!a.modelValue,disable:a.disabled||a.loading,color:`primary`,label:e(h)(`autoLoop.finalReview.enabled`),"onUpdate:modelValue":y},null,8,[`model-value`,`disable`,`label`]),f(`p`,z,t(e(h)(`autoLoop.finalReview.hint`)),1),a.modelValue?(i(),s(m,{key:0,flat:``,dense:``,"no-caps":``,disable:a.disabled||a.loading,label:e(h)(`autoLoop.finalReview.configure`),onClick:r[0]||=e=>_.value=!0},null,8,[`disable`,`label`])):c(``,!0),o(L,{modelValue:_.value,"onUpdate:modelValue":r[1]||=e=>_.value=e,mode:`auto-loop`,loading:a.loading??!1,workspace:a.workspace,"scheduled-configuration":v.value??a.modelValue,"can-return-to-session":!0,onSubmit:b},null,8,[`modelValue`,`loading`,`workspace`,`scheduled-configuration`])]))}}),[[`__scopeId`,`data-v-b7f36f83`]]);export{L as n,B as t};
|