circuschief 0.7.0 → 1.0.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/adapters/CodexAdapter.js +5 -4
- package/packages/server/src/api/canvas.js +22 -57
- package/packages/server/src/api/commandButtons.js +16 -15
- package/packages/server/src/api/index.js +2 -0
- package/packages/server/src/api/kanban.js +4 -2
- package/packages/server/src/api/projects-commandButtons.js +6 -6
- package/packages/server/src/api/projects-helpers.js +20 -3
- 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 +57 -5
- package/packages/server/src/api/projects-templates.js +38 -0
- package/packages/server/src/api/projects.js +28 -170
- package/packages/server/src/api/providers.js +11 -1
- package/packages/server/src/api/sessions-commands.js +46 -25
- package/packages/server/src/api/sessions-lifecycle.js +10 -10
- package/packages/server/src/api/sessions-patch.js +45 -1
- package/packages/server/src/api/sessions.js +6 -5
- package/packages/server/src/database.js +0 -2
- package/packages/server/src/db/DatabaseManager.js +5 -1
- package/packages/server/src/db/ProjectDefaultsRepository.js +3 -3
- package/packages/server/src/db/ProviderRepository.js +87 -32
- package/packages/server/src/db/SessionRepository.js +2 -1
- package/packages/server/src/db/SessionTemplateRepository.js +23 -2
- package/packages/server/src/db/index.js +0 -3
- package/packages/server/src/db/migrations/index.js +60 -7
- package/packages/server/src/db/migrations/kanbanMigrations.js +1 -1
- package/packages/server/src/db/migrations/miscMigrations.js +59 -184
- package/packages/server/src/db/migrations/projectsMigrations.js +31 -0
- package/packages/server/src/db/migrations/providerCommitAttributionMigrations.js +30 -0
- package/packages/server/src/db/migrations/providerMigrations.js +165 -0
- package/packages/server/src/db/migrations/sessionTableRecreate.js +136 -0
- package/packages/server/src/db/migrations/sessionsMigrations.js +18 -5
- package/packages/server/src/db/seedBaselineData.js +137 -0
- package/packages/server/src/db/session-helpers.js +32 -4
- package/packages/server/src/middleware/sessionLookup.js +81 -8
- package/packages/server/src/schema.sql +153 -132
- package/packages/server/src/scripts/backupDatabase.js +21 -0
- package/packages/server/src/scripts/dbUtils.js +81 -0
- package/packages/server/src/scripts/inspectDatabaseSchema.js +81 -0
- package/packages/server/src/scripts/validateDatabaseBaseline.js +212 -0
- package/packages/server/src/services/codexSpawnHelper.js +9 -0
- package/packages/server/src/services/commandButtonPrompts.js +14 -12
- package/packages/server/src/services/commandRunner.js +7 -1
- package/packages/server/src/services/e2eSpawnCapture.js +147 -0
- package/packages/server/src/services/gitCommitAttribution.js +150 -0
- package/packages/server/src/services/gitDiff.js +132 -0
- package/packages/server/src/services/gitRepoUrl.js +174 -0
- package/packages/server/src/services/gitService.js +48 -311
- package/packages/server/src/services/gitSessionSetup.js +11 -1
- package/packages/server/src/services/gitWorktree.js +127 -0
- package/packages/server/src/services/kanbanTriggers.js +6 -3
- package/packages/server/src/services/nodeSpawnHelper.js +9 -0
- package/packages/server/src/services/prUrlService.js +3 -3
- package/packages/server/src/services/queryParamBuilder.js +90 -0
- package/packages/server/src/services/sessionDuplicator.js +1 -5
- package/packages/server/src/services/sessionExecution.js +56 -108
- package/packages/server/src/services/sessionPrompts.js +12 -47
- package/packages/server/src/services/sessionProvider.js +10 -0
- package/packages/server/src/services/summaryService.js +5 -3
- package/packages/server/src/services/summaryStaleCheck.js +23 -4
- package/packages/server/src/services/templateTriggerService.js +3 -1
- package/packages/shared/src/constants.js +3 -0
- package/packages/shared/src/contracts/commandButtons.js +16 -2
- package/packages/shared/src/contracts/projects.js +2 -2
- package/packages/shared/src/contracts/providers.js +60 -0
- package/packages/shared/src/contracts/sessions.js +29 -2
- package/packages/shared/src/contracts/templates.js +12 -2
- package/packages/shared/src/types.js +1 -9
- package/packages/shared/src/utils.js +2 -2
- package/packages/web/dist/assets/{ActiveSessionsView-UJsCILDL.js → ActiveSessionsView-Cxh8mHmB.js} +1 -1
- package/packages/web/dist/assets/{AgentLogsView-BGFPLjLa.js → AgentLogsView-xdfI2bR6.js} +2 -2
- package/packages/web/dist/assets/ApiClient-DfbJwzpz.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-DXZYdzHR.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-DeoCVGXt.css +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-D8xfqLAp.js +1 -0
- package/packages/web/dist/assets/CommandButtonDetailView-D9zjx9ME.css +1 -0
- package/packages/web/dist/assets/EffortLevelSelector-D2Hdzc_8.js +1 -0
- package/packages/web/dist/assets/{GeneralSettingsView-DsHChEhv.js → GeneralSettingsView-sPXkLlLy.js} +1 -1
- package/packages/web/dist/assets/InputWithButton-B-o0DgMH.js +1 -0
- package/packages/web/dist/assets/{InputWithButton-cYdrEmTs.css → InputWithButton-D9HMvfR5.css} +1 -1
- package/packages/web/dist/assets/{InterpolationHelp-CIkOSkWX.js → InterpolationHelp-Dxn1li4l.js} +1 -1
- package/packages/web/dist/assets/MarkdownEditor-D4Kbb-9l.js +2 -0
- package/packages/web/dist/assets/ModelSelector-72C7MUH4.js +1 -0
- package/packages/web/dist/assets/{ModelSelector-D8hbTRIt.css → ModelSelector-BNYKujL-.css} +1 -1
- package/packages/web/dist/assets/NewSessionView-BR_COfgW.js +3 -0
- package/packages/web/dist/assets/NewSessionView-DBl7T2Xp.css +1 -0
- package/packages/web/dist/assets/ProjectEditView-DbqTbA0q.css +1 -0
- package/packages/web/dist/assets/ProjectEditView-WImU7sNd.js +1 -0
- package/packages/web/dist/assets/{ProjectListView-B9FuWESY.js → ProjectListView-CYmmAcBD.js} +1 -1
- package/packages/web/dist/assets/{ProjectNewView-D62jYlBL.js → ProjectNewView-DEhqw3Jv.js} +1 -1
- package/packages/web/dist/assets/ProvidersView-XZh3jkmH.js +1 -0
- package/packages/web/dist/assets/ProvidersView-bZemq_Rv.css +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-BqmnTd-D.js +1 -0
- package/packages/web/dist/assets/QuickResponsesPanel-dk-Rj8xx.css +1 -0
- package/packages/web/dist/assets/ResizableTextarea-BQNw5e0C.css +1 -0
- package/packages/web/dist/assets/ResizableTextarea-DpWdIAP6.js +1 -0
- package/packages/web/dist/assets/SessionCard-Bw77-KwD.js +1 -0
- package/packages/web/dist/assets/SessionDetailView-B59TEkr-.js +36 -0
- package/packages/web/dist/assets/SessionDetailView-CKVBnR4T.css +1 -0
- package/packages/web/dist/assets/{SessionFormOptions-DYUISplS.js → SessionFormOptions-hqijxc0S.js} +1 -1
- package/packages/web/dist/assets/SessionListView-3-xx6EVs.css +1 -0
- package/packages/web/dist/assets/SessionListView-DYXHM9I-.js +1 -0
- package/packages/web/dist/assets/{SessionLogStream-DpUE6Xsh.js → SessionLogStream-5NfVr9pF.js} +6 -6
- package/packages/web/dist/assets/{SettingsView-BC055tIA.js → SettingsView-DI8ncOAV.js} +1 -1
- package/packages/web/dist/assets/{SlashCommandWizard-DmTyNG9O.js → SlashCommandWizard-BQ_rMzn-.js} +1 -1
- package/packages/web/dist/assets/{SlashCommandWizard-Dn7sNaBd.css → SlashCommandWizard-DJzw3LP5.css} +1 -1
- package/packages/web/dist/assets/{SummarySettingsView-BgnRCwlq.js → SummarySettingsView-C2Qs35mm.js} +1 -1
- package/packages/web/dist/assets/TemplateDetailView-B5NI2oTR.css +1 -0
- package/packages/web/dist/assets/TemplateDetailView-zVkIvgtu.js +1 -0
- package/packages/web/dist/assets/{commandButtons-D4RPpLiu.js → commandButtons-CoU3G4zK.js} +1 -1
- package/packages/web/dist/assets/index-9yF1uCCA.js +1 -0
- package/packages/web/dist/assets/index-BKstCaYU.js +1 -0
- package/packages/web/dist/assets/index-BhbH7eOk.js +1 -0
- package/packages/web/dist/assets/{index-BGwH4Cfn.js → index-BjuRttEY.js} +3 -3
- package/packages/web/dist/assets/index-Bo7PdwM5.js +1 -0
- package/packages/web/dist/assets/index-C2QFVD7d.js +83 -0
- package/packages/web/dist/assets/index-C7Ww2auW.js +1 -0
- package/packages/web/dist/assets/index-CAGdsDh7.js +1 -0
- package/packages/web/dist/assets/index-CLRsVASf.js +3 -0
- package/packages/web/dist/assets/{index-Bn5xdGFM.js → index-CP-SxOlV.js} +1 -1
- package/packages/web/dist/assets/index-CslU0psO.js +1 -0
- package/packages/web/dist/assets/index-DI4NxaWD.js +1 -0
- package/packages/web/dist/assets/index-DOzONENy.js +1 -0
- package/packages/web/dist/assets/index-DUa7adFh.js +1 -0
- package/packages/web/dist/assets/index-DZBpETI5.js +1 -0
- package/packages/web/dist/assets/index-DsjWqc6R.js +7 -0
- package/packages/web/dist/assets/index-c99Bo3JV.js +1 -0
- package/packages/web/dist/assets/index-mT1JpxDc.js +1 -0
- package/packages/web/dist/assets/index-rkQx2tso.js +1 -0
- package/packages/web/dist/assets/{index-Cs2nxhrT.css → index-uySCcnA_.css} +1 -1
- package/packages/web/dist/assets/projectDefaults-B8esIcYq.js +1 -0
- package/packages/web/dist/assets/{projects-BUiOGmmb.js → projects-C-8PSxKi.js} +1 -1
- package/packages/web/dist/assets/{providers-Bh1ZiiJi.js → providers-oXifvvqN.js} +1 -1
- package/packages/web/dist/assets/sessions-Nq5VafSf.js +1 -0
- package/packages/web/dist/assets/{settings-Z4AVVmkJ.js → settings-DtpuiyT6.js} +1 -1
- package/packages/web/dist/index.html +2 -2
- package/packages/server/src/api/sessions-notes.js +0 -51
- package/packages/server/src/db/SessionNoteRepository.js +0 -60
- package/packages/web/dist/assets/ApiClient-B4YTtyY4.js +0 -1
- package/packages/web/dist/assets/ArchiveConfirmModal-BQ-4gI0R.css +0 -1
- package/packages/web/dist/assets/ArchiveConfirmModal-OFaj_uX5.js +0 -1
- package/packages/web/dist/assets/CommandButtonDetailView-D8S258uP.js +0 -1
- package/packages/web/dist/assets/CommandButtonDetailView-DBm3rzhw.css +0 -1
- package/packages/web/dist/assets/EffortLevelSelector-C2378L8e.js +0 -1
- package/packages/web/dist/assets/InputWithButton-Ci15ox0a.js +0 -1
- package/packages/web/dist/assets/MarkdownEditor-5-bexzUT.js +0 -2
- package/packages/web/dist/assets/ModelSelector-BMpR0DPr.js +0 -1
- package/packages/web/dist/assets/NewSessionView-BCqtIgWH.js +0 -3
- package/packages/web/dist/assets/NewSessionView-CUUdHkfv.css +0 -1
- package/packages/web/dist/assets/ProjectEditView-D9sK0fdH.css +0 -1
- package/packages/web/dist/assets/ProjectEditView-RFaxHhAX.js +0 -1
- package/packages/web/dist/assets/ProvidersView-DDKMIQWZ.js +0 -1
- package/packages/web/dist/assets/ProvidersView-DE82G_5W.css +0 -1
- package/packages/web/dist/assets/QuickResponseSettings-B8188A1D.css +0 -1
- package/packages/web/dist/assets/QuickResponseSettings-CDm5vwP7.js +0 -1
- package/packages/web/dist/assets/QuickResponsesPanel-BlFDvnZ2.css +0 -1
- package/packages/web/dist/assets/QuickResponsesPanel-DZ_Lre_l.js +0 -1
- package/packages/web/dist/assets/ResizableTextarea-DiIOEGjN.js +0 -1
- package/packages/web/dist/assets/ResizableTextarea-DsU3TVwF.css +0 -1
- package/packages/web/dist/assets/SessionCard-DmjnVYWn.js +0 -1
- package/packages/web/dist/assets/SessionDetailView-CL7nmfiB.js +0 -36
- package/packages/web/dist/assets/SessionDetailView-CupIkI7u.css +0 -1
- package/packages/web/dist/assets/SessionListView-BcxGz4aC.js +0 -1
- package/packages/web/dist/assets/SessionListView-fHlQyecX.css +0 -1
- package/packages/web/dist/assets/TemplateDetailView-BlhOmLUX.js +0 -1
- package/packages/web/dist/assets/TemplateDetailView-DT2m06W7.css +0 -1
- package/packages/web/dist/assets/index-4rhEeO0B.js +0 -1
- package/packages/web/dist/assets/index-9vb2KaAd.js +0 -1
- package/packages/web/dist/assets/index-B0CvZXuN.js +0 -7
- package/packages/web/dist/assets/index-B6G18FqB.js +0 -82
- package/packages/web/dist/assets/index-BUhvkAdF.js +0 -1
- package/packages/web/dist/assets/index-BcnkUk2o.js +0 -1
- package/packages/web/dist/assets/index-CNwkdB0T.js +0 -1
- package/packages/web/dist/assets/index-CfL84oGW.js +0 -1
- package/packages/web/dist/assets/index-CkmxO8Mm.js +0 -1
- package/packages/web/dist/assets/index-Cpy4-yv3.js +0 -1
- package/packages/web/dist/assets/index-CrAQJmoZ.js +0 -1
- package/packages/web/dist/assets/index-D6Ky9vJe.js +0 -3
- package/packages/web/dist/assets/index-DfrE0gAC.js +0 -1
- package/packages/web/dist/assets/index-KwEyz0F3.js +0 -1
- package/packages/web/dist/assets/index-OfCywayk.js +0 -1
- package/packages/web/dist/assets/index-PDesaJc6.js +0 -1
- package/packages/web/dist/assets/index-uB6nhSvz.js +0 -1
- package/packages/web/dist/assets/sessions-DH1R-NhV.js +0 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createClaudeCodeSpawner } from './nodeSpawnHelper.js';
|
|
2
|
+
import {
|
|
3
|
+
buildSystemPromptConfig,
|
|
4
|
+
getPermissionModeForSession,
|
|
5
|
+
getSandboxModeForSession,
|
|
6
|
+
} from './sessionPrompts.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build query parameters for the Claude Code adapter.
|
|
10
|
+
* @returns {Object}
|
|
11
|
+
*/
|
|
12
|
+
function buildClaudeCodeQueryParams({
|
|
13
|
+
prompt, workingDirectory, controller, session, sessionId, systemPrompt,
|
|
14
|
+
model, sessionEnv, resumeSessionId = null,
|
|
15
|
+
}) {
|
|
16
|
+
const isVCR = Boolean(process.env.VCR_MODE);
|
|
17
|
+
const effectiveModel = isVCR ? 'claude-haiku-4-5-20251001' : model;
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
prompt,
|
|
21
|
+
options: {
|
|
22
|
+
cwd: workingDirectory,
|
|
23
|
+
abortController: controller,
|
|
24
|
+
includePartialMessages: true,
|
|
25
|
+
permissionMode: getPermissionModeForSession(session.mode),
|
|
26
|
+
// Match normal Claude Code CLI behavior: load user-level settings
|
|
27
|
+
// such as configured MCP servers, then project/local overrides.
|
|
28
|
+
settingSources: ['user', 'project', 'local'],
|
|
29
|
+
...(resumeSessionId && { resume: resumeSessionId }),
|
|
30
|
+
env: sessionEnv,
|
|
31
|
+
spawnClaudeCodeProcess: createClaudeCodeSpawner(),
|
|
32
|
+
model: effectiveModel,
|
|
33
|
+
systemPrompt: buildSystemPromptConfig(sessionId, session.projectId, systemPrompt, session.mode),
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build query parameters for the Codex adapter.
|
|
40
|
+
*
|
|
41
|
+
* Codex in v1 is a simple Chat-Completions-shaped executor. It does not need
|
|
42
|
+
* or accept Claude-specific options such as permissionMode, settingSources,
|
|
43
|
+
* includePartialMessages, spawnClaudeCodeProcess, or resume.
|
|
44
|
+
*
|
|
45
|
+
* @returns {Object}
|
|
46
|
+
*/
|
|
47
|
+
function buildCodexQueryParams({
|
|
48
|
+
prompt, workingDirectory, controller, session, sessionId, systemPrompt, model, sessionEnv,
|
|
49
|
+
}) {
|
|
50
|
+
const isVCR = Boolean(process.env.VCR_MODE);
|
|
51
|
+
const effectiveModel = isVCR ? 'gpt-4o-mini' : model;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
prompt,
|
|
55
|
+
options: {
|
|
56
|
+
cwd: workingDirectory,
|
|
57
|
+
abortController: controller,
|
|
58
|
+
env: sessionEnv,
|
|
59
|
+
model: effectiveModel,
|
|
60
|
+
effortLevel: session?.effortLevel ?? null,
|
|
61
|
+
systemPrompt: buildSystemPromptConfig(sessionId, session.projectId, systemPrompt, session.mode),
|
|
62
|
+
sandboxMode: getSandboxModeForSession(session?.mode),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Build query parameters for executing a session via the configured agent.
|
|
69
|
+
* Shared by runSession, continueSession, and continueSessionWithExistingMessage.
|
|
70
|
+
*
|
|
71
|
+
* @param {Object} options
|
|
72
|
+
* @param {string} options.prompt - The prompt text to send
|
|
73
|
+
* @param {string} options.workingDirectory - Session working directory
|
|
74
|
+
* @param {AbortController} options.controller - Abort controller for the session
|
|
75
|
+
* @param {Object} options.session - Session object from DB
|
|
76
|
+
* @param {string} options.sessionId - Session ID
|
|
77
|
+
* @param {string|null} options.systemPrompt - Custom system prompt from project settings
|
|
78
|
+
* @param {string|null} options.model - Model to use
|
|
79
|
+
* @param {Object} options.sessionEnv - Environment variables for the session
|
|
80
|
+
* @param {string|null} [options.resumeSessionId] - Session ID to resume (null for new session)
|
|
81
|
+
* @param {string} [options.agentType] - 'claude-code' (default) | 'codex'
|
|
82
|
+
* @returns {Object} Query parameters for agent.execute()
|
|
83
|
+
*/
|
|
84
|
+
export function buildQueryParams(options) {
|
|
85
|
+
const { agentType = 'claude-code' } = options || {};
|
|
86
|
+
if (agentType === 'codex') {
|
|
87
|
+
return buildCodexQueryParams(options);
|
|
88
|
+
}
|
|
89
|
+
return buildClaudeCodeQueryParams(options);
|
|
90
|
+
}
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
conversations,
|
|
4
4
|
messages,
|
|
5
5
|
canvasItems,
|
|
6
|
-
sessionNotes,
|
|
7
6
|
sessionSummaries,
|
|
8
7
|
projects,
|
|
9
8
|
} from '../db/index.js';
|
|
@@ -47,10 +46,7 @@ export async function duplicateSession(sourceSessionId, options = {}) {
|
|
|
47
46
|
// 5. Duplicate canvas items
|
|
48
47
|
canvasItems.duplicateForSession(sourceSessionId, newSession.id);
|
|
49
48
|
|
|
50
|
-
// 6. Duplicate session
|
|
51
|
-
sessionNotes.duplicateForSession(sourceSessionId, newSession.id);
|
|
52
|
-
|
|
53
|
-
// 7. Duplicate session summary (if exists)
|
|
49
|
+
// 6. Duplicate session summary (if exists)
|
|
54
50
|
sessionSummaries.duplicateForSession(sourceSessionId, newSession.id);
|
|
55
51
|
|
|
56
52
|
// Return the updated session
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { sessions, messages, attachments, conversations } from '../database.js';
|
|
2
|
-
import { createClaudeCodeSpawner } from './nodeSpawnHelper.js';
|
|
3
2
|
import { createCodexSpawner } from './codexSpawnHelper.js';
|
|
4
|
-
import { resolveProviderFromModel, buildSessionEnv } from './sessionProvider.js';
|
|
3
|
+
import { resolveProviderFromModel, resolveProviderMetadataFromModel, buildSessionEnv } from './sessionProvider.js';
|
|
5
4
|
import { agentGateway } from '../agents/AgentGateway.js';
|
|
6
5
|
import { LoggingAgentWrapper } from '../agents/LoggingAgentWrapper.js';
|
|
7
6
|
import { VCRAgentAdapter } from '../agents/vcr/VCRAgentAdapter.js';
|
|
7
|
+
import { isE2ESpawnCaptureEnabled } from './e2eSpawnCapture.js';
|
|
8
|
+
export { buildQueryParams } from './queryParamBuilder.js';
|
|
9
|
+
import { buildQueryParams } from './queryParamBuilder.js';
|
|
8
10
|
import {
|
|
9
|
-
buildSystemPromptConfig,
|
|
10
|
-
getPermissionModeForSession,
|
|
11
|
-
getSandboxModeForSession,
|
|
12
11
|
buildPromptWithAttachments,
|
|
13
12
|
} from './sessionPrompts.js';
|
|
14
13
|
import {
|
|
@@ -23,6 +22,7 @@ import {
|
|
|
23
22
|
import { shouldRescheduleOnError, _checkProactiveReschedule } from './sessionErrors.js';
|
|
24
23
|
import { schedulerService } from './schedulerService.js';
|
|
25
24
|
import { buildConversationContextForModelSwitch, buildConversationContextForContinuation } from './conversationContext.js';
|
|
25
|
+
import { ensureWorktreeCommitAttributionHook } from './gitService.js';
|
|
26
26
|
import { broadcastToSession } from '../websocket.js';
|
|
27
27
|
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
28
28
|
|
|
@@ -40,6 +40,34 @@ function buildAgentConfig(agentType) {
|
|
|
40
40
|
return {};
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export function buildAgentEnv(sessionEnv, commitAttributionOverride) {
|
|
44
|
+
const env = { ...(sessionEnv || {}) };
|
|
45
|
+
if (commitAttributionOverride) {
|
|
46
|
+
env.CIRCUSCHIEF_COMMIT_ATTRIBUTION = commitAttributionOverride;
|
|
47
|
+
} else {
|
|
48
|
+
delete env.CIRCUSCHIEF_COMMIT_ATTRIBUTION;
|
|
49
|
+
}
|
|
50
|
+
return env;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function resolveInitialSessionModelEnv(session, model) {
|
|
54
|
+
const effectiveModel = model || session.model;
|
|
55
|
+
const provider = resolveProviderFromModel(effectiveModel);
|
|
56
|
+
const providerMetadata = resolveProviderMetadataFromModel(effectiveModel);
|
|
57
|
+
const commitAttributionOverride = providerMetadata?.commitAttributionOverride ?? null;
|
|
58
|
+
|
|
59
|
+
if (session.gitWorktree && commitAttributionOverride) {
|
|
60
|
+
await ensureWorktreeCommitAttributionHook(session.gitWorktree);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const baseSessionEnv = buildSessionEnv(provider, session.thinkingEnabled, session.effortLevel);
|
|
64
|
+
return {
|
|
65
|
+
effectiveModel,
|
|
66
|
+
sessionEnv: buildAgentEnv(baseSessionEnv, commitAttributionOverride),
|
|
67
|
+
commitAttributionOverride,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
43
71
|
/**
|
|
44
72
|
* Create the agent for a session, using gateway + logging + VCR.
|
|
45
73
|
*
|
|
@@ -56,7 +84,7 @@ export function createAgentForSession(agentType = 'claude-code', config = {}) {
|
|
|
56
84
|
const baseAgent = agentGateway.createAgent(agentType, mergedConfig);
|
|
57
85
|
|
|
58
86
|
// Wrap with VCR adapter if in VCR mode
|
|
59
|
-
const agent = process.env.VCR_MODE
|
|
87
|
+
const agent = process.env.VCR_MODE && !isE2ESpawnCaptureEnabled()
|
|
60
88
|
? new VCRAgentAdapter(baseAgent, { cassetteDir: 'tests/e2e/cassettes' })
|
|
61
89
|
: baseAgent;
|
|
62
90
|
|
|
@@ -64,97 +92,6 @@ export function createAgentForSession(agentType = 'claude-code', config = {}) {
|
|
|
64
92
|
return new LoggingAgentWrapper(agent);
|
|
65
93
|
}
|
|
66
94
|
|
|
67
|
-
/**
|
|
68
|
-
* Build query parameters for the Claude Code adapter.
|
|
69
|
-
* @returns {Object}
|
|
70
|
-
*/
|
|
71
|
-
function buildClaudeCodeQueryParams({
|
|
72
|
-
prompt, workingDirectory, controller, session, sessionId, systemPrompt,
|
|
73
|
-
model, sessionEnv, resumeSessionId = null,
|
|
74
|
-
}) {
|
|
75
|
-
const isVCR = Boolean(process.env.VCR_MODE);
|
|
76
|
-
const effectiveModel = isVCR ? 'claude-haiku-4-5-20251001' : model;
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
prompt,
|
|
80
|
-
options: {
|
|
81
|
-
cwd: workingDirectory,
|
|
82
|
-
abortController: controller,
|
|
83
|
-
includePartialMessages: true,
|
|
84
|
-
permissionMode: getPermissionModeForSession(session.mode),
|
|
85
|
-
// Match normal Claude Code CLI behavior: load user-level settings
|
|
86
|
-
// such as configured MCP servers, then project/local overrides.
|
|
87
|
-
settingSources: ['user', 'project', 'local'],
|
|
88
|
-
...(resumeSessionId && { resume: resumeSessionId }),
|
|
89
|
-
env: sessionEnv,
|
|
90
|
-
spawnClaudeCodeProcess: createClaudeCodeSpawner(),
|
|
91
|
-
model: effectiveModel,
|
|
92
|
-
systemPrompt: buildSystemPromptConfig(sessionId, session.projectId, systemPrompt, session.mode),
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Build query parameters for the Codex adapter.
|
|
99
|
-
*
|
|
100
|
-
* Codex in v1 is a simple Chat-Completions-shaped executor — it doesn't need
|
|
101
|
-
* or accept Claude-specific options (permissionMode, settingSources,
|
|
102
|
-
* includePartialMessages, spawnClaudeCodeProcess, resume).
|
|
103
|
-
*
|
|
104
|
-
* Codex does have its own sandboxing model, driven from {@code session.mode}
|
|
105
|
-
* via {@link getSandboxModeForSession}. Codex CLI v0.124.0 also supports
|
|
106
|
-
* resume via `codex resume` / `codex exec resume`, but Circus Chief v1
|
|
107
|
-
* intentionally does NOT pass a resume token — wiring is deferred to a
|
|
108
|
-
* later phase (see canvas plan §Phase 4.5).
|
|
109
|
-
*
|
|
110
|
-
* @returns {Object}
|
|
111
|
-
*/
|
|
112
|
-
function buildCodexQueryParams({
|
|
113
|
-
prompt, workingDirectory, controller, session, sessionId, systemPrompt, model, sessionEnv,
|
|
114
|
-
}) {
|
|
115
|
-
const isVCR = Boolean(process.env.VCR_MODE);
|
|
116
|
-
// In VCR mode, force the cheapest commonly-cassetted OpenAI model.
|
|
117
|
-
const effectiveModel = isVCR ? 'gpt-4o-mini' : model;
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
prompt,
|
|
121
|
-
options: {
|
|
122
|
-
cwd: workingDirectory,
|
|
123
|
-
abortController: controller,
|
|
124
|
-
env: sessionEnv,
|
|
125
|
-
model: effectiveModel,
|
|
126
|
-
effortLevel: session?.effortLevel ?? null,
|
|
127
|
-
systemPrompt: buildSystemPromptConfig(sessionId, session.projectId, systemPrompt, session.mode),
|
|
128
|
-
sandboxMode: getSandboxModeForSession(session?.mode),
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Build query parameters for executing a session via the configured agent.
|
|
135
|
-
* Shared by runSession, continueSession, and continueSessionWithExistingMessage.
|
|
136
|
-
*
|
|
137
|
-
* @param {Object} options
|
|
138
|
-
* @param {string} options.prompt - The prompt text to send
|
|
139
|
-
* @param {string} options.workingDirectory - Session working directory
|
|
140
|
-
* @param {AbortController} options.controller - Abort controller for the session
|
|
141
|
-
* @param {Object} options.session - Session object from DB
|
|
142
|
-
* @param {string} options.sessionId - Session ID
|
|
143
|
-
* @param {string|null} options.systemPrompt - Custom system prompt from project settings
|
|
144
|
-
* @param {string|null} options.model - Model to use
|
|
145
|
-
* @param {Object} options.sessionEnv - Environment variables for the session
|
|
146
|
-
* @param {string|null} [options.resumeSessionId] - Session ID to resume (null for new session)
|
|
147
|
-
* @param {string} [options.agentType] - 'claude-code' (default) | 'codex'
|
|
148
|
-
* @returns {Object} Query parameters for agent.execute()
|
|
149
|
-
*/
|
|
150
|
-
export function buildQueryParams(options) {
|
|
151
|
-
const { agentType = 'claude-code' } = options || {};
|
|
152
|
-
if (agentType === 'codex') {
|
|
153
|
-
return buildCodexQueryParams(options);
|
|
154
|
-
}
|
|
155
|
-
return buildClaudeCodeQueryParams(options);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
95
|
/**
|
|
159
96
|
* Execute the agent stream loop and handle post-turn completion, errors, and cleanup.
|
|
160
97
|
* This is the shared core of runSession, continueSession, and continueSessionWithExistingMessage.
|
|
@@ -260,7 +197,12 @@ function buildContinueModelAndEnv(session, sessionId, model) {
|
|
|
260
197
|
|
|
261
198
|
// Derive provider from the effective model ID (returns null for Anthropic/SDK defaults)
|
|
262
199
|
const provider = resolveProviderFromModel(effectiveModel);
|
|
263
|
-
const
|
|
200
|
+
const providerMetadata = resolveProviderMetadataFromModel(effectiveModel);
|
|
201
|
+
const commitAttributionOverride = providerMetadata?.commitAttributionOverride ?? null;
|
|
202
|
+
const sessionEnv = buildAgentEnv(
|
|
203
|
+
buildSessionEnv(provider, session.thinkingEnabled, session.effortLevel),
|
|
204
|
+
commitAttributionOverride
|
|
205
|
+
);
|
|
264
206
|
|
|
265
207
|
// Check if model changed from the session's last requested model
|
|
266
208
|
// When model changes, we can't resume the previous session - thinking blocks and
|
|
@@ -275,7 +217,13 @@ function buildContinueModelAndEnv(session, sessionId, model) {
|
|
|
275
217
|
updatedSession = sessions.getById(sessionId); // refresh
|
|
276
218
|
}
|
|
277
219
|
|
|
278
|
-
return {
|
|
220
|
+
return {
|
|
221
|
+
effectiveModel,
|
|
222
|
+
sessionEnv,
|
|
223
|
+
commitAttributionOverride,
|
|
224
|
+
modelChanged,
|
|
225
|
+
session: updatedSession,
|
|
226
|
+
};
|
|
279
227
|
}
|
|
280
228
|
|
|
281
229
|
/**
|
|
@@ -286,7 +234,7 @@ function buildContinueModelAndEnv(session, sessionId, model) {
|
|
|
286
234
|
async function buildContinueParams({
|
|
287
235
|
sessionId, session, model, systemPrompt, effectiveModel, sessionEnv,
|
|
288
236
|
modelChanged, activeConversation, promptWithAttachments,
|
|
289
|
-
workingDirectory, controller, agentType, agent,
|
|
237
|
+
workingDirectory, controller, agentType, agent, commitAttributionOverride,
|
|
290
238
|
}) {
|
|
291
239
|
// Only resume if we have a session ID AND model hasn't changed AND the
|
|
292
240
|
// agent supports resume.
|
|
@@ -308,6 +256,7 @@ async function buildContinueParams({
|
|
|
308
256
|
sessionEnv,
|
|
309
257
|
resumeSessionId: canResume ? activeConversation.claudeSessionId : null,
|
|
310
258
|
agentType,
|
|
259
|
+
commitAttributionOverride,
|
|
311
260
|
});
|
|
312
261
|
|
|
313
262
|
// Logging metadata for agent call tracking
|
|
@@ -394,11 +343,15 @@ export async function continueSessionCore(sessionId, content, workingDirectory,
|
|
|
394
343
|
// Resolve model/provider and detect model changes
|
|
395
344
|
const modelEnv = buildContinueModelAndEnv(session, sessionId, model);
|
|
396
345
|
session = modelEnv.session;
|
|
346
|
+
if (session.gitWorktree && modelEnv.commitAttributionOverride) {
|
|
347
|
+
await ensureWorktreeCommitAttributionHook(session.gitWorktree);
|
|
348
|
+
}
|
|
397
349
|
|
|
398
350
|
// Build query params and agent call meta
|
|
399
351
|
const { queryParams, agentCallMeta } = await buildContinueParams({
|
|
400
352
|
sessionId, session, model, systemPrompt,
|
|
401
353
|
effectiveModel: modelEnv.effectiveModel, sessionEnv: modelEnv.sessionEnv,
|
|
354
|
+
commitAttributionOverride: modelEnv.commitAttributionOverride,
|
|
402
355
|
modelChanged: modelEnv.modelChanged, activeConversation, promptWithAttachments,
|
|
403
356
|
workingDirectory, controller, agentType, agent,
|
|
404
357
|
});
|
|
@@ -459,14 +412,8 @@ export async function runSessionCore(sessionId, prompt, workingDirectory, config
|
|
|
459
412
|
const agentType = session.agentType || 'claude-code';
|
|
460
413
|
const agent = createAgentForSession(agentType);
|
|
461
414
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
// correctness if called directly).
|
|
465
|
-
const effectiveModel = model || session.model;
|
|
466
|
-
|
|
467
|
-
// Derive provider from the effective model ID (returns null for Anthropic/SDK defaults)
|
|
468
|
-
const provider = resolveProviderFromModel(effectiveModel);
|
|
469
|
-
const sessionEnv = buildSessionEnv(provider, session.thinkingEnabled, session.effortLevel);
|
|
415
|
+
const { effectiveModel, sessionEnv, commitAttributionOverride } =
|
|
416
|
+
await resolveInitialSessionModelEnv(session, model);
|
|
470
417
|
|
|
471
418
|
const queryParams = buildQueryParams({
|
|
472
419
|
prompt: promptWithAttachments,
|
|
@@ -478,6 +425,7 @@ export async function runSessionCore(sessionId, prompt, workingDirectory, config
|
|
|
478
425
|
model: effectiveModel,
|
|
479
426
|
sessionEnv,
|
|
480
427
|
agentType,
|
|
428
|
+
commitAttributionOverride,
|
|
481
429
|
});
|
|
482
430
|
|
|
483
431
|
// Log query params for debugging third-party provider issues
|
|
@@ -162,10 +162,7 @@ CRITICAL: Do NOT start coding until you have presented a plan and received appro
|
|
|
162
162
|
*/
|
|
163
163
|
function buildCanvasWriteSystemPrompt(session) {
|
|
164
164
|
const apiUrl = getApiBaseUrl();
|
|
165
|
-
|
|
166
|
-
const sessionId = session
|
|
167
|
-
? (sessions.getRootSessionId(session.id) || session.id)
|
|
168
|
-
: 'unknown-session';
|
|
165
|
+
const sessionId = session?.id || 'unknown-session';
|
|
169
166
|
return `When you generate artifacts that should be displayed on the canvas (images, markdown documents, code snippets, data visualizations, PDFs), POST them to:
|
|
170
167
|
|
|
171
168
|
POST ${apiUrl}/api/sessions/${sessionId}/canvas
|
|
@@ -187,10 +184,7 @@ The file type is automatically detected from the file extension. Supported forma
|
|
|
187
184
|
*/
|
|
188
185
|
function buildCanvasReadSystemPrompt(session) {
|
|
189
186
|
const apiUrl = getApiBaseUrl();
|
|
190
|
-
|
|
191
|
-
const sessionId = session
|
|
192
|
-
? (sessions.getRootSessionId(session.id) || session.id)
|
|
193
|
-
: 'unknown-session';
|
|
187
|
+
const sessionId = session?.id || 'unknown-session';
|
|
194
188
|
return `## Reading from Canvas
|
|
195
189
|
|
|
196
190
|
To list all files on the canvas:
|
|
@@ -225,9 +219,10 @@ function buildSessionCrudOps(apiUrl, projectId) {
|
|
|
225
219
|
\`\`\`bash
|
|
226
220
|
curl -X POST ${apiUrl}/api/projects/${projectId}/sessions \\
|
|
227
221
|
-H "Content-Type: application/json" \\
|
|
228
|
-
-d '{"prompt": "Your task description here"
|
|
222
|
+
-d '{"prompt": "Your task description here"}'
|
|
229
223
|
\`\`\`
|
|
230
|
-
|
|
224
|
+
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\` (ISO 8601 date-time string with timezone, e.g. \`"2026-06-12T14:00:00Z"\`), \`autoRescheduleEnabled\`, \`rescheduleDelayMinutes\`, \`rescheduleOnTokenLimit\`, \`rescheduleOnServiceError\`, \`maxRescheduleCount\`, \`maxTotalTokens\`, and \`rescheduleAtTokenCount\`.
|
|
231
226
|
|
|
232
227
|
### Send a Follow-up Message
|
|
233
228
|
\`\`\`bash
|
|
@@ -258,8 +253,8 @@ curl -X PATCH ${apiUrl}/api/sessions/<session_id> \\
|
|
|
258
253
|
\`\`\``;
|
|
259
254
|
}
|
|
260
255
|
|
|
261
|
-
/** Build project
|
|
262
|
-
function
|
|
256
|
+
/** Build project and summary operations section */
|
|
257
|
+
function buildProjectOps(apiUrl, sessionId) {
|
|
263
258
|
return `### Project Operations
|
|
264
259
|
\`\`\`bash
|
|
265
260
|
curl ${apiUrl}/api/projects # List all projects
|
|
@@ -271,18 +266,10 @@ curl -X POST ${apiUrl}/api/projects \\
|
|
|
271
266
|
\`\`\`
|
|
272
267
|
Optional field: \`systemPrompt\`
|
|
273
268
|
|
|
274
|
-
###
|
|
269
|
+
### Workflow Summary
|
|
275
270
|
\`\`\`bash
|
|
276
|
-
curl ${apiUrl}/api/sessions
|
|
277
|
-
curl -X POST ${apiUrl}/api/sessions
|
|
278
|
-
-H "Content-Type: application/json" \\
|
|
279
|
-
-d '{"content": "Note content"}'
|
|
280
|
-
\`\`\`
|
|
281
|
-
|
|
282
|
-
### Session Summary
|
|
283
|
-
\`\`\`bash
|
|
284
|
-
curl "${apiUrl}/api/sessions/<session_id>/summary?generate=true"
|
|
285
|
-
curl -X POST ${apiUrl}/api/sessions/<session_id>/summary # Regenerate
|
|
271
|
+
curl "${apiUrl}/api/sessions/${sessionId}/summary?generate=true"
|
|
272
|
+
curl -X POST ${apiUrl}/api/sessions/${sessionId}/summary # Regenerate
|
|
286
273
|
\`\`\``;
|
|
287
274
|
}
|
|
288
275
|
|
|
@@ -299,7 +286,7 @@ You can create and modify sessions in this system using curl or similar HTTP too
|
|
|
299
286
|
|
|
300
287
|
${buildSessionCrudOps(apiUrl, projectId)}
|
|
301
288
|
|
|
302
|
-
${
|
|
289
|
+
${buildProjectOps(apiUrl, sessionId)}`;
|
|
303
290
|
}
|
|
304
291
|
|
|
305
292
|
/**
|
|
@@ -336,7 +323,7 @@ ${laneContext}
|
|
|
336
323
|
curl ${apiUrl}/api/projects/${projectId}/kanban
|
|
337
324
|
\`\`\`
|
|
338
325
|
|
|
339
|
-
### Add
|
|
326
|
+
### Add Current Workflow to the Board
|
|
340
327
|
\`\`\`bash
|
|
341
328
|
curl -X POST ${apiUrl}/api/projects/${projectId}/kanban/cards \\
|
|
342
329
|
-H "Content-Type: application/json" \\
|
|
@@ -394,26 +381,6 @@ This session is running in an isolated git worktree:
|
|
|
394
381
|
CRITICAL: Do NOT use \`cd\` to navigate to the main repository. Your working directory is already set to the worktree. Running \`cd /home/ubuntu/workspace/circus-chief && ...\` will escape the worktree isolation and affect the main repository instead.`;
|
|
395
382
|
}
|
|
396
383
|
|
|
397
|
-
/**
|
|
398
|
-
* Build child session context for system prompt
|
|
399
|
-
* @param {Object} session - Session object
|
|
400
|
-
* @returns {string} Child session context or empty string
|
|
401
|
-
*/
|
|
402
|
-
function buildChildSessionContext(session) {
|
|
403
|
-
if (!session || !session.parentSessionId) {
|
|
404
|
-
return '';
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Get root session ID using existing method
|
|
408
|
-
const rootSessionId = sessions.getRootSessionId(session.id);
|
|
409
|
-
|
|
410
|
-
return `## Child Session
|
|
411
|
-
|
|
412
|
-
This session is part of a multi-session workflow:
|
|
413
|
-
- Parent Session ID: ${session.parentSessionId}
|
|
414
|
-
- Root Session ID: ${rootSessionId}`;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
384
|
/**
|
|
418
385
|
* Build the full system prompt configuration
|
|
419
386
|
* @param {string} sessionId
|
|
@@ -432,7 +399,6 @@ export function buildSystemPromptConfig(sessionId, projectId, customSystemPrompt
|
|
|
432
399
|
const kanbanApiInstructions = buildKanbanApiInstructions(sessionId, projectId);
|
|
433
400
|
const attachmentsContext = getSessionAttachmentsContext(sessionId);
|
|
434
401
|
const worktreeContext = buildWorktreeContext(session);
|
|
435
|
-
const childSessionContext = buildChildSessionContext(session);
|
|
436
402
|
const basePrompt = customSystemPrompt || DEFAULT_SYSTEM_PROMPT;
|
|
437
403
|
|
|
438
404
|
// Prepend plan mode instructions if in plan mode
|
|
@@ -442,7 +408,6 @@ export function buildSystemPromptConfig(sessionId, projectId, customSystemPrompt
|
|
|
442
408
|
const parts = [
|
|
443
409
|
modePrompt,
|
|
444
410
|
basePrompt,
|
|
445
|
-
childSessionContext,
|
|
446
411
|
worktreeContext,
|
|
447
412
|
attachmentsContext,
|
|
448
413
|
canvasWriteInstructions,
|
|
@@ -11,6 +11,16 @@ export function resolveProviderFromModel(modelId) {
|
|
|
11
11
|
return modelProviders.getProviderByModelId(modelId);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export function resolveProviderMetadataFromModel(modelId) {
|
|
15
|
+
if (!modelId) {
|
|
16
|
+
return modelProviders.getById?.('anthropic-default') || null;
|
|
17
|
+
}
|
|
18
|
+
if (typeof modelProviders.getProviderMetadataByModelId === 'function') {
|
|
19
|
+
return modelProviders.getProviderMetadataByModelId(modelId);
|
|
20
|
+
}
|
|
21
|
+
return modelProviders.getProviderByModelId(modelId);
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
/**
|
|
15
25
|
* Resolve the agent type (claude-code vs codex) for a given model ID.
|
|
16
26
|
* Uses the owning provider's kind:
|
|
@@ -200,18 +200,19 @@ function updateSessionFromSummary(sessionId, session, summaryData) {
|
|
|
200
200
|
if (summaryData.sessionTitle || summaryData.prUrl) {
|
|
201
201
|
const updateData = {};
|
|
202
202
|
const freshSession = sessions.getById(sessionId);
|
|
203
|
+
const shouldApplySummaryPrUrl = summaryData.prUrl && !freshSession.prUrlAutoLinkDisabled;
|
|
203
204
|
|
|
204
205
|
if (summaryData.sessionTitle && !freshSession.manuallyNamed) {
|
|
205
206
|
updateData.name = summaryData.sessionTitle;
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
if (
|
|
209
|
+
if (shouldApplySummaryPrUrl) {
|
|
209
210
|
updateData.prUrl = summaryData.prUrl;
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
const updatedSession = sessions.update(sessionId, updateData);
|
|
213
214
|
|
|
214
|
-
if (
|
|
215
|
+
if (shouldApplySummaryPrUrl) {
|
|
215
216
|
propagatePrUrlToParent(sessionId, summaryData.prUrl);
|
|
216
217
|
}
|
|
217
218
|
|
|
@@ -521,7 +522,7 @@ export function propagatePrUrlToParent(sessionId, prUrl) {
|
|
|
521
522
|
if (!rootId || rootId === sessionId) return;
|
|
522
523
|
|
|
523
524
|
const root = sessions.getById(rootId);
|
|
524
|
-
if (!root || root.prUrl) return; // Don't overwrite existing PR URL
|
|
525
|
+
if (!root || root.prUrl || root.prUrlAutoLinkDisabled) return; // Don't overwrite existing or user-cleared PR URL
|
|
525
526
|
|
|
526
527
|
sessions.update(root.id, { prUrl });
|
|
527
528
|
|
|
@@ -536,6 +537,7 @@ export {
|
|
|
536
537
|
MAX_MESSAGES, MIN_MESSAGES_FOR_SUMMARY, MAX_RETRIES, DEFAULT_SESSION_TITLE_PROMPT,
|
|
537
538
|
SUMMARY_SYSTEM_PROMPT, formatMessages, buildIncrementalPrompt, parseSummaryResponse,
|
|
538
539
|
stripMarkdownCodeBlock as _stripMarkdownCodeBlock, trackMessageMetadata as _trackMessageMetadata,
|
|
540
|
+
updateSessionFromSummary as _updateSessionFromSummary,
|
|
539
541
|
};
|
|
540
542
|
export { callSummaryModel };
|
|
541
543
|
export { callClaude } from './summaryClaudeClient.js';
|
|
@@ -3,10 +3,24 @@
|
|
|
3
3
|
* Extracted from summaryService.js for modularity.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { sessionSummaries, messages } from '../database.js';
|
|
6
|
+
import { sessionSummaries, messages, sessions } from '../database.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Check if
|
|
9
|
+
* Check if any descendant session has a summary newer than the given timestamp.
|
|
10
|
+
* @param {string} sessionId
|
|
11
|
+
* @param {number} generatedAt
|
|
12
|
+
* @returns {boolean}
|
|
13
|
+
*/
|
|
14
|
+
function hasNewerDescendantSummary(sessionId, generatedAt) {
|
|
15
|
+
const descendantIds = sessions.getAllDescendantIds(sessionId);
|
|
16
|
+
if (descendantIds.length === 0) return false;
|
|
17
|
+
|
|
18
|
+
const descendantSummaries = sessionSummaries.getBySessionIds(descendantIds);
|
|
19
|
+
return descendantSummaries.some(ds => ds.generatedAt > generatedAt);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if a summary is stale (message count, last message ID, or descendant summaries have changed)
|
|
10
24
|
* @param {string} sessionId
|
|
11
25
|
* @returns {boolean}
|
|
12
26
|
*/
|
|
@@ -27,9 +41,14 @@ export function isSummaryStale(sessionId) {
|
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
// Also validate count as a secondary check (defensive programming)
|
|
30
|
-
|
|
44
|
+
if (allMessages.length !== summary.messageCount) return true;
|
|
31
45
|
}
|
|
32
46
|
|
|
33
47
|
// Fallback to count-based staleness detection for old summaries
|
|
34
|
-
|
|
48
|
+
if (allMessages.length !== summary.messageCount) return true;
|
|
49
|
+
|
|
50
|
+
// Check if any descendant session has a newer summary
|
|
51
|
+
if (hasNewerDescendantSummary(sessionId, summary.generatedAt)) return true;
|
|
52
|
+
|
|
53
|
+
return false;
|
|
35
54
|
}
|
|
@@ -4,7 +4,7 @@ import { setupGitForSession } from './gitSessionSetup.js';
|
|
|
4
4
|
import { runSession } from './sessionManager.js';
|
|
5
5
|
import { broadcastToProject } from '../websocket.js';
|
|
6
6
|
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
7
|
-
import { resolveAgentTypeFromModel } from './sessionProvider.js';
|
|
7
|
+
import { resolveAgentTypeFromModel, resolveProviderMetadataFromModel } from './sessionProvider.js';
|
|
8
8
|
|
|
9
9
|
const liquid = new Liquid();
|
|
10
10
|
|
|
@@ -100,6 +100,8 @@ async function resolveWorkingDirectory(parentSession, project, settings, newSess
|
|
|
100
100
|
gitBranch: settings.gitBranch,
|
|
101
101
|
sessionId: newSessionId,
|
|
102
102
|
worktreeBasePath: project.worktreePath || null,
|
|
103
|
+
commitAttributionOverride:
|
|
104
|
+
resolveProviderMetadataFromModel(settings.model)?.commitAttributionOverride ?? null,
|
|
103
105
|
});
|
|
104
106
|
return { workingDirectory: gitSetup.workingDirectory, gitWorktree: gitSetup.gitWorktree };
|
|
105
107
|
}
|
|
@@ -20,6 +20,9 @@ export const WS_RECONNECT_MAX_DELAY = 30000;
|
|
|
20
20
|
|
|
21
21
|
export const TOAST_DURATION = 5000;
|
|
22
22
|
|
|
23
|
+
/** Default delay (in minutes) before auto-rescheduling a session. */
|
|
24
|
+
export const DEFAULT_RESCHEDULE_DELAY_MINUTES = 60;
|
|
25
|
+
|
|
23
26
|
export const DEFAULT_SYSTEM_PROMPT = `You are Claude Code, 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.
|
|
24
27
|
|
|
25
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.
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
const OPTION_TOKEN_DASHES = /(^|\s)([-\u2010-\u2015\u2212]+)(?=[A-Za-z0-9-])/g;
|
|
4
|
+
const UNICODE_DASHES = /[\u2010-\u2015\u2212]/g;
|
|
5
|
+
const HAS_UNICODE_DASH = /[\u2010-\u2015\u2212]/;
|
|
6
|
+
|
|
7
|
+
export function normalizeCommandOptionDashes(command) {
|
|
8
|
+
return command.replace(OPTION_TOKEN_DASHES, (match, prefix, dashes) => {
|
|
9
|
+
if (!HAS_UNICODE_DASH.test(dashes)) {
|
|
10
|
+
return match;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return `${prefix}${dashes.replace(UNICODE_DASHES, '-')}`;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
3
17
|
export const CreateCommandButtonRequest = z.object({
|
|
4
18
|
label: z.string().min(1, 'Label is required'),
|
|
5
|
-
command: z.string().min(1, 'Command is required'),
|
|
19
|
+
command: z.string().min(1, 'Command is required').transform(normalizeCommandOptionDashes),
|
|
6
20
|
sortOrder: z.number().int().optional().default(0),
|
|
7
21
|
showOnList: z.boolean().optional().default(false),
|
|
8
22
|
});
|
|
9
23
|
|
|
10
24
|
export const UpdateCommandButtonRequest = z.object({
|
|
11
25
|
label: z.string().min(1).optional(),
|
|
12
|
-
command: z.string().min(1).optional(),
|
|
26
|
+
command: z.string().min(1).transform(normalizeCommandOptionDashes).optional(),
|
|
13
27
|
sortOrder: z.number().int().optional(),
|
|
14
28
|
showOnList: z.boolean().optional(),
|
|
15
29
|
}).refine(obj => Object.keys(obj).length > 0, 'At least one field must be provided for update');
|
|
@@ -46,7 +46,7 @@ export const ProjectSessionDefaultsRequest = z.object({
|
|
|
46
46
|
thinkingEnabled: z.boolean().nullable().optional(),
|
|
47
47
|
effortLevel: z.enum(['low', 'medium', 'high', 'max', 'auto']).nullable().optional(),
|
|
48
48
|
startImmediately: z.boolean().nullable().optional(),
|
|
49
|
-
gitMode: z.enum(['branch', 'worktree']).nullable().optional(),
|
|
49
|
+
gitMode: z.enum(['branch', 'worktree', 'current']).nullable().optional(),
|
|
50
50
|
gitBranch: z.string().nullable().optional(),
|
|
51
51
|
model: z.string().nullable().optional(),
|
|
52
52
|
providerId: z.string().nullable().optional(),
|
|
@@ -59,7 +59,7 @@ export const ProjectSessionDefaultsResponse = z.object({
|
|
|
59
59
|
thinkingEnabled: z.boolean().nullable(),
|
|
60
60
|
effortLevel: z.enum(['low', 'medium', 'high', 'max', 'auto']).nullable(),
|
|
61
61
|
startImmediately: z.boolean().nullable(),
|
|
62
|
-
gitMode: z.enum(['branch', 'worktree']).nullable(),
|
|
62
|
+
gitMode: z.enum(['branch', 'worktree', 'current']).nullable(),
|
|
63
63
|
gitBranch: z.string().nullable(),
|
|
64
64
|
model: z.string().nullable(),
|
|
65
65
|
providerId: z.string().nullable(),
|