fix(delegation): strip claudecode in core delegation spawn paths

- add shared CLAUDECODE sanitizer for child env construction

- apply sanitizer in execClaude and headless delegation execution

- sanitize GLMT embedded Claude spawn environment

Refs #588
This commit is contained in:
Tam Nhu Tran
2026-02-20 23:00:33 +07:00
parent 4303ee48c9
commit 50412dc679
3 changed files with 23 additions and 6 deletions
+3 -3
View File
@@ -37,7 +37,7 @@ import { handleShellCompletionCommand } from './commands/shell-completion-comman
import { handleUpdateCommand } from './commands/update-command';
// Import extracted utility functions
import { execClaude, escapeShellArg } from './utils/shell-executor';
import { execClaude, escapeShellArg, stripClaudeCodeEnv } from './utils/shell-executor';
import { wireChildProcessSignals } from './utils/signal-forwarder';
// Import target adapter system
@@ -197,13 +197,13 @@ async function execClaudeWithProxy(
const needsShell = isWindows && /\.(cmd|bat)$/i.test(claudeCli);
const webSearchEnv = getWebSearchHookEnv();
const imageAnalysisEnv = getImageAnalysisHookEnv(profileName);
const env = {
const env = stripClaudeCodeEnv({
...process.env,
...envVars,
...webSearchEnv,
...imageAnalysisEnv,
CCS_PROFILE_TYPE: 'settings', // Signal to WebSearch hook this is a third-party provider
};
});
let claude: ChildProcess;
if (isPowerShellScript) {
+2 -1
View File
@@ -17,6 +17,7 @@ import { StreamBuffer, formatToolVerbose } from './executor/stream-parser';
import { buildExecutionResult } from './executor/result-aggregator';
import { getCcsDir, getModelDisplayName } from '../utils/config-manager';
import { getProfileLookupCandidates } from '../utils/profile-compat';
import { stripClaudeCodeEnv } from '../utils/shell-executor';
// Re-export types for consumers
export type { ExecutionOptions, ExecutionResult, StreamMessage } from './executor/types';
@@ -209,7 +210,7 @@ export class HeadlessExecutor {
// Strip Claude Code nested session guard env var to allow CCS delegation
// (Claude Code v2.1.39+ sets CLAUDECODE to detect nested sessions)
const { CLAUDECODE: _nested, ...cleanEnv } = process.env;
const cleanEnv = stripClaudeCodeEnv(process.env);
const proc = spawn(claudeCli, args, {
cwd,
+18 -2
View File
@@ -24,6 +24,22 @@ export function stripAnthropicEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
return result;
}
/**
* Strip Claude Code nested-session guard env var from a process environment.
*
* Note: Windows env keys are case-insensitive, so remove case-insensitively
* to avoid missing variants like `claudecode`.
*/
export function stripClaudeCodeEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
const result: NodeJS.ProcessEnv = {};
for (const key of Object.keys(env)) {
if (key.toUpperCase() !== 'CLAUDECODE') {
result[key] = env[key];
}
}
return result;
}
/**
* Escape arguments for shell execution (cross-platform)
*
@@ -80,13 +96,13 @@ export function execClaude(
: process.env;
// Prepare environment (merge with base env if envVars provided)
const env = envVars
const mergedEnv = envVars
? { ...baseEnv, ...envVars, ...webSearchEnv }
: { ...baseEnv, ...webSearchEnv };
// Strip Claude Code nested session guard env var to allow CCS delegation
// (Claude Code v2.1.39+ sets CLAUDECODE to detect nested sessions)
delete env.CLAUDECODE;
const env = stripClaudeCodeEnv(mergedEnv);
// propagate key env vars to tmux session so agent team teammates
// (spawned via tmux split-window) inherit the correct config dir