circuschief 0.5.0 → 0.7.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 +2 -1
- package/packages/server/src/agents/AgentGateway.js +36 -3
- package/packages/server/src/agents/BaseAgent.js +15 -1
- package/packages/server/src/agents/LoggingAgentWrapper.js +4 -0
- package/packages/server/src/agents/adapters/ClaudeCodeAdapter.js +9 -6
- package/packages/server/src/agents/adapters/CodexAdapter.js +262 -14
- package/packages/server/src/agents/adapters/codexCliRunner.js +185 -0
- package/packages/server/src/agents/adapters/codexEventMapper.js +235 -0
- package/packages/server/src/agents/types.js +1 -0
- package/packages/server/src/agents/vcr/VCRAgentAdapter.js +8 -0
- package/packages/server/src/api/agents.js +27 -0
- package/packages/server/src/api/canvas.js +20 -0
- package/packages/server/src/api/index.js +2 -0
- package/packages/server/src/api/projects-session-helpers.js +25 -0
- package/packages/server/src/api/projects.js +8 -0
- package/packages/server/src/api/providers.js +1 -0
- package/packages/server/src/api/sessions-draft.js +1 -0
- package/packages/server/src/api/sessions-messages.js +6 -0
- package/packages/server/src/api/settings.js +52 -4
- package/packages/server/src/db/ConversationRepository.js +16 -3
- package/packages/server/src/db/ProjectDefaultsRepository.js +47 -37
- package/packages/server/src/db/ProviderRepository.js +62 -6
- package/packages/server/src/db/SessionRepository.js +74 -14
- package/packages/server/src/db/SettingsRepository.js +44 -16
- package/packages/server/src/db/conversation-helpers.js +1 -0
- package/packages/server/src/db/migrations/conversationsMigrations.js +4 -0
- package/packages/server/src/db/migrations/index.js +4 -0
- package/packages/server/src/db/migrations/miscMigrations.js +53 -3
- package/packages/server/src/db/migrations/sessionsMigrations.js +6 -1
- package/packages/server/src/db/session-helpers.js +8 -0
- package/packages/server/src/schema.sql +9 -0
- package/packages/server/src/services/agentCallLogger.js +1 -1
- package/packages/server/src/services/codexSpawnHelper.js +37 -0
- package/packages/server/src/services/commandButtonPrompts.js +48 -0
- package/packages/server/src/services/conversationContext.js +27 -0
- package/packages/server/src/services/draftSessionService.js +15 -2
- package/packages/server/src/services/kanbanTriggers.js +3 -0
- package/packages/server/src/services/providerTestService.js +115 -15
- package/packages/server/src/services/sessionAgentGuard.js +38 -0
- package/packages/server/src/services/sessionExecution.js +127 -33
- package/packages/server/src/services/sessionManager.js +45 -8
- package/packages/server/src/services/sessionPrompts.js +29 -0
- package/packages/server/src/services/sessionProvider.js +160 -41
- package/packages/server/src/services/streamEventCallbacks.js +72 -40
- package/packages/server/src/services/streamEventHandler.js +16 -2
- package/packages/server/src/services/streamUsageHandler.js +6 -0
- package/packages/server/src/services/summaryClaudeClient.js +37 -12
- package/packages/server/src/services/summaryModelClient.js +154 -0
- package/packages/server/src/services/summaryModelResolver.js +148 -0
- package/packages/server/src/services/summaryService.js +6 -4
- package/packages/server/src/services/templateTriggerService.js +2 -0
- package/packages/server/src/services/usageTracker.js +5 -1
- package/packages/server/src/services/visibleFinalErrorMessage.js +123 -0
- package/packages/shared/src/constants.js +1 -2
- package/packages/shared/src/contracts/projects.js +2 -0
- package/packages/shared/src/contracts/providers.js +24 -7
- package/packages/shared/src/contracts/sessions.js +1 -1
- package/packages/shared/src/index.js +1 -0
- package/packages/shared/src/types.js +28 -0
- package/packages/shared/src/utils.js +9 -17
- package/packages/web/dist/assets/ActiveSessionsView-UJsCILDL.js +1 -0
- package/packages/web/dist/assets/{AgentLogsView-DCF2WvP2.js → AgentLogsView-BGFPLjLa.js} +1 -1
- package/packages/web/dist/assets/ApiClient-B4YTtyY4.js +1 -0
- package/packages/web/dist/assets/{ArchiveConfirmModal-fgoEQhfq.js → ArchiveConfirmModal-OFaj_uX5.js} +1 -1
- package/packages/web/dist/assets/{CommandButtonDetailView-DAg07cDQ.js → CommandButtonDetailView-D8S258uP.js} +1 -1
- package/packages/web/dist/assets/EffortLevelSelector-C2378L8e.js +1 -0
- package/packages/web/dist/assets/{GeneralSettingsView-Cn9VI2du.js → GeneralSettingsView-DsHChEhv.js} +1 -1
- package/packages/web/dist/assets/{InputWithButton-BvboBGbz.js → InputWithButton-Ci15ox0a.js} +1 -1
- package/packages/web/dist/assets/{InterpolationHelp-0GoSBPgf.js → InterpolationHelp-CIkOSkWX.js} +1 -1
- package/packages/web/dist/assets/MarkdownEditor-5-bexzUT.js +2 -0
- package/packages/web/dist/assets/ModelSelector-BMpR0DPr.js +1 -0
- package/packages/web/dist/assets/{ModelSelector-DPPD-92R.css → ModelSelector-D8hbTRIt.css} +1 -1
- package/packages/web/dist/assets/{NewSessionView-C77YVqgY.js → NewSessionView-BCqtIgWH.js} +2 -2
- package/packages/web/dist/assets/{NewSessionView-D_Hi7M9g.css → NewSessionView-CUUdHkfv.css} +1 -1
- package/packages/web/dist/assets/ProjectEditView-D9sK0fdH.css +1 -0
- package/packages/web/dist/assets/ProjectEditView-RFaxHhAX.js +1 -0
- package/packages/web/dist/assets/{ProjectListView-CLwtuJ0J.js → ProjectListView-B9FuWESY.js} +1 -1
- package/packages/web/dist/assets/{ProjectNewView-CzDtVibO.js → ProjectNewView-D62jYlBL.js} +1 -1
- package/packages/web/dist/assets/ProvidersView-DDKMIQWZ.js +1 -0
- package/packages/web/dist/assets/ProvidersView-DE82G_5W.css +1 -0
- package/packages/web/dist/assets/QuickResponseSettings-CDm5vwP7.js +1 -0
- package/packages/web/dist/assets/{QuickResponsesPanel-DIBQFj0W.css → QuickResponsesPanel-BlFDvnZ2.css} +1 -1
- package/packages/web/dist/assets/{QuickResponsesPanel-CTXYjMF-.js → QuickResponsesPanel-DZ_Lre_l.js} +1 -1
- package/packages/web/dist/assets/{ResizableTextarea-Cw6aL4rp.js → ResizableTextarea-DiIOEGjN.js} +1 -1
- package/packages/web/dist/assets/ResizableTextarea-DsU3TVwF.css +1 -0
- package/packages/web/dist/assets/SessionCard-BMGC2HqI.css +1 -0
- package/packages/web/dist/assets/SessionCard-DmjnVYWn.js +1 -0
- package/packages/web/dist/assets/SessionDetailView-CL7nmfiB.js +36 -0
- package/packages/web/dist/assets/SessionDetailView-CupIkI7u.css +1 -0
- package/packages/web/dist/assets/SessionFormOptions-BpUALRKn.css +1 -0
- package/packages/web/dist/assets/SessionFormOptions-DYUISplS.js +1 -0
- package/packages/web/dist/assets/SessionListView-BcxGz4aC.js +1 -0
- package/packages/web/dist/assets/{SessionListView-DVhoZHN9.css → SessionListView-fHlQyecX.css} +1 -1
- package/packages/web/dist/assets/{SessionLogStream-DIndOyFR.js → SessionLogStream-DpUE6Xsh.js} +1 -1
- package/packages/web/dist/assets/{SettingsView-CmJ5JPd5.js → SettingsView-BC055tIA.js} +1 -1
- package/packages/web/dist/assets/SlashCommandWizard-DmTyNG9O.js +1 -0
- package/packages/web/dist/assets/SlashCommandWizard-Dn7sNaBd.css +1 -0
- package/packages/web/dist/assets/SummarySettingsView-BgnRCwlq.js +1 -0
- package/packages/web/dist/assets/SummarySettingsView-l2bxHmZZ.css +1 -0
- package/packages/web/dist/assets/TemplateDetailView-BlhOmLUX.js +1 -0
- package/packages/web/dist/assets/{commandButtons-D74TkPNU.js → commandButtons-D4RPpLiu.js} +1 -1
- package/packages/web/dist/assets/index-4rhEeO0B.js +1 -0
- package/packages/web/dist/assets/index-9vb2KaAd.js +1 -0
- package/packages/web/dist/assets/index-B0CvZXuN.js +7 -0
- package/packages/web/dist/assets/index-B6G18FqB.js +82 -0
- package/packages/web/dist/assets/{index-DMZZCi2u.js → index-BGwH4Cfn.js} +3 -3
- package/packages/web/dist/assets/index-BUhvkAdF.js +1 -0
- package/packages/web/dist/assets/index-BcnkUk2o.js +1 -0
- package/packages/web/dist/assets/{index-DQMHi05L.js → index-Bn5xdGFM.js} +2 -2
- package/packages/web/dist/assets/index-CNwkdB0T.js +1 -0
- package/packages/web/dist/assets/index-CfL84oGW.js +1 -0
- package/packages/web/dist/assets/index-CkmxO8Mm.js +1 -0
- package/packages/web/dist/assets/index-Cpy4-yv3.js +1 -0
- package/packages/web/dist/assets/index-CrAQJmoZ.js +1 -0
- package/packages/web/dist/assets/{index-gmCCsCQ1.css → index-Cs2nxhrT.css} +1 -1
- package/packages/web/dist/assets/index-D6Ky9vJe.js +3 -0
- package/packages/web/dist/assets/index-DfrE0gAC.js +1 -0
- package/packages/web/dist/assets/index-KwEyz0F3.js +1 -0
- package/packages/web/dist/assets/index-OfCywayk.js +1 -0
- package/packages/web/dist/assets/index-PDesaJc6.js +1 -0
- package/packages/web/dist/assets/index-uB6nhSvz.js +1 -0
- package/packages/web/dist/assets/{projects-D_C9dE9s.js → projects-BUiOGmmb.js} +1 -1
- package/packages/web/dist/assets/providers-Bh1ZiiJi.js +1 -0
- package/packages/web/dist/assets/sessions-DH1R-NhV.js +1 -0
- package/packages/web/dist/assets/settings-Z4AVVmkJ.js +1 -0
- package/packages/web/dist/index.html +2 -2
- package/packages/web/dist/assets/ActiveSessionsView-BafIafEu.js +0 -1
- package/packages/web/dist/assets/ApiClient-CcqJ-GAv.js +0 -1
- package/packages/web/dist/assets/EffortLevelSelector-xE3gidpq.js +0 -1
- package/packages/web/dist/assets/MarkdownEditor-HCKnwRye.js +0 -2
- package/packages/web/dist/assets/ModelSelector-B0RdlCHT.js +0 -1
- package/packages/web/dist/assets/ProjectEditView-BBHOsgBV.js +0 -1
- package/packages/web/dist/assets/ProjectEditView-CpeKj-_w.css +0 -1
- package/packages/web/dist/assets/ProvidersView-Eg93KbyC.js +0 -1
- package/packages/web/dist/assets/ProvidersView-uD8SKWpA.css +0 -1
- package/packages/web/dist/assets/QuickResponseSettings-BBHMapcA.js +0 -1
- package/packages/web/dist/assets/ResizableTextarea-B5nAA0RV.css +0 -1
- package/packages/web/dist/assets/SessionCard-CCapYVjy.js +0 -1
- package/packages/web/dist/assets/SessionCard-CcqIjL8q.css +0 -1
- package/packages/web/dist/assets/SessionDetailView-BL83oPiI.css +0 -1
- package/packages/web/dist/assets/SessionDetailView-CrZvMb3j.js +0 -36
- package/packages/web/dist/assets/SessionFormOptions-BuLlDF-7.css +0 -1
- package/packages/web/dist/assets/SessionFormOptions-Em7sQCGb.js +0 -1
- package/packages/web/dist/assets/SessionListView-3zdDtqhw.js +0 -1
- package/packages/web/dist/assets/SlashCommandWizard-BB30cSvo.css +0 -1
- package/packages/web/dist/assets/SlashCommandWizard-C_cSgF-P.js +0 -1
- package/packages/web/dist/assets/SummarySettingsView-DQM1n3bc.js +0 -1
- package/packages/web/dist/assets/SummarySettingsView-DcsmSVJI.css +0 -1
- package/packages/web/dist/assets/TemplateDetailView-B8clSBPk.js +0 -1
- package/packages/web/dist/assets/index-B5ocUoPf.js +0 -1
- package/packages/web/dist/assets/index-BELtFs3n.js +0 -1
- package/packages/web/dist/assets/index-BGAW2Nqa.js +0 -82
- package/packages/web/dist/assets/index-BsDR4w2c.js +0 -1
- package/packages/web/dist/assets/index-CVozYqQ-.js +0 -3
- package/packages/web/dist/assets/index-CefzeYRE.js +0 -1
- package/packages/web/dist/assets/index-CrLh8vw5.js +0 -1
- package/packages/web/dist/assets/index-DIvveuSK.js +0 -1
- package/packages/web/dist/assets/index-DPt6qBRK.js +0 -1
- package/packages/web/dist/assets/index-DYWZ8lD-.js +0 -1
- package/packages/web/dist/assets/index-DrlwE0Zo.js +0 -7
- package/packages/web/dist/assets/index-DuXChAe-.js +0 -1
- package/packages/web/dist/assets/index-Dz7jFUYU.js +0 -1
- package/packages/web/dist/assets/index-Gre8tUfC.js +0 -1
- package/packages/web/dist/assets/index-_Lv79l46.js +0 -1
- package/packages/web/dist/assets/index-f315nDFm.js +0 -1
- package/packages/web/dist/assets/index-rjbX81sm.js +0 -1
- package/packages/web/dist/assets/providers-BdvbPVdE.js +0 -1
- package/packages/web/dist/assets/sessions-Bs5FA6JZ.js +0 -1
- package/packages/web/dist/assets/settings-6Rw9xt-G.js +0 -1
|
@@ -12,6 +12,7 @@ const UPSERT_FIELD_MAP = [
|
|
|
12
12
|
{ key: 'gitMode', column: 'git_mode' },
|
|
13
13
|
{ key: 'gitBranch', column: 'git_branch' },
|
|
14
14
|
{ key: 'model', column: 'model' },
|
|
15
|
+
{ key: 'providerId', column: 'provider_id' },
|
|
15
16
|
{ key: 'effortLevel', column: 'effort_level' },
|
|
16
17
|
];
|
|
17
18
|
|
|
@@ -47,6 +48,7 @@ export class ProjectDefaultsRepository extends BaseRepository {
|
|
|
47
48
|
gitMode: row.git_mode || null,
|
|
48
49
|
gitBranch: row.git_branch || null,
|
|
49
50
|
model: row.model || null,
|
|
51
|
+
providerId: row.provider_id || null,
|
|
50
52
|
effortLevel: row.effort_level || null,
|
|
51
53
|
createdAt: row.created_at,
|
|
52
54
|
updatedAt: row.updated_at,
|
|
@@ -72,47 +74,53 @@ export class ProjectDefaultsRepository extends BaseRepository {
|
|
|
72
74
|
* @param {Object} data - Defaults data (all fields optional)
|
|
73
75
|
* @returns {Object} Updated defaults object
|
|
74
76
|
*/
|
|
77
|
+
#insertDefaults(projectId, data) {
|
|
78
|
+
const id = databaseManager.generateId();
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
|
|
81
|
+
this.db
|
|
82
|
+
.prepare(
|
|
83
|
+
`INSERT INTO project_session_defaults
|
|
84
|
+
(id, project_id, mode, thinking_enabled, start_immediately, git_mode, git_branch, model, provider_id, effort_level, created_at, updated_at)
|
|
85
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
86
|
+
)
|
|
87
|
+
.run(
|
|
88
|
+
id,
|
|
89
|
+
projectId,
|
|
90
|
+
data.mode || null,
|
|
91
|
+
data.thinkingEnabled !== undefined ? (data.thinkingEnabled ? 1 : 0) : null,
|
|
92
|
+
data.startImmediately !== undefined ? (data.startImmediately ? 1 : 0) : null,
|
|
93
|
+
data.gitMode || null,
|
|
94
|
+
data.gitBranch || null,
|
|
95
|
+
data.model || null,
|
|
96
|
+
data.providerId || null,
|
|
97
|
+
data.effortLevel || null,
|
|
98
|
+
now,
|
|
99
|
+
now
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#updateDefaults(projectId, data) {
|
|
104
|
+
const { updates, values } = buildUpdateFields(data);
|
|
105
|
+
|
|
106
|
+
if (updates.length > 0) {
|
|
107
|
+
updates.push('updated_at = ?');
|
|
108
|
+
values.push(Date.now());
|
|
109
|
+
values.push(projectId);
|
|
110
|
+
|
|
111
|
+
this.db
|
|
112
|
+
.prepare(`UPDATE project_session_defaults SET ${updates.join(', ')} WHERE project_id = ?`)
|
|
113
|
+
.run(...values);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
75
117
|
upsert(projectId, data) {
|
|
76
|
-
// Get existing defaults if any
|
|
77
118
|
const existing = this.getByProjectId(projectId);
|
|
78
119
|
|
|
79
120
|
if (!existing) {
|
|
80
|
-
|
|
81
|
-
const id = databaseManager.generateId();
|
|
82
|
-
const now = Date.now();
|
|
83
|
-
|
|
84
|
-
this.db
|
|
85
|
-
.prepare(
|
|
86
|
-
`INSERT INTO project_session_defaults
|
|
87
|
-
(id, project_id, mode, thinking_enabled, start_immediately, git_mode, git_branch, model, effort_level, created_at, updated_at)
|
|
88
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
89
|
-
)
|
|
90
|
-
.run(
|
|
91
|
-
id,
|
|
92
|
-
projectId,
|
|
93
|
-
data.mode || null,
|
|
94
|
-
data.thinkingEnabled !== undefined ? (data.thinkingEnabled ? 1 : 0) : null,
|
|
95
|
-
data.startImmediately !== undefined ? (data.startImmediately ? 1 : 0) : null,
|
|
96
|
-
data.gitMode || null,
|
|
97
|
-
data.gitBranch || null,
|
|
98
|
-
data.model || null,
|
|
99
|
-
data.effortLevel || null,
|
|
100
|
-
now,
|
|
101
|
-
now
|
|
102
|
-
);
|
|
121
|
+
this.#insertDefaults(projectId, data);
|
|
103
122
|
} else {
|
|
104
|
-
|
|
105
|
-
const { updates, values } = buildUpdateFields(data);
|
|
106
|
-
|
|
107
|
-
if (updates.length > 0) {
|
|
108
|
-
updates.push('updated_at = ?');
|
|
109
|
-
values.push(Date.now());
|
|
110
|
-
values.push(projectId);
|
|
111
|
-
|
|
112
|
-
this.db
|
|
113
|
-
.prepare(`UPDATE project_session_defaults SET ${updates.join(', ')} WHERE project_id = ?`)
|
|
114
|
-
.run(...values);
|
|
115
|
-
}
|
|
123
|
+
this.#updateDefaults(projectId, data);
|
|
116
124
|
}
|
|
117
125
|
|
|
118
126
|
return this.getByProjectId(projectId);
|
|
@@ -137,7 +145,8 @@ export class ProjectDefaultsRepository extends BaseRepository {
|
|
|
137
145
|
.prepare(
|
|
138
146
|
`UPDATE project_session_defaults
|
|
139
147
|
SET mode = NULL, thinking_enabled = NULL, start_immediately = NULL,
|
|
140
|
-
git_mode = NULL, git_branch = NULL, model = NULL,
|
|
148
|
+
git_mode = NULL, git_branch = NULL, model = NULL, provider_id = NULL,
|
|
149
|
+
effort_level = NULL, updated_at = ?
|
|
141
150
|
WHERE project_id = ?`
|
|
142
151
|
)
|
|
143
152
|
.run(Date.now(), projectId);
|
|
@@ -167,6 +176,7 @@ export class ProjectDefaultsRepository extends BaseRepository {
|
|
|
167
176
|
gitMode: null,
|
|
168
177
|
gitBranch: null,
|
|
169
178
|
model: null,
|
|
179
|
+
providerId: null,
|
|
170
180
|
effortLevel: null,
|
|
171
181
|
};
|
|
172
182
|
}
|
|
@@ -2,6 +2,22 @@ import { BaseRepository } from './BaseRepository.js';
|
|
|
2
2
|
import { databaseManager } from './DatabaseManager.js';
|
|
3
3
|
import { encrypt, decrypt } from '../services/encryption.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Valid values for `providers.kind`. Maps 1:1 to an agent adapter:
|
|
7
|
+
* - 'anthropic' → 'claude-code'
|
|
8
|
+
* - 'openai' → 'codex'
|
|
9
|
+
*/
|
|
10
|
+
export const PROVIDER_KINDS = Object.freeze(['anthropic', 'openai']);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Mapping from provider kind to the agent adapter that should drive sessions
|
|
14
|
+
* backed by that provider.
|
|
15
|
+
*/
|
|
16
|
+
export const AGENT_TYPE_BY_KIND = Object.freeze({
|
|
17
|
+
anthropic: 'claude-code',
|
|
18
|
+
openai: 'codex',
|
|
19
|
+
});
|
|
20
|
+
|
|
5
21
|
/**
|
|
6
22
|
* Provider repository class (replaces ModelProviderRepository).
|
|
7
23
|
*
|
|
@@ -11,6 +27,8 @@ import { encrypt, decrypt } from '../services/encryption.js';
|
|
|
11
27
|
* - No auto-sync logic (#syncDefaultModels removed)
|
|
12
28
|
* - Auth tokens are encrypted at rest (AES-256-GCM via encryption service)
|
|
13
29
|
* - `getProviderByModelId` includes models (needed for buildProviderEnv)
|
|
30
|
+
* - Providers carry a `kind` (`'anthropic'` | `'openai'`) that selects the
|
|
31
|
+
* agent adapter and env-var convention. `kind` is **immutable** after create.
|
|
14
32
|
*/
|
|
15
33
|
export class ProviderRepository extends BaseRepository {
|
|
16
34
|
constructor() {
|
|
@@ -27,6 +45,7 @@ export class ProviderRepository extends BaseRepository {
|
|
|
27
45
|
apiTimeoutMs: row.api_timeout_ms,
|
|
28
46
|
additionalEnvVars: row.additional_env_vars ? JSON.parse(row.additional_env_vars) : null,
|
|
29
47
|
isBuiltIn: row.is_built_in === 1,
|
|
48
|
+
kind: row.kind || 'anthropic',
|
|
30
49
|
createdAt: row.created_at,
|
|
31
50
|
updatedAt: row.updated_at,
|
|
32
51
|
};
|
|
@@ -64,12 +83,20 @@ export class ProviderRepository extends BaseRepository {
|
|
|
64
83
|
authToken = null,
|
|
65
84
|
apiTimeoutMs = null,
|
|
66
85
|
additionalEnvVars = null,
|
|
86
|
+
kind = 'anthropic',
|
|
67
87
|
} = data;
|
|
68
88
|
|
|
89
|
+
// Application-layer validation: give a clear error ahead of the DB CHECK.
|
|
90
|
+
if (!PROVIDER_KINDS.includes(kind)) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Invalid provider kind "${kind}". Must be one of: ${PROVIDER_KINDS.join(', ')}.`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
69
96
|
this.db
|
|
70
97
|
.prepare(
|
|
71
|
-
`INSERT INTO providers (id, name, base_url, auth_token, api_timeout_ms, additional_env_vars, created_at, updated_at)
|
|
72
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
98
|
+
`INSERT INTO providers (id, name, base_url, auth_token, api_timeout_ms, additional_env_vars, kind, created_at, updated_at)
|
|
99
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
73
100
|
)
|
|
74
101
|
.run(
|
|
75
102
|
id,
|
|
@@ -78,6 +105,7 @@ export class ProviderRepository extends BaseRepository {
|
|
|
78
105
|
encrypt(authToken),
|
|
79
106
|
apiTimeoutMs,
|
|
80
107
|
additionalEnvVars ? JSON.stringify(additionalEnvVars) : null,
|
|
108
|
+
kind,
|
|
81
109
|
now,
|
|
82
110
|
now
|
|
83
111
|
);
|
|
@@ -115,6 +143,15 @@ export class ProviderRepository extends BaseRepository {
|
|
|
115
143
|
* @returns {Object} Updated provider (with models array)
|
|
116
144
|
*/
|
|
117
145
|
update(id, data) {
|
|
146
|
+
// `kind` is immutable after create. Existing models + env wiring depend on it,
|
|
147
|
+
// so changing it in place would silently corrupt sessions already attached to
|
|
148
|
+
// this provider.
|
|
149
|
+
if (data && Object.prototype.hasOwnProperty.call(data, 'kind')) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"Provider kind is immutable after create. Delete and recreate the provider to change kind."
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
118
155
|
const updates = [];
|
|
119
156
|
const values = [];
|
|
120
157
|
|
|
@@ -279,12 +316,15 @@ export class ProviderRepository extends BaseRepository {
|
|
|
279
316
|
return null;
|
|
280
317
|
}
|
|
281
318
|
|
|
282
|
-
//
|
|
319
|
+
// Prefer custom providers over built-ins for duplicate model IDs. This
|
|
320
|
+
// preserves user-managed OpenAI providers (alternate base URLs, keys, or
|
|
321
|
+
// env vars) even when official OpenAI models are also seeded built-ins.
|
|
283
322
|
const row = this.db
|
|
284
323
|
.prepare(
|
|
285
324
|
`SELECT p.id FROM providers p
|
|
286
325
|
JOIN provider_models pm ON p.id = pm.provider_id
|
|
287
|
-
WHERE pm.model_id =
|
|
326
|
+
WHERE pm.model_id = ?
|
|
327
|
+
ORDER BY p.is_built_in ASC, p.name ASC`
|
|
288
328
|
)
|
|
289
329
|
.get(modelId);
|
|
290
330
|
|
|
@@ -297,11 +337,27 @@ export class ProviderRepository extends BaseRepository {
|
|
|
297
337
|
const provider = this.getById(row.id);
|
|
298
338
|
if (!provider) return null;
|
|
299
339
|
|
|
300
|
-
// Built-in Anthropic provider falls through to SDK defaults
|
|
301
|
-
|
|
340
|
+
// Built-in **Anthropic** provider falls through to SDK defaults (keeps
|
|
341
|
+
// historical behavior of letting @anthropic-ai/claude-agent-sdk pick its
|
|
342
|
+
// own env). Built-in OpenAI (or any future non-Anthropic built-in) still
|
|
343
|
+
// needs its env vars to flow, so we return the provider object.
|
|
344
|
+
if (provider.isBuiltIn && provider.kind === 'anthropic') {
|
|
302
345
|
return null;
|
|
303
346
|
}
|
|
304
347
|
|
|
305
348
|
return provider;
|
|
306
349
|
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Resolve a provider's agent type from its id.
|
|
353
|
+
* @param {string|null|undefined} providerId
|
|
354
|
+
* @returns {string|null} 'claude-code' for anthropic-kind, 'codex' for openai-kind,
|
|
355
|
+
* or null if the provider is unknown.
|
|
356
|
+
*/
|
|
357
|
+
getAgentTypeForProvider(providerId) {
|
|
358
|
+
if (!providerId) return null;
|
|
359
|
+
const provider = this.getById(providerId);
|
|
360
|
+
if (!provider) return null;
|
|
361
|
+
return AGENT_TYPE_BY_KIND[provider.kind] || null;
|
|
362
|
+
}
|
|
307
363
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BaseRepository } from './BaseRepository.js';
|
|
2
2
|
import { databaseManager } from './DatabaseManager.js';
|
|
3
|
-
import { messages, conversations } from './index.js';
|
|
3
|
+
import { messages, conversations, modelProviders } from './index.js';
|
|
4
4
|
import {
|
|
5
5
|
ACTIVITY_FIELDS_SQL,
|
|
6
6
|
mapTokenUsage,
|
|
@@ -9,6 +9,26 @@ import {
|
|
|
9
9
|
buildUpdateClauses,
|
|
10
10
|
} from './session-helpers.js';
|
|
11
11
|
|
|
12
|
+
const DEFAULT_AGENT_TYPE = 'claude-code';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the agent type ('claude-code' or 'codex') from a model ID by looking
|
|
16
|
+
* up which provider owns the model. This is the same logic as
|
|
17
|
+
* sessionProvider.resolveAgentTypeFromModel but inlined here to avoid a
|
|
18
|
+
* circular dependency:
|
|
19
|
+
* database.js (index) → SessionRepository → sessionProvider → database.js
|
|
20
|
+
* @param {string|null} modelId
|
|
21
|
+
* @returns {'claude-code'|'codex'}
|
|
22
|
+
*/
|
|
23
|
+
function resolveAgentTypeFromModel(modelId) {
|
|
24
|
+
if (!modelId) return DEFAULT_AGENT_TYPE;
|
|
25
|
+
const provider = modelProviders.getProviderByModelId(modelId);
|
|
26
|
+
if (!provider) return DEFAULT_AGENT_TYPE;
|
|
27
|
+
// ProviderRepository.getAgentTypeForProvider maps kind → agent adapter
|
|
28
|
+
const agentType = modelProviders.getAgentTypeForProvider(provider.id);
|
|
29
|
+
return agentType || DEFAULT_AGENT_TYPE;
|
|
30
|
+
}
|
|
31
|
+
|
|
12
32
|
/**
|
|
13
33
|
* Session repository class
|
|
14
34
|
*/
|
|
@@ -25,6 +45,7 @@ export class SessionRepository extends BaseRepository {
|
|
|
25
45
|
status: row.status,
|
|
26
46
|
mode: row.mode,
|
|
27
47
|
model: row.model,
|
|
48
|
+
providerId: row.provider_id || null,
|
|
28
49
|
thinkingEnabled: Boolean(row.thinking_enabled),
|
|
29
50
|
archived: Boolean(row.archived),
|
|
30
51
|
starred: Boolean(row.starred),
|
|
@@ -42,6 +63,8 @@ export class SessionRepository extends BaseRepository {
|
|
|
42
63
|
effortLevel: row.effort_level || null,
|
|
43
64
|
autoSendPendingPrompt: Boolean(row.auto_send_pending_prompt),
|
|
44
65
|
slashCommands: row.slash_commands || null,
|
|
66
|
+
// Agent runtime driving this session (fallback to 'claude-code' for legacy rows).
|
|
67
|
+
agentType: row.agent_type || DEFAULT_AGENT_TYPE,
|
|
45
68
|
...mapTokenUsage(row),
|
|
46
69
|
...mapScheduling(row),
|
|
47
70
|
// Kanban fields
|
|
@@ -49,7 +72,7 @@ export class SessionRepository extends BaseRepository {
|
|
|
49
72
|
laneTriggerDepth: row.lane_trigger_depth || 0,
|
|
50
73
|
createdAt: row.created_at,
|
|
51
74
|
updatedAt: row.updated_at,
|
|
52
|
-
lastActivityAt: row.last_activity_at
|
|
75
|
+
lastActivityAt: row.last_activity_at ?? null,
|
|
53
76
|
activeTimeMs: row.active_time_ms || 0,
|
|
54
77
|
};
|
|
55
78
|
}
|
|
@@ -62,18 +85,39 @@ export class SessionRepository extends BaseRepository {
|
|
|
62
85
|
return this.map(row);
|
|
63
86
|
}
|
|
64
87
|
|
|
65
|
-
/** Create a new session with optional config (mode, thinkingEnabled, gitBranch, parentSessionId, status, model, effortLevel) */
|
|
88
|
+
/** Create a new session with optional config (mode, thinkingEnabled, gitBranch, parentSessionId, status, model, providerId, effortLevel, agentType) */
|
|
66
89
|
create(projectId, name, prompt, options = {}) {
|
|
67
90
|
const config = parseCreateConfig(options, Array.prototype.slice.call(arguments, 4));
|
|
68
91
|
|
|
92
|
+
// Resolve agentType: explicit override → model-based derivation → fallback
|
|
93
|
+
const agentType =
|
|
94
|
+
config.agentType
|
|
95
|
+
?? (config.model ? resolveAgentTypeFromModel(config.model) : null)
|
|
96
|
+
?? DEFAULT_AGENT_TYPE;
|
|
97
|
+
|
|
69
98
|
const id = databaseManager.generateId();
|
|
70
99
|
const now = Date.now();
|
|
71
100
|
this.db
|
|
72
101
|
.prepare(
|
|
73
|
-
`INSERT INTO sessions (id, project_id, name, status, mode, thinking_enabled, git_branch, parent_session_id, model, effort_level, created_at, updated_at)
|
|
74
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
102
|
+
`INSERT INTO sessions (id, project_id, name, status, mode, thinking_enabled, git_branch, parent_session_id, model, provider_id, effort_level, agent_type, created_at, updated_at)
|
|
103
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
75
104
|
)
|
|
76
|
-
.run(
|
|
105
|
+
.run(
|
|
106
|
+
id,
|
|
107
|
+
projectId,
|
|
108
|
+
name,
|
|
109
|
+
config.status,
|
|
110
|
+
config.mode,
|
|
111
|
+
config.thinkingEnabled ? 1 : 0,
|
|
112
|
+
config.gitBranch,
|
|
113
|
+
config.parentSessionId,
|
|
114
|
+
config.model,
|
|
115
|
+
config.providerId,
|
|
116
|
+
config.effortLevel,
|
|
117
|
+
agentType,
|
|
118
|
+
now,
|
|
119
|
+
now
|
|
120
|
+
);
|
|
77
121
|
|
|
78
122
|
// Create initial conversation
|
|
79
123
|
const conversation = conversations.create(id, 'Initial', true);
|
|
@@ -103,6 +147,7 @@ export class SessionRepository extends BaseRepository {
|
|
|
103
147
|
|
|
104
148
|
sql += ` ORDER BY
|
|
105
149
|
starred DESC,
|
|
150
|
+
COALESCE(last_activity_at, updated_at, created_at) DESC,
|
|
106
151
|
updated_at DESC,
|
|
107
152
|
created_at DESC,
|
|
108
153
|
rowid DESC`;
|
|
@@ -141,7 +186,7 @@ export class SessionRepository extends BaseRepository {
|
|
|
141
186
|
`SELECT s.*, p.name as project_name, p.working_directory as project_working_directory, ${ACTIVITY_FIELDS_SQL}
|
|
142
187
|
FROM sessions s JOIN projects p ON s.project_id = p.id
|
|
143
188
|
WHERE s.status IN ('starting', 'running', 'waiting') AND s.archived = 0
|
|
144
|
-
ORDER BY s.starred DESC, s.updated_at DESC, s.created_at DESC, s.rowid DESC`
|
|
189
|
+
ORDER BY s.starred DESC, COALESCE(last_activity_at, s.updated_at, s.created_at) DESC, s.updated_at DESC, s.created_at DESC, s.rowid DESC`
|
|
145
190
|
)
|
|
146
191
|
.all();
|
|
147
192
|
return rows.map(row => ({
|
|
@@ -157,7 +202,7 @@ export class SessionRepository extends BaseRepository {
|
|
|
157
202
|
.prepare(
|
|
158
203
|
`SELECT s.*, ${ACTIVITY_FIELDS_SQL} FROM sessions s
|
|
159
204
|
WHERE parent_session_id = ?
|
|
160
|
-
ORDER BY updated_at DESC, created_at DESC, rowid DESC`
|
|
205
|
+
ORDER BY COALESCE(last_activity_at, updated_at, created_at) DESC, updated_at DESC, created_at DESC, rowid DESC`
|
|
161
206
|
)
|
|
162
207
|
.all(parentSessionId);
|
|
163
208
|
return this.mapAll(rows);
|
|
@@ -213,6 +258,18 @@ export class SessionRepository extends BaseRepository {
|
|
|
213
258
|
return this.getById(id);
|
|
214
259
|
}
|
|
215
260
|
|
|
261
|
+
/**
|
|
262
|
+
* Touch a session to update its updated_at timestamp without changing other fields.
|
|
263
|
+
* This is used to mark a session as recently active (e.g., when a message is added).
|
|
264
|
+
* @param {string} id - Session ID
|
|
265
|
+
* @returns {Object|null} The updated session or null if not found
|
|
266
|
+
*/
|
|
267
|
+
touch(id) {
|
|
268
|
+
const now = Date.now();
|
|
269
|
+
this.db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, id);
|
|
270
|
+
return this.getById(id);
|
|
271
|
+
}
|
|
272
|
+
|
|
216
273
|
/** Duplicate a session with a new ID and reset state (does NOT handle git or conversation setup) */
|
|
217
274
|
duplicate(sourceSessionId, { name } = {}) {
|
|
218
275
|
const source = this.getById(sourceSessionId);
|
|
@@ -227,10 +284,10 @@ export class SessionRepository extends BaseRepository {
|
|
|
227
284
|
// Insert new session with same settings but new ID and status
|
|
228
285
|
this.db
|
|
229
286
|
.prepare(
|
|
230
|
-
`INSERT INTO sessions (id, project_id, name, status, mode, thinking_enabled, git_branch, model, effort_level, context_window,
|
|
231
|
-
input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens,
|
|
287
|
+
`INSERT INTO sessions (id, project_id, name, status, mode, thinking_enabled, git_branch, model, provider_id, effort_level, agent_type, context_window,
|
|
288
|
+
input_tokens, output_tokens, thinking_tokens, cache_read_input_tokens, cache_creation_input_tokens,
|
|
232
289
|
web_search_requests, cost_usd, created_at, updated_at)
|
|
233
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
290
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
234
291
|
)
|
|
235
292
|
.run(
|
|
236
293
|
id,
|
|
@@ -241,10 +298,13 @@ export class SessionRepository extends BaseRepository {
|
|
|
241
298
|
source.thinkingEnabled ? 1 : 0,
|
|
242
299
|
source.gitBranch, // Copy branch name (NOT worktree path)
|
|
243
300
|
source.model,
|
|
301
|
+
source.providerId,
|
|
244
302
|
source.effortLevel,
|
|
303
|
+
source.agentType || DEFAULT_AGENT_TYPE,
|
|
245
304
|
source.contextWindow,
|
|
246
305
|
source.inputTokens,
|
|
247
306
|
source.outputTokens,
|
|
307
|
+
source.thinkingTokens,
|
|
248
308
|
source.cacheReadInputTokens,
|
|
249
309
|
source.cacheCreationInputTokens,
|
|
250
310
|
source.webSearchRequests,
|
|
@@ -261,7 +321,7 @@ export class SessionRepository extends BaseRepository {
|
|
|
261
321
|
const rows = this.db
|
|
262
322
|
.prepare(
|
|
263
323
|
`SELECT s.*, ${ACTIVITY_FIELDS_SQL} FROM sessions s
|
|
264
|
-
WHERE pr_url IS NOT NULL ORDER BY updated_at DESC, created_at DESC, rowid DESC`
|
|
324
|
+
WHERE pr_url IS NOT NULL ORDER BY COALESCE(last_activity_at, updated_at, created_at) DESC, updated_at DESC, created_at DESC, rowid DESC`
|
|
265
325
|
)
|
|
266
326
|
.all();
|
|
267
327
|
return this.mapAll(rows);
|
|
@@ -270,11 +330,11 @@ export class SessionRepository extends BaseRepository {
|
|
|
270
330
|
updateUsage(id, usage) {
|
|
271
331
|
this.db
|
|
272
332
|
.prepare(
|
|
273
|
-
`UPDATE sessions SET input_tokens = ?, output_tokens = ?, cache_read_input_tokens = ?,
|
|
333
|
+
`UPDATE sessions SET input_tokens = ?, output_tokens = ?, thinking_tokens = ?, cache_read_input_tokens = ?,
|
|
274
334
|
cache_creation_input_tokens = ?, web_search_requests = ?, context_window = ?, updated_at = ?
|
|
275
335
|
WHERE id = ?`
|
|
276
336
|
)
|
|
277
|
-
.run(usage.inputTokens, usage.outputTokens, usage.cacheReadInputTokens,
|
|
337
|
+
.run(usage.inputTokens, usage.outputTokens, usage.thinkingTokens || 0, usage.cacheReadInputTokens,
|
|
278
338
|
usage.cacheCreationInputTokens, usage.webSearchRequests, usage.contextWindow, Date.now(), id);
|
|
279
339
|
return this.getById(id);
|
|
280
340
|
}
|
|
@@ -5,6 +5,13 @@ const TOKEN_WEIGHTS_KEY = 'token_cost_weights';
|
|
|
5
5
|
const SUMMARY_SETTINGS_KEY = 'summary_settings';
|
|
6
6
|
const GENERAL_SETTINGS_KEY = 'general_settings';
|
|
7
7
|
|
|
8
|
+
const DEFAULT_SUMMARY_SETTINGS = Object.freeze({
|
|
9
|
+
disableSessionSummaries: false,
|
|
10
|
+
sessionTitlePrompt: '',
|
|
11
|
+
summaryModel: '',
|
|
12
|
+
summaryProviderId: null,
|
|
13
|
+
});
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* Settings repository for managing application-wide settings
|
|
10
17
|
*/
|
|
@@ -114,22 +121,13 @@ export class SettingsRepository {
|
|
|
114
121
|
getSummarySettings() {
|
|
115
122
|
const value = this.get(SUMMARY_SETTINGS_KEY);
|
|
116
123
|
if (!value) {
|
|
117
|
-
return {
|
|
118
|
-
disableSessionSummaries: false,
|
|
119
|
-
sessionTitlePrompt: '',
|
|
120
|
-
};
|
|
124
|
+
return { ...DEFAULT_SUMMARY_SETTINGS };
|
|
121
125
|
}
|
|
122
126
|
try {
|
|
123
127
|
const parsed = JSON.parse(value);
|
|
124
|
-
return
|
|
125
|
-
disableSessionSummaries: parsed.disableSessionSummaries || false,
|
|
126
|
-
sessionTitlePrompt: parsed.sessionTitlePrompt || '',
|
|
127
|
-
};
|
|
128
|
+
return normalizeStoredSummarySettings(parsed);
|
|
128
129
|
} catch {
|
|
129
|
-
return {
|
|
130
|
-
disableSessionSummaries: false,
|
|
131
|
-
sessionTitlePrompt: '',
|
|
132
|
-
};
|
|
130
|
+
return { ...DEFAULT_SUMMARY_SETTINGS };
|
|
133
131
|
}
|
|
134
132
|
}
|
|
135
133
|
|
|
@@ -138,11 +136,22 @@ export class SettingsRepository {
|
|
|
138
136
|
* @param {Object} settings - Summary settings
|
|
139
137
|
* @param {boolean} settings.disableSessionSummaries - Disable session summaries
|
|
140
138
|
* @param {string} settings.sessionTitlePrompt - Custom session title prompt
|
|
139
|
+
* @param {string} [settings.summaryModel] - Summary model id; empty string means auto
|
|
140
|
+
* @param {string|null} [settings.summaryProviderId] - Provider id owning summaryModel
|
|
141
141
|
*/
|
|
142
142
|
setSummarySettings(settings) {
|
|
143
|
+
const summaryModel = String(settings.summaryModel || '');
|
|
144
|
+
const summaryProviderId = typeof settings.summaryProviderId === 'string'
|
|
145
|
+
? settings.summaryProviderId
|
|
146
|
+
: null;
|
|
147
|
+
if (summaryModel && !summaryProviderId) {
|
|
148
|
+
throw new Error('summaryProviderId is required when summaryModel is set');
|
|
149
|
+
}
|
|
143
150
|
const validated = {
|
|
144
151
|
disableSessionSummaries: Boolean(settings.disableSessionSummaries),
|
|
145
152
|
sessionTitlePrompt: String(settings.sessionTitlePrompt || ''),
|
|
153
|
+
summaryModel,
|
|
154
|
+
summaryProviderId: summaryModel ? summaryProviderId : null,
|
|
146
155
|
};
|
|
147
156
|
this.set(SUMMARY_SETTINGS_KEY, JSON.stringify(validated));
|
|
148
157
|
return validated;
|
|
@@ -154,10 +163,7 @@ export class SettingsRepository {
|
|
|
154
163
|
*/
|
|
155
164
|
resetSummarySettings() {
|
|
156
165
|
this.delete(SUMMARY_SETTINGS_KEY);
|
|
157
|
-
return {
|
|
158
|
-
disableSessionSummaries: false,
|
|
159
|
-
sessionTitlePrompt: '',
|
|
160
|
-
};
|
|
166
|
+
return { ...DEFAULT_SUMMARY_SETTINGS };
|
|
161
167
|
}
|
|
162
168
|
|
|
163
169
|
// General Settings
|
|
@@ -209,3 +215,25 @@ export class SettingsRepository {
|
|
|
209
215
|
};
|
|
210
216
|
}
|
|
211
217
|
}
|
|
218
|
+
|
|
219
|
+
function normalizeStoredSummarySettings(parsed) {
|
|
220
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
221
|
+
return { ...DEFAULT_SUMMARY_SETTINGS };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const summaryModel = typeof parsed.summaryModel === 'string' ? parsed.summaryModel : '';
|
|
225
|
+
const summaryProviderId = typeof parsed.summaryProviderId === 'string' && parsed.summaryProviderId
|
|
226
|
+
? parsed.summaryProviderId
|
|
227
|
+
: null;
|
|
228
|
+
|
|
229
|
+
if (summaryModel && !summaryProviderId) {
|
|
230
|
+
return { ...DEFAULT_SUMMARY_SETTINGS };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
disableSessionSummaries: Boolean(parsed.disableSessionSummaries),
|
|
235
|
+
sessionTitlePrompt: typeof parsed.sessionTitlePrompt === 'string' ? parsed.sessionTitlePrompt : '',
|
|
236
|
+
summaryModel,
|
|
237
|
+
summaryProviderId: summaryModel ? summaryProviderId : null,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
@@ -22,6 +22,7 @@ export function mapConversationRow(row) {
|
|
|
22
22
|
// Token usage fields
|
|
23
23
|
inputTokens: row.input_tokens || 0,
|
|
24
24
|
outputTokens: row.output_tokens || 0,
|
|
25
|
+
thinkingTokens: row.thinking_tokens || 0,
|
|
25
26
|
cacheReadInputTokens: row.cache_read_input_tokens || 0,
|
|
26
27
|
cacheCreationInputTokens: row.cache_creation_input_tokens || 0,
|
|
27
28
|
webSearchRequests: row.web_search_requests || 0,
|
|
@@ -113,6 +113,10 @@ export const conversationsMigrations = [
|
|
|
113
113
|
name: 'conversations-add-output_tokens',
|
|
114
114
|
up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'output_tokens', COL_INTEGER_DEFAULT_0); },
|
|
115
115
|
},
|
|
116
|
+
{
|
|
117
|
+
name: 'conversations-add-thinking_tokens',
|
|
118
|
+
up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'thinking_tokens', COL_INTEGER_DEFAULT_0); },
|
|
119
|
+
},
|
|
116
120
|
{
|
|
117
121
|
name: 'conversations-add-cache_read_input_tokens',
|
|
118
122
|
up(db) { addColumnIfMissing(db, TABLE_CONVERSATIONS, 'cache_read_input_tokens', COL_INTEGER_DEFAULT_0); },
|
|
@@ -114,6 +114,7 @@ export const allMigrations = validateMigrations([
|
|
|
114
114
|
c.get('conversations-add-claude_session_id'),
|
|
115
115
|
c.get('conversations-add-input_tokens'),
|
|
116
116
|
c.get('conversations-add-output_tokens'),
|
|
117
|
+
c.get('conversations-add-thinking_tokens'),
|
|
117
118
|
c.get('conversations-add-cache_read_input_tokens'),
|
|
118
119
|
c.get('conversations-add-cache_creation_input_tokens'),
|
|
119
120
|
c.get('conversations-add-web_search_requests'),
|
|
@@ -123,6 +124,7 @@ export const allMigrations = validateMigrations([
|
|
|
123
124
|
// --- Sessions token usage ---
|
|
124
125
|
s.get('sessions-add-input_tokens'),
|
|
125
126
|
s.get('sessions-add-output_tokens'),
|
|
127
|
+
s.get('sessions-add-thinking_tokens'),
|
|
126
128
|
s.get('sessions-add-cache_read_input_tokens'),
|
|
127
129
|
s.get('sessions-add-cache_creation_input_tokens'),
|
|
128
130
|
s.get('sessions-add-web_search_requests'),
|
|
@@ -168,7 +170,9 @@ export const allMigrations = validateMigrations([
|
|
|
168
170
|
|
|
169
171
|
// --- Providers + provider_models tables + seed ---
|
|
170
172
|
m.get('providers-create-tables'),
|
|
173
|
+
m.get('providers-add-kind'),
|
|
171
174
|
m.get('providers-seed-built-in'),
|
|
175
|
+
m.get('providers-seed-built-in-openai'),
|
|
172
176
|
|
|
173
177
|
// --- Sessions provider_id (from providers FK) ---
|
|
174
178
|
s.get('sessions-add-provider_id-from-providers'),
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
* Each export is an array of { name, up(db) } migration objects.
|
|
5
5
|
*/
|
|
6
6
|
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { OPENAI_MODELS } from '../../../../shared/src/index.js';
|
|
7
8
|
import { addColumnIfMissing, tableExists } from './migrationUtils.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Seed the built-in Anthropic provider if it doesn't exist.
|
|
11
12
|
*/
|
|
12
|
-
function
|
|
13
|
+
function seedBuiltInAnthropicProvider(db) {
|
|
13
14
|
const providerId = 'anthropic-default';
|
|
14
15
|
|
|
15
16
|
const existing = db
|
|
@@ -43,6 +44,35 @@ function seedBuiltInProvider(db) {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Seed the built-in OpenAI/Codex provider if it doesn't exist.
|
|
49
|
+
*/
|
|
50
|
+
function seedBuiltInOpenAIProvider(db) {
|
|
51
|
+
const providerId = 'openai-default';
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
|
|
54
|
+
db.prepare(
|
|
55
|
+
`INSERT OR IGNORE INTO providers (
|
|
56
|
+
id, name, base_url, auth_token, kind, is_built_in, created_at, updated_at
|
|
57
|
+
)
|
|
58
|
+
VALUES (?, ?, NULL, NULL, 'openai', 1, ?, ?)`
|
|
59
|
+
).run(providerId, 'OpenAI (Official)', now, now);
|
|
60
|
+
|
|
61
|
+
const insertModel = db.prepare(
|
|
62
|
+
`INSERT OR IGNORE INTO provider_models (id, provider_id, model_id, display_name, description, tier, created_at)
|
|
63
|
+
VALUES (?, ?, ?, ?, ?, 'custom', ?)`
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
for (const model of OPENAI_MODELS) {
|
|
67
|
+
insertModel.run(model.seedId, providerId, model.id, model.name, model.description, now);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function seedBuiltInProviders(db) {
|
|
72
|
+
seedBuiltInAnthropicProvider(db);
|
|
73
|
+
seedBuiltInOpenAIProvider(db);
|
|
74
|
+
}
|
|
75
|
+
|
|
46
76
|
/**
|
|
47
77
|
* Update built-in models to 4.6 versions.
|
|
48
78
|
*/
|
|
@@ -217,6 +247,7 @@ export const miscMigrations = [
|
|
|
217
247
|
api_timeout_ms INTEGER,
|
|
218
248
|
additional_env_vars TEXT,
|
|
219
249
|
is_built_in INTEGER NOT NULL DEFAULT 0,
|
|
250
|
+
kind TEXT NOT NULL DEFAULT 'anthropic' CHECK(kind IN ('anthropic','openai')),
|
|
220
251
|
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
|
|
221
252
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)
|
|
222
253
|
);
|
|
@@ -235,10 +266,29 @@ export const miscMigrations = [
|
|
|
235
266
|
},
|
|
236
267
|
},
|
|
237
268
|
|
|
238
|
-
// ---
|
|
269
|
+
// --- Add `kind` column to providers (for DBs that predate the CREATE TABLE change) ---
|
|
270
|
+
{
|
|
271
|
+
name: 'providers-add-kind',
|
|
272
|
+
up(db) {
|
|
273
|
+
addColumnIfMissing(
|
|
274
|
+
db,
|
|
275
|
+
'providers',
|
|
276
|
+
'kind',
|
|
277
|
+
"TEXT NOT NULL DEFAULT 'anthropic' CHECK(kind IN ('anthropic','openai'))"
|
|
278
|
+
);
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
// --- Seed built-in providers ---
|
|
239
283
|
{
|
|
240
284
|
name: 'providers-seed-built-in',
|
|
241
|
-
up(db) {
|
|
285
|
+
up(db) { seedBuiltInProviders(db); },
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
// --- Seed built-in OpenAI provider for DBs that already ran the original seed ---
|
|
289
|
+
{
|
|
290
|
+
name: 'providers-seed-built-in-openai',
|
|
291
|
+
up(db) { seedBuiltInOpenAIProvider(db); },
|
|
242
292
|
},
|
|
243
293
|
|
|
244
294
|
// --- Update built-in models to 4.6 ---
|