circuschief 0.6.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 +35 -4
- package/packages/server/src/api/projects.js +11 -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-draft.js +1 -0
- 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/api/settings.js +52 -4
- package/packages/server/src/database.js +0 -2
- package/packages/server/src/db/ConversationRepository.js +16 -3
- package/packages/server/src/db/DatabaseManager.js +5 -1
- package/packages/server/src/db/ProjectDefaultsRepository.js +50 -40
- package/packages/server/src/db/ProviderRepository.js +87 -32
- package/packages/server/src/db/SessionRepository.js +13 -8
- package/packages/server/src/db/SettingsRepository.js +44 -16
- package/packages/server/src/db/conversation-helpers.js +1 -0
- package/packages/server/src/db/index.js +0 -3
- package/packages/server/src/db/migrations/index.js +36 -200
- package/packages/server/src/db/seedBaselineData.js +137 -0
- package/packages/server/src/db/session-helpers.js +9 -3
- package/packages/server/src/middleware/sessionLookup.js +81 -8
- package/packages/server/src/schema.sql +157 -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/agentCallLogger.js +1 -1
- package/packages/server/src/services/codexSpawnHelper.js +9 -0
- package/packages/server/src/services/commandButtonPrompts.js +48 -0
- package/packages/server/src/services/commandRunner.js +7 -1
- package/packages/server/src/services/draftSessionService.js +2 -0
- 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 -106
- package/packages/server/src/services/sessionPrompts.js +16 -47
- package/packages/server/src/services/sessionProvider.js +16 -8
- package/packages/server/src/services/streamEventCallbacks.js +72 -40
- package/packages/server/src/services/streamEventHandler.js +13 -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 +11 -7
- package/packages/server/src/services/summaryStaleCheck.js +23 -4
- package/packages/server/src/services/templateTriggerService.js +3 -1
- package/packages/server/src/services/usageTracker.js +5 -1
- package/packages/server/src/services/visibleFinalErrorMessage.js +123 -0
- package/packages/shared/src/constants.js +4 -2
- package/packages/shared/src/contracts/commandButtons.js +16 -2
- package/packages/shared/src/contracts/projects.js +4 -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 +11 -19
- package/packages/web/dist/assets/ActiveSessionsView-B0XHqLmv.js +1 -0
- package/packages/web/dist/assets/{AgentLogsView-Cdw4nmvd.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-DnFhJY5A.js → CommandButtonDetailView-CdSCPp78.js} +1 -1
- package/packages/web/dist/assets/EffortLevelSelector-hc2MNKg6.js +1 -0
- package/packages/web/dist/assets/{GeneralSettingsView-CQkmdczf.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-PfYR3KJo.js → InterpolationHelp-BO1j9Z3_.js} +1 -1
- package/packages/web/dist/assets/MarkdownEditor-ucRAP_UM.js +2 -0
- package/packages/web/dist/assets/{ModelSelector-BZOT1Jc6.css → ModelSelector-BSxKUSus.css} +1 -1
- package/packages/web/dist/assets/ModelSelector-CwTz8ZWO.js +1 -0
- package/packages/web/dist/assets/NewSessionView-BDPb-1qr.css +1 -0
- 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-J15mcsWz.css +1 -0
- package/packages/web/dist/assets/{ProjectListView-CuYMmd3O.js → ProjectListView-DcNyuINs.js} +1 -1
- package/packages/web/dist/assets/{ProjectNewView-CNaA4Maf.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-BQwQXuL7.js +1 -0
- package/packages/web/dist/assets/{QuickResponsesPanel-DIBQFj0W.css → QuickResponsesPanel-BlFDvnZ2.css} +1 -1
- package/packages/web/dist/assets/{QuickResponsesPanel-BqMYSHb0.js → QuickResponsesPanel-BzSYcCSP.js} +1 -1
- package/packages/web/dist/assets/{ResizableTextarea-wYF3K2RO.js → ResizableTextarea-B3YIdIXv.js} +1 -1
- package/packages/web/dist/assets/{SessionCard-bLaQEWWX.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-B6AxyREh.js +1 -0
- package/packages/web/dist/assets/SessionFormOptions-BpUALRKn.css +1 -0
- 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-DTnDAF95.js → SessionLogStream-LlZ3z_Xj.js} +1 -1
- package/packages/web/dist/assets/{SettingsView-DNLUSsHV.js → SettingsView-CTGiGvR2.js} +1 -1
- package/packages/web/dist/assets/{SlashCommandWizard-CRGFaO8t.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-BR2ZjEa3.js +1 -0
- package/packages/web/dist/assets/SummarySettingsView-l2bxHmZZ.css +1 -0
- package/packages/web/dist/assets/TemplateDetailView-DH6Oswsp.js +1 -0
- package/packages/web/dist/assets/{commandButtons-Bbjf3fCt.js → commandButtons-BfqR-fqq.js} +1 -1
- package/packages/web/dist/assets/index-1zziPL6l.js +1 -0
- package/packages/web/dist/assets/index-7kzHPxSF.js +1 -0
- package/packages/web/dist/assets/index-B0N_obMc.js +1 -0
- package/packages/web/dist/assets/index-BNk_gdfI.js +1 -0
- package/packages/web/dist/assets/{index-gmCCsCQ1.css → index-BY174HVJ.css} +1 -1
- package/packages/web/dist/assets/index-CSqaAH-0.js +1 -0
- package/packages/web/dist/assets/index-C_q4WlK8.js +1 -0
- package/packages/web/dist/assets/index-D1wpU4y0.js +7 -0
- package/packages/web/dist/assets/index-D5zCA8sD.js +1 -0
- package/packages/web/dist/assets/index-DGR8ELWY.js +1 -0
- package/packages/web/dist/assets/index-DHga8pXo.js +1 -0
- package/packages/web/dist/assets/index-DSby02Wl.js +1 -0
- package/packages/web/dist/assets/{index-Cf6vdW-B.js → index-DgkC10TW.js} +3 -3
- package/packages/web/dist/assets/index-DqjXJTVI.js +1 -0
- package/packages/web/dist/assets/{index-Bs7Qf5D6.js → index-DtfUt785.js} +2 -2
- package/packages/web/dist/assets/index-_4S2uLDI.js +1 -0
- package/packages/web/dist/assets/{index-BQL_L4gL.js → index-fK8FIZgP.js} +15 -14
- package/packages/web/dist/assets/index-gmiZeFXN.js +1 -0
- package/packages/web/dist/assets/index-irD539ZM.js +3 -0
- package/packages/web/dist/assets/index-yq-E1Y00.js +1 -0
- package/packages/web/dist/assets/{projects-CPt3AB7U.js → projects-DXYQNJIi.js} +1 -1
- package/packages/web/dist/assets/{providers-ChfeMvUq.js → providers-1bnH-exJ.js} +1 -1
- package/packages/web/dist/assets/sessions-6zGUlFrt.js +1 -0
- package/packages/web/dist/assets/settings-MbfRir0d.js +1 -0
- 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 -183
- 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 -282
- package/packages/web/dist/assets/ActiveSessionsView-UCbQrF1b.js +0 -1
- package/packages/web/dist/assets/ApiClient-CWbXWDUY.js +0 -1
- package/packages/web/dist/assets/ArchiveConfirmModal-BQ-4gI0R.css +0 -1
- package/packages/web/dist/assets/ArchiveConfirmModal-J48eh3zw.js +0 -1
- package/packages/web/dist/assets/EffortLevelSelector-bXbPo4Zw.js +0 -1
- package/packages/web/dist/assets/InputWithButton-XyM3k6lN.js +0 -1
- package/packages/web/dist/assets/MarkdownEditor-P8F5kO-o.js +0 -2
- package/packages/web/dist/assets/ModelSelector-CowKfGMP.js +0 -1
- package/packages/web/dist/assets/NewSessionView-D_Hi7M9g.css +0 -1
- package/packages/web/dist/assets/NewSessionView-DkjFLvHU.js +0 -3
- package/packages/web/dist/assets/ProjectEditView-CpeKj-_w.css +0 -1
- package/packages/web/dist/assets/ProjectEditView-embVT7NC.js +0 -1
- package/packages/web/dist/assets/ProvidersView-C7rydtOd.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-BTQEKhwJ.js +0 -1
- package/packages/web/dist/assets/SessionDetailView-Cv-xMzXp.css +0 -1
- package/packages/web/dist/assets/SessionDetailView-CvQOUsW2.js +0 -36
- package/packages/web/dist/assets/SessionFormOptions-3pzbgI2Q.js +0 -1
- package/packages/web/dist/assets/SessionFormOptions-DhhIkjIS.css +0 -1
- package/packages/web/dist/assets/SessionListView-Dranfb72.js +0 -1
- package/packages/web/dist/assets/SessionListView-fHlQyecX.css +0 -1
- package/packages/web/dist/assets/SummarySettingsView-C7G_suHp.js +0 -1
- package/packages/web/dist/assets/SummarySettingsView-DcsmSVJI.css +0 -1
- package/packages/web/dist/assets/TemplateDetailView-B78_DLMR.js +0 -1
- package/packages/web/dist/assets/index--V7c-VZf.js +0 -1
- package/packages/web/dist/assets/index-8Q04yd7H.js +0 -1
- package/packages/web/dist/assets/index-B47XRBDH.js +0 -1
- package/packages/web/dist/assets/index-BXbgZrhS.js +0 -1
- package/packages/web/dist/assets/index-CGhDVPen.js +0 -1
- package/packages/web/dist/assets/index-CKcRO1A6.js +0 -1
- package/packages/web/dist/assets/index-CTq-SLIW.js +0 -1
- package/packages/web/dist/assets/index-CYyos3iC.js +0 -1
- package/packages/web/dist/assets/index-CsCREAxF.js +0 -1
- package/packages/web/dist/assets/index-DJTTk_8T.js +0 -3
- package/packages/web/dist/assets/index-DPqUJ5JK.js +0 -1
- package/packages/web/dist/assets/index-EwAe1dKg.js +0 -1
- package/packages/web/dist/assets/index-JBA8axyA.js +0 -1
- package/packages/web/dist/assets/index-JkVHFtK5.js +0 -7
- package/packages/web/dist/assets/index-gMPUwT55.js +0 -1
- package/packages/web/dist/assets/index-wadc_0zT.js +0 -1
- package/packages/web/dist/assets/sessions-CwPsJOb1.js +0 -1
- package/packages/web/dist/assets/settings-BOj6wq6t.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,95 +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
|
-
settingSources: ['project'],
|
|
86
|
-
...(resumeSessionId && { resume: resumeSessionId }),
|
|
87
|
-
env: sessionEnv,
|
|
88
|
-
spawnClaudeCodeProcess: createClaudeCodeSpawner(),
|
|
89
|
-
model: effectiveModel,
|
|
90
|
-
systemPrompt: buildSystemPromptConfig(sessionId, session.projectId, systemPrompt, session.mode),
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Build query parameters for the Codex adapter.
|
|
97
|
-
*
|
|
98
|
-
* Codex in v1 is a simple Chat-Completions-shaped executor — it doesn't need
|
|
99
|
-
* or accept Claude-specific options (permissionMode, settingSources,
|
|
100
|
-
* includePartialMessages, spawnClaudeCodeProcess, resume).
|
|
101
|
-
*
|
|
102
|
-
* Codex does have its own sandboxing model, driven from {@code session.mode}
|
|
103
|
-
* via {@link getSandboxModeForSession}. Codex CLI v0.124.0 also supports
|
|
104
|
-
* resume via `codex resume` / `codex exec resume`, but Circus Chief v1
|
|
105
|
-
* intentionally does NOT pass a resume token — wiring is deferred to a
|
|
106
|
-
* later phase (see canvas plan §Phase 4.5).
|
|
107
|
-
*
|
|
108
|
-
* @returns {Object}
|
|
109
|
-
*/
|
|
110
|
-
function buildCodexQueryParams({
|
|
111
|
-
prompt, workingDirectory, controller, session, sessionId, systemPrompt, model, sessionEnv,
|
|
112
|
-
}) {
|
|
113
|
-
const isVCR = Boolean(process.env.VCR_MODE);
|
|
114
|
-
// In VCR mode, force the cheapest commonly-cassetted OpenAI model.
|
|
115
|
-
const effectiveModel = isVCR ? 'gpt-4o-mini' : model;
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
prompt,
|
|
119
|
-
options: {
|
|
120
|
-
cwd: workingDirectory,
|
|
121
|
-
abortController: controller,
|
|
122
|
-
env: sessionEnv,
|
|
123
|
-
model: effectiveModel,
|
|
124
|
-
effortLevel: session?.effortLevel ?? null,
|
|
125
|
-
systemPrompt: buildSystemPromptConfig(sessionId, session.projectId, systemPrompt, session.mode),
|
|
126
|
-
sandboxMode: getSandboxModeForSession(session?.mode),
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Build query parameters for executing a session via the configured agent.
|
|
133
|
-
* Shared by runSession, continueSession, and continueSessionWithExistingMessage.
|
|
134
|
-
*
|
|
135
|
-
* @param {Object} options
|
|
136
|
-
* @param {string} options.prompt - The prompt text to send
|
|
137
|
-
* @param {string} options.workingDirectory - Session working directory
|
|
138
|
-
* @param {AbortController} options.controller - Abort controller for the session
|
|
139
|
-
* @param {Object} options.session - Session object from DB
|
|
140
|
-
* @param {string} options.sessionId - Session ID
|
|
141
|
-
* @param {string|null} options.systemPrompt - Custom system prompt from project settings
|
|
142
|
-
* @param {string|null} options.model - Model to use
|
|
143
|
-
* @param {Object} options.sessionEnv - Environment variables for the session
|
|
144
|
-
* @param {string|null} [options.resumeSessionId] - Session ID to resume (null for new session)
|
|
145
|
-
* @param {string} [options.agentType] - 'claude-code' (default) | 'codex'
|
|
146
|
-
* @returns {Object} Query parameters for agent.execute()
|
|
147
|
-
*/
|
|
148
|
-
export function buildQueryParams(options) {
|
|
149
|
-
const { agentType = 'claude-code' } = options || {};
|
|
150
|
-
if (agentType === 'codex') {
|
|
151
|
-
return buildCodexQueryParams(options);
|
|
152
|
-
}
|
|
153
|
-
return buildClaudeCodeQueryParams(options);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
95
|
/**
|
|
157
96
|
* Execute the agent stream loop and handle post-turn completion, errors, and cleanup.
|
|
158
97
|
* This is the shared core of runSession, continueSession, and continueSessionWithExistingMessage.
|
|
@@ -258,7 +197,12 @@ function buildContinueModelAndEnv(session, sessionId, model) {
|
|
|
258
197
|
|
|
259
198
|
// Derive provider from the effective model ID (returns null for Anthropic/SDK defaults)
|
|
260
199
|
const provider = resolveProviderFromModel(effectiveModel);
|
|
261
|
-
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
|
+
);
|
|
262
206
|
|
|
263
207
|
// Check if model changed from the session's last requested model
|
|
264
208
|
// When model changes, we can't resume the previous session - thinking blocks and
|
|
@@ -273,7 +217,13 @@ function buildContinueModelAndEnv(session, sessionId, model) {
|
|
|
273
217
|
updatedSession = sessions.getById(sessionId); // refresh
|
|
274
218
|
}
|
|
275
219
|
|
|
276
|
-
return {
|
|
220
|
+
return {
|
|
221
|
+
effectiveModel,
|
|
222
|
+
sessionEnv,
|
|
223
|
+
commitAttributionOverride,
|
|
224
|
+
modelChanged,
|
|
225
|
+
session: updatedSession,
|
|
226
|
+
};
|
|
277
227
|
}
|
|
278
228
|
|
|
279
229
|
/**
|
|
@@ -284,7 +234,7 @@ function buildContinueModelAndEnv(session, sessionId, model) {
|
|
|
284
234
|
async function buildContinueParams({
|
|
285
235
|
sessionId, session, model, systemPrompt, effectiveModel, sessionEnv,
|
|
286
236
|
modelChanged, activeConversation, promptWithAttachments,
|
|
287
|
-
workingDirectory, controller, agentType, agent,
|
|
237
|
+
workingDirectory, controller, agentType, agent, commitAttributionOverride,
|
|
288
238
|
}) {
|
|
289
239
|
// Only resume if we have a session ID AND model hasn't changed AND the
|
|
290
240
|
// agent supports resume.
|
|
@@ -306,6 +256,7 @@ async function buildContinueParams({
|
|
|
306
256
|
sessionEnv,
|
|
307
257
|
resumeSessionId: canResume ? activeConversation.claudeSessionId : null,
|
|
308
258
|
agentType,
|
|
259
|
+
commitAttributionOverride,
|
|
309
260
|
});
|
|
310
261
|
|
|
311
262
|
// Logging metadata for agent call tracking
|
|
@@ -392,11 +343,15 @@ export async function continueSessionCore(sessionId, content, workingDirectory,
|
|
|
392
343
|
// Resolve model/provider and detect model changes
|
|
393
344
|
const modelEnv = buildContinueModelAndEnv(session, sessionId, model);
|
|
394
345
|
session = modelEnv.session;
|
|
346
|
+
if (session.gitWorktree && modelEnv.commitAttributionOverride) {
|
|
347
|
+
await ensureWorktreeCommitAttributionHook(session.gitWorktree);
|
|
348
|
+
}
|
|
395
349
|
|
|
396
350
|
// Build query params and agent call meta
|
|
397
351
|
const { queryParams, agentCallMeta } = await buildContinueParams({
|
|
398
352
|
sessionId, session, model, systemPrompt,
|
|
399
353
|
effectiveModel: modelEnv.effectiveModel, sessionEnv: modelEnv.sessionEnv,
|
|
354
|
+
commitAttributionOverride: modelEnv.commitAttributionOverride,
|
|
400
355
|
modelChanged: modelEnv.modelChanged, activeConversation, promptWithAttachments,
|
|
401
356
|
workingDirectory, controller, agentType, agent,
|
|
402
357
|
});
|
|
@@ -457,14 +412,8 @@ export async function runSessionCore(sessionId, prompt, workingDirectory, config
|
|
|
457
412
|
const agentType = session.agentType || 'claude-code';
|
|
458
413
|
const agent = createAgentForSession(agentType);
|
|
459
414
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
// correctness if called directly).
|
|
463
|
-
const effectiveModel = model || session.model;
|
|
464
|
-
|
|
465
|
-
// Derive provider from the effective model ID (returns null for Anthropic/SDK defaults)
|
|
466
|
-
const provider = resolveProviderFromModel(effectiveModel);
|
|
467
|
-
const sessionEnv = buildSessionEnv(provider, session.thinkingEnabled, session.effortLevel);
|
|
415
|
+
const { effectiveModel, sessionEnv, commitAttributionOverride } =
|
|
416
|
+
await resolveInitialSessionModelEnv(session, model);
|
|
468
417
|
|
|
469
418
|
const queryParams = buildQueryParams({
|
|
470
419
|
prompt: promptWithAttachments,
|
|
@@ -476,6 +425,7 @@ export async function runSessionCore(sessionId, prompt, workingDirectory, config
|
|
|
476
425
|
model: effectiveModel,
|
|
477
426
|
sessionEnv,
|
|
478
427
|
agentType,
|
|
428
|
+
commitAttributionOverride,
|
|
479
429
|
});
|
|
480
430
|
|
|
481
431
|
// Log query params for debugging third-party provider issues
|