circuschief 0.7.0 → 0.8.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/index.js +2 -0
- package/packages/server/src/api/kanban.js +4 -2
- package/packages/server/src/api/projects-helpers.js +20 -3
- package/packages/server/src/api/projects-session-helpers.js +10 -4
- package/packages/server/src/api/projects.js +10 -0
- package/packages/server/src/api/providers.js +11 -1
- package/packages/server/src/api/sessions-commands.js +35 -17
- package/packages/server/src/api/sessions-lifecycle.js +10 -10
- package/packages/server/src/api/sessions-patch.js +4 -0
- 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 +1 -0
- package/packages/server/src/db/index.js +0 -3
- package/packages/server/src/db/migrations/index.js +36 -202
- package/packages/server/src/db/seedBaselineData.js +137 -0
- package/packages/server/src/db/session-helpers.js +6 -3
- package/packages/server/src/middleware/sessionLookup.js +81 -8
- package/packages/server/src/schema.sql +149 -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 +8 -8
- 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 +120 -0
- package/packages/server/src/services/gitService.js +11 -2
- package/packages/server/src/services/gitSessionSetup.js +11 -1
- 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 +2 -1
- package/packages/shared/src/contracts/templates.js +2 -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-B0XHqLmv.js} +1 -1
- package/packages/web/dist/assets/{AgentLogsView-BGFPLjLa.js → AgentLogsView-DmsjUMlB.js} +2 -2
- package/packages/web/dist/assets/ApiClient-C3ztI9s9.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-BlCyn5Vt.js +1 -0
- package/packages/web/dist/assets/ArchiveConfirmModal-DeoCVGXt.css +1 -0
- package/packages/web/dist/assets/{CommandButtonDetailView-D8S258uP.js → CommandButtonDetailView-CdSCPp78.js} +1 -1
- package/packages/web/dist/assets/EffortLevelSelector-hc2MNKg6.js +1 -0
- package/packages/web/dist/assets/{GeneralSettingsView-DsHChEhv.js → GeneralSettingsView-D1nI8_zk.js} +1 -1
- package/packages/web/dist/assets/InputWithButton-CAkttyqx.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-BO1j9Z3_.js} +1 -1
- package/packages/web/dist/assets/MarkdownEditor-ucRAP_UM.js +2 -0
- package/packages/web/dist/assets/{ModelSelector-D8hbTRIt.css → ModelSelector-BSxKUSus.css} +1 -1
- package/packages/web/dist/assets/{ModelSelector-BMpR0DPr.js → ModelSelector-CwTz8ZWO.js} +1 -1
- package/packages/web/dist/assets/{NewSessionView-CUUdHkfv.css → NewSessionView-BDPb-1qr.css} +1 -1
- package/packages/web/dist/assets/NewSessionView-BsDrp8mj.js +3 -0
- package/packages/web/dist/assets/ProjectEditView-CwTOeSun.js +1 -0
- package/packages/web/dist/assets/{ProjectEditView-D9sK0fdH.css → ProjectEditView-J15mcsWz.css} +1 -1
- package/packages/web/dist/assets/{ProjectListView-B9FuWESY.js → ProjectListView-DcNyuINs.js} +1 -1
- package/packages/web/dist/assets/{ProjectNewView-D62jYlBL.js → ProjectNewView-B5YV62hv.js} +1 -1
- package/packages/web/dist/assets/ProvidersView-bZemq_Rv.css +1 -0
- package/packages/web/dist/assets/ProvidersView-nY9GnDdO.js +1 -0
- package/packages/web/dist/assets/QuickResponseSettings-B352c75l.css +1 -0
- package/packages/web/dist/assets/{QuickResponseSettings-CDm5vwP7.js → QuickResponseSettings-BQwQXuL7.js} +1 -1
- package/packages/web/dist/assets/{QuickResponsesPanel-DZ_Lre_l.js → QuickResponsesPanel-BzSYcCSP.js} +1 -1
- package/packages/web/dist/assets/{ResizableTextarea-DiIOEGjN.js → ResizableTextarea-B3YIdIXv.js} +1 -1
- package/packages/web/dist/assets/{SessionCard-DmjnVYWn.js → SessionCard-CjE1tXiT.js} +1 -1
- package/packages/web/dist/assets/SessionDetailView-3cPZrbS3.js +36 -0
- package/packages/web/dist/assets/SessionDetailView-CZRZMrfM.css +1 -0
- package/packages/web/dist/assets/{SessionFormOptions-DYUISplS.js → SessionFormOptions-B6AxyREh.js} +1 -1
- package/packages/web/dist/assets/SessionListView-B5_6gW49.css +1 -0
- package/packages/web/dist/assets/SessionListView-CLXBfLcq.js +1 -0
- package/packages/web/dist/assets/{SessionLogStream-DpUE6Xsh.js → SessionLogStream-LlZ3z_Xj.js} +1 -1
- package/packages/web/dist/assets/{SettingsView-BC055tIA.js → SettingsView-CTGiGvR2.js} +1 -1
- package/packages/web/dist/assets/{SlashCommandWizard-DmTyNG9O.js → SlashCommandWizard-Cy04d7-o.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-BR2ZjEa3.js} +1 -1
- package/packages/web/dist/assets/{TemplateDetailView-BlhOmLUX.js → TemplateDetailView-DH6Oswsp.js} +1 -1
- package/packages/web/dist/assets/{commandButtons-D4RPpLiu.js → commandButtons-BfqR-fqq.js} +1 -1
- package/packages/web/dist/assets/{index-CfL84oGW.js → index-1zziPL6l.js} +1 -1
- package/packages/web/dist/assets/{index-OfCywayk.js → index-7kzHPxSF.js} +1 -1
- package/packages/web/dist/assets/{index-PDesaJc6.js → index-B0N_obMc.js} +1 -1
- package/packages/web/dist/assets/{index-Cpy4-yv3.js → index-BNk_gdfI.js} +1 -1
- package/packages/web/dist/assets/{index-Cs2nxhrT.css → index-BY174HVJ.css} +1 -1
- package/packages/web/dist/assets/{index-9vb2KaAd.js → index-CSqaAH-0.js} +1 -1
- package/packages/web/dist/assets/{index-CNwkdB0T.js → index-C_q4WlK8.js} +1 -1
- package/packages/web/dist/assets/{index-B0CvZXuN.js → index-D1wpU4y0.js} +1 -1
- package/packages/web/dist/assets/{index-4rhEeO0B.js → index-D5zCA8sD.js} +1 -1
- package/packages/web/dist/assets/{index-CkmxO8Mm.js → index-DGR8ELWY.js} +1 -1
- package/packages/web/dist/assets/{index-CrAQJmoZ.js → index-DHga8pXo.js} +1 -1
- package/packages/web/dist/assets/{index-BUhvkAdF.js → index-DSby02Wl.js} +1 -1
- package/packages/web/dist/assets/{index-BGwH4Cfn.js → index-DgkC10TW.js} +3 -3
- package/packages/web/dist/assets/{index-DfrE0gAC.js → index-DqjXJTVI.js} +1 -1
- package/packages/web/dist/assets/{index-Bn5xdGFM.js → index-DtfUt785.js} +1 -1
- package/packages/web/dist/assets/{index-KwEyz0F3.js → index-_4S2uLDI.js} +1 -1
- package/packages/web/dist/assets/{index-B6G18FqB.js → index-fK8FIZgP.js} +15 -14
- package/packages/web/dist/assets/{index-BcnkUk2o.js → index-gmiZeFXN.js} +1 -1
- package/packages/web/dist/assets/{index-D6Ky9vJe.js → index-irD539ZM.js} +1 -1
- package/packages/web/dist/assets/{index-uB6nhSvz.js → index-yq-E1Y00.js} +1 -1
- package/packages/web/dist/assets/{projects-BUiOGmmb.js → projects-DXYQNJIi.js} +1 -1
- package/packages/web/dist/assets/{providers-Bh1ZiiJi.js → providers-1bnH-exJ.js} +1 -1
- package/packages/web/dist/assets/sessions-6zGUlFrt.js +1 -0
- package/packages/web/dist/assets/{settings-Z4AVVmkJ.js → settings-MbfRir0d.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/server/src/db/migrations/canvasItemsMigrations.js +0 -109
- package/packages/server/src/db/migrations/conversationsMigrations.js +0 -187
- package/packages/server/src/db/migrations/kanbanMigrations.js +0 -99
- package/packages/server/src/db/migrations/miscMigrations.js +0 -369
- package/packages/server/src/db/migrations/projectsMigrations.js +0 -99
- package/packages/server/src/db/migrations/sessionsMigrations.js +0 -287
- 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/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/NewSessionView-BCqtIgWH.js +0 -3
- 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/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/sessions-DH1R-NhV.js +0 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { chmod, mkdir, writeFile } from 'fs/promises';
|
|
5
|
+
|
|
6
|
+
const execAsync = promisify(exec);
|
|
7
|
+
const MANAGED_HOOKS_PATH = '.circuschief-hooks';
|
|
8
|
+
const ATTRIBUTION_CONFIG_KEY = 'circuschief.commitAttribution';
|
|
9
|
+
const ATTRIBUTION_ENV_KEY = 'CIRCUSCHIEF_COMMIT_ATTRIBUTION';
|
|
10
|
+
|
|
11
|
+
async function git(directory, command) {
|
|
12
|
+
const { stdout } = await execAsync(`git ${command}`, { cwd: directory });
|
|
13
|
+
return stdout.trim();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function gitConfigValue(directory, key) {
|
|
17
|
+
try {
|
|
18
|
+
return await git(directory, `config --get ${key}`);
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function shellQuote(value) {
|
|
25
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildCommitMsgHook() {
|
|
29
|
+
return `#!/bin/sh
|
|
30
|
+
set -eu
|
|
31
|
+
|
|
32
|
+
msg_file="$1"
|
|
33
|
+
trailer="\${${ATTRIBUTION_ENV_KEY}:-}"
|
|
34
|
+
|
|
35
|
+
[ -n "$trailer" ] || exit 0
|
|
36
|
+
[ -n "$(printf '%s' "$trailer" | tr -d '[:space:]')" ] || exit 0
|
|
37
|
+
|
|
38
|
+
if printf '%s' "$trailer" | grep '[[:cntrl:]]' >/dev/null 2>&1; then
|
|
39
|
+
echo "${ATTRIBUTION_ENV_KEY} must be a canonical Co-authored-by: Name <email> trailer." >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
if ! printf '%s\\n' "$trailer" | grep -E '^Co-authored-by: [^<>[:space:]][^<>]* <[^[:space:]<>@]+@[^[:space:]<>@]+\\.[^[:space:]<>@]+>$' >/dev/null 2>&1; then
|
|
44
|
+
echo "${ATTRIBUTION_ENV_KEY} must be a canonical Co-authored-by: Name <email> trailer." >&2
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
if grep -F -x -i -- "$trailer" "$msg_file" >/dev/null 2>&1; then
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
git interpret-trailers --trailer "$trailer" --in-place "$msg_file"
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function clearWorktreeCommitAttribution(worktreePath) {
|
|
57
|
+
const currentAttribution = await gitConfigValue(worktreePath, ATTRIBUTION_CONFIG_KEY);
|
|
58
|
+
const currentHooksPath = await gitConfigValue(worktreePath, 'core.hooksPath');
|
|
59
|
+
if (!currentAttribution && currentHooksPath !== MANAGED_HOOKS_PATH) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await git(worktreePath, 'config extensions.worktreeConfig true');
|
|
64
|
+
|
|
65
|
+
if (currentAttribution) {
|
|
66
|
+
try {
|
|
67
|
+
await git(worktreePath, `config --worktree --unset ${ATTRIBUTION_CONFIG_KEY}`);
|
|
68
|
+
} catch {
|
|
69
|
+
// Unset is idempotent for callers that are clearing a value that was never set.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (currentHooksPath === MANAGED_HOOKS_PATH) {
|
|
74
|
+
try {
|
|
75
|
+
await git(worktreePath, 'config --worktree --unset core.hooksPath');
|
|
76
|
+
} catch {
|
|
77
|
+
// Unset is idempotent for callers that are clearing stale managed hook config.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Install/update managed commit attribution enforcement for a worktree.
|
|
85
|
+
*
|
|
86
|
+
* Runtime attribution is process-scoped: the hook reads
|
|
87
|
+
* CIRCUSCHIEF_COMMIT_ATTRIBUTION from the `git commit` process environment.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} worktreePath - The worktree directory
|
|
90
|
+
* @returns {Promise<boolean>} True when a hook is installed or updated
|
|
91
|
+
*/
|
|
92
|
+
export async function ensureWorktreeCommitAttributionHook(worktreePath) {
|
|
93
|
+
await git(worktreePath, 'config extensions.worktreeConfig true');
|
|
94
|
+
|
|
95
|
+
const currentHooksPath = await gitConfigValue(worktreePath, 'core.hooksPath');
|
|
96
|
+
if (currentHooksPath && currentHooksPath !== MANAGED_HOOKS_PATH) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Cannot install managed commit attribution hook: worktree already has core.hooksPath set to "${currentHooksPath}"`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
await git(worktreePath, `config --worktree --unset ${ATTRIBUTION_CONFIG_KEY}`);
|
|
104
|
+
} catch {
|
|
105
|
+
// Unset is idempotent for stale worktrees that never stored attribution.
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await git(worktreePath, `config --worktree core.hooksPath ${shellQuote(MANAGED_HOOKS_PATH)}`);
|
|
109
|
+
|
|
110
|
+
const hooksDir = path.join(worktreePath, MANAGED_HOOKS_PATH);
|
|
111
|
+
const hookPath = path.join(hooksDir, 'commit-msg');
|
|
112
|
+
await mkdir(hooksDir, { recursive: true });
|
|
113
|
+
await writeFile(hookPath, buildCommitMsgHook(), 'utf8');
|
|
114
|
+
await chmod(hookPath, 0o755);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function configureWorktreeCommitAttribution(worktreePath, _commitAttribution) {
|
|
119
|
+
return ensureWorktreeCommitAttributionHook(worktreePath);
|
|
120
|
+
}
|
|
@@ -2,6 +2,11 @@ import { exec } from 'child_process';
|
|
|
2
2
|
import { promisify } from 'util';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { realpath } from 'fs/promises';
|
|
5
|
+
export {
|
|
6
|
+
clearWorktreeCommitAttribution,
|
|
7
|
+
configureWorktreeCommitAttribution,
|
|
8
|
+
ensureWorktreeCommitAttributionHook,
|
|
9
|
+
} from './gitCommitAttribution.js';
|
|
5
10
|
|
|
6
11
|
const execAsync = promisify(exec);
|
|
7
12
|
|
|
@@ -76,6 +81,10 @@ async function git(directory, command, opts = {}) {
|
|
|
76
81
|
return stdout.trim();
|
|
77
82
|
}
|
|
78
83
|
|
|
84
|
+
function shellQuote(value) {
|
|
85
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
86
|
+
}
|
|
87
|
+
|
|
79
88
|
/**
|
|
80
89
|
* Detect the default branch using git commands (symbolic-ref, rev-parse).
|
|
81
90
|
* @param {string} directory
|
|
@@ -515,8 +524,8 @@ export async function pinAuthorInWorktree(worktreePath, projectDir, { env } = {}
|
|
|
515
524
|
|
|
516
525
|
// Pin the human's identity in the worktree config so they are always
|
|
517
526
|
// the commit Author, regardless of what the session does later
|
|
518
|
-
await git(worktreePath, `config --worktree user.name
|
|
519
|
-
await git(worktreePath, `config --worktree user.email
|
|
527
|
+
await git(worktreePath, `config --worktree user.name ${shellQuote(author.name)}`);
|
|
528
|
+
await git(worktreePath, `config --worktree user.email ${shellQuote(author.email)}`);
|
|
520
529
|
|
|
521
530
|
return true;
|
|
522
531
|
}
|
|
@@ -9,9 +9,17 @@ import * as gitService from './gitService.js';
|
|
|
9
9
|
* @param {string|null} options.gitBranch - Branch name
|
|
10
10
|
* @param {string} options.sessionId - Session ID
|
|
11
11
|
* @param {string|null} [options.worktreeBasePath] - Custom base path for worktrees (overrides default .worktrees)
|
|
12
|
+
* @param {string|null} [options.commitAttributionOverride] - Deprecated; attribution is process-scoped at agent launch
|
|
12
13
|
* @returns {Promise<{workingDirectory: string, gitWorktree: string|null}>}
|
|
13
14
|
*/
|
|
14
|
-
export async function setupGitForSession({
|
|
15
|
+
export async function setupGitForSession({
|
|
16
|
+
projectDir,
|
|
17
|
+
gitMode,
|
|
18
|
+
gitBranch,
|
|
19
|
+
sessionId,
|
|
20
|
+
worktreeBasePath,
|
|
21
|
+
commitAttributionOverride = null,
|
|
22
|
+
}) {
|
|
15
23
|
// No git operations if gitMode is not specified
|
|
16
24
|
if (!gitMode || !gitBranch) {
|
|
17
25
|
return {
|
|
@@ -35,6 +43,8 @@ export async function setupGitForSession({ projectDir, gitMode, gitBranch, sessi
|
|
|
35
43
|
await gitService.createWorktreeForBranch(projectDir, gitBranch, worktreePath);
|
|
36
44
|
// Pin the human developer's git identity so they are the commit Author
|
|
37
45
|
await gitService.pinAuthorInWorktree(worktreePath, projectDir);
|
|
46
|
+
void commitAttributionOverride;
|
|
47
|
+
await gitService.ensureWorktreeCommitAttributionHook(worktreePath);
|
|
38
48
|
return {
|
|
39
49
|
workingDirectory: worktreePath,
|
|
40
50
|
gitWorktree: worktreePath,
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
projects,
|
|
6
6
|
} from '../database.js';
|
|
7
7
|
import { broadcastToProject } from '../websocket.js';
|
|
8
|
-
import { WS_MESSAGE_TYPES } from '../../../shared/src/index.js';
|
|
8
|
+
import { WS_MESSAGE_TYPES, DEFAULT_RESCHEDULE_DELAY_MINUTES } from '../../../shared/src/index.js';
|
|
9
9
|
import { renderTemplatePrompt, getRootSession } from './templateTriggerService.js';
|
|
10
10
|
import { setupGitForSession } from './gitSessionSetup.js';
|
|
11
11
|
import { runSession } from './sessionManager.js';
|
|
12
|
-
import { resolveAgentTypeFromModel } from './sessionProvider.js';
|
|
12
|
+
import { resolveAgentTypeFromModel, resolveProviderMetadataFromModel } from './sessionProvider.js';
|
|
13
13
|
|
|
14
14
|
// Maximum depth for recursive lane-entry template triggers
|
|
15
15
|
export const MAX_LANE_TRIGGER_DEPTH = 5;
|
|
@@ -56,6 +56,8 @@ export async function determineWorkingDirectory(parentSession, project, gitOptio
|
|
|
56
56
|
gitBranch: gitOptions.gitBranch || null,
|
|
57
57
|
sessionId: gitOptions.sessionId,
|
|
58
58
|
worktreeBasePath: project.worktreePath || null,
|
|
59
|
+
commitAttributionOverride:
|
|
60
|
+
resolveProviderMetadataFromModel(gitOptions.model)?.commitAttributionOverride ?? null,
|
|
59
61
|
});
|
|
60
62
|
return { workingDirectory: gitSetup.workingDirectory, gitWorktree: gitSetup.gitWorktree };
|
|
61
63
|
}
|
|
@@ -190,6 +192,7 @@ export async function triggerOnEnterTemplate(sessionId, lane, options = {}) {
|
|
|
190
192
|
gitMode: settings.gitMode,
|
|
191
193
|
gitBranch: settings.gitBranch,
|
|
192
194
|
sessionId: newSession.id,
|
|
195
|
+
model: settings.model,
|
|
193
196
|
});
|
|
194
197
|
if (gitWorktree) {
|
|
195
198
|
sessions.update(newSession.id, { gitWorktree });
|
|
@@ -250,7 +253,7 @@ async function buildChildSessionFromPrompt(lane, session, depth) {
|
|
|
250
253
|
if (lane.onEnterAutoRescheduleEnabled) {
|
|
251
254
|
Object.assign(sessionUpdates, {
|
|
252
255
|
autoRescheduleEnabled: true,
|
|
253
|
-
rescheduleDelayMinutes: lane.onEnterRescheduleDelayMinutes ||
|
|
256
|
+
rescheduleDelayMinutes: lane.onEnterRescheduleDelayMinutes || DEFAULT_RESCHEDULE_DELAY_MINUTES,
|
|
254
257
|
rescheduleOnTokenLimit: lane.onEnterRescheduleOnTokenLimit ?? true,
|
|
255
258
|
rescheduleOnServiceError: lane.onEnterRescheduleOnServiceError ?? true,
|
|
256
259
|
maxRescheduleCount: lane.onEnterMaxRescheduleCount || null,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import {
|
|
4
|
+
captureSpawnAttempt,
|
|
5
|
+
createCapturedSpawnProcess,
|
|
6
|
+
isE2ESpawnCaptureEnabled,
|
|
7
|
+
} from './e2eSpawnCapture.js';
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
10
|
* Get the directory containing the current Node.js executable.
|
|
@@ -42,6 +47,10 @@ export function createRobustEnv(baseEnv = process.env) {
|
|
|
42
47
|
export function createClaudeCodeSpawner() {
|
|
43
48
|
return (options) => {
|
|
44
49
|
const { command, args, cwd, env, signal } = options;
|
|
50
|
+
if (isE2ESpawnCaptureEnabled()) {
|
|
51
|
+
captureSpawnAttempt('claude-code', options);
|
|
52
|
+
return createCapturedSpawnProcess('claude-code');
|
|
53
|
+
}
|
|
45
54
|
|
|
46
55
|
// Replace 'node' with the absolute path to the current Node executable
|
|
47
56
|
// This ensures we use the same Node that's running our app
|
|
@@ -126,8 +126,8 @@ export async function extractPrUrlIfNeeded(sessionId) {
|
|
|
126
126
|
const session = sessions.getById(sessionId);
|
|
127
127
|
if (!session) return;
|
|
128
128
|
|
|
129
|
-
// Skip if session already has a PR URL
|
|
130
|
-
if (session.prUrl) return;
|
|
129
|
+
// Skip if session already has a PR URL or the user cleared automatic PR linking.
|
|
130
|
+
if (session.prUrl || session.prUrlAutoLinkDisabled) return;
|
|
131
131
|
|
|
132
132
|
// Extract PR URL from messages
|
|
133
133
|
const prUrl = extractPrUrlFromMessages(sessionId);
|
|
@@ -145,7 +145,7 @@ export async function extractPrUrlIfNeeded(sessionId) {
|
|
|
145
145
|
if (session.parentSessionId) {
|
|
146
146
|
const rootId = sessions.getRootSessionId(sessionId);
|
|
147
147
|
const root = rootId && rootId !== sessionId ? sessions.getById(rootId) : null;
|
|
148
|
-
if (root && !root.prUrl) {
|
|
148
|
+
if (root && !root.prUrl && !root.prUrlAutoLinkDisabled) {
|
|
149
149
|
sessions.update(root.id, { prUrl });
|
|
150
150
|
console.log(`[PrUrlService] Propagated PR URL from session ${sessionId} to root ${root.id}: ${prUrl}`);
|
|
151
151
|
|
|
@@ -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
|