@integrity-labs/agt-cli 0.28.1006 → 0.28.1007

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.
@@ -39,6 +39,15 @@ import {
39
39
  import {
40
40
  reapOrphanChannelMcps
41
41
  } from "./chunk-XWVM4KPK.js";
42
+ import {
43
+ CLAUDE_CONFIG_DIR_ENV,
44
+ archiveLegacyClaudeJson,
45
+ claudeConfigDirFor,
46
+ claudeConfigDirIsReady,
47
+ claudeJsonPathFor,
48
+ legacyClaudeJsonPathFor,
49
+ seedClaudeConfigFromLegacy
50
+ } from "./chunk-EL5MWGGH.js";
42
51
  import {
43
52
  __require
44
53
  } from "./chunk-DGUM43GV.js";
@@ -2998,10 +3007,10 @@ function assessClaudeConfig(probe) {
2998
3007
  }
2999
3008
  return { verdict: "ok", detail: `${path} is valid (${raw.length} bytes)`, blocksSpawn: false };
3000
3009
  }
3001
- function describeClaudeConfigForLog(homeDir) {
3010
+ function describeClaudeConfigForLog(claudeJsonFilePath) {
3002
3011
  try {
3003
3012
  const { readFileSync: readFileSync8, statSync: statSync2 } = __require("fs");
3004
- const path = `${homeDir.replace(/\/+$/, "")}/.claude.json`;
3013
+ const path = claudeJsonFilePath;
3005
3014
  let raw = null;
3006
3015
  let readFailure;
3007
3016
  try {
@@ -4020,6 +4029,19 @@ function ensureClaudeStateLayout(args) {
4020
4029
  mkdirSync6(join7(projects, `${TRANSCRIPT_MASK_DIR_PREFIX}${args.agentId}`), { recursive: true });
4021
4030
  mkdirSync6(join7(projects, encodeClaudeProjectPath(args.projectDir)), { recursive: true });
4022
4031
  }
4032
+ function legacyClaudeJsonIsFile(path) {
4033
+ try {
4034
+ return lstatSync3(path).isFile();
4035
+ } catch {
4036
+ return false;
4037
+ }
4038
+ }
4039
+ function configDirIsIsolationSafe(homeDir) {
4040
+ return claudeConfigDirFor(homeDir) === join7(homeDir, ".claude");
4041
+ }
4042
+ function sessionHomeDirFor() {
4043
+ return process.env.HOME?.trim() || homedir5();
4044
+ }
4023
4045
  function buildDockerRunCommand(args) {
4024
4046
  const { codeName, agentId, wrapperPath, projectDir, homeDir, runId, passApiKey, passOpenRouter, passModelPolicyGateway, passModelPolicyModel, egress, forwardSlackReplyBinding, forwardBlockTurnEndAllMarkers, forwardKanbanWaiting, forwardNotifyDispatch, forwardTimezone, forwardTurnFailureNotice, forwardChannelSecrets, agentClass } = args;
4025
4047
  const q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
@@ -4027,7 +4049,11 @@ function buildDockerRunCommand(args) {
4027
4049
  const agentIdDir = join7(homeDir, ".augmented", agentId);
4028
4050
  const mcpDir = join7(homeDir, ".augmented", "_mcp");
4029
4051
  const claudeHome = join7(homeDir, ".claude");
4030
- const claudeJson = join7(homeDir, ".claude.json");
4052
+ const legacyClaudeJson = legacyClaudeJsonPathFor(homeDir);
4053
+ const configDir = claudeConfigDirFor(homeDir);
4054
+ const configDirIsolationSafe = configDirIsIsolationSafe(homeDir);
4055
+ const useConfigDir = (args.claudeConfigDirReady ?? claudeConfigDirIsReady(homeDir)) && configDirIsolationSafe;
4056
+ const legacyExists = args.legacyClaudeJsonExists ?? legacyClaudeJsonIsFile(legacyClaudeJson);
4031
4057
  const claudeProjects = join7(claudeHome, "projects");
4032
4058
  const transcriptMask = join7(claudeProjects, `${TRANSCRIPT_MASK_DIR_PREFIX}${agentId}`);
4033
4059
  const ownTranscripts = join7(claudeProjects, encodeClaudeProjectPath(projectDir));
@@ -4041,7 +4067,13 @@ function buildDockerRunCommand(args) {
4041
4067
  `-v ${q(`${claudeHome}:${claudeHome}`)}`,
4042
4068
  `-v ${q(`${transcriptMask}:${claudeProjects}`)}`,
4043
4069
  `-v ${q(`${ownTranscripts}:${ownTranscripts}`)}`,
4044
- `-v ${q(`${claudeJson}:${claudeJson}`)}`,
4070
+ // ENG-10869: a single-FILE bind pins the inode at container start, and
4071
+ // Claude Code replaces this file by atomic rename - so every host write
4072
+ // orphaned the container's view until respawn (six live versions across
4073
+ // eight agents, agt-aws-1 2026-09-22). Once the config dir holds the file
4074
+ // it is reached through the `${claudeHome}` DIRECTORY mount above, where
4075
+ // renames propagate both ways and a container can write atomically.
4076
+ ...useConfigDir || !legacyExists ? [] : [`-v ${q(`${legacyClaudeJson}:${legacyClaudeJson}`)}`],
4045
4077
  // ENG-8854: the rest of the per-session state, from the agent's own id-keyed tree.
4046
4078
  ...CLAUDE_PER_AGENT_DIRS.map(
4047
4079
  (name) => `-v ${q(`${join7(claudeStateDirFor(homeDir, agentId), name)}:${join7(claudeHome, name)}`)}`
@@ -4055,6 +4087,7 @@ function buildDockerRunCommand(args) {
4055
4087
  const cpus = process.env.AGT_ISOLATION_CPUS || "1.0";
4056
4088
  const pids = process.env.AGT_ISOLATION_PIDS || "512";
4057
4089
  const envArgs = [`-e ${q(`HOME=${homeDir}`)}`];
4090
+ if (useConfigDir) envArgs.push(`-e ${q(`${CLAUDE_CONFIG_DIR_ENV}=${configDir}`)}`);
4058
4091
  envArgs.push(`-e ${q(`npm_config_cache=${join7(agentDir2, ".npm-cache")}`)}`);
4059
4092
  envArgs.push(`-e ${q(`${CLAUDE_CODE_TMPDIR_ENV}=${agentTmpDirFor(agentDir2)}`)}`);
4060
4093
  if (passApiKey) envArgs.push("-e ANTHROPIC_API_KEY");
@@ -4761,6 +4794,13 @@ function spawnSession(config, session) {
4761
4794
  });
4762
4795
  const tmuxSessionEnvArgs = [];
4763
4796
  tmuxSessionEnvArgs.push("-e", `AGT_AGENT_ID=${config.agentId}`);
4797
+ const tmuxSessionHomeDir = sessionHomeDirFor();
4798
+ if (claudeConfigDirIsReady(tmuxSessionHomeDir)) {
4799
+ tmuxSessionEnvArgs.push(
4800
+ "-e",
4801
+ `${CLAUDE_CONFIG_DIR_ENV}=${claudeConfigDirFor(tmuxSessionHomeDir)}`
4802
+ );
4803
+ }
4764
4804
  tmuxSessionEnvArgs.push(
4765
4805
  "-e",
4766
4806
  `${CLAUDE_CODE_TMPDIR_ENV}=${agentTmpDirFor(dirname5(config.projectDir))}`
@@ -4805,7 +4845,7 @@ function spawnSession(config, session) {
4805
4845
  for (const [key, value] of Object.entries(config.channelSpawnSecrets ?? {})) {
4806
4846
  tmuxSessionEnvArgs.push("-e", `${key}=${value}`);
4807
4847
  }
4808
- const sessionHomeDir = process.env.HOME?.trim() || homedir5();
4848
+ const sessionHomeDir = sessionHomeDirFor();
4809
4849
  const effectiveMode = effectiveIsolationMode(codeName, log2);
4810
4850
  let egress;
4811
4851
  if (effectiveMode === "docker" && egressMode(codeName) === "allowlist") {
@@ -4818,6 +4858,30 @@ function spawnSession(config, session) {
4818
4858
  ensureClaudeStateLayout({ homeDir: sessionHomeDir, agentId: config.agentId, projectDir });
4819
4859
  }
4820
4860
  const claudeCmd = effectiveMode === "docker" ? buildDockerRunCommand({
4861
+ // ENG-10869: seed-then-decide, in that order and in one place. A
4862
+ // container handed CLAUDE_CONFIG_DIR before the file exists there
4863
+ // starts against an empty config - credentials resolve, identity does
4864
+ // not, and that is the login picker. So the seed runs first and this
4865
+ // flag reports what actually landed, rather than what we intended.
4866
+ claudeConfigDirReady: (() => {
4867
+ seedClaudeConfigFromLegacy({ homeDir: sessionHomeDir, log: log2 });
4868
+ const ready = claudeConfigDirIsReady(sessionHomeDir);
4869
+ if (!ready) return false;
4870
+ if (!configDirIsIsolationSafe(sessionHomeDir)) {
4871
+ log2(
4872
+ `[claude-config] NOT archiving for '${codeName}': ${claudeConfigDirFor(sessionHomeDir)} is not under ${join7(sessionHomeDir, ".claude")}, so directory mode is refused and the legacy bind is what this container will use`
4873
+ );
4874
+ return false;
4875
+ }
4876
+ const archived = archiveLegacyClaudeJson({ homeDir: sessionHomeDir, log: log2 });
4877
+ if (archived === "refused-legacy-newer" || archived === "failed") {
4878
+ log2(
4879
+ `[claude-config] staying on the legacy bind for '${codeName}': archive=${archived}`
4880
+ );
4881
+ return false;
4882
+ }
4883
+ return true;
4884
+ })(),
4821
4885
  codeName,
4822
4886
  agentId: config.agentId,
4823
4887
  wrapperPath,
@@ -5108,7 +5172,7 @@ async function acceptDialogs(tmuxSession, codeName, log2, primaryModel = null, s
5108
5172
  if (isLoginPickerVisible(screen)) {
5109
5173
  noteSessionBlockedOnPrompt(codeName, "login-picker", "spawn-dialogs");
5110
5174
  if (!loginPickerReported) {
5111
- const health = describeClaudeConfigForLog(homedir5());
5175
+ const health = describeClaudeConfigForLog(claudeJsonPathFor());
5112
5176
  if (health) {
5113
5177
  log2(`[persistent-session] CLAUDE LOGIN REQUIRED for '${codeName}' \u2014 ${health}`);
5114
5178
  } else {
@@ -5731,6 +5795,7 @@ export {
5731
5795
  CLAUDE_HOST_WIDE,
5732
5796
  claudeStateDirFor,
5733
5797
  ensureClaudeStateLayout,
5798
+ configDirIsIsolationSafe,
5734
5799
  buildDockerRunCommand,
5735
5800
  writePersistentClaudeWrapper,
5736
5801
  PANE_AGENT_ID_PROBE_TIMEOUT_MS,
@@ -5776,4 +5841,4 @@ export {
5776
5841
  stopAllSessionsAndWait,
5777
5842
  getProjectDir
5778
5843
  };
5779
- //# sourceMappingURL=chunk-O6R6HS2Z.js.map
5844
+ //# sourceMappingURL=chunk-SQX4ZGD5.js.map