circuschief 0.8.0 → 1.1.0
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/package.json +1 -1
- package/packages/server/src/agents/AgentGateway.js +2 -0
- package/packages/server/src/agents/adapters/GeminiAdapter.js +105 -0
- package/packages/server/src/agents/adapters/cliUtils.js +15 -0
- package/packages/server/src/agents/adapters/codexCliRunner.js +1 -8
- package/packages/server/src/agents/adapters/geminiCliRunner.js +183 -0
- package/packages/server/src/agents/adapters/geminiEventMapper.js +195 -0
- package/packages/server/src/api/commandButtons.js +16 -15
- package/packages/server/src/api/projects-commandButtons.js +6 -6
- package/packages/server/src/api/projects-session-create.js +109 -0
- package/packages/server/src/api/projects-session-defaults.js +51 -0
- package/packages/server/src/api/projects-session-helpers.js +47 -1
- package/packages/server/src/api/projects-templates.js +38 -0
- package/packages/server/src/api/projects.js +28 -180
- package/packages/server/src/api/sessions-commands.js +21 -18
- package/packages/server/src/api/sessions-patch.js +41 -1
- package/packages/server/src/db/ProviderRepository.js +4 -2
- package/packages/server/src/db/SessionRepository.js +1 -1
- package/packages/server/src/db/SessionTemplateRepository.js +23 -2
- package/packages/server/src/db/migrations/canvasItemsMigrations.js +109 -0
- package/packages/server/src/db/migrations/conversationsMigrations.js +187 -0
- package/packages/server/src/db/migrations/index.js +234 -6
- package/packages/server/src/db/migrations/kanbanMigrations.js +99 -0
- package/packages/server/src/db/migrations/miscMigrations.js +244 -0
- package/packages/server/src/db/migrations/projectsMigrations.js +130 -0
- package/packages/server/src/db/migrations/providerCommitAttributionMigrations.js +30 -0
- package/packages/server/src/db/migrations/providerMigrations.js +250 -0
- package/packages/server/src/db/migrations/sessionTableRecreate.js +136 -0
- package/packages/server/src/db/migrations/sessionsMigrations.js +300 -0
- package/packages/server/src/db/seedBaselineData.js +23 -1
- package/packages/server/src/db/session-helpers.js +26 -1
- package/packages/server/src/schema.sql +5 -1
- package/packages/server/src/services/commandButtonPrompts.js +9 -7
- package/packages/server/src/services/e2eSpawnCapture.js +47 -6
- package/packages/server/src/services/geminiSpawnHelper.js +47 -0
- package/packages/server/src/services/gitCommitAttribution.js +38 -8
- package/packages/server/src/services/gitDiff.js +107 -0
- package/packages/server/src/services/gitRepoUrl.js +174 -0
- package/packages/server/src/services/gitService.js +43 -311
- package/packages/server/src/services/gitWorktree.js +127 -0
- package/packages/server/src/services/providerTestService.js +59 -1
- package/packages/server/src/services/queryParamBuilder.js +33 -1
- package/packages/server/src/services/sessionExecution.js +4 -0
- package/packages/server/src/services/sessionPrompts.js +23 -1
- package/packages/server/src/services/sessionProvider.js +41 -1
- package/packages/shared/src/constants.js +1 -1
- package/packages/shared/src/contracts/providers.js +1 -1
- package/packages/shared/src/contracts/sessions.js +27 -1
- package/packages/shared/src/contracts/templates.js +10 -0
- package/packages/shared/src/types.js +7 -0
- package/packages/web/dist/assets/{ActiveSessionsView-B0XHqLmv.js → ActiveSessionsView-EdNxmPmZ.js} +1 -1
- package/packages/web/dist/assets/{AgentLogsView-DmsjUMlB.js → AgentLogsView-C2wX0JPP.js} +2 -2
- package/packages/web/dist/assets/ApiClient-DfbJwzpz.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-DJERn5XO.js +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-CBPI8-US.js +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-D9zjx9ME.css +1 -0
- package/packages/web/dist/assets/EffortLevelSelector-PaBpUveC.js +1 -0
- package/packages/web/dist/assets/{GeneralSettingsView-D1nI8_zk.js → GeneralSettingsView-Dw-x83R0.js} +1 -1
- package/packages/web/dist/assets/{InputWithButton-CAkttyqx.js → InputWithButton-CHHcpF4I.js} +1 -1
- package/packages/web/dist/assets/{InterpolationHelp-BO1j9Z3_.js → InterpolationHelp-CLNPz8s8.js} +1 -1
- package/packages/web/dist/assets/MarkdownEditor-DYi1igfT.js +2 -0
- package/packages/web/dist/assets/ModelSelector-Cko_yTO5.js +1 -0
- package/packages/web/dist/assets/{ModelSelector-BSxKUSus.css → ModelSelector-Dtwe5xLH.css} +1 -1
- package/packages/web/dist/assets/{NewSessionView-BDPb-1qr.css → NewSessionView-DBl7T2Xp.css} +1 -1
- package/packages/web/dist/assets/NewSessionView-DwUfBg70.js +3 -0
- package/packages/web/dist/assets/ProjectEditView-CSbsea3U.js +1 -0
- package/packages/web/dist/assets/ProjectEditView-DbqTbA0q.css +1 -0
- package/packages/web/dist/assets/{ProjectListView-DcNyuINs.js → ProjectListView-CEc_LWZL.js} +1 -1
- package/packages/web/dist/assets/{ProjectNewView-B5YV62hv.js → ProjectNewView-D4U0uRlp.js} +1 -1
- package/packages/web/dist/assets/ProvidersView-2KCOiY6Q.css +1 -0
- package/packages/web/dist/assets/ProvidersView-CD1j8BOv.js +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-Dp39f12o.js +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-dk-Rj8xx.css +1 -0
- package/packages/web/dist/assets/ResizableTextarea-BWywIqOv.js +1 -0
- package/packages/web/dist/assets/ResizableTextarea-DERSH3Wz.css +1 -0
- package/packages/web/dist/assets/SessionCard-B6d5ijDW.js +1 -0
- package/packages/web/dist/assets/SessionDetailView-DWbXdx7A.js +36 -0
- package/packages/web/dist/assets/SessionDetailView-ULeIkWS0.css +1 -0
- package/packages/web/dist/assets/{SessionFormOptions-B6AxyREh.js → SessionFormOptions-Dz9ik4Fo.js} +1 -1
- package/packages/web/dist/assets/{SessionListView-B5_6gW49.css → SessionListView-3-xx6EVs.css} +1 -1
- package/packages/web/dist/assets/SessionListView-C129buBe.js +1 -0
- package/packages/web/dist/assets/{SessionLogStream-LlZ3z_Xj.js → SessionLogStream-BvXUNNBZ.js} +6 -6
- package/packages/web/dist/assets/{SettingsView-CTGiGvR2.js → SettingsView-DW1NvpX_.js} +1 -1
- package/packages/web/dist/assets/SlashCommandWizard-DleYBxrE.js +1 -0
- package/packages/web/dist/assets/{SummarySettingsView-BR2ZjEa3.js → SummarySettingsView-CLUfcWvf.js} +1 -1
- package/packages/web/dist/assets/TemplateDetailView-B5NI2oTR.css +1 -0
- package/packages/web/dist/assets/TemplateDetailView-Cukb205e.js +1 -0
- package/packages/web/dist/assets/{commandButtons-BfqR-fqq.js → commandButtons-DejH0rVN.js} +1 -1
- package/packages/web/dist/assets/index-BD7Y3rBE.js +3 -0
- package/packages/web/dist/assets/{index-BY174HVJ.css → index-Bd20AzX1.css} +1 -1
- package/packages/web/dist/assets/index-BgJiarKe.js +1 -0
- package/packages/web/dist/assets/index-Bk32fSSG.js +1 -0
- package/packages/web/dist/assets/index-BkA6pF2Z.js +1 -0
- package/packages/web/dist/assets/index-Cltr-Ldt.js +7 -0
- package/packages/web/dist/assets/index-Co-46Tp3.js +1 -0
- package/packages/web/dist/assets/index-Cpykk857.js +1 -0
- package/packages/web/dist/assets/index-CtABl0D1.js +1 -0
- package/packages/web/dist/assets/index-Cuqk5m9S.js +1 -0
- package/packages/web/dist/assets/{index-fK8FIZgP.js → index-CvXApbVC.js} +15 -15
- package/packages/web/dist/assets/index-D2gN-xEH.js +1 -0
- package/packages/web/dist/assets/index-Dd3WpmyQ.js +1 -0
- package/packages/web/dist/assets/index-Dk6--9rj.js +1 -0
- package/packages/web/dist/assets/{index-DgkC10TW.js → index-MZf7MlPX.js} +3 -3
- package/packages/web/dist/assets/{index-DtfUt785.js → index-NShCcwfj.js} +1 -1
- package/packages/web/dist/assets/index-hA3VEuSq.js +1 -0
- package/packages/web/dist/assets/index-p0mp3nca.js +1 -0
- package/packages/web/dist/assets/index-qntNa5r_.js +1 -0
- package/packages/web/dist/assets/index-qq9ceNSK.js +1 -0
- package/packages/web/dist/assets/projectDefaults-D9xkp2XR.js +1 -0
- package/packages/web/dist/assets/{projects-DXYQNJIi.js → projects-BvLADGKx.js} +1 -1
- package/packages/web/dist/assets/{providers-1bnH-exJ.js → providers-DZ-fOa4G.js} +1 -1
- package/packages/web/dist/assets/{sessions-6zGUlFrt.js → sessions-DETEyjPI.js} +1 -1
- package/packages/web/dist/assets/{settings-MbfRir0d.js → settings-TWfbahn5.js} +1 -1
- package/packages/web/dist/index.html +2 -2
- package/packages/web/dist/assets/ApiClient-C3ztI9s9.js +0 -1
- package/packages/web/dist/assets/ArchiveConfirmModal-BlCyn5Vt.js +0 -1
- package/packages/web/dist/assets/CommandButtonDetailView-CdSCPp78.js +0 -1
- package/packages/web/dist/assets/CommandButtonDetailView-DBm3rzhw.css +0 -1
- package/packages/web/dist/assets/EffortLevelSelector-hc2MNKg6.js +0 -1
- package/packages/web/dist/assets/MarkdownEditor-ucRAP_UM.js +0 -2
- package/packages/web/dist/assets/ModelSelector-CwTz8ZWO.js +0 -1
- package/packages/web/dist/assets/NewSessionView-BsDrp8mj.js +0 -3
- package/packages/web/dist/assets/ProjectEditView-CwTOeSun.js +0 -1
- package/packages/web/dist/assets/ProjectEditView-J15mcsWz.css +0 -1
- package/packages/web/dist/assets/ProvidersView-bZemq_Rv.css +0 -1
- package/packages/web/dist/assets/ProvidersView-nY9GnDdO.js +0 -1
- package/packages/web/dist/assets/QuickResponseSettings-B352c75l.css +0 -1
- package/packages/web/dist/assets/QuickResponseSettings-BQwQXuL7.js +0 -1
- package/packages/web/dist/assets/QuickResponsesPanel-BlFDvnZ2.css +0 -1
- package/packages/web/dist/assets/QuickResponsesPanel-BzSYcCSP.js +0 -1
- package/packages/web/dist/assets/ResizableTextarea-B3YIdIXv.js +0 -1
- package/packages/web/dist/assets/ResizableTextarea-DsU3TVwF.css +0 -1
- package/packages/web/dist/assets/SessionCard-CjE1tXiT.js +0 -1
- package/packages/web/dist/assets/SessionDetailView-3cPZrbS3.js +0 -36
- package/packages/web/dist/assets/SessionDetailView-CZRZMrfM.css +0 -1
- package/packages/web/dist/assets/SessionListView-CLXBfLcq.js +0 -1
- package/packages/web/dist/assets/SlashCommandWizard-Cy04d7-o.js +0 -1
- package/packages/web/dist/assets/TemplateDetailView-DH6Oswsp.js +0 -1
- package/packages/web/dist/assets/TemplateDetailView-DT2m06W7.css +0 -1
- package/packages/web/dist/assets/index-1zziPL6l.js +0 -1
- package/packages/web/dist/assets/index-7kzHPxSF.js +0 -1
- package/packages/web/dist/assets/index-B0N_obMc.js +0 -1
- package/packages/web/dist/assets/index-BNk_gdfI.js +0 -1
- package/packages/web/dist/assets/index-CSqaAH-0.js +0 -1
- package/packages/web/dist/assets/index-C_q4WlK8.js +0 -1
- package/packages/web/dist/assets/index-D1wpU4y0.js +0 -7
- package/packages/web/dist/assets/index-D5zCA8sD.js +0 -1
- package/packages/web/dist/assets/index-DGR8ELWY.js +0 -1
- package/packages/web/dist/assets/index-DHga8pXo.js +0 -1
- package/packages/web/dist/assets/index-DSby02Wl.js +0 -1
- package/packages/web/dist/assets/index-DqjXJTVI.js +0 -1
- package/packages/web/dist/assets/index-_4S2uLDI.js +0 -1
- package/packages/web/dist/assets/index-gmiZeFXN.js +0 -1
- package/packages/web/dist/assets/index-irD539ZM.js +0 -3
- package/packages/web/dist/assets/index-yq-E1Y00.js +0 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { git, getOriginDefaultBranch } from './gitService.js';
|
|
2
|
+
|
|
3
|
+
// Configurable logger for warning messages
|
|
4
|
+
let logger = {
|
|
5
|
+
warn: (...args) => console.warn(...args),
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Set a custom logger for git worktree warnings.
|
|
10
|
+
* @param {Object} customLogger - Logger object with a warn method
|
|
11
|
+
*/
|
|
12
|
+
export function _setWorktreeLogger(customLogger) {
|
|
13
|
+
logger = customLogger;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Safely fetch from origin remote.
|
|
18
|
+
* Logs a warning if fetch fails but does not throw.
|
|
19
|
+
* @param {string} directory - The git repository directory
|
|
20
|
+
* @returns {Promise<boolean>} - True if fetch succeeded, false otherwise
|
|
21
|
+
*/
|
|
22
|
+
async function safeFetchOrigin(directory) {
|
|
23
|
+
try {
|
|
24
|
+
await git(directory, 'fetch origin');
|
|
25
|
+
return true;
|
|
26
|
+
} catch (err) {
|
|
27
|
+
// No origin or network unavailable, proceed without fetch
|
|
28
|
+
logger.warn('Could not fetch from origin, proceeding with local refs:', err.message);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check if a branch exists
|
|
35
|
+
* @param {string} directory
|
|
36
|
+
* @param {string} branch
|
|
37
|
+
* @returns {Promise<boolean>}
|
|
38
|
+
*/
|
|
39
|
+
export async function branchExists(directory, branch) {
|
|
40
|
+
try {
|
|
41
|
+
await git(directory, `rev-parse --verify refs/heads/${branch}`);
|
|
42
|
+
return true;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Checkout a branch, creating it if it doesn't exist
|
|
50
|
+
* @param {string} directory
|
|
51
|
+
* @param {string} branch
|
|
52
|
+
* @returns {Promise<void>}
|
|
53
|
+
*/
|
|
54
|
+
export async function checkoutBranch(directory, branch) {
|
|
55
|
+
const exists = await branchExists(directory, branch);
|
|
56
|
+
if (exists) {
|
|
57
|
+
await git(directory, `checkout "${branch}"`);
|
|
58
|
+
} else {
|
|
59
|
+
await git(directory, `checkout -b "${branch}"`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a new worktree
|
|
65
|
+
* @param {string} directory
|
|
66
|
+
* @param {string} branch
|
|
67
|
+
* @param {string} worktreePath
|
|
68
|
+
* @param {Object} options
|
|
69
|
+
* @param {boolean} options.skipFetch - Skip fetching from origin (default: false)
|
|
70
|
+
* @returns {Promise<{path: string, branch: string}>}
|
|
71
|
+
*/
|
|
72
|
+
export async function createWorktree(directory, branch, worktreePath, options = {}) {
|
|
73
|
+
const { skipFetch = false } = options;
|
|
74
|
+
|
|
75
|
+
// Fetch latest from origin to ensure we have up-to-date default branch
|
|
76
|
+
if (!skipFetch) {
|
|
77
|
+
await safeFetchOrigin(directory);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Get the default branch from origin (main or master)
|
|
81
|
+
const defaultBranch = await getOriginDefaultBranch(directory);
|
|
82
|
+
// Base new branch on origin's default branch to avoid including unrelated commits from HEAD
|
|
83
|
+
// Use --no-track to prevent the new branch from tracking the start-point (main/master)
|
|
84
|
+
await git(directory, `worktree add --no-track "${worktreePath}" -b "${branch}" ${defaultBranch}`);
|
|
85
|
+
return { path: worktreePath, branch };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Remove a worktree
|
|
90
|
+
* @param {string} directory
|
|
91
|
+
* @param {string} path
|
|
92
|
+
* @param {boolean} force - Force removal even if worktree has uncommitted changes
|
|
93
|
+
*/
|
|
94
|
+
export async function removeWorktree(directory, worktreePath, force = false) {
|
|
95
|
+
const forceFlag = force ? '--force' : '';
|
|
96
|
+
await git(directory, `worktree remove ${forceFlag} "${worktreePath}"`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Create a worktree for a branch (creates branch if it doesn't exist)
|
|
101
|
+
* @param {string} directory - Main repo directory
|
|
102
|
+
* @param {string} branch - Branch name
|
|
103
|
+
* @param {string} worktreePath - Path for the new worktree
|
|
104
|
+
* @param {Object} options
|
|
105
|
+
* @param {boolean} options.skipFetch - Skip fetching from origin (default: false)
|
|
106
|
+
* @returns {Promise<{path: string, branch: string}>}
|
|
107
|
+
*/
|
|
108
|
+
export async function createWorktreeForBranch(directory, branch, worktreePath, options = {}) {
|
|
109
|
+
const { skipFetch = false } = options;
|
|
110
|
+
|
|
111
|
+
// Fetch latest from origin to ensure we have up-to-date default branch
|
|
112
|
+
if (!skipFetch) {
|
|
113
|
+
await safeFetchOrigin(directory);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const exists = await branchExists(directory, branch);
|
|
117
|
+
if (exists) {
|
|
118
|
+
await git(directory, `worktree add "${worktreePath}" "${branch}"`);
|
|
119
|
+
} else {
|
|
120
|
+
// Get the default branch from origin (main or master)
|
|
121
|
+
const defaultBranch = await getOriginDefaultBranch(directory);
|
|
122
|
+
// Base new branch on origin's default branch to avoid including unrelated commits from HEAD
|
|
123
|
+
// Use --no-track to prevent the new branch from tracking the start-point (main/master)
|
|
124
|
+
await git(directory, `worktree add --no-track -b "${branch}" "${worktreePath}" ${defaultBranch}`);
|
|
125
|
+
}
|
|
126
|
+
return { path: worktreePath, branch };
|
|
127
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
2
|
import OpenAI from 'openai';
|
|
3
|
+
import { createGeminiSpawner } from './geminiSpawnHelper.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Test a provider configuration by making a minimal API call.
|
|
@@ -22,11 +23,14 @@ import OpenAI from 'openai';
|
|
|
22
23
|
* @param {number} [config.apiTimeoutMs] - API timeout in milliseconds
|
|
23
24
|
* @returns {Promise<{success: boolean, message: string, details?: Object}>}
|
|
24
25
|
*/
|
|
25
|
-
export async function testProviderConnection(config) {
|
|
26
|
+
export async function testProviderConnection(config, deps = {}) {
|
|
26
27
|
const { kind = 'anthropic' } = config || {};
|
|
27
28
|
if (kind === 'openai') {
|
|
28
29
|
return testOpenAIConnection(config);
|
|
29
30
|
}
|
|
31
|
+
if (kind === 'google') {
|
|
32
|
+
return testGoogleConnection(config, deps);
|
|
33
|
+
}
|
|
30
34
|
return testAnthropicConnection(config);
|
|
31
35
|
}
|
|
32
36
|
|
|
@@ -127,6 +131,60 @@ async function testOpenAIChatEndpoint(client, config) {
|
|
|
127
131
|
});
|
|
128
132
|
}
|
|
129
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Google/Gemini connection test. Spawns `gemini -p "Hi" --output-format json`
|
|
136
|
+
* and checks for a clean exit. No SDK dependency needed.
|
|
137
|
+
* @private
|
|
138
|
+
*/
|
|
139
|
+
async function testGoogleConnection(config, deps = {}) {
|
|
140
|
+
try {
|
|
141
|
+
const env = {};
|
|
142
|
+
if (config.authToken) env.GEMINI_API_KEY = config.authToken;
|
|
143
|
+
const timeoutMs = config.apiTimeoutMs || 30000;
|
|
144
|
+
const spawnGeminiProcess = deps.spawnGeminiProcess || createGeminiSpawner();
|
|
145
|
+
const child = spawnGeminiProcess({
|
|
146
|
+
command: 'gemini',
|
|
147
|
+
args: ['-p', 'Hi', '--output-format', 'json', '--skip-trust', '--approval-mode=auto_edit', '-m', 'gemini-2.5-flash'],
|
|
148
|
+
cwd: config.workingDirectory,
|
|
149
|
+
env,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return await new Promise((resolve) => {
|
|
153
|
+
let stderr = '';
|
|
154
|
+
let killed = false;
|
|
155
|
+
|
|
156
|
+
const timer = setTimeout(() => {
|
|
157
|
+
killed = true;
|
|
158
|
+
try { child.kill('SIGTERM'); } catch { /* ignore */ }
|
|
159
|
+
resolve(failureResponse(new Error(`Gemini CLI timed out after ${timeoutMs}ms`)));
|
|
160
|
+
}, timeoutMs);
|
|
161
|
+
|
|
162
|
+
child.stdout?.on('data', () => { /* drain */ });
|
|
163
|
+
child.stderr?.on('data', (d) => { stderr += d; });
|
|
164
|
+
child.on('error', (error) => {
|
|
165
|
+
clearTimeout(timer);
|
|
166
|
+
if (killed) return;
|
|
167
|
+
if (error.code === 'ENOENT') {
|
|
168
|
+
resolve(failureResponse(new Error('Gemini CLI not found. Install via: npm install -g @google/gemini-cli')));
|
|
169
|
+
} else {
|
|
170
|
+
resolve(failureResponse(error));
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
child.on('exit', (code) => {
|
|
174
|
+
clearTimeout(timer);
|
|
175
|
+
if (killed) return;
|
|
176
|
+
if (code === 0) {
|
|
177
|
+
resolve(connectionSuccess({ model: 'gemini-2.5-flash' }));
|
|
178
|
+
} else {
|
|
179
|
+
resolve(failureResponse(new Error(stderr.trim() || `Gemini CLI exited with code ${code}`)));
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
return failureResponse(error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
130
188
|
function connectionSuccess(details) {
|
|
131
189
|
return {
|
|
132
190
|
success: true,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createClaudeCodeSpawner } from './nodeSpawnHelper.js';
|
|
2
2
|
import {
|
|
3
3
|
buildSystemPromptConfig,
|
|
4
|
+
getGeminiApprovalModeForSession,
|
|
4
5
|
getPermissionModeForSession,
|
|
5
6
|
getSandboxModeForSession,
|
|
6
7
|
} from './sessionPrompts.js';
|
|
@@ -64,6 +65,34 @@ function buildCodexQueryParams({
|
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Build query parameters for the Gemini adapter.
|
|
70
|
+
*
|
|
71
|
+
* Gemini CLI is prompt-driven via `-p` flag. It does not need Claude-specific
|
|
72
|
+
* options (permissionMode, settingSources, resume) or Codex-specific options
|
|
73
|
+
* (sandboxMode, effortLevel).
|
|
74
|
+
*
|
|
75
|
+
* @returns {Object}
|
|
76
|
+
*/
|
|
77
|
+
function buildGeminiQueryParams({
|
|
78
|
+
prompt, workingDirectory, controller, session, sessionId, systemPrompt, model, sessionEnv,
|
|
79
|
+
}) {
|
|
80
|
+
const isVCR = Boolean(process.env.VCR_MODE);
|
|
81
|
+
const effectiveModel = isVCR ? 'gemini-2.5-flash' : model;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
prompt,
|
|
85
|
+
options: {
|
|
86
|
+
cwd: workingDirectory,
|
|
87
|
+
abortController: controller,
|
|
88
|
+
env: sessionEnv,
|
|
89
|
+
model: effectiveModel,
|
|
90
|
+
approvalMode: getGeminiApprovalModeForSession(session?.mode),
|
|
91
|
+
systemPrompt: buildSystemPromptConfig(sessionId, session.projectId, systemPrompt, session.mode),
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
67
96
|
/**
|
|
68
97
|
* Build query parameters for executing a session via the configured agent.
|
|
69
98
|
* Shared by runSession, continueSession, and continueSessionWithExistingMessage.
|
|
@@ -78,7 +107,7 @@ function buildCodexQueryParams({
|
|
|
78
107
|
* @param {string|null} options.model - Model to use
|
|
79
108
|
* @param {Object} options.sessionEnv - Environment variables for the session
|
|
80
109
|
* @param {string|null} [options.resumeSessionId] - Session ID to resume (null for new session)
|
|
81
|
-
* @param {string} [options.agentType] - 'claude-code' (default) | 'codex'
|
|
110
|
+
* @param {string} [options.agentType] - 'claude-code' (default) | 'codex' | 'gemini'
|
|
82
111
|
* @returns {Object} Query parameters for agent.execute()
|
|
83
112
|
*/
|
|
84
113
|
export function buildQueryParams(options) {
|
|
@@ -86,5 +115,8 @@ export function buildQueryParams(options) {
|
|
|
86
115
|
if (agentType === 'codex') {
|
|
87
116
|
return buildCodexQueryParams(options);
|
|
88
117
|
}
|
|
118
|
+
if (agentType === 'gemini') {
|
|
119
|
+
return buildGeminiQueryParams(options);
|
|
120
|
+
}
|
|
89
121
|
return buildClaudeCodeQueryParams(options);
|
|
90
122
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { sessions, messages, attachments, conversations } from '../database.js';
|
|
2
2
|
import { createCodexSpawner } from './codexSpawnHelper.js';
|
|
3
|
+
import { createGeminiSpawner } from './geminiSpawnHelper.js';
|
|
3
4
|
import { resolveProviderFromModel, resolveProviderMetadataFromModel, buildSessionEnv } from './sessionProvider.js';
|
|
4
5
|
import { agentGateway } from '../agents/AgentGateway.js';
|
|
5
6
|
import { LoggingAgentWrapper } from '../agents/LoggingAgentWrapper.js';
|
|
@@ -37,6 +38,9 @@ function buildAgentConfig(agentType) {
|
|
|
37
38
|
if (agentType === 'codex') {
|
|
38
39
|
return { spawnCodexProcess: createCodexSpawner() };
|
|
39
40
|
}
|
|
41
|
+
if (agentType === 'gemini') {
|
|
42
|
+
return { spawnGeminiProcess: createGeminiSpawner() };
|
|
43
|
+
}
|
|
40
44
|
return {};
|
|
41
45
|
}
|
|
42
46
|
|
|
@@ -134,6 +134,28 @@ export function getSandboxModeForSession(mode) {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Map session mode to Gemini CLI --approval-mode.
|
|
139
|
+
*
|
|
140
|
+
* plan -> plan
|
|
141
|
+
* standard -> auto_edit
|
|
142
|
+
* yolo -> yolo
|
|
143
|
+
*
|
|
144
|
+
* @param {string} mode - Session mode ('plan', 'standard', 'yolo')
|
|
145
|
+
* @returns {string} Gemini CLI approval mode
|
|
146
|
+
*/
|
|
147
|
+
export function getGeminiApprovalModeForSession(mode) {
|
|
148
|
+
switch (mode) {
|
|
149
|
+
case 'plan':
|
|
150
|
+
return 'plan';
|
|
151
|
+
case 'yolo':
|
|
152
|
+
return 'yolo';
|
|
153
|
+
case 'standard':
|
|
154
|
+
default:
|
|
155
|
+
return 'auto_edit';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
137
159
|
/** Plan mode system prompt instructions */
|
|
138
160
|
export const PLAN_MODE_PROMPT = `## Plan Mode Active
|
|
139
161
|
|
|
@@ -222,7 +244,7 @@ curl -X POST ${apiUrl}/api/projects/${projectId}/sessions \\
|
|
|
222
244
|
-d '{"prompt": "Your task description here"}'
|
|
223
245
|
\`\`\`
|
|
224
246
|
Only \`prompt\` is required. Omitted settings are automatically derived from the project's session defaults, then system defaults, matching UI behavior.
|
|
225
|
-
Optional override fields: \`name\`, \`mode\`, \`thinkingEnabled\` (boolean), \`effortLevel\` (low/medium/high/max/auto), \`model\`, \`providerId\`, \`gitBranch\`, \`gitMode\`, \`templateId\`, \`nextTemplateId\`, \`parentSessionId\` (to create a related follow-up session from the current session), \`startImmediately\`, \`scheduledAt
|
|
247
|
+
Optional override fields: \`name\`, \`mode\`, \`thinkingEnabled\` (boolean), \`effortLevel\` (low/medium/high/max/auto), \`model\`, \`providerId\`, \`gitBranch\`, \`gitMode\`, \`templateId\`, \`nextTemplateId\`, \`parentSessionId\` (to create a related follow-up session from the current session), \`startImmediately\`, \`scheduledAt\` (ISO 8601 date-time string with timezone, e.g. \`"2026-06-12T14:00:00Z"\`), \`autoRescheduleEnabled\`, \`rescheduleDelayMinutes\`, \`rescheduleOnTokenLimit\`, \`rescheduleOnServiceError\`, \`maxRescheduleCount\`, \`maxTotalTokens\`, and \`rescheduleAtTokenCount\`.
|
|
226
248
|
|
|
227
249
|
### Send a Follow-up Message
|
|
228
250
|
\`\`\`bash
|
|
@@ -41,6 +41,7 @@ export function resolveAgentTypeFromModel(modelId) {
|
|
|
41
41
|
// Fallback for test doubles that don't implement getAgentTypeForProvider:
|
|
42
42
|
// derive from kind directly.
|
|
43
43
|
if (provider.kind === 'openai') return 'codex';
|
|
44
|
+
if (provider.kind === 'google') return 'gemini';
|
|
44
45
|
return 'claude-code';
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -62,7 +63,9 @@ export function buildProviderEnv(provider) {
|
|
|
62
63
|
const kind = provider.kind || 'anthropic';
|
|
63
64
|
const env = kind === 'openai'
|
|
64
65
|
? buildOpenAIProviderEnv(provider)
|
|
65
|
-
:
|
|
66
|
+
: kind === 'google'
|
|
67
|
+
? buildGoogleProviderEnv(provider)
|
|
68
|
+
: buildAnthropicProviderEnv(provider);
|
|
66
69
|
|
|
67
70
|
if (provider.apiTimeoutMs) {
|
|
68
71
|
env.API_TIMEOUT_MS = String(provider.apiTimeoutMs);
|
|
@@ -78,6 +81,12 @@ export function buildProviderEnv(provider) {
|
|
|
78
81
|
return env;
|
|
79
82
|
}
|
|
80
83
|
|
|
84
|
+
function buildGoogleProviderEnv(provider) {
|
|
85
|
+
const env = {};
|
|
86
|
+
if (provider.authToken) env.GEMINI_API_KEY = provider.authToken;
|
|
87
|
+
return env;
|
|
88
|
+
}
|
|
89
|
+
|
|
81
90
|
function buildOpenAIProviderEnv(provider) {
|
|
82
91
|
const env = {};
|
|
83
92
|
if (provider.baseUrl) env.OPENAI_BASE_URL = provider.baseUrl;
|
|
@@ -120,6 +129,14 @@ function logProviderEnv(provider, kind, env) {
|
|
|
120
129
|
return;
|
|
121
130
|
}
|
|
122
131
|
|
|
132
|
+
if (kind === 'google') {
|
|
133
|
+
console.log(`[SessionManager] buildProviderEnv: Provider "${provider.name}" (google) env vars:`, {
|
|
134
|
+
GEMINI_API_KEY: env.GEMINI_API_KEY ? '[SET]' : '[NOT SET]',
|
|
135
|
+
API_TIMEOUT_MS: env.API_TIMEOUT_MS,
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
123
140
|
console.log(`[SessionManager] buildProviderEnv: Provider "${provider.name}" (anthropic) env vars:`, {
|
|
124
141
|
ANTHROPIC_BASE_URL: env.ANTHROPIC_BASE_URL,
|
|
125
142
|
ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY ? '[SET]' : '[NOT SET]',
|
|
@@ -164,8 +181,11 @@ export function buildSessionEnv(provider, thinkingEnabled = false, effortLevel =
|
|
|
164
181
|
stripProviderRuntimeEnv(sessionEnv);
|
|
165
182
|
} else if (kind === 'openai') {
|
|
166
183
|
applyOpenAISessionEnv(sessionEnv, providerEnv);
|
|
184
|
+
} else if (kind === 'google') {
|
|
185
|
+
applyGoogleSessionEnv(sessionEnv, providerEnv);
|
|
167
186
|
} else {
|
|
168
187
|
stripOpenAIHostEnv(sessionEnv);
|
|
188
|
+
stripGoogleHostEnv(sessionEnv);
|
|
169
189
|
}
|
|
170
190
|
|
|
171
191
|
// Claude-only session env vars. Only set for Anthropic-kind providers
|
|
@@ -194,10 +214,22 @@ function stripProviderRuntimeEnv(env) {
|
|
|
194
214
|
delete target.ANTHROPIC_BASE_URL;
|
|
195
215
|
delete target.OPENAI_API_KEY;
|
|
196
216
|
delete target.OPENAI_BASE_URL;
|
|
217
|
+
delete target.GEMINI_API_KEY;
|
|
218
|
+
delete target.GOOGLE_CLOUD_PROJECT;
|
|
219
|
+
delete target.GOOGLE_CLOUD_LOCATION;
|
|
220
|
+
delete target.GOOGLE_GENAI_USE_VERTEXAI;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function applyGoogleSessionEnv(sessionEnv, providerEnv) {
|
|
224
|
+
stripAnthropicHostEnv(sessionEnv);
|
|
225
|
+
stripOpenAIHostEnv(sessionEnv);
|
|
226
|
+
// Apply provider-specific env vars
|
|
227
|
+
Object.assign(sessionEnv, providerEnv);
|
|
197
228
|
}
|
|
198
229
|
|
|
199
230
|
function applyOpenAISessionEnv(sessionEnv, providerEnv) {
|
|
200
231
|
stripAnthropicHostEnv(sessionEnv);
|
|
232
|
+
stripGoogleHostEnv(sessionEnv);
|
|
201
233
|
if (!providerEnv.OPENAI_API_KEY) {
|
|
202
234
|
replaceWithCodexCliEnv(sessionEnv, providerEnv);
|
|
203
235
|
return;
|
|
@@ -234,3 +266,11 @@ function stripOpenAIHostEnv(env) {
|
|
|
234
266
|
delete target.OPENAI_API_KEY;
|
|
235
267
|
delete target.OPENAI_BASE_URL;
|
|
236
268
|
}
|
|
269
|
+
|
|
270
|
+
function stripGoogleHostEnv(env) {
|
|
271
|
+
const target = env;
|
|
272
|
+
delete target.GEMINI_API_KEY;
|
|
273
|
+
delete target.GOOGLE_CLOUD_PROJECT;
|
|
274
|
+
delete target.GOOGLE_CLOUD_LOCATION;
|
|
275
|
+
delete target.GOOGLE_GENAI_USE_VERTEXAI;
|
|
276
|
+
}
|
|
@@ -23,7 +23,7 @@ export const TOAST_DURATION = 5000;
|
|
|
23
23
|
/** Default delay (in minutes) before auto-rescheduling a session. */
|
|
24
24
|
export const DEFAULT_RESCHEDULE_DELAY_MINUTES = 60;
|
|
25
25
|
|
|
26
|
-
export const DEFAULT_SYSTEM_PROMPT = `You are
|
|
26
|
+
export const DEFAULT_SYSTEM_PROMPT = `You are an AI coding assistant. You help users with software engineering tasks including writing code, debugging, refactoring, and explaining code. You have full access to the shell and can execute any commands needed to assist the user. Be helpful, accurate, and thorough.
|
|
27
27
|
|
|
28
28
|
IMPORTANT: Your working directory is already set correctly for this session. NEVER use \`cd\` to change to a hardcoded project path before running commands (e.g., \`cd /path/to/project && git status\`). This bypasses git worktree isolation and causes commands to run in the wrong directory. Always run commands directly without changing directory.
|
|
29
29
|
|
|
@@ -64,7 +64,7 @@ const CommitAttributionOverride = z
|
|
|
64
64
|
* Kind is **immutable after create**; switching kind invalidates the provider's
|
|
65
65
|
* registered models.
|
|
66
66
|
*/
|
|
67
|
-
export const ProviderKind = z.enum(['anthropic', 'openai']);
|
|
67
|
+
export const ProviderKind = z.enum(['anthropic', 'openai', 'google']);
|
|
68
68
|
|
|
69
69
|
export const CreateProviderRequest = z.object({
|
|
70
70
|
name: z.string().min(1).max(100),
|
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
const SCHEDULED_AT_FORMAT_MESSAGE = 'scheduledAt must be a valid ISO 8601 date-time string with a timezone';
|
|
4
|
+
const ISO_8601_DATE_TIME_WITH_TIMEZONE = /^(\d{4})-(\d{2})-(\d{2})T([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(?:\.\d+)?(Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;
|
|
5
|
+
|
|
6
|
+
function hasValidDateParts(year, month, day) {
|
|
7
|
+
const parsed = new Date(Date.UTC(year, month - 1, day));
|
|
8
|
+
return parsed.getUTCFullYear() === year
|
|
9
|
+
&& parsed.getUTCMonth() === month - 1
|
|
10
|
+
&& parsed.getUTCDate() === day;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isScheduledAtIsoString(value) {
|
|
14
|
+
const match = ISO_8601_DATE_TIME_WITH_TIMEZONE.exec(value);
|
|
15
|
+
if (!match) return false;
|
|
16
|
+
|
|
17
|
+
const year = Number(match[1]);
|
|
18
|
+
const month = Number(match[2]);
|
|
19
|
+
const day = Number(match[3]);
|
|
20
|
+
if (!hasValidDateParts(year, month, day)) return false;
|
|
21
|
+
|
|
22
|
+
return Number.isFinite(Date.parse(value));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ScheduledAtIsoString = z.string().refine(isScheduledAtIsoString, {
|
|
26
|
+
message: SCHEDULED_AT_FORMAT_MESSAGE,
|
|
27
|
+
});
|
|
28
|
+
|
|
3
29
|
export const CreateSessionRequest = z.object({
|
|
4
30
|
prompt: z.string().min(1),
|
|
5
31
|
name: z.string().optional(),
|
|
@@ -11,7 +37,7 @@ export const CreateSessionRequest = z.object({
|
|
|
11
37
|
templateId: z.string().uuid().optional(), // Template to apply on session creation
|
|
12
38
|
nextTemplateId: z.string().uuid().nullable().optional(),
|
|
13
39
|
// Scheduling fields
|
|
14
|
-
scheduledAt:
|
|
40
|
+
scheduledAt: ScheduledAtIsoString.optional(),
|
|
15
41
|
autoRescheduleEnabled: z.boolean().optional(),
|
|
16
42
|
rescheduleDelayMinutes: z.number().min(5).max(1440).optional(), // 5 min to 24 hours
|
|
17
43
|
rescheduleOnTokenLimit: z.boolean().optional(),
|
|
@@ -11,6 +11,9 @@ export const CreateSessionTemplateRequest = z.object({
|
|
|
11
11
|
mode: z.enum(['plan', 'standard', 'yolo']).nullable().optional(),
|
|
12
12
|
effortLevel: z.enum(['low', 'medium', 'high', 'max', 'auto']).nullable().optional(),
|
|
13
13
|
targetLaneId: z.string().uuid().nullable().optional(), // Lane to place session in when created from this template
|
|
14
|
+
showInQuickResponses: z.boolean().optional(),
|
|
15
|
+
quickResponseAutoSubmit: z.boolean().optional(),
|
|
16
|
+
quickResponseSortOrder: z.number().int().optional(),
|
|
14
17
|
});
|
|
15
18
|
|
|
16
19
|
export const UpdateSessionTemplateRequest = z.object({
|
|
@@ -24,6 +27,9 @@ export const UpdateSessionTemplateRequest = z.object({
|
|
|
24
27
|
mode: z.enum(['plan', 'standard', 'yolo']).nullable().optional(),
|
|
25
28
|
effortLevel: z.enum(['low', 'medium', 'high', 'max', 'auto']).nullable().optional(),
|
|
26
29
|
targetLaneId: z.string().uuid().nullable().optional(),
|
|
30
|
+
showInQuickResponses: z.boolean().optional(),
|
|
31
|
+
quickResponseAutoSubmit: z.boolean().optional(),
|
|
32
|
+
quickResponseSortOrder: z.number().int().optional(),
|
|
27
33
|
});
|
|
28
34
|
|
|
29
35
|
export const SessionTemplateResponse = z.object({
|
|
@@ -39,6 +45,10 @@ export const SessionTemplateResponse = z.object({
|
|
|
39
45
|
mode: z.string().nullable(),
|
|
40
46
|
effortLevel: z.enum(['low', 'medium', 'high', 'max', 'auto']).nullable(),
|
|
41
47
|
targetLaneId: z.string().uuid().nullable(),
|
|
48
|
+
showInQuickResponses: z.boolean(),
|
|
49
|
+
quickResponseAutoSubmit: z.boolean(),
|
|
50
|
+
quickResponseSortOrder: z.number(),
|
|
51
|
+
legacyQuickResponseId: z.string().nullable(),
|
|
42
52
|
createdAt: z.number(),
|
|
43
53
|
updatedAt: z.number(),
|
|
44
54
|
});
|
|
@@ -142,6 +142,13 @@ export const OPENAI_MODELS = [
|
|
|
142
142
|
];
|
|
143
143
|
export const DEFAULT_OPENAI_MODEL = 'gpt-5.5';
|
|
144
144
|
|
|
145
|
+
export const GEMINI_MODELS = [
|
|
146
|
+
{ id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro', description: 'Most capable reasoning model', seedId: 'google-gemini-2-5-pro' },
|
|
147
|
+
{ id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash', description: 'Fast & cost-efficient', seedId: 'google-gemini-2-5-flash' },
|
|
148
|
+
{ id: 'gemini-2.5-flash-lite', name: 'Gemini 2.5 Flash Lite', description: 'Lightweight & cost-efficient', seedId: 'google-gemini-2-5-flash-lite' },
|
|
149
|
+
];
|
|
150
|
+
export const DEFAULT_GEMINI_MODEL = 'gemini-2.5-flash';
|
|
151
|
+
|
|
145
152
|
/**
|
|
146
153
|
* @typedef {Object} KanbanBoard
|
|
147
154
|
* @property {string} id
|
package/packages/web/dist/assets/{ActiveSessionsView-B0XHqLmv.js → ActiveSessionsView-EdNxmPmZ.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as K}from"./sessions-
|
|
1
|
+
import{u as K}from"./sessions-DETEyjPI.js";import{u as Q}from"./commandButtons-DejH0rVN.js";import{E as j,W as c,_ as X,o as Z,G as ee,l as te,a,c as l,b as f,F as C,r as D,J as O,u as S,t as b,d as se,w as re,f as ne,z as A,g as oe,n as ie,C as R,x as ae}from"./index-CvXApbVC.js";import{a as x}from"./ApiClient-DfbJwzpz.js";import{S as le}from"./SessionCard-B6d5ijDW.js";import"./SessionLogStream-BvXUNNBZ.js";function ce(){const{on:E,off:s}=j(),d=h=>p=>{const i=F=>{F.session&&p(F.session,F.projectId)};return E(h,i),()=>s(h,i)},I=d(c.SESSION_CREATED),w=d(c.SESSION_UPDATED);return{onSessionCreated:I,onSessionUpdated:w,onSessionDeleted:h=>{const p=i=>{i.sessionId&&h(i.sessionId,i.projectId)};return E(c.SESSION_DELETED,p),()=>s(c.SESSION_DELETED,p)},onSessionSummaryUpdated:h=>{const p=i=>{i.sessionId&&i.summary&&h(i.sessionId,i.summary,i.projectId)};return E(c.SESSION_SUMMARY_UPDATED,p),()=>s(c.SESSION_SUMMARY_UPDATED,p)}}}const de=["data-state"],ue={class:"filters-container"},fe={class:"status-filters"},Se=["onClick"],pe=["title"],me={key:0,class:"star-icon"},he={key:1,class:"star-icon star-crossed"},_e={key:2,class:"star-icon"},ve={key:0,class:"skeleton-list"},ye={key:1,class:"error-message"},Fe={key:2,class:"empty-state","data-testid":"active-sessions-empty"},Ee={key:3,class:"empty-state","data-testid":"active-sessions-empty"},Ie={key:4,class:"session-list"},we={__name:"ActiveSessionsView",setup(E){const s=K(),d=Q(),I=["waiting","stopped","error"],w=["running","starting"],y=ie(new Set),g=e=>{s.statusFilter===e?s.setStatusFilter(null):s.setStatusFilter(e)},h=()=>{s.starredFilter===null?s.setStarredFilter("starred"):s.starredFilter==="starred"?s.setStarredFilter("unstarred"):s.setStarredFilter(null)},p=A(()=>s.starredFilter==="starred"?"Showing starred sessions only. Click to filter unstarred.":s.starredFilter==="unstarred"?"Showing unstarred sessions only. Click to show all.":"Showing all sessions. Click to filter by starred."),i=A(()=>{let e=s.activeSessions;return s.statusFilter&&(e=e.filter(t=>{const r=t.status;return!!(s.statusFilter==="idle"&&I.includes(r)||s.statusFilter==="running"&&w.includes(r))})),s.starredFilter==="starred"?e=e.filter(t=>t.starred):s.starredFilter==="unstarred"&&(e=e.filter(t=>!t.starred)),e}),F=A(()=>s.loading?"loading":s.error?"error":s.activeSessions.length===0?"empty-all":i.value.length===0?"empty-filtered":"results"),_=R({}),u=R({}),m=R({});let N=null;const v=[],{onSessionCreated:P,onSessionUpdated:B,onSessionDeleted:L,onSessionSummaryUpdated:V}=ce();async function M(){const e=new Set(s.activeSessions.map(t=>t.projectId).filter(t=>t&&!y.value.has(t)));for(const t of e)try{await d.fetchButtons(t),await d.fetchLatestRunsForProject(t),y.value.add(t)}catch(r){console.error(`Failed to fetch buttons for project ${t}:`,r)}}function U(e,t,r){d.runs[e]||(d.runs[e]={runId:e,buttonId:t,sessionId:r,status:"running",output:"",exitCode:null,startedAt:Date.now(),outputTruncated:!1})}function H(e){["running","waiting","starting"].includes(e.status)&&(s.activeSessions.some(r=>r.id===e.id)||(s.activeSessions.unshift(e),e.projectId&&!y.value.has(e.projectId)&&(d.fetchButtons(e.projectId),y.value.add(e.projectId))))}function G(e){const t=["running","waiting","starting"].includes(e.status),r=s.activeSessions.findIndex(n=>n.id===e.id);t?r>=0?s.activeSessions[r]=e:s.activeSessions.unshift(e):r>=0&&(s.activeSessions.splice(r,1),delete _[e.id],delete u[e.id],delete m[e.id])}function W(e){const t=s.activeSessions.findIndex(r=>r.id===e);t>=0&&s.activeSessions.splice(t,1),delete _[e],delete u[e],delete m[e]}function Y(e,t){_[e]=t,u[e]=!1,m[e]=!1}function $(e,t,r){const n=o=>{U(o.runId,o.buttonId,o.sessionId),d.appendOutput(o.runId,o.output)};e(c.COMMAND_RUN_OUTPUT,n),r.push(()=>t(c.COMMAND_RUN_OUTPUT,n));const k=o=>{U(o.runId,o.buttonId,o.sessionId),d.completeRun(o.runId,o.exitCode,o.output)};e(c.COMMAND_RUN_COMPLETE,k),r.push(()=>t(c.COMMAND_RUN_COMPLETE,k));const T=o=>{U(o.runId,o.buttonId,o.sessionId),d.errorRun(o.runId,o.error)};e(c.COMMAND_RUN_ERROR,T),r.push(()=>t(c.COMMAND_RUN_ERROR,T))}Z(async()=>{s.restoreStatusFilter(),s.restoreStarredFilter(),await s.fetchActiveSessions(),await M(),v.push(P(H)),v.push(B(G)),v.push(L(W)),v.push(V(Y));const{on:e,off:t}=j();$(e,t,v),N=setInterval(()=>{s.fetchActiveSessions(!1)},3e4)}),ee(()=>{v.forEach(e=>e()),N&&clearInterval(N)}),te(()=>s.activeSessions,()=>{z(),M()},{immediate:!0});async function z(){const t=s.activeSessions.filter(r=>!_[r.id]&&!u[r.id]).map(r=>r.id);if(t.length!==0){for(const r of t)u[r]=!0,m[r]=!1;try{const r=await x.getSessionSummariesBatch(t);for(const n of t)r[n]&&(_[n]=r[n]),u[n]=!1}catch(r){console.warn("Failed to fetch summaries batch:",r.message);for(const n of t)m[n]=!0,u[n]=!1}}}async function q(e){var t;u[e]=!0,m[e]=!1;try{const r=await x.getSessionSummary(e);r&&(_[e]=r)}catch(r){((t=r.response)==null?void 0:t.status)!==404&&(console.warn(`Failed to fetch summary for session ${e}:`,r.message),m[e]=!0)}finally{u[e]=!1}}async function J(e){m[e]=!1,await q(e)}return(e,t)=>{const r=oe("router-link");return a(),l("div",{class:"container","data-testid":"active-sessions-view","data-state":F.value},[t[3]||(t[3]=f("div",{class:"page-header"},[f("div",null,[f("p",{class:"page-description"}," All active sessions across your projects ")])],-1)),f("div",ue,[f("div",fe,[(a(),l(C,null,D(["running","idle"],n=>f("button",{key:n,class:O(["filter-btn",{active:S(s).statusFilter===n}]),onClick:k=>g(n)},b(n),11,Se)),64)),f("button",{class:O(["filter-btn star-btn",{"star-filter-active":S(s).starredFilter==="starred","star-filter-unstarred":S(s).starredFilter==="unstarred","star-filter-all":S(s).starredFilter===null}]),title:p.value,onClick:h},[S(s).starredFilter==="starred"?(a(),l("span",me,"⭐")):S(s).starredFilter==="unstarred"?(a(),l("span",he,"⭐")):(a(),l("span",_e,"☆"))],10,pe)])]),S(s).loading?(a(),l("div",ve,[(a(),l(C,null,D(3,n=>f("div",{key:n,class:"skeleton card",style:{height:"120px"}})),64))])):S(s).error?(a(),l("div",ye,b(S(s).error),1)):S(s).activeSessions.length===0?(a(),l("div",Fe,[t[1]||(t[1]=f("p",null,"No active sessions. All sessions are completed or there are no sessions yet.",-1)),se(r,{to:"/",class:"btn btn-primary"},{default:re(()=>[...t[0]||(t[0]=[ne(" View Projects ",-1)])]),_:1})])):i.value.length===0?(a(),l("div",Ee,[...t[2]||(t[2]=[f("p",null,"No sessions match the current filter.",-1)])])):(a(),l("div",Ie,[(a(!0),l(C,null,D(i.value,n=>(a(),ae(le,{key:n.id,session:n,"show-project":!0,"show-summary":!0,summary:_[n.id],"summary-loading":u[n.id],"summary-error":m[n.id],onRetrySummary:J},null,8,["session","summary","summary-loading","summary-error"]))),128))]))],8,de)}}},Re=X(we,[["__scopeId","data-v-9a53578e"]]);export{Re as default};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
`)||"No token data"}function b(l){return l==null||l===0?"0":l.toLocaleString()}function v(l){if(l==null)return"—";if(l<1e3)return`${l}ms`;if(l<6e4)return`${(l/1e3).toFixed(1)}s`;const d=Math.floor(l/6e4),C=Math.floor(l%6e4/1e3);return`${d}m ${C}s`}function a(l){const d=Date.now()-l;return d<6e4?"just now":d<36e5?`${Math.floor(d/6e4)}m ago`:d<864e5?`${Math.floor(d/36e5)}h ago`:`${Math.floor(d/864e5)}d ago`}function n(l){try{if(l.metadata)return(typeof l.metadata=="string"?JSON.parse(l.metadata):l.metadata).effortLevel||null}catch{}return null}function P(l){return{auto:"Auto",low:"Low",medium:"Med",high:"High",max:"Max"}[l]||l}return(l,d)=>{const C=W("router-link");return r(),i("div",pt,[s.loading?(r(),i("div",ht,[...d[0]||(d[0]=[t("div",{class:"spinner"},null,-1)])])):_("",!0),t("table",yt,[t("thead",null,[t("tr",null,[(r(),i(k,null,$(e,o=>t("th",{key:o.key,class:A(["th",o.sortable?"sortable":""]),onClick:S=>o.sortable?l.$emit("sort",o.key):null},[F(f(o.label)+" ",1),o.sortable&&s.sortBy===o.key?(r(),i("span",mt,f(s.sortOrder==="ASC"?"↑":"↓"),1)):_("",!0)],10,bt)),64))])]),t("tbody",null,[!s.loading&&s.logs.length===0?(r(),i("tr",kt,[t("td",{colspan:e.length,class:"empty-cell"}," No agent call logs found. ",8,$t)])):_("",!0),(r(!0),i(k,null,$(s.logs,o=>(r(),i("tr",{key:o.id,class:"log-row"},[t("td",Ct,[t("span",{class:A(["status-dot",y(o.status)])},null,2),t("span",_t,f(o.status),1)]),t("td",Tt,f(o.agentType||"—"),1),t("td",At,f(o.callType||"—"),1),t("td",Pt,f(o.model||"—"),1),t("td",Lt,[o.sessionId?(r(),z(C,{key:0,to:`/sessions/${o.sessionId}`,class:"session-link"},{default:
|
|
1
|
+
import{p as q,_ as O,a as r,c as i,b as t,F as k,r as $,t as f,i as _,J as A,z as p,g as W,f as F,x as z,w as J,o as G,G as H,d as w,u as m,n as B}from"./index-CvXApbVC.js";import{a as L}from"./ApiClient-DfbJwzpz.js";const U=q("agentLogs",{state:()=>({logs:[],pagination:{total:0,limit:25,offset:0,hasMore:!1},filters:{agentType:null,callType:null,status:null,model:null,startDate:null,endDate:null,sessionId:null},filterOptions:{agentTypes:[],callTypes:[],statuses:[],models:[]},perPage:25,currentPage:1,sortBy:"started_at",sortOrder:"DESC",loading:!1,error:null}),getters:{totalPages:s=>Math.ceil(s.pagination.total/s.perPage)||0,activeFilters:s=>{const e={};for(const[y,h]of Object.entries(s.filters))h!=null&&(e[y]=h);return e}},actions:{async fetchLogs(){this.loading=!0,this.error=null;try{const s=(this.currentPage-1)*this.perPage,e=await L.getAgentCallLogs({limit:this.perPage,offset:s,...this.activeFilters,sortBy:this.sortBy,sortOrder:this.sortOrder});this.logs=e.logs,this.pagination=e.pagination}catch(s){this.error=s.message}finally{this.loading=!1}},async fetchFilterOptions(){try{const s=await L.getAgentCallFilterOptions();this.filterOptions=s}catch(s){console.error("Failed to fetch filter options:",s)}},setFilter(s,e){this.filters[s]=e||null,this.currentPage=1,this.fetchLogs()},clearFilters(){Object.keys(this.filters).forEach(s=>this.filters[s]=null),this.currentPage=1,this.fetchLogs()},setPage(s){this.currentPage=s,this.fetchLogs()},setPerPage(s){this.perPage=s,this.currentPage=1,this.fetchLogs()},setSort(s,e){this.sortBy=s,this.sortOrder=e,this.currentPage=1,this.fetchLogs()},async clearAllLogs(){this.loading=!0,this.error=null;try{await L.deleteAllAgentCallLogs(),this.logs=[],this.pagination={total:0,limit:this.perPage,offset:0,hasMore:!1},this.currentPage=1,await this.fetchFilterOptions()}catch(s){this.error=s.message}finally{this.loading=!1}}}}),Z={class:"filter-bar"},Q={class:"filter-row"},X={class:"filter-group"},Y=["value"],K={class:"filter-group"},tt=["value"],et={class:"filter-group"},st=["value"],lt=["value"],at={class:"filter-group"},nt=["value"],ot=["value"],rt={class:"filter-group"},it=["value"],ut=["value"],dt={class:"filter-group"},ct=["value"],ft=["value"],gt={__name:"AgentLogFilters",props:{filters:{type:Object,required:!0},filterOptions:{type:Object,required:!0},hasActiveFilters:{type:Boolean,default:!1},confirming:{type:Boolean,default:!1}},emits:["filter-change","date-change","clear-filters","clear-all"],setup(s){const e=s;function y(v){return new Date(v).toISOString().slice(0,10)}const h=p(()=>e.filters.startDate?y(e.filters.startDate):""),b=p(()=>e.filters.endDate?y(e.filters.endDate):"");return(v,a)=>(r(),i("div",Z,[t("div",Q,[t("div",X,[a[8]||(a[8]=t("label",{class:"filter-label"},"From",-1)),t("input",{type:"date",class:"filter-input",value:h.value,onChange:a[0]||(a[0]=n=>v.$emit("date-change","startDate",n))},null,40,Y)]),t("div",K,[a[9]||(a[9]=t("label",{class:"filter-label"},"To",-1)),t("input",{type:"date",class:"filter-input",value:b.value,onChange:a[1]||(a[1]=n=>v.$emit("date-change","endDate",n))},null,40,tt)]),t("div",et,[a[11]||(a[11]=t("label",{class:"filter-label"},"Agent Type",-1)),t("select",{class:"filter-select",value:s.filters.agentType||"",onChange:a[2]||(a[2]=n=>v.$emit("filter-change","agentType",n.target.value))},[a[10]||(a[10]=t("option",{value:""}," All ",-1)),(r(!0),i(k,null,$(s.filterOptions.agentTypes,n=>(r(),i("option",{key:n,value:n},f(n),9,lt))),128))],40,st)]),t("div",at,[a[13]||(a[13]=t("label",{class:"filter-label"},"Call Type",-1)),t("select",{class:"filter-select",value:s.filters.callType||"",onChange:a[3]||(a[3]=n=>v.$emit("filter-change","callType",n.target.value))},[a[12]||(a[12]=t("option",{value:""}," All ",-1)),(r(!0),i(k,null,$(s.filterOptions.callTypes,n=>(r(),i("option",{key:n,value:n},f(n),9,ot))),128))],40,nt)]),t("div",rt,[a[15]||(a[15]=t("label",{class:"filter-label"},"Status",-1)),t("select",{class:"filter-select",value:s.filters.status||"",onChange:a[4]||(a[4]=n=>v.$emit("filter-change","status",n.target.value))},[a[14]||(a[14]=t("option",{value:""}," All ",-1)),(r(!0),i(k,null,$(s.filterOptions.statuses,n=>(r(),i("option",{key:n,value:n},f(n),9,ut))),128))],40,it)]),t("div",dt,[a[17]||(a[17]=t("label",{class:"filter-label"},"Model",-1)),t("select",{class:"filter-select",value:s.filters.model||"",onChange:a[5]||(a[5]=n=>v.$emit("filter-change","model",n.target.value))},[a[16]||(a[16]=t("option",{value:""}," All ",-1)),(r(!0),i(k,null,$(s.filterOptions.models,n=>(r(),i("option",{key:n,value:n},f(n),9,ft))),128))],40,ct)]),s.hasActiveFilters?(r(),i("button",{key:0,class:"btn-clear",onClick:a[6]||(a[6]=n=>v.$emit("clear-filters"))}," Clear Filters ")):_("",!0),t("button",{class:A(["btn-clear-all",{confirming:s.confirming}]),onClick:a[7]||(a[7]=n=>v.$emit("clear-all"))},f(s.confirming?"Confirm Clear All?":"Clear All Logs"),3)])]))}},vt=O(gt,[["__scopeId","data-v-780329d3"]]),pt={class:"table-wrapper"},ht={key:0,class:"loading-overlay"},yt={class:"log-table"},bt=["onClick"],mt={key:0,class:"sort-indicator"},kt={key:0},$t=["colspan"],Ct={class:"td"},_t={class:"status-text"},Tt={class:"td font-mono text-sm"},At={class:"td font-mono text-sm"},Pt={class:"td model-cell"},Lt={class:"td"},Ot={key:1},St={class:"td"},Dt={key:1},Ft=["title"],wt={class:"td text-right"},Bt=["title"],Mt={__name:"AgentLogTable",props:{logs:{type:Array,required:!0},loading:{type:Boolean,default:!1},sortBy:{type:String,default:"started_at"},sortOrder:{type:String,default:"DESC"}},emits:["sort"],setup(s){const e=[{key:"status",label:"Status",sortable:!0},{key:"agent_type",label:"Agent Type",sortable:!0},{key:"call_type",label:"Call Type",sortable:!0},{key:"model",label:"Model",sortable:!0},{key:"session",label:"Session",sortable:!1},{key:"effort",label:"Effort",sortable:!1},{key:"total_tokens",label:"Tokens",sortable:!0},{key:"duration_ms",label:"Duration",sortable:!0},{key:"started_at",label:"Started",sortable:!0}];function y(l){switch(l){case"completed":return"dot-completed";case"error":return"dot-error";case"streaming":case"pending":return"dot-pending";default:return"dot-default"}}function h(l){const d=[];return l.inputTokens&&d.push(`Input: ${b(l.inputTokens)}`),l.outputTokens&&d.push(`Output: ${b(l.outputTokens)}`),l.thinkingTokens&&d.push(`Thinking: ${b(l.thinkingTokens)}`),l.cacheReadTokens&&d.push(`Cache Read: ${b(l.cacheReadTokens)}`),l.cacheWriteTokens&&d.push(`Cache Write: ${b(l.cacheWriteTokens)}`),d.join(`
|
|
2
|
+
`)||"No token data"}function b(l){return l==null||l===0?"0":l.toLocaleString()}function v(l){if(l==null)return"—";if(l<1e3)return`${l}ms`;if(l<6e4)return`${(l/1e3).toFixed(1)}s`;const d=Math.floor(l/6e4),C=Math.floor(l%6e4/1e3);return`${d}m ${C}s`}function a(l){const d=Date.now()-l;return d<6e4?"just now":d<36e5?`${Math.floor(d/6e4)}m ago`:d<864e5?`${Math.floor(d/36e5)}h ago`:`${Math.floor(d/864e5)}d ago`}function n(l){try{if(l.metadata)return(typeof l.metadata=="string"?JSON.parse(l.metadata):l.metadata).effortLevel||null}catch{}return null}function P(l){return{auto:"Auto",low:"Low",medium:"Med",high:"High",max:"Max"}[l]||l}return(l,d)=>{const C=W("router-link");return r(),i("div",pt,[s.loading?(r(),i("div",ht,[...d[0]||(d[0]=[t("div",{class:"spinner"},null,-1)])])):_("",!0),t("table",yt,[t("thead",null,[t("tr",null,[(r(),i(k,null,$(e,o=>t("th",{key:o.key,class:A(["th",o.sortable?"sortable":""]),onClick:S=>o.sortable?l.$emit("sort",o.key):null},[F(f(o.label)+" ",1),o.sortable&&s.sortBy===o.key?(r(),i("span",mt,f(s.sortOrder==="ASC"?"↑":"↓"),1)):_("",!0)],10,bt)),64))])]),t("tbody",null,[!s.loading&&s.logs.length===0?(r(),i("tr",kt,[t("td",{colspan:e.length,class:"empty-cell"}," No agent call logs found. ",8,$t)])):_("",!0),(r(!0),i(k,null,$(s.logs,o=>(r(),i("tr",{key:o.id,class:"log-row"},[t("td",Ct,[t("span",{class:A(["status-dot",y(o.status)])},null,2),t("span",_t,f(o.status),1)]),t("td",Tt,f(o.agentType||"—"),1),t("td",At,f(o.callType||"—"),1),t("td",Pt,f(o.model||"—"),1),t("td",Lt,[o.sessionId?(r(),z(C,{key:0,to:`/sessions/${o.sessionId}`,class:"session-link"},{default:J(()=>[F(f(o.sessionName||o.sessionId),1)]),_:2},1032,["to"])):(r(),i("span",Ot,"—"))]),t("td",St,[n(o)?(r(),i("span",{key:0,class:A(["effort-badge",`effort-${n(o)}`])},f(P(n(o))),3)):(r(),i("span",Dt,"—"))]),t("td",{class:"td text-right",title:h(o)},f(b(o.totalTokens)),9,Ft),t("td",wt,f(v(o.durationMs)),1),t("td",{class:"td text-right",title:o.startedAt?new Date(o.startedAt).toLocaleString():""},f(o.startedAt?a(o.startedAt):"—"),9,Bt)]))),128))])])])}}},It=O(Mt,[["__scopeId","data-v-20d5856a"]]),Nt={class:"agent-logs"},Et={key:0,class:"error-banner"},Vt={key:1,class:"pagination-bar"},xt={class:"per-page-control"},jt=["value"],Rt=["value"],qt={class:"page-info"},Wt={class:"page-buttons"},zt=["disabled"],Jt=["disabled"],Gt={key:0,class:"page-ellipsis"},Ht=["onClick"],Ut=["disabled"],Zt=["disabled"],Qt={__name:"AgentLogsView",setup(s){const e=U(),y=B(!1),h=B(null),b=p(()=>e.logs),v=p(()=>e.pagination),a=p(()=>e.filters),n=p(()=>e.filterOptions),P=p(()=>e.perPage),l=p(()=>e.currentPage),d=p(()=>e.totalPages),C=p(()=>e.sortBy),o=p(()=>e.sortOrder),S=p(()=>e.loading),D=p(()=>e.error),M=p(()=>Object.values(e.filters).some(g=>g!=null)),I=p(()=>v.value.total===0?0:v.value.offset+1),N=p(()=>Math.min(v.value.offset+P.value,v.value.total)),E=p(()=>{const g=d.value,c=l.value;if(g<=7)return Array.from({length:g},(T,R)=>R+1);const u=[];return c<=4?u.push(1,2,3,4,5,"...",g):c>=g-3?u.push(1,"...",g-4,g-3,g-2,g-1,g):u.push(1,"...",c-1,c,c+1,"...",g),u});function V(g){C.value===g?e.setSort(g,o.value==="ASC"?"DESC":"ASC"):e.setSort(g,"DESC")}function x(g,c){const u=c.target.value;if(!u){e.setFilter(g,null);return}const T=new Date(`${u}T00:00:00Z`).getTime();e.filters[g]=T,e.currentPage=1,e.fetchLogs()}function j(){y.value?(h.value&&(clearTimeout(h.value),h.value=null),y.value=!1,e.clearAllLogs()):(y.value=!0,h.value=setTimeout(()=>{y.value=!1,h.value=null},3e3))}return G(()=>{e.fetchLogs(),e.fetchFilterOptions()}),H(()=>{h.value&&clearTimeout(h.value)}),(g,c)=>(r(),i("div",Nt,[w(vt,{filters:a.value,"filter-options":n.value,"has-active-filters":M.value,confirming:y.value,onFilterChange:c[0]||(c[0]=(u,T)=>m(e).setFilter(u,T)),onDateChange:x,onClearFilters:c[1]||(c[1]=u=>m(e).clearFilters()),onClearAll:j},null,8,["filters","filter-options","has-active-filters","confirming"]),D.value?(r(),i("div",Et,[t("span",null,f(D.value),1),t("button",{class:"btn-retry",onClick:c[2]||(c[2]=u=>m(e).fetchLogs())}," Retry ")])):_("",!0),w(It,{logs:b.value,loading:S.value,"sort-by":C.value,"sort-order":o.value,onSort:V},null,8,["logs","loading","sort-by","sort-order"]),v.value.total>0?(r(),i("div",Vt,[t("div",xt,[c[8]||(c[8]=t("label",{class:"filter-label"},"Per page",-1)),t("select",{class:"filter-select per-page-select",value:P.value,onChange:c[3]||(c[3]=u=>m(e).setPerPage(parseInt(u.target.value)))},[(r(),i(k,null,$([10,25,50,100],u=>t("option",{key:u,value:u},f(u),9,Rt)),64))],40,jt)]),t("div",qt," Showing "+f(I.value)+"–"+f(N.value)+" of "+f(v.value.total)+" logs ",1),t("div",Wt,[t("button",{class:"page-btn",disabled:l.value===1,onClick:c[4]||(c[4]=u=>m(e).setPage(1))}," « ",8,zt),t("button",{class:"page-btn",disabled:l.value===1,onClick:c[5]||(c[5]=u=>m(e).setPage(l.value-1))}," ‹ ",8,Jt),(r(!0),i(k,null,$(E.value,u=>(r(),i(k,{key:u},[u==="..."?(r(),i("span",Gt,"…")):(r(),i("button",{key:1,class:A(["page-btn",{"page-btn-active":u===l.value}]),onClick:T=>m(e).setPage(u)},f(u),11,Ht))],64))),128)),t("button",{class:"page-btn",disabled:l.value===d.value,onClick:c[6]||(c[6]=u=>m(e).setPage(l.value+1))}," › ",8,Ut),t("button",{class:"page-btn",disabled:l.value===d.value,onClick:c[7]||(c[7]=u=>m(e).setPage(d.value))}," » ",8,Zt)])])):_("",!0)]))}},Kt=O(Qt,[["__scopeId","data-v-e1df2011"]]);export{Kt as default};
|