@loicngr/kobo 1.7.16 → 1.7.17
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 +35 -4
- package/CHANGELOG.md +5 -0
- package/README.md +2 -1
- package/dist/mcp-server/kobo-tasks-handlers.js +48 -5
- package/dist/mcp-server/kobo-tasks-server.js +8 -20
- package/dist/server/db/migrations.js +19 -0
- package/dist/server/db/schema.js +9 -0
- package/dist/server/routes/settings.js +2 -0
- package/dist/server/routes/workspaces.js +242 -56
- package/dist/server/services/agent/engines/claude-code/engine.js +51 -7
- package/dist/server/services/agent/engines/claude-code/options-builder.js +2 -2
- package/dist/server/services/agent/engines/codex/options-builder.js +2 -2
- package/dist/server/services/agent/orchestrator.js +1 -0
- package/dist/server/services/change-source-branch-service.js +150 -0
- package/dist/server/services/chat-history-service.js +41 -0
- package/dist/server/services/file-editor-service.js +59 -0
- package/dist/server/services/forge/github/provider.js +121 -0
- package/dist/server/services/forge/gitlab/provider.js +178 -0
- package/dist/server/services/forge/none.js +23 -0
- package/dist/server/services/forge/registry.js +17 -0
- package/dist/server/services/forge/resolve.js +34 -0
- package/dist/server/services/forge/types.js +9 -0
- package/dist/server/services/git-stats-service.js +32 -0
- package/dist/server/services/pr-watcher-service.js +33 -3
- package/dist/server/services/settings-defaults.js +77 -0
- package/dist/server/services/settings-service.js +34 -0
- package/dist/server/utils/git-ops.js +121 -134
- package/package.json +1 -1
- package/src/client/dist/spa/assets/ActivityFeed-CDhLuhI3.js +8 -0
- package/src/client/dist/spa/assets/{ClosePopup-CxvZA3ft.js → ClosePopup-A-tSm4aa.js} +1 -1
- package/src/client/dist/spa/assets/{CreatePage-CdZr7f3j.js → CreatePage-xIjxPliD.js} +1 -1
- package/src/client/dist/spa/assets/DiffViewer-C4L5y8Ho.css +1 -0
- package/src/client/dist/spa/assets/DiffViewer-CcgF65Mo.js +8 -0
- package/src/client/dist/spa/assets/{HealthPage-z1uIOpYk.js → HealthPage-Bw-9__wY.js} +1 -1
- package/src/client/dist/spa/assets/MainLayout-C2ULeep-.js +37 -0
- package/src/client/dist/spa/assets/{MainLayout-BJmBXwYn.css → MainLayout-KEr19FOv.css} +1 -1
- package/src/client/dist/spa/assets/{QExpansionItem-BTd5m2yV.js → QExpansionItem-CgJQdznK.js} +1 -1
- package/src/client/dist/spa/assets/{QMenu-C2Wwwf2E.js → QMenu-NVDU7D3u.js} +1 -1
- package/src/client/dist/spa/assets/{QScrollArea-A1wI0IXU.js → QScrollArea-_Ji1cgqL.js} +1 -1
- package/src/client/dist/spa/assets/{QTooltip-Bfdmzm_m.js → QTooltip-BC7PnZJ1.js} +1 -1
- package/src/client/dist/spa/assets/{SearchPage-ChmKHNKn.js → SearchPage-D2x2X7K7.js} +1 -1
- package/src/client/dist/spa/assets/SettingsPage-BLb9B9iY.js +9 -0
- package/src/client/dist/spa/assets/{SettingsPage-BJLyYrBN.css → SettingsPage-BTGPZaqC.css} +1 -1
- package/src/client/dist/spa/assets/{TouchPan-BIE5rs7U.js → TouchPan-D0fJnlOC.js} +1 -1
- package/src/client/dist/spa/assets/WorkspacePage-BlAVs03z.js +4 -0
- package/src/client/dist/spa/assets/{WorkspacePage-tFBswKV9.css → WorkspacePage-DTV0oWHS.css} +1 -1
- package/src/client/dist/spa/assets/{build-path-tree-BGUV3nY1.js → build-path-tree-CyqReJkk.js} +1 -1
- package/src/client/dist/spa/assets/{cssMode-BU4X8R6a.js → cssMode-BsT_HBz-.js} +1 -1
- package/src/client/dist/spa/assets/{editor.api-B4xBDzmJ.js → editor.api-CIxiApSC.js} +1 -1
- package/src/client/dist/spa/assets/{editor.main-CSZRkloL.js → editor.main-D-1-e3_n.js} +3 -3
- package/src/client/dist/spa/assets/{engineFeatures-CLOVr5b4.js → engineFeatures-baMvMT98.js} +1 -1
- package/src/client/dist/spa/assets/{expand-template-BxUkuL5g.js → expand-template-CF0lBr4L.js} +1 -1
- package/src/client/dist/spa/assets/{freemarker2-DRz20wAV.js → freemarker2-q2PyKiM2.js} +1 -1
- package/src/client/dist/spa/assets/{handlebars-C0dsvPnC.js → handlebars-DoaZIK6r.js} +1 -1
- package/src/client/dist/spa/assets/{html-Cqvj1pWs.js → html-DHcse-fd.js} +1 -1
- package/src/client/dist/spa/assets/{htmlMode-BTHNvkm6.js → htmlMode-DPZCU7DB.js} +1 -1
- package/src/client/dist/spa/assets/i18n-DwzfgKc3.js +1 -0
- package/src/client/dist/spa/assets/index-DMUFfCIq.js +52 -0
- package/src/client/dist/spa/assets/{javascript-C8n3U02v.js → javascript-Ddw7c3eO.js} +1 -1
- package/src/client/dist/spa/assets/{jsonMode-C3AFxQ6K.js → jsonMode-vmAmvg_N.js} +1 -1
- package/src/client/dist/spa/assets/{kobo-commands-BuxgteGZ.js → kobo-commands-B2AhWe1S.js} +1 -1
- package/src/client/dist/spa/assets/{liquid-C4wtUDrJ.js → liquid-Bwz3vr4k.js} +1 -1
- package/src/client/dist/spa/assets/{mdx-CaT1p1F2.js → mdx-B2uBtVef.js} +1 -1
- package/src/client/dist/spa/assets/{monaco.contribution-CJg5GKVf.js → monaco.contribution-B3cRHiXp.js} +2 -2
- package/src/client/dist/spa/assets/{notifications-BC6en6Lt.js → notifications-Hq-6rEYv.js} +1 -1
- package/src/client/dist/spa/assets/{permissionModes-BQHBTBwa.js → permissionModes-DuwIe4ty.js} +1 -1
- package/src/client/dist/spa/assets/{python-Cj54W2Tg.js → python-D5ykDuc8.js} +1 -1
- package/src/client/dist/spa/assets/{razor-D3gJxoX_.js → razor-CStvtec5.js} +1 -1
- package/src/client/dist/spa/assets/{render-chat-markdown-DxEHr3lW.js → render-chat-markdown-BywKNkXe.js} +1 -1
- package/src/client/dist/spa/assets/{tsMode-B6S4PLWH.js → tsMode-WEgYYKFt.js} +1 -1
- package/src/client/dist/spa/assets/{typescript-Ca8AEX3t.js → typescript-BzFHuirT.js} +1 -1
- package/src/client/dist/spa/assets/{use-onboarding-CNeLPDtv.js → use-onboarding-C98jCHZu.js} +1 -1
- package/src/client/dist/spa/assets/{xml-CsKo4k8C.js → xml-NuCdCQMI.js} +1 -1
- package/src/client/dist/spa/assets/{yaml-X5yKmi6z.js → yaml-CkTTgcUh.js} +1 -1
- package/src/client/dist/spa/index.html +2 -2
- package/src/mcp-server/kobo-tasks-handlers.ts +56 -5
- package/src/mcp-server/kobo-tasks-server.ts +8 -19
- package/src/client/dist/spa/assets/ActivityFeed-DU6lDEP0.js +0 -8
- package/src/client/dist/spa/assets/DiffViewer-DTdDcKZC.css +0 -1
- package/src/client/dist/spa/assets/DiffViewer-m801GPfI.js +0 -7
- package/src/client/dist/spa/assets/MainLayout-oJdQ-QKM.js +0 -37
- package/src/client/dist/spa/assets/SettingsPage-B59LoCos.js +0 -9
- package/src/client/dist/spa/assets/WorkspacePage-Bj1PJSWT.js +0 -4
- package/src/client/dist/spa/assets/i18n-D1I-Us2H.js +0 -1
- package/src/client/dist/spa/assets/index-KABmOIkF.js +0 -2
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fetchSourceBranchAsync } from '../utils/git-ops.js';
|
|
2
2
|
import { stopDevServer } from './dev-server-service.js';
|
|
3
|
+
import { getForgeProvider } from './forge/registry.js';
|
|
4
|
+
import { resolveForge } from './forge/resolve.js';
|
|
5
|
+
import { computeGitStats } from './git-stats-service.js';
|
|
3
6
|
import { destroyTerminal } from './terminal-service.js';
|
|
4
7
|
import { emitEphemeral } from './websocket-service.js';
|
|
5
8
|
import { archiveWorkspace, getWorkspace, listWorkspaces, updateWorkspaceSourceBranch } from './workspace-service.js';
|
|
@@ -17,6 +20,8 @@ let checking = false;
|
|
|
17
20
|
/** Tracks the last known PR snapshot per workspace, used to detect transitions
|
|
18
21
|
* (state, base, reviewDecision). */
|
|
19
22
|
const lastKnownPr = new Map();
|
|
23
|
+
/** Latest git-stats snapshot per workspace, refreshed each watcher tick. */
|
|
24
|
+
const lastKnownGitStats = new Map();
|
|
20
25
|
/**
|
|
21
26
|
* Read-only snapshot map, keyed by workspace id. Used by the drawer indicator
|
|
22
27
|
* AND the Git panel. Workspaces without a known PR are absent.
|
|
@@ -28,12 +33,22 @@ export function getAllPrSnapshots() {
|
|
|
28
33
|
}
|
|
29
34
|
return out;
|
|
30
35
|
}
|
|
36
|
+
/** Read-only git-stats map, keyed by workspace id. Used by the bulk
|
|
37
|
+
* `/api/workspaces/info` endpoint. */
|
|
38
|
+
export function getAllGitStats() {
|
|
39
|
+
const out = {};
|
|
40
|
+
for (const [id, s] of lastKnownGitStats) {
|
|
41
|
+
out[id] = s;
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
31
45
|
/**
|
|
32
46
|
* Test-only escape hatch — drops the in-memory cache so each test starts
|
|
33
47
|
* from a clean slate. Not part of the public API.
|
|
34
48
|
*/
|
|
35
49
|
export function _resetForTest() {
|
|
36
50
|
lastKnownPr.clear();
|
|
51
|
+
lastKnownGitStats.clear();
|
|
37
52
|
}
|
|
38
53
|
export async function checkPrStatuses() {
|
|
39
54
|
const workspaces = listWorkspaces(false); // non-archived only
|
|
@@ -43,9 +58,24 @@ export async function checkPrStatuses() {
|
|
|
43
58
|
lastKnownPr.delete(id);
|
|
44
59
|
}
|
|
45
60
|
}
|
|
61
|
+
for (const id of lastKnownGitStats.keys()) {
|
|
62
|
+
if (!workspaces.some((ws) => ws.id === id)) {
|
|
63
|
+
lastKnownGitStats.delete(id);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
46
66
|
for (const ws of workspaces) {
|
|
47
67
|
try {
|
|
48
|
-
const pr = await
|
|
68
|
+
const pr = await getForgeProvider(resolveForge(ws.projectPath)).getPrStatus(ws.worktreePath, ws.workingBranch);
|
|
69
|
+
// Git stats — best-effort, cached independently of the PR-transition
|
|
70
|
+
// logic below. Its own try/catch so a git failure neither skips PR
|
|
71
|
+
// transitions nor poisons other workspaces.
|
|
72
|
+
try {
|
|
73
|
+
void fetchSourceBranchAsync(ws.worktreePath, ws.sourceBranch).catch(() => { });
|
|
74
|
+
lastKnownGitStats.set(ws.id, await computeGitStats(ws, pr));
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
console.error(`[pr-watcher] computeGitStats failed for '${ws.name}':`, err instanceof Error ? err.message : err);
|
|
78
|
+
}
|
|
49
79
|
if (!pr)
|
|
50
80
|
continue;
|
|
51
81
|
const prev = lastKnownPr.get(ws.id);
|
|
@@ -163,7 +193,7 @@ export async function refreshPrSnapshot(workspaceId) {
|
|
|
163
193
|
const ws = getWorkspace(workspaceId);
|
|
164
194
|
if (!ws)
|
|
165
195
|
throw new Error(`Workspace '${workspaceId}' not found`);
|
|
166
|
-
const snap = await
|
|
196
|
+
const snap = await getForgeProvider(resolveForge(ws.projectPath)).getPrStatus(ws.worktreePath, ws.workingBranch);
|
|
167
197
|
if (snap === null) {
|
|
168
198
|
lastKnownPr.delete(workspaceId);
|
|
169
199
|
return null;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash equivalent of the built-in change-source-branch cherry-pick. Seeded
|
|
3
|
+
* into `global.changeSourceBranchScript` by settings migration v33; served to
|
|
4
|
+
* the client via `GET /api/settings/defaults` for the Settings "Reset to Kōbō
|
|
5
|
+
* default" button. Clearing the textarea disables the feature (empty script
|
|
6
|
+
* → menu item hidden).
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_CHANGE_SOURCE_BRANCH_SCRIPT = `#!/usr/bin/env bash
|
|
9
|
+
# Kōbō default change-source-branch script — edit at will.
|
|
10
|
+
# Replaces the built-in cherry-pick when this field is non-empty.
|
|
11
|
+
#
|
|
12
|
+
# Env vars Kōbō exports for you:
|
|
13
|
+
# KOBO_NEW_BASE new source branch chosen in the dialog
|
|
14
|
+
# KOBO_OLD_BASE workspace's previous source branch
|
|
15
|
+
# KOBO_WORKING_BRANCH workspace's working branch
|
|
16
|
+
# KOBO_WORKTREE_PATH absolute path of the worktree (also cwd)
|
|
17
|
+
# KOBO_PROJECT_PATH absolute path of the main project repo
|
|
18
|
+
# KOBO_PROJECT_NAME project directory name (basename of KOBO_PROJECT_PATH)
|
|
19
|
+
# KOBO_WORKSPACE_ID Kōbō workspace id (stable across renames)
|
|
20
|
+
# KOBO_WORKSPACE_NAME workspace display name
|
|
21
|
+
# KOBO_FORGE resolved forge: github / gitlab / none
|
|
22
|
+
# KOBO_PR_NUMBER PR/MR number on the resolved forge (empty if none open)
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
|
|
25
|
+
# Safety limit — refuse and ask for a manual rebase above this many commits.
|
|
26
|
+
MAX_PROPER_COMMITS=50
|
|
27
|
+
|
|
28
|
+
git fetch origin
|
|
29
|
+
|
|
30
|
+
# Commits proper to the working branch (in neither base).
|
|
31
|
+
COMMITS=$(git log --reverse --format=%H "$KOBO_WORKING_BRANCH" \\
|
|
32
|
+
--not "origin/$KOBO_NEW_BASE" "origin/$KOBO_OLD_BASE" || true)
|
|
33
|
+
COUNT=$(printf '%s\\n' "$COMMITS" | grep -c '^[0-9a-f]' || true)
|
|
34
|
+
|
|
35
|
+
# Backup branch so you can always recover: \`git reset --hard kobo-backup/…\`.
|
|
36
|
+
git branch "kobo-backup/\${KOBO_WORKING_BRANCH}-$(date +%s)" "$KOBO_WORKING_BRANCH"
|
|
37
|
+
|
|
38
|
+
if [ "$COUNT" -eq 0 ]; then
|
|
39
|
+
git reset --hard "origin/$KOBO_NEW_BASE"
|
|
40
|
+
elif [ "$COUNT" -gt "$MAX_PROPER_COMMITS" ]; then
|
|
41
|
+
echo "Too many proper commits ($COUNT > $MAX_PROPER_COMMITS) — rebase manually" >&2
|
|
42
|
+
exit 1
|
|
43
|
+
else
|
|
44
|
+
git reset --hard "origin/$KOBO_NEW_BASE"
|
|
45
|
+
printf '%s\\n' "$COMMITS" | xargs git cherry-pick
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Re-target the PR / MR. Probe the CLI first so a missing tool degrades
|
|
49
|
+
# gracefully under \`set -e\`.
|
|
50
|
+
case "\${KOBO_FORGE:-none}" in
|
|
51
|
+
github)
|
|
52
|
+
if command -v gh >/dev/null 2>&1; then
|
|
53
|
+
gh pr edit --base "$KOBO_NEW_BASE" 2>/dev/null || true
|
|
54
|
+
else
|
|
55
|
+
echo "warn: 'gh' CLI not installed — skipping PR base update" >&2
|
|
56
|
+
fi
|
|
57
|
+
;;
|
|
58
|
+
gitlab)
|
|
59
|
+
if command -v glab >/dev/null 2>&1; then
|
|
60
|
+
glab mr update --target-branch "$KOBO_NEW_BASE" 2>/dev/null || true
|
|
61
|
+
else
|
|
62
|
+
echo "warn: 'glab' CLI not installed — skipping MR base update" >&2
|
|
63
|
+
fi
|
|
64
|
+
;;
|
|
65
|
+
*) : ;;
|
|
66
|
+
esac
|
|
67
|
+
|
|
68
|
+
# Force-push if the branch is tracked upstream.
|
|
69
|
+
if git rev-parse --abbrev-ref "@{upstream}" >/dev/null 2>&1; then
|
|
70
|
+
git push --force-with-lease origin "$KOBO_WORKING_BRANCH"
|
|
71
|
+
fi
|
|
72
|
+
`;
|
|
73
|
+
export function getSettingsDefaults() {
|
|
74
|
+
return {
|
|
75
|
+
changeSourceBranchScript: DEFAULT_CHANGE_SOURCE_BRANCH_SCRIPT,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -8,6 +8,7 @@ import { getSettingsPath } from '../utils/paths.js';
|
|
|
8
8
|
import { InvalidWorktreesPathError, resolveGlobalWorktreesRoot, sanitizeWorktreesPath, validateWorktreesPath, } from '../utils/worktree-paths.js';
|
|
9
9
|
import { DEFAULT_NOTION_INITIAL_PROMPT, DEFAULT_SENTRY_INITIAL_PROMPT } from './initial-prompt-template-service.js';
|
|
10
10
|
import { DEFAULT_REVIEW_PROMPT_TEMPLATE } from './review-template-service.js';
|
|
11
|
+
import { DEFAULT_CHANGE_SOURCE_BRANCH_SCRIPT } from './settings-defaults.js';
|
|
11
12
|
import { AGNOSTIC_PROMPTS } from './skill-suite-prompts.js';
|
|
12
13
|
export const DEFAULT_GIT_CONVENTIONS = `# Git conventions
|
|
13
14
|
|
|
@@ -530,6 +531,31 @@ const settingsMigrations = [
|
|
|
530
531
|
}
|
|
531
532
|
},
|
|
532
533
|
},
|
|
534
|
+
{
|
|
535
|
+
version: 32,
|
|
536
|
+
name: 'add-project-forge',
|
|
537
|
+
migrate: ({ projects }) => {
|
|
538
|
+
for (const p of projects) {
|
|
539
|
+
if (typeof p.forge !== 'string')
|
|
540
|
+
p.forge = 'auto';
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
version: 33,
|
|
546
|
+
name: 'add-change-source-branch-script',
|
|
547
|
+
migrate: ({ global, projects }) => {
|
|
548
|
+
// Seed global with the default so the feature is enabled out-of-the-box;
|
|
549
|
+
// projects stay empty (= inherit global).
|
|
550
|
+
if (typeof global.changeSourceBranchScript !== 'string') {
|
|
551
|
+
global.changeSourceBranchScript = DEFAULT_CHANGE_SOURCE_BRANCH_SCRIPT;
|
|
552
|
+
}
|
|
553
|
+
for (const p of projects) {
|
|
554
|
+
if (typeof p.changeSourceBranchScript !== 'string')
|
|
555
|
+
p.changeSourceBranchScript = '';
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
},
|
|
533
559
|
];
|
|
534
560
|
/** Current settings schema version — always equals the highest migration version. */
|
|
535
561
|
export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
|
|
@@ -569,6 +595,7 @@ function defaultSettings() {
|
|
|
569
595
|
cleanupScriptMode: 'no-tasks',
|
|
570
596
|
cleanupScriptOnlyOnChanges: false,
|
|
571
597
|
archiveScript: '',
|
|
598
|
+
changeSourceBranchScript: DEFAULT_CHANGE_SOURCE_BRANCH_SCRIPT,
|
|
572
599
|
editorCommand: '',
|
|
573
600
|
browserNotifications: true,
|
|
574
601
|
audioNotifications: true,
|
|
@@ -623,6 +650,7 @@ function defaultProjectSettings(projectPath) {
|
|
|
623
650
|
cleanupScript: '',
|
|
624
651
|
cleanupScriptMode: '',
|
|
625
652
|
archiveScript: '',
|
|
653
|
+
changeSourceBranchScript: '',
|
|
626
654
|
devServer: {
|
|
627
655
|
startCommand: '',
|
|
628
656
|
stopCommand: '',
|
|
@@ -636,6 +664,7 @@ function defaultProjectSettings(projectPath) {
|
|
|
636
664
|
prompt: DEFAULT_FINALIZATION_PROMPT,
|
|
637
665
|
},
|
|
638
666
|
color: null,
|
|
667
|
+
forge: 'auto',
|
|
639
668
|
};
|
|
640
669
|
}
|
|
641
670
|
function pickKnownKeys(data, allowedKeys) {
|
|
@@ -837,6 +866,7 @@ export function getEffectiveSettings(projectPath) {
|
|
|
837
866
|
cleanupScriptMode: settings.global.cleanupScriptMode,
|
|
838
867
|
cleanupScriptOnlyOnChanges: settings.global.cleanupScriptOnlyOnChanges,
|
|
839
868
|
archiveScript: settings.global.archiveScript,
|
|
869
|
+
changeSourceBranchScript: settings.global.changeSourceBranchScript,
|
|
840
870
|
notionStatusProperty: settings.global.notionStatusProperty,
|
|
841
871
|
notionInProgressStatus: settings.global.notionInProgressStatus,
|
|
842
872
|
};
|
|
@@ -862,6 +892,7 @@ export function getEffectiveSettings(projectPath) {
|
|
|
862
892
|
cleanupScriptMode: (project.cleanupScriptMode || settings.global.cleanupScriptMode),
|
|
863
893
|
cleanupScriptOnlyOnChanges: settings.global.cleanupScriptOnlyOnChanges,
|
|
864
894
|
archiveScript: project.archiveScript || settings.global.archiveScript,
|
|
895
|
+
changeSourceBranchScript: project.changeSourceBranchScript || settings.global.changeSourceBranchScript,
|
|
865
896
|
notionStatusProperty: settings.global.notionStatusProperty,
|
|
866
897
|
notionInProgressStatus: settings.global.notionInProgressStatus,
|
|
867
898
|
};
|
|
@@ -897,6 +928,7 @@ export function updateGlobalSettings(data) {
|
|
|
897
928
|
'cleanupScriptMode',
|
|
898
929
|
'cleanupScriptOnlyOnChanges',
|
|
899
930
|
'archiveScript',
|
|
931
|
+
'changeSourceBranchScript',
|
|
900
932
|
'editorCommand',
|
|
901
933
|
'browserNotifications',
|
|
902
934
|
'audioNotifications',
|
|
@@ -1009,10 +1041,12 @@ export function upsertProject(projectPath, data) {
|
|
|
1009
1041
|
'cleanupScript',
|
|
1010
1042
|
'cleanupScriptMode',
|
|
1011
1043
|
'archiveScript',
|
|
1044
|
+
'changeSourceBranchScript',
|
|
1012
1045
|
'devServer',
|
|
1013
1046
|
'e2e',
|
|
1014
1047
|
'finalization',
|
|
1015
1048
|
'color',
|
|
1049
|
+
'forge',
|
|
1016
1050
|
];
|
|
1017
1051
|
const allowedDevServerKeys = ['startCommand', 'stopCommand'];
|
|
1018
1052
|
const allowedE2eKeys = ['framework', 'skill', 'prompt'];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFile as execFileCb, execFileSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
const execFileAsync = promisify(execFileCb);
|
|
@@ -139,6 +139,20 @@ export function fetchSourceBranch(repoPath, sourceBranch, remote = 'origin') {
|
|
|
139
139
|
throw new Error(`Failed to fetch '${sourceBranch}' from '${remote}': ${message}`);
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Fetch every branch from the remote (`git fetch <remote>` with no refspec).
|
|
144
|
+
* Throws if the fetch fails. Call this before computing branch divergence so
|
|
145
|
+
* all `origin/*` refs are current.
|
|
146
|
+
*/
|
|
147
|
+
export function fetchAllBranches(repoPath, remote = 'origin') {
|
|
148
|
+
try {
|
|
149
|
+
git(repoPath, ['fetch', remote]);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
153
|
+
throw new Error(`Failed to fetch from '${remote}': ${message}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
142
156
|
/** Pull the current branch from the remote using fast-forward only. */
|
|
143
157
|
export function pullBranch(repoPath, branchName, remote = 'origin') {
|
|
144
158
|
try {
|
|
@@ -149,8 +163,8 @@ export function pullBranch(repoPath, branchName, remote = 'origin') {
|
|
|
149
163
|
throw new Error(`Failed to pull branch '${branchName}' from '${remote}': ${message}`);
|
|
150
164
|
}
|
|
151
165
|
}
|
|
152
|
-
/** Thrown when a rebase or
|
|
153
|
-
* so the caller can decide between abort and agent-assisted resolution. */
|
|
166
|
+
/** Thrown when a rebase, merge or cherry-pick produces conflicts. Leaves the repo in the
|
|
167
|
+
* mid-operation state so the caller can decide between abort and agent-assisted resolution. */
|
|
154
168
|
export class GitConflictError extends Error {
|
|
155
169
|
operation;
|
|
156
170
|
files;
|
|
@@ -174,7 +188,7 @@ export function getConflictedFiles(repoPath) {
|
|
|
174
188
|
return [];
|
|
175
189
|
}
|
|
176
190
|
}
|
|
177
|
-
/** Detect whether a merge or
|
|
191
|
+
/** Detect whether a merge, rebase or cherry-pick is currently in progress in the worktree. */
|
|
178
192
|
export function getOngoingGitOperation(repoPath) {
|
|
179
193
|
try {
|
|
180
194
|
const gitDir = git(repoPath, ['rev-parse', '--git-dir']);
|
|
@@ -183,6 +197,8 @@ export function getOngoingGitOperation(repoPath) {
|
|
|
183
197
|
return 'merge';
|
|
184
198
|
if (existsSync(join(dir, 'rebase-merge')) || existsSync(join(dir, 'rebase-apply')))
|
|
185
199
|
return 'rebase';
|
|
200
|
+
if (existsSync(join(dir, 'CHERRY_PICK_HEAD')) || existsSync(join(dir, 'sequencer')))
|
|
201
|
+
return 'cherry-pick';
|
|
186
202
|
return null;
|
|
187
203
|
}
|
|
188
204
|
catch {
|
|
@@ -230,7 +246,7 @@ export function mergeBranch(repoPath, baseBranch) {
|
|
|
230
246
|
throw new Error(`Merge of 'origin/${baseBranch}' failed: ${message}`);
|
|
231
247
|
}
|
|
232
248
|
}
|
|
233
|
-
/** Abort an in-progress merge or
|
|
249
|
+
/** Abort an in-progress merge, rebase or cherry-pick. No-op if nothing is in progress. */
|
|
234
250
|
export function abortOngoingGitOperation(repoPath) {
|
|
235
251
|
const op = getOngoingGitOperation(repoPath);
|
|
236
252
|
if (op === 'merge') {
|
|
@@ -239,6 +255,9 @@ export function abortOngoingGitOperation(repoPath) {
|
|
|
239
255
|
else if (op === 'rebase') {
|
|
240
256
|
git(repoPath, ['rebase', '--abort']);
|
|
241
257
|
}
|
|
258
|
+
else if (op === 'cherry-pick') {
|
|
259
|
+
git(repoPath, ['cherry-pick', '--abort']);
|
|
260
|
+
}
|
|
242
261
|
return op;
|
|
243
262
|
}
|
|
244
263
|
/** Try a git command with `base`, falling back to `origin/base` if the local ref is missing. */
|
|
@@ -290,6 +309,90 @@ export function getCommitsBehind(repoPath, base, head) {
|
|
|
290
309
|
return 0;
|
|
291
310
|
}
|
|
292
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* List the commits that belong to `workingBranch` itself — reachable from it
|
|
314
|
+
* but present in neither `newBase` nor `oldBase`. This is the set to replay
|
|
315
|
+
* onto the new base. Returned oldest-first (ready for sequential cherry-pick).
|
|
316
|
+
*
|
|
317
|
+
* Both `origin/<base>` and the bare `<base>` are excluded when they exist, so
|
|
318
|
+
* the result is correct whether the caller fetched the base or not.
|
|
319
|
+
*/
|
|
320
|
+
export function listProperCommits(repoPath, workingBranch, newBase, oldBase) {
|
|
321
|
+
const excludes = [];
|
|
322
|
+
for (const base of [newBase, oldBase]) {
|
|
323
|
+
for (const ref of [`origin/${base}`, base]) {
|
|
324
|
+
try {
|
|
325
|
+
git(repoPath, ['rev-parse', '--verify', '--quiet', ref]);
|
|
326
|
+
excludes.push(`^${ref}`);
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// ref absent — skip
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const output = git(repoPath, ['log', '--reverse', '--format=%H', workingBranch, ...excludes]);
|
|
334
|
+
return output
|
|
335
|
+
.split('\n')
|
|
336
|
+
.map((s) => s.trim())
|
|
337
|
+
.filter(Boolean);
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Rebuild `workingBranch` on top of the new base by cherry-picking the given
|
|
341
|
+
* commits (oldest-first). Creates a backup branch at the current tip first and
|
|
342
|
+
* returns its name. On a cherry-pick conflict, leaves the operation in progress
|
|
343
|
+
* and throws `GitConflictError`.
|
|
344
|
+
*
|
|
345
|
+
* The base is resolved as `origin/<newBase>` when that ref exists, else the
|
|
346
|
+
* bare `<newBase>` (so it works both with a fetched remote and a local-only
|
|
347
|
+
* base). The caller must ensure the worktree is clean for the conflict path.
|
|
348
|
+
* An empty `commits` array performs the reset only — the "already aligned"
|
|
349
|
+
* fast path.
|
|
350
|
+
*
|
|
351
|
+
* IMPORTANT: this function resets the branch CURRENTLY checked out in
|
|
352
|
+
* `repoPath`. The caller (and the Kōbō worktree orchestrator) must ensure
|
|
353
|
+
* `workingBranch` is the active branch — do NOT add a `git checkout` here.
|
|
354
|
+
*/
|
|
355
|
+
export function reconstructBranchOnto(repoPath, workingBranch, newBase, commits) {
|
|
356
|
+
const baseRef = resolveBase(repoPath, newBase);
|
|
357
|
+
const backupBranch = `kobo-backup/${workingBranch}-${Date.now()}`;
|
|
358
|
+
git(repoPath, ['branch', backupBranch, workingBranch]);
|
|
359
|
+
git(repoPath, ['reset', '--hard', baseRef]);
|
|
360
|
+
if (commits.length > 0) {
|
|
361
|
+
try {
|
|
362
|
+
git(repoPath, ['cherry-pick', ...commits]);
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
const conflicted = getConflictedFiles(repoPath);
|
|
366
|
+
if (conflicted.length > 0 || getOngoingGitOperation(repoPath) === 'cherry-pick') {
|
|
367
|
+
throw new GitConflictError('cherry-pick', conflicted);
|
|
368
|
+
}
|
|
369
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
370
|
+
throw new Error(`Cherry-pick onto '${newBase}' failed: ${message}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return backupBranch;
|
|
374
|
+
}
|
|
375
|
+
/** List `kobo-backup/<workingBranch>-<ts>` branches, newest timestamp first. */
|
|
376
|
+
export function listBackupBranches(repoPath, workingBranch) {
|
|
377
|
+
try {
|
|
378
|
+
const prefix = `kobo-backup/${workingBranch}-`;
|
|
379
|
+
const out = git(repoPath, ['branch', '--list', `${prefix}*`, '--format=%(refname:short)']);
|
|
380
|
+
return out
|
|
381
|
+
.split('\n')
|
|
382
|
+
.map((s) => s.trim())
|
|
383
|
+
.filter((b) => b.startsWith(prefix) && /^\d+$/.test(b.slice(prefix.length)))
|
|
384
|
+
.sort((a, b) => Number(b.slice(prefix.length)) - Number(a.slice(prefix.length)));
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
return [];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/** Abort any in-progress operation, then hard-reset `workingBranch` to a backup branch. */
|
|
391
|
+
export function restoreBranchFromBackup(repoPath, workingBranch, backupBranch) {
|
|
392
|
+
abortOngoingGitOperation(repoPath);
|
|
393
|
+
git(repoPath, ['checkout', '-q', workingBranch]);
|
|
394
|
+
git(repoPath, ['reset', '--hard', backupBranch]);
|
|
395
|
+
}
|
|
293
396
|
/** Return structured diff shortstat between two refs (three-dot merge base). */
|
|
294
397
|
export function getStructuredDiffStatsBetween(repoPath, base, head) {
|
|
295
398
|
try {
|
|
@@ -397,105 +500,6 @@ export function listCommitsBehind(repoPath, sourceBranch, workingBranch, limit =
|
|
|
397
500
|
}
|
|
398
501
|
return commits;
|
|
399
502
|
}
|
|
400
|
-
/** Get the GitHub PR URL for a branch using `gh pr view`. Returns null if no PR exists. */
|
|
401
|
-
export function getPrUrl(repoPath, branchName) {
|
|
402
|
-
try {
|
|
403
|
-
return (execFileSync('gh', ['pr', 'view', branchName, '--json', 'url', '--jq', '.url'], {
|
|
404
|
-
cwd: repoPath,
|
|
405
|
-
encoding: 'utf-8',
|
|
406
|
-
}).trim() || null);
|
|
407
|
-
}
|
|
408
|
-
catch {
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
// NOTE: `reviewThreads` is intentionally NOT in this list.
|
|
413
|
-
// As of `gh` CLI 2.92 (latest at time of writing) the `--json reviewThreads`
|
|
414
|
-
// flag is rejected with `Unknown JSON field: "reviewThreads"` — there is no
|
|
415
|
-
// stable `gh` version that exposes it via `pr view --json`. Until upstream
|
|
416
|
-
// adds it, `unresolvedReviewThreadsCount` stays at 0 and the
|
|
417
|
-
// "hide changes-requested badge when all threads are resolved" feature
|
|
418
|
-
// degrades to "badge stays visible". To restore that feature later, either
|
|
419
|
-
// (a) re-add the field here once `gh` supports it, or (b) make a separate
|
|
420
|
-
// `gh api graphql` call for `pullRequest.reviewThreads.nodes`.
|
|
421
|
-
const GH_PR_FIELDS = [
|
|
422
|
-
'number',
|
|
423
|
-
'title',
|
|
424
|
-
'url',
|
|
425
|
-
'state',
|
|
426
|
-
'baseRefName',
|
|
427
|
-
'reviewDecision',
|
|
428
|
-
'author',
|
|
429
|
-
'assignees',
|
|
430
|
-
'labels',
|
|
431
|
-
'latestReviews',
|
|
432
|
-
'reviewRequests',
|
|
433
|
-
'statusCheckRollup',
|
|
434
|
-
'updatedAt',
|
|
435
|
-
].join(',');
|
|
436
|
-
function mapGhPrToSnapshot(raw) {
|
|
437
|
-
const reviewers = [];
|
|
438
|
-
const seen = new Set();
|
|
439
|
-
for (const r of raw.latestReviews ?? []) {
|
|
440
|
-
const login = r.author?.login;
|
|
441
|
-
if (!login || seen.has(login))
|
|
442
|
-
continue;
|
|
443
|
-
seen.add(login);
|
|
444
|
-
reviewers.push({ login, state: r.state ?? 'COMMENTED' });
|
|
445
|
-
}
|
|
446
|
-
for (const r of raw.reviewRequests ?? []) {
|
|
447
|
-
if (!r.login || seen.has(r.login))
|
|
448
|
-
continue;
|
|
449
|
-
seen.add(r.login);
|
|
450
|
-
reviewers.push({ login: r.login, state: 'PENDING' });
|
|
451
|
-
}
|
|
452
|
-
const checks = (raw.statusCheckRollup ?? []).map((c) => ({
|
|
453
|
-
name: c.name,
|
|
454
|
-
conclusion: c.conclusion ?? null,
|
|
455
|
-
status: c.status,
|
|
456
|
-
detailsUrl: c.detailsUrl ?? null,
|
|
457
|
-
}));
|
|
458
|
-
let rollup = null;
|
|
459
|
-
if (checks.length > 0) {
|
|
460
|
-
if (checks.some((c) => c.conclusion === 'FAILURE'))
|
|
461
|
-
rollup = 'FAILURE';
|
|
462
|
-
else if (checks.some((c) => c.status !== 'COMPLETED'))
|
|
463
|
-
rollup = 'PENDING';
|
|
464
|
-
else
|
|
465
|
-
rollup = 'SUCCESS';
|
|
466
|
-
}
|
|
467
|
-
const unresolvedReviewThreadsCount = (raw.reviewThreads ?? []).reduce((acc, t) => acc + (t.isResolved ? 0 : 1), 0);
|
|
468
|
-
return {
|
|
469
|
-
number: raw.number,
|
|
470
|
-
title: raw.title,
|
|
471
|
-
url: raw.url,
|
|
472
|
-
state: raw.state,
|
|
473
|
-
base: raw.baseRefName ?? '',
|
|
474
|
-
reviewDecision: raw.reviewDecision ?? null,
|
|
475
|
-
author: { login: raw.author?.login ?? '' },
|
|
476
|
-
assignees: (raw.assignees ?? []).map((a) => ({ login: a.login })),
|
|
477
|
-
reviewers,
|
|
478
|
-
labels: (raw.labels ?? []).map((l) => ({ name: l.name, color: l.color })),
|
|
479
|
-
ci: { rollup, checks },
|
|
480
|
-
updatedAt: raw.updatedAt ?? '',
|
|
481
|
-
unresolvedReviewThreadsCount,
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
/** Get a rich snapshot of the PR for a branch. Returns null if no PR exists. */
|
|
485
|
-
export function getPrStatus(repoPath, branchName) {
|
|
486
|
-
try {
|
|
487
|
-
const raw = execFileSync('gh', ['pr', 'view', branchName, '--json', GH_PR_FIELDS], {
|
|
488
|
-
cwd: repoPath,
|
|
489
|
-
encoding: 'utf-8',
|
|
490
|
-
}).trim();
|
|
491
|
-
if (!raw)
|
|
492
|
-
return null;
|
|
493
|
-
return mapGhPrToSnapshot(JSON.parse(raw));
|
|
494
|
-
}
|
|
495
|
-
catch {
|
|
496
|
-
return null;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
503
|
/**
|
|
500
504
|
* Rename a branch in-place (`git branch -m <old> <new>`). Must be run inside
|
|
501
505
|
* the worktree (or any directory tracking the repo) — the new name replaces
|
|
@@ -759,6 +763,10 @@ export function getFileContent(repoPath, filePath) {
|
|
|
759
763
|
return null;
|
|
760
764
|
}
|
|
761
765
|
}
|
|
766
|
+
/** Write content to an absolute path inside a worktree. Caller validates the path. */
|
|
767
|
+
export function writeFileInWorktree(absPath, content) {
|
|
768
|
+
writeFileSync(absPath, content, 'utf-8');
|
|
769
|
+
}
|
|
762
770
|
/** Parse `git status --porcelain` into counts of staged, modified, and untracked files. */
|
|
763
771
|
export function getWorkingTreeStatus(repoPath) {
|
|
764
772
|
try {
|
|
@@ -846,36 +854,7 @@ export function getWorkingTreeDiffStats(repoPath) {
|
|
|
846
854
|
}
|
|
847
855
|
}
|
|
848
856
|
// ── Async versions ───────────────────────────────────────────────────────────
|
|
849
|
-
// Non-blocking alternatives for hot paths (
|
|
850
|
-
/** Async version of getPrUrl. Returns null if no PR exists. */
|
|
851
|
-
export async function getPrUrlAsync(repoPath, branchName) {
|
|
852
|
-
try {
|
|
853
|
-
const { stdout } = await execFileAsync('gh', ['pr', 'view', branchName, '--json', 'url', '--jq', '.url'], {
|
|
854
|
-
cwd: repoPath,
|
|
855
|
-
encoding: 'utf-8',
|
|
856
|
-
});
|
|
857
|
-
return stdout.trim() || null;
|
|
858
|
-
}
|
|
859
|
-
catch {
|
|
860
|
-
return null;
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
/** Async version of getPrStatus. Returns null if no PR exists. */
|
|
864
|
-
export async function getPrStatusAsync(repoPath, branchName) {
|
|
865
|
-
try {
|
|
866
|
-
const { stdout } = await execFileAsync('gh', ['pr', 'view', branchName, '--json', GH_PR_FIELDS], {
|
|
867
|
-
cwd: repoPath,
|
|
868
|
-
encoding: 'utf-8',
|
|
869
|
-
});
|
|
870
|
-
const raw = stdout.trim();
|
|
871
|
-
if (!raw)
|
|
872
|
-
return null;
|
|
873
|
-
return mapGhPrToSnapshot(JSON.parse(raw));
|
|
874
|
-
}
|
|
875
|
-
catch {
|
|
876
|
-
return null;
|
|
877
|
-
}
|
|
878
|
-
}
|
|
857
|
+
// Non-blocking alternatives for hot paths (route handlers).
|
|
879
858
|
/**
|
|
880
859
|
* Async version of `getUnpushedCount`. Same `origin/<workingBranch>` semantic:
|
|
881
860
|
* returns `-1` when the remote ref does not exist (never pushed), `0` when
|
|
@@ -917,3 +896,11 @@ export async function fetchSourceBranchAsync(repoPath, branch, remote = 'origin'
|
|
|
917
896
|
console.warn(`[git-ops] fetchSourceBranchAsync(${remote}/${branch}) failed: ${msg}`);
|
|
918
897
|
}
|
|
919
898
|
}
|
|
899
|
+
/** Stash all changes (including untracked). */
|
|
900
|
+
export function stashPush(repoPath, label) {
|
|
901
|
+
git(repoPath, ['stash', 'push', '--include-untracked', '-m', label]);
|
|
902
|
+
}
|
|
903
|
+
/** Pop the most recent stash entry. */
|
|
904
|
+
export function stashPop(repoPath) {
|
|
905
|
+
git(repoPath, ['stash', 'pop']);
|
|
906
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.17",
|
|
4
4
|
"description": "Kōbō — multi-workspace agent manager for Claude Code. Orchestrates isolated git worktrees with dev servers, Notion integration, and MCP tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{D as e,G as t,I as n,N as r,P as i,R as a,St as o,W as s,_ as c,at as l,d as u,et as d,f,g as p,l as m,p as h,r as g,u as _,xt as v,y,yt as b}from"./runtime-core.esm-bundler-D_RRiKBh.js";import{U as x,l as S,t as C}from"./QIcon-BmEX2rXO.js";import{c as w,s as T}from"./notifications-Hq-6rEYv.js";import{t as E}from"./QBtn-CoU-UC_j.js";import{n as D}from"./vue-i18n-Cq-KgjJC.js";import{_ as O,c as k,m as A,u as j}from"./index-DMUFfCIq.js";import{t as M}from"./QSpinnerDots-DspFKwCZ.js";import{t as N}from"./QTooltip-BC7PnZJ1.js";import{t as ee}from"./QExpansionItem-CgJQdznK.js";import{t as te}from"./QScrollArea-_Ji1cgqL.js";import{i as ne,n as re,t as P}from"./render-chat-markdown-BywKNkXe.js";import{t as F}from"./documents-B3nitIYF.js";import{t as I}from"./_plugin-vue_export-helper-Cj6tcsj6.js";function ie(e,t,n=!0){let r=[],i=new Map,a=new Map;for(let n=0;n<e.length;n++){let o=e[n],s=t?.[n];switch(o.kind){case`message:text`:{let e=i.get(o.messageId);if(e)e.text+=o.text,e.streaming=o.streaming;else{let e={type:`text`,messageId:o.messageId,text:o.text,streaming:o.streaming,ts:s};i.set(o.messageId,e),r.push(e)}break}case`message:end`:{let e=i.get(o.messageId);e&&(e.streaming=!1);break}case`message:thinking`:r.push({type:`thinking`,messageId:o.messageId,text:o.text,ts:s});break;case`tool:call`:{let e={type:`tool`,toolCallId:o.toolCallId,name:o.name,input:o.input,ts:s};a.set(o.toolCallId,e),r.push(e);break}case`tool:result`:{let e=a.get(o.toolCallId);e&&(e.result={output:o.output,isError:o.isError});break}case`session:started`:r.push({type:`session`,kind:`started`,detail:{engineSessionId:o.engineSessionId,model:o.model},ts:s});break;case`session:ended`:r.push({type:`session`,kind:`ended`,detail:{reason:o.reason,exitCode:o.exitCode},ts:s});break;case`session:compacted`:r.push({type:`session`,kind:`compacted`,ts:s});break;case`session:brainstorm-complete`:case`session:user-input-requested`:case`message:raw`:case`skills:discovered`:case`usage`:case`rate_limit`:case`subagent:progress`:case`error`:break;default:}}let o=null;for(let e of r)e.type===`text`&&e.streaming&&(o&&(o.streaming=!1),o=e);return o&&!n&&(o.streaming=!1),r}function ae(e,t){if(t.length===0)return e;let n=t.map(e=>({type:`user`,content:e.content,sender:e.sender,ts:e.ts})),r=[...e,...n];r.sort((e,t)=>{let n=e.ts??``,r=t.ts??``;return n===r?0:n?r?n<r?-1:1:-1:1});let i;for(let e of r)e.type===`user`&&e.sender!==`system-prompt`&&e.ts&&(!i||e.ts>i)&&(i=e.ts);if(i)for(let e of r)e.type===`text`&&e.streaming&&(!e.ts||e.ts<i)&&(e.streaming=!1);return r}var L=new Set([`setup`,`cleanup`,`archive`]);function R(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:L.has(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function oe(e){let t=[],n=null,r=null;for(let i of e){let e=R(i),a=e===`session`||e===`system-prompt`,o=e===`script`&&i.type===`user`?`script:${i.sender}`:e;!n||r!==o||a?(n={speaker:e,ts:i.ts,items:[i]},r=o,t.push(n),a&&(n=null)):n.items.push(i)}return t}var z={class:`text-caption text-grey-6`},B=y({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,r=m(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return`session.ended`;case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(n(),h(`span`,z,o(e.$t(r.value)),1))}});function se(e,t){if(t.length===0||e.length===0)return e;let n=[...t].sort((e,t)=>t.length-e.length),r=new DOMParser().parseFromString(`<div>${e}</div>`,`text/html`),i=r.body.firstChild;if(!i)return e;function a(e){if(e.nodeType===Node.TEXT_NODE){V(e,n,r);return}if(e.nodeName===`A`)return;let t=Array.from(e.childNodes);for(let e of t)a(e)}return a(i),i.innerHTML}function V(e,t,n){let r=e.textContent??``;if(!t.some(e=>r.includes(e)))return;let i=n.createDocumentFragment(),a=0;for(;a<r.length;){let e=H(r,a,t);if(!e){i.appendChild(n.createTextNode(r.slice(a)));break}e.index>a&&i.appendChild(n.createTextNode(r.slice(a,e.index)));let o=n.createElement(`a`);o.className=`document-link`,o.setAttribute(`data-document-path`,e.path),o.setAttribute(`href`,`#`),o.textContent=e.path,i.appendChild(o),a=e.index+e.path.length}e.parentNode?.replaceChild(i,e)}function H(e,t,n){let r=null;for(let i of n){let n=e.indexOf(i,t);n<0||(!r||n<r.index||n===r.index&&i.length>r.path.length)&&(r={index:n,path:i})}return r}var U=[`innerHTML`],ce=I(y({__name:`TextMessageItem`,props:{item:{}},setup(e){let t=e,r=F(),i=k(),a=m(()=>{let e=i.selectedWorkspaceId;return e?r.documentsFor(e).map(e=>e.path):[]}),o=m(()=>re(se(ne.parse(t.item.text,{async:!1,breaks:!0,gfm:!0}),a.value),{addAttr:[`data-document-path`]}));function s(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let n=t.getAttribute(`data-document-path`),a=i.selectedWorkspaceId;!n||!a||r.openDocumentByPath(a,n)}return(t,r)=>(n(),h(`div`,{class:`markdown-message`,onClick:s},[_(`div`,{innerHTML:o.value},null,8,U),e.item.streaming?(n(),u(S,{key:0,size:`xs`,class:`q-ml-xs`})):f(``,!0)]))}}),[[`__scopeId`,`data-v-1b7bd8ca`]]),W={key:0,class:`text-caption text-grey-5`,style:{"font-style":`italic`}},G=[`innerHTML`],K={key:1,style:{"white-space":`pre-wrap`}},le=I(y({__name:`ThinkingItem`,props:{item:{}},setup(e){let r=e,i=m(()=>r.item.text.trim().slice(0,100)),a=m(()=>r.item.text.trim().length>0),s=m(()=>r.item.text.trim().length>100),c=m(()=>P(r.item.text));return(r,l)=>a.value?(n(),h(`div`,W,[s.value?(n(),u(ee,{key:0,dense:``,"dense-toggle":``,label:i.value,"header-class":`text-grey-5 text-caption`,style:{"font-style":`italic`}},{default:t(()=>[_(`div`,{class:`q-py-xs markdown-thinking`,innerHTML:c.value},null,8,G)]),_:1},8,[`label`])):(n(),h(`span`,K,o(e.item.text),1))])):f(``,!0)}}),[[`__scopeId`,`data-v-7f45ed94`]]);function ue(e,t){let n=e.split(`
|
|
2
|
+
`),r=t.split(`
|
|
3
|
+
`),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array(a+1).fill(0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)n[e]===r[t]?o[e][t]=o[e+1][t+1]+1:o[e][t]=Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push({type:`context`,content:n[c]}),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push({type:`del`,content:n[c]}),c++):(s.push({type:`add`,content:r[l]}),l++);for(;c<i;)s.push({type:`del`,content:n[c++]});for(;l<a;)s.push({type:`add`,content:r[l++]});return s}function de(e){let t=e.split(`
|
|
4
|
+
`),n=[];for(let e of t)e.startsWith(`@@`)||e.startsWith(`+++`)||e.startsWith(`---`)||(e.startsWith(`+`)?n.push({type:`add`,content:e.slice(1)}):e.startsWith(`-`)?n.push({type:`del`,content:e.slice(1)}):e.startsWith(` `)?n.push({type:`context`,content:e.slice(1)}):e.length>0&&n.push({type:`context`,content:e}));return n}function fe(e,t){if(!t||typeof t!=`object`)return null;let n=t;if(e===`Edit`){let e=n.file_path;if(!e)return null;let t=n.old_string??``,r=n.new_string??``,i=typeof n.diff==`string`?n.diff:``;if(!t&&!r&&i.length>0){let t=de(i);return{toolName:`Edit`,filePath:e,additions:t.filter(e=>e.type===`add`).length,deletions:t.filter(e=>e.type===`del`).length,diffLines:t}}return{toolName:`Edit`,filePath:e,oldString:t,newString:r,replaceAll:n.replace_all??!1,additions:r?r.split(`
|
|
5
|
+
`).length:0,deletions:t?t.split(`
|
|
6
|
+
`).length:0}}if(e===`Write`){let e=n.file_path;if(!e)return null;let t=n.content??``;return{toolName:`Write`,filePath:e,content:t,additions:t?t.split(`
|
|
7
|
+
`).length:0,deletions:0}}if(e===`Bash`){let e=(n.command??``).match(/^\s*rm\s+(?:-[a-zA-Z]*\s+)*(.+)/);if(e)return{toolName:`Bash:rm`,filePath:e[1].trim().replace(/["']/g,``),additions:0,deletions:1}}return null}function q(e,t){if(!e||!t?.projectPath)return e;let n=t.worktreePath;if(n){let t=J(e,n);if(t!==e)return t}let r=J(e,`${t.projectPath}/${w}/${t.workingBranch}`);return r===e?J(e,t.projectPath):r}function J(e,t){if(!t)return e;let n=pe(t);return n?e.replace(RegExp(`${n}[\\\\/]+`,`g`),``).replace(RegExp(`${n}(?=\\s|$|["'\`])`,`g`),`.`):e}function pe(e){let t=e.replace(/[\\/]+$/,``);return t?`${/^[\\/]+/.test(t)?`[\\\\/]+`:``}${t.split(/[\\/]+/).filter(Boolean).map(me).join(`[\\\\/]+`)}`:``}function me(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}var Y={class:`tool-name`},X=[`title`],he={key:0,class:`tool-stat-add`},ge={key:1,class:`tool-stat-del`},Z={class:`diff-sign`},Q={class:`tool-name`},$=[`title`],_e=I(y({__name:`ToolCallItem`,props:{item:{}},setup(e){let t=e,r=d(!1),i=k(),l=m(()=>fe(t.item.name,t.item.input)),v=m(()=>l.value?q(l.value.filePath,i.selectedWorkspace):``),y={Bash:`terminal`,Read:`description`,Edit:`edit`,Write:`edit_note`,MultiEdit:`edit`,Glob:`folder_open`,Grep:`manage_search`,LS:`list`,Skill:`auto_awesome`,Task:`hub`,Agent:`hub`,TodoWrite:`checklist`,TodoRead:`checklist`,ToolSearch:`search`,WebFetch:`public`,WebSearch:`travel_explore`,NotebookRead:`book`,NotebookEdit:`edit_note`,SendMessage:`send`,ExitPlanMode:`check_circle_outline`,KillShell:`stop_circle`,BashOutput:`terminal`},S=m(()=>y[t.item.name]??`build`),w=m(()=>{if(l.value)return``;let e=t.item.input,n=T(e);return n?q(n,i.selectedWorkspace):``});function T(e){if(!e||typeof e!=`object`)return typeof e==`string`?e:``;let t=e;for(let e of[`file_path`,`path`,`command`,`pattern`,`query`,`url`,`skill`,`description`,`subject`,`prompt`]){let n=t[e];if(typeof n==`string`&&n.length>0)return n}for(let e of Object.values(t))if(typeof e==`string`&&e.length>0)return e;return``}let E=m(()=>{let e=l.value;return e?e.diffLines?e.diffLines:e.toolName===`Edit`&&e.oldString!==void 0&&e.newString!==void 0?ue(e.oldString,e.newString):e.toolName===`Write`&&e.content!==void 0?e.content.split(`
|
|
8
|
+
`).map(e=>({type:`add`,content:e})):e.toolName===`Bash:rm`?[{type:`del`,content:`File deleted`}]:null:null}),D=m(()=>{let e=t.item.result;if(!e)return``;if(typeof e.output==`string`)return e.output;try{return JSON.stringify(e.output)}catch{return String(e.output)}}),O=new Set([`Read`]),A=m(()=>!!(t.item.result&&D.value)&&(!O.has(t.item.name)||t.item.result?.isError===!0));function j(){r.value=!r.value}return s(()=>t.item.result?.isError===!0,e=>{e&&(r.value=!0)},{immediate:!0}),(t,i)=>l.value?(n(),h(`div`,{key:0,class:b([`tool-row`,{"tool-row-expanded":r.value}])},[_(`div`,{class:`tool-header`,onClick:j},[c(C,{name:S.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,Y,o(l.value.toolName===`Bash:rm`?`Bash`:l.value.toolName),1),_(`span`,{class:`tool-path`,title:l.value.filePath},o(v.value),9,X),l.value.additions>0?(n(),h(`span`,he,`+`+o(l.value.additions),1)):f(``,!0),l.value.deletions>0?(n(),h(`span`,ge,`-`+o(l.value.deletions),1)):f(``,!0),e.item.result?.isError?(n(),u(C,{key:2,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-xs`})):e.item.result?(n(),u(C,{key:3,name:`check`,color:`positive`,size:`xs`,class:`q-ml-xs`})):f(``,!0),c(C,{name:r.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-auto text-grey-6`},null,8,[`name`])]),r.value&&E.value?(n(),h(`div`,{key:0,class:`tool-diff`,onClick:i[0]||=x(()=>{},[`stop`])},[(n(!0),h(g,null,a(E.value,(e,t)=>(n(),h(`div`,{key:t,class:b([`diff-line`,{"diff-del":e.type===`del`,"diff-add":e.type===`add`,"diff-context":e.type===`context`}])},[_(`span`,Z,o(e.type===`del`?`-`:e.type===`add`?`+`:` `),1),p(o(e.content),1)],2))),128))])):f(``,!0)],2)):(n(),h(`div`,{key:1,class:b([`tool-row tool-row-generic`,{"tool-row-expanded":r.value,"tool-row--toggleable":A.value}])},[_(`div`,{class:`tool-header`,onClick:i[1]||=e=>A.value&&j()},[c(C,{name:S.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,Q,o(e.item.name),1),w.value?(n(),h(`span`,{key:0,class:`tool-arg`,title:T(e.item.input)||w.value},o(w.value),9,$)):f(``,!0),e.item.result?.isError?(n(),u(C,{key:1,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-auto`})):e.item.result?(n(),u(C,{key:2,name:`check`,color:`positive`,size:`xs`,class:`q-ml-auto`})):f(``,!0),A.value?(n(),u(C,{key:3,name:r.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-xs text-grey-6`},null,8,[`name`])):f(``,!0)]),r.value&&A.value?(n(),h(`div`,{key:0,class:`tool-output`,onClick:i[2]||=x(()=>{},[`stop`])},o(D.value),1)):f(``,!0)],2))}}),[[`__scopeId`,`data-v-1702f1be`]]);function ve(e,t){return t?e.replace(/\[image:\s+([^\]]+)\]/g,(e,n)=>{let r=String(n).trim();return/^(\.ai\/images\/|images\/)/.test(r)?`}/images/file?path=${encodeURIComponent(r)}`})`:e}):e}var ye=[`innerHTML`],be=[`innerHTML`],xe=[`src`],Se=I(y({__name:`UserMessageItem`,props:{item:{}},setup(e){let r=e,i=k(),a=m(()=>r.item.sender===`system-prompt`),o=m(()=>P(ve(r.item.content,i.selectedWorkspaceId??``))),s=d(null),l=d(!1);function p(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(s.value=n.src,l.value=!0)}return(e,r)=>(n(),h(g,null,[a.value?(n(),u(ee,{key:0,dense:``,"dense-toggle":``,label:e.$t(`chat.systemPrompt`),"header-class":`text-grey-5 text-caption`},{default:t(()=>[_(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:o.value},null,8,ye)]),_:1},8,[`label`])):(n(),h(`div`,{key:1,class:`markdown-message`,onClick:p},[_(`div`,{innerHTML:o.value},null,8,be)])),c(O,{modelValue:l.value,"onUpdate:modelValue":r[1]||=e=>l.value=e},{default:t(()=>[s.value?(n(),h(`img`,{key:0,src:s.value,alt:``,class:`image-lightbox-img`,onClick:r[0]||=e=>l.value=!1},null,8,xe)):f(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-f34be4c5`]]),Ce={class:`turn-header`},we={key:0,class:`turn-time`},Te={class:`turn-time turn-time-updated`},Ee={key:2,class:`turn-actions`},De={class:`turn-body`},Oe={key:0,class:`turn-scroll-top`},ke=I(y({__name:`TurnCard`,props:{turn:{}},emits:[`scrollTo`],setup(e,{emit:r}){let i=e,s=r,{t:y}=D(),x=d(null);function S(){let e=x.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;s(`scrollTo`,Math.max(0,n-8))}let w=m(()=>{switch(i.turn.speaker){case`user`:return{label:y(`chat.you`),accent:`#ce93d8`,badgeClass:`turn-badge-user`};case`agent`:return{label:y(`chat.agent`),accent:`#7986cb`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:y(`chat.systemPrompt`),accent:`#757575`,badgeClass:`turn-badge-system`};case`session`:return{label:y(`chat.session`),accent:`#616161`,badgeClass:`turn-badge-session`};case`script`:{let e=i.turn.items[0],t=e?.type===`user`?e.sender:``;return{label:y(t===`archive`?`chat.archiveScript`:t===`setup`?`chat.setupScript`:`chat.cleanupScript`),accent:`#4db6ac`,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 O=m(()=>T(i.turn.ts)),k=m(()=>{let e=i.turn.items;if(e.length===0)return null;for(let t=e.length-1;t>=0;t--){let n=e[t].ts;if(n)return n}return null}),A=m(()=>{let e=i.turn.ts,t=k.value;if(!t||!e||t===e)return``;let n=new Date(e).getTime(),r=new Date(t).getTime();return Number.isNaN(n)||Number.isNaN(r)||r<=n?``:T(t,r-n<6e4)}),j=m(()=>A.value!==``),M=m(()=>i.turn.items.filter(e=>e.type===`tool`).length);return(r,i)=>(n(),h(`div`,{ref_key:`cardEl`,ref:x,class:b([`turn-card`,{"turn-card--user":e.turn.speaker===`user`}]),style:v({"--turn-accent":w.value.accent})},[_(`div`,Ce,[_(`span`,{class:b([`turn-badge`,w.value.badgeClass])},o(w.value.label),3),O.value?(n(),h(`span`,we,o(O.value),1)):f(``,!0),j.value?(n(),h(g,{key:1},[c(C,{name:`arrow_forward`,size:`10px`,color:`grey-7`,class:`turn-time-arrow`}),_(`span`,Te,[p(o(A.value)+` `,1),c(N,null,{default:t(()=>[p(o(l(y)(`chat.lastUpdatedAt`,{time:A.value})),1)]),_:1})])],64)):f(``,!0),M.value>0?(n(),h(`span`,Ee,` · `+o(l(y)(`chat.nActions`,{n:M.value})),1)):f(``,!0)]),_(`div`,De,[(n(!0),h(g,null,a(e.turn.items,(e,t)=>(n(),h(g,{key:t},[e.type===`text`?(n(),u(ce,{key:0,item:e},null,8,[`item`])):e.type===`thinking`?(n(),u(le,{key:1,item:e},null,8,[`item`])):e.type===`tool`?(n(),u(_e,{key:2,item:e},null,8,[`item`])):e.type===`user`?(n(),u(Se,{key:3,item:e},null,8,[`item`])):e.type===`session`?(n(),u(B,{key:4,item:e},null,8,[`item`])):f(``,!0)],64))),128))]),e.turn.items.length>4?(n(),h(`div`,Oe,[c(E,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`grey-6`,class:`turn-scroll-top-btn`,onClick:S},{default:t(()=>[c(N,null,{default:t(()=>[p(o(l(y)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):f(``,!0)],6))}}),[[`__scopeId`,`data-v-ce5d132b`]]),Ae={key:0,class:`activity-feed-switching`},je={key:1,class:`activity-feed-wrap`},Me={key:0,class:`text-center q-py-sm text-caption text-grey-6`},Ne={class:`q-pa-md`},Pe={key:1,class:`q-px-md q-pb-md`},Fe={class:`activity-feed-nav-cluster`},Ie=60,Le=200,Re=200,ze=400,Be=200,Ve=180,He=I(y({__name:`ActivityFeed`,props:{workspaceId:{}},setup(l){let v=l,y=A(),b=T(),x=k(),C=m(()=>x.selectedSessionId),w=m(()=>x.sessions.find(e=>e.id===C.value)?.engineSessionId??null),D=m(()=>{let e=x.sessions;return e.length===0?!1:C.value===e[e.length-1].id});function O(e){return C.value?e?e===C.value||e===w.value:D.value:!0}let N=m(()=>(x.activityFeeds[v.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&O(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,ts:e.timestamp,sessionId:e.sessionId}))),ne=m(()=>j(x.workspaces.find(e=>e.id===v.workspaceId)?.status)),re=m(()=>{let e=y.eventsFor(v.workspaceId),t=y.timestampsFor(v.workspaceId),n=y.sessionIdsFor(v.workspaceId),r=[],i=[];for(let a=0;a<e.length;a++)O(n[a])&&(r.push(e[a]),i.push(t[a]));let a=ae(ie(r,i,ne.value),N.value);return oe(b.showVerboseSystemMessages?a:a.filter(e=>e.type!==`session`))}),P=m(()=>b.showVerboseSystemMessages?y.eventsFor(v.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),F=d(null),I=d(!0),L=d(!1),R=!1,z=d(!0),B=d(new Map);function se(e){I.value=e.verticalSize-e.verticalPosition-e.verticalContainerSize<=Ie,R&&e.verticalPosition<=Le&&!L.value&&H()&&W()}function V(e,t){return`${e}:${t}`}function H(){let e=C.value;return e?B.value.get(V(v.workspaceId,e))??!0:y.hasMoreOlderFor(v.workspaceId)}function U(e,t,n){B.value.set(V(e,t),n)}function ce(e){if(!C.value)return y.oldestIdFor(e);let t=y.eventIdsFor(e),n=y.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!O(n[e]))continue;let r=t[e];if(r)return r}}async function W(){let t=v.workspaceId,n=C.value,r=ce(t);if(!r)return;L.value=!0;let i=Date.now();try{let i=F.value;await e();let a=i?.getScroll().verticalSize??0,o=i?.getScroll().verticalPosition??0,s=new URLSearchParams({before:r,limit:`200`});n&&s.set(`session`,n);let c=fetch(`/api/workspaces/${t}/events?${s.toString()}`),l=new Promise(e=>setTimeout(e,Re)),[u]=await Promise.all([c,l]);if(!u.ok){n?U(t,n,!1):y.prepend(t,[],[],{oldestId:r,hasMoreOlder:!1});return}let d=await u.json(),f=d.events??[],p=f.filter(e=>e.type===`agent:event`&&e.workspaceId===t),m=f.filter(e=>e.type===`user:message`&&e.workspaceId===t),h=p.map(e=>e.payload),g=p.map(e=>e.createdAt),_=p.map(e=>e.sessionId??null),v=p.map(e=>e.id),b=f.length>0?f[0].id:r;n&&U(t,n,d.hasMore),y.prepend(t,h,g,{oldestId:b,hasMoreOlder:n?y.hasMoreOlderFor(t):d.hasMore,sessionIds:_,eventIds:v});for(let e of m){let n=e.payload;typeof n.content==`string`&&x.addActivityItem(t,{id:e.id,type:`text`,content:n.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:n.sender??`user`}})}if(await e(),i){let e=i.getScroll().verticalSize-a;if(e>0){let t=Math.max(o+e,Le+50);i.setScrollPosition(`vertical`,t,0)}}}catch(e){console.error(`[ActivityFeed] failed to load older events:`,e)}finally{let e=Date.now()-i,t=Math.max(0,Re-e);await new Promise(e=>setTimeout(e,t+ze)),L.value=!1}}async function G(t=0){await e();let n=F.value;if(!n)return;let r=n.getScroll();n.setScrollPosition(`vertical`,r.verticalSize,t)}let K=null,le=0;function ue(){if(K!=null)return;let e=performance.now()-le<Ve;K=requestAnimationFrame(()=>{K=null,le=performance.now(),G(e?0:180)})}function de(e){let t=F.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}let fe=d([]),q=d(null);function J(){let e=re.value,t=fe.value,n=[];if(t.length===e.length){for(let r=0;r<e.length;r++){if(e[r].speaker!==`user`)continue;let i=t[r]?.$el;i&&n.push(i)}if(n.length>0)return n}let r=q.value?.parentElement;if(r){let e=r.querySelectorAll(`.turn-card--user`);for(let t of e)n.push(t)}return n}function pe(){let e=F.value;if(!e)return null;let t=q.value;if(!t)return null;let n=e.getScroll().verticalPosition,r=t.getBoundingClientRect().top,i=null;for(let e of J()){let t=e.getBoundingClientRect().top-r;if(t<n-40)i=t;else break}return i}async function me(){let t=F.value;if(!t)return;let n=pe();if(n===null)for(let t=0;t<15&&H();t++){for(;L.value;)await new Promise(e=>setTimeout(e,50));if(await W(),await e(),n=pe(),n!==null)break}n!==null&&t.setScrollPosition(`vertical`,Math.max(0,n-12),250)}async function Y(){R=!1,await e(),await G(0),requestAnimationFrame(()=>{requestAnimationFrame(()=>{R=!0})})}let X=m(()=>{let e=y.sessionIdsFor(v.workspaceId);if(!C.value)return e.length;let t=0;for(let n of e)O(n)&&t++;return t}),he=m(()=>y.eventsFor(v.workspaceId).length);async function ge(){z.value=!0;let e=Date.now();await new Promise(e=>setTimeout(e,Be));let t=e+5e3;for(;he.value===0&&Date.now()<t;)await new Promise(e=>setTimeout(e,50));z.value=!1}s(z,async e=>{!e&&X.value>0&&await Y(),!e&&X.value===0&&C.value&&$()}),r(()=>{ge(),X.value>0&&Y(),C.value&&$()});let Z=!1;s(X,async(e,t)=>{if(!Z&&e>0){Z=!0,await Y();return}e>t&&I.value&&!L.value&&ue()}),i(()=>{K!=null&&(cancelAnimationFrame(K),K=null)}),s(()=>v.workspaceId,()=>{I.value=!0,Z=!1,R=!1,ge(),X.value>0&&Y()}),s(()=>x.selectedSessionId,async()=>{I.value=!0,R=!1,await Y(),$()});let Q=new Set;async function $(){let t=C.value;if(!t||X.value>0)return;let n=V(v.workspaceId,t);if(!Q.has(n)){Q.add(n);try{let n=await fetch(`/api/workspaces/${v.workspaceId}/events?session=${encodeURIComponent(t)}&limit=500`);if(!n.ok)return;let r=await n.json(),i=r.events??[];if(i.length===0)return;let a=i.filter(e=>e.type===`agent:event`&&e.workspaceId===v.workspaceId),o=i.filter(e=>e.type===`user:message`&&e.workspaceId===v.workspaceId),s=a.map(e=>e.payload),c=a.map(e=>e.createdAt),l=a.map(e=>e.sessionId??null),u=a.map(e=>e.id);U(v.workspaceId,t,r.hasMore),s.length>0&&y.prepend(v.workspaceId,s,c,{oldestId:i[0].id,hasMoreOlder:y.hasMoreOlderFor(v.workspaceId),sessionIds:l,eventIds:u});for(let e of o){let t=e.payload;typeof t.content==`string`&&x.addActivityItem(v.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await e(),await G(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Q.delete(n)}}}s(m(()=>N.value.filter(e=>e.sender!==`system-prompt`).length),async(e,t)=>{e>t&&(I.value=!0,await G(180))});async function _e(){I.value=!0,await G(250)}return(e,r)=>z.value?(n(),h(`div`,Ae,[c(M,{size:`40px`,color:`indigo-4`})])):(n(),h(`div`,je,[c(te,{ref_key:`scrollRef`,ref:F,class:`activity-feed-scroll`,onScroll:se},{default:t(()=>[_(`div`,{ref_key:`contentOriginRef`,ref:q,class:`content-origin-marker`},null,512),L.value?(n(),h(`div`,Me,[c(S,{size:`sm`}),p(` `+o(e.$t(`activity.loading_older`)),1)])):f(``,!0),_(`div`,Ne,[(n(!0),h(g,null,a(re.value,(e,t)=>(n(),u(ke,{key:t,ref_for:!0,ref_key:`turnRefs`,ref:fe,turn:e,onScrollTo:de},null,8,[`turn`]))),128))]),P.value.length?(n(),h(`div`,Pe,[c(ee,{label:e.$t(`activity.raw_lines`,{n:P.value.length}),dense:``},{default:t(()=>[(n(!0),h(g,null,a(P.value,(e,t)=>(n(),h(`div`,{key:t,class:`text-caption text-grey q-pa-xs`},o(e),1))),128))]),_:1},8,[`label`])])):f(``,!0)]),_:1},512),_(`div`,Fe,[I.value?f(``,!0):(n(),u(E,{key:0,round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.scroll_to_bottom`),onClick:_e},null,8,[`title`])),c(E,{round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.prev_user_message`),onClick:me},null,8,[`title`])])]))}}),[[`__scopeId`,`data-v-c133a623`]]);export{He as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{F as e,b as t}from"./QIcon-BmEX2rXO.js";import{T as n,w as r}from"./notifications-
|
|
1
|
+
import{F as e,b as t}from"./QIcon-BmEX2rXO.js";import{T as n,w as r}from"./notifications-Hq-6rEYv.js";function i(e){if(e===!1)return 0;if(e===!0||e===void 0)return 1;let t=parseInt(e,10);return isNaN(t)?0:t}var a=e({name:`close-popup`,beforeMount(e,{value:a}){let o={depth:i(a),handler(t){o.depth!==0&&setTimeout(()=>{let i=n(e);i!==void 0&&r(i,t,o.depth)})},handlerKey(e){t(e,13)===!0&&o.handler(e)}};e.__qclosepopup=o,e.addEventListener(`click`,o.handler),e.addEventListener(`keyup`,o.handlerKey)},updated(e,{value:t,oldValue:n}){t!==n&&(e.__qclosepopup.depth=i(t))},beforeUnmount(e){let t=e.__qclosepopup;e.removeEventListener(`click`,t.handler),e.removeEventListener(`keyup`,t.handlerKey),delete e.__qclosepopup}});export{a as t};
|