mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-07 16:09:43 +02:00
Merge feat/docker-session-mode into master (docker deep-review fixes)
Brings the docker session-mode deep-review work (intended for the skipped 1.4.2) onto the 1.5.x line: deterministic-conversation-id resume across container stop/recreate, config-drift detection + POST /api/docker-cases/:name/recreate, docker model-picker support, import-manifest hardening, remote-daemon (context/ daemonHost) correctness, comma-in-path rejection, and the zh-CN README re-translation. Conflicts resolved to preserve BOTH the multi-user security scoping already on master (ownership checks, workingDir confinement, permission downgrade) AND the docker features. Version kept at master's 1.5.0 (the 1.4.2 bump is superseded; a fresh changeset bumps to 1.5.1). tsc, eslint, and test:ci all green (3548 tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+34
-6
@@ -873,15 +873,15 @@ export function dockerTmuxSessionName(sessionId: string): string {
|
||||
const RESUME_ID_SAFE = /^[A-Za-z0-9._-]+$/;
|
||||
|
||||
/**
|
||||
* Append the CLI-specific resume flag to a pane command. Only fires when the
|
||||
* in-container tmux is RE-CREATED (`new-session -A` makes the flag inert on a
|
||||
* live reattach), i.e. exactly when the previous live agent was lost and we want
|
||||
* to resume the conversation from the bind-mounted transcript.
|
||||
* Append the CLI-specific resume flag to a pane command (codex/gemini). Only fires
|
||||
* when the in-container tmux is RE-CREATED (`new-session -A` makes the flag inert
|
||||
* on a live reattach), i.e. exactly when the previous live agent was lost and we
|
||||
* want to resume the conversation from the bind-mounted transcript. Claude mode
|
||||
* uses claudeDockerPaneCommand instead.
|
||||
*/
|
||||
function appendResumeFlag(modeCommand: string, mode: SessionMode, resumeId: string): string {
|
||||
if (!RESUME_ID_SAFE.test(resumeId)) return modeCommand;
|
||||
switch (mode) {
|
||||
case 'claude':
|
||||
case 'gemini':
|
||||
return `${modeCommand} --resume ${resumeId}`;
|
||||
case 'codex':
|
||||
@@ -891,6 +891,30 @@ function appendResumeFlag(modeCommand: string, mode: SessionMode, resumeId: stri
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Claude-mode pane command with a DETERMINISTIC conversation id (the docker analog
|
||||
* of buildSpawnCommand's --resume/--session-id logic). A fresh launch passes
|
||||
* `--session-id <sessionId>`, so the in-container conversation id is knowable
|
||||
* host-side (resume-id capture + subagent/workflow correlation) WITHOUT relying on
|
||||
* hook reachability. When the in-container tmux was re-created after a container
|
||||
* stop/reboot, the same command re-runs against the surviving transcript:
|
||||
* `--session-id` exits 1 ("already in use") and the `||` fallback RESUMES that
|
||||
* conversation (verified CLI behavior). An explicit resumeId gets the local
|
||||
* builder's shape — resume first, session-id fallback — so a stale id never
|
||||
* dead-panes. The leading `exec ` is stripped: an exec'd first branch could never
|
||||
* fall back.
|
||||
*/
|
||||
function claudeDockerPaneCommand(modeCommand: string, sessionId: string, resumeId?: string): string {
|
||||
if (!RESUME_ID_SAFE.test(sessionId)) return modeCommand; // defensive — ids are server-minted uuids
|
||||
const cmd = modeCommand.replace(/^exec\s+/, '');
|
||||
const rid = resumeId && RESUME_ID_SAFE.test(resumeId) ? resumeId : undefined;
|
||||
if (rid && rid !== sessionId) {
|
||||
return `${cmd} --resume ${rid} || ${cmd} --session-id ${sessionId}`;
|
||||
}
|
||||
const cid = rid ?? sessionId;
|
||||
return `${cmd} --session-id ${cid} || ${cmd} --resume ${cid}`;
|
||||
}
|
||||
|
||||
/** Fully-resolved inputs for buildDockerLaunchCommand (pure). */
|
||||
export interface DockerLaunchOptions {
|
||||
mode: SessionMode;
|
||||
@@ -930,7 +954,11 @@ export function buildDockerLaunchCommand(opts: DockerLaunchOptions): string {
|
||||
const sid = sessionId.slice(0, 8);
|
||||
|
||||
let modeCommand = docker.commands?.[mode as DockerCommandMode] || defaultDockerCommandForMode(mode);
|
||||
if (resumeSessionId) modeCommand = appendResumeFlag(modeCommand, mode, resumeSessionId);
|
||||
if (mode === 'claude') {
|
||||
modeCommand = claudeDockerPaneCommand(modeCommand, sessionId, resumeSessionId);
|
||||
} else if (resumeSessionId) {
|
||||
modeCommand = appendResumeFlag(modeCommand, mode, resumeSessionId);
|
||||
}
|
||||
// Run by tmux via /bin/sh -c, so the path is shell-quoted here. `exec` makes the
|
||||
// pane PID the agent itself.
|
||||
const paneCommand = `cd ${workdir} && ${modeCommand}`;
|
||||
|
||||
Reference in New Issue
Block a user