@integrity-labs/agt-cli 0.28.958 → 0.28.960

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.
@@ -4,6 +4,7 @@ import {
4
4
  PLATFORM_STORAGE_RULE,
5
5
  SECRET_PATTERNS,
6
6
  claudeModelAlias,
7
+ containerMemoryForAgentClass,
7
8
  encodeClaudeProjectPath,
8
9
  getOrCreateDailySession,
9
10
  isClaudeFastMode,
@@ -19,7 +20,7 @@ import {
19
20
  rotateDailySession,
20
21
  sessionFileExists,
21
22
  todayLocalIso
22
- } from "./chunk-CCQ3WAVH.js";
23
+ } from "./chunk-GSRUWWT6.js";
23
24
  import {
24
25
  classifyUnanswerablePane,
25
26
  findUsageLimitResetHint,
@@ -146,6 +147,121 @@ function hardBoundedCapture(file, args, opts) {
146
147
  return r.kind === "ok" ? r.stdout : null;
147
148
  }
148
149
 
150
+ // src/lib/container-memory-sync.ts
151
+ var DOCKER_MEMORY_OVERRIDE = /^[1-9]\d*[bkmg]?$/i;
152
+ var DOCKER_MIN_MEMORY_BYTES = 6 * 1024 ** 2;
153
+ function validMemoryOverride() {
154
+ const raw = process.env.AGT_ISOLATION_MEMORY?.trim();
155
+ if (!raw || !DOCKER_MEMORY_OVERRIDE.test(raw)) return null;
156
+ const bytes = parseDockerMemoryBytes(raw);
157
+ return bytes !== null && bytes >= DOCKER_MIN_MEMORY_BYTES ? raw : null;
158
+ }
159
+ function isInvalidMemoryOverride() {
160
+ const raw = process.env.AGT_ISOLATION_MEMORY?.trim();
161
+ return !!raw && validMemoryOverride() === null;
162
+ }
163
+ function resolveContainerMemory(agentClass) {
164
+ return validMemoryOverride() ?? containerMemoryForAgentClass(agentClass);
165
+ }
166
+ var UNIT_BYTES = {
167
+ "": 1,
168
+ k: 1024,
169
+ m: 1024 ** 2,
170
+ g: 1024 ** 3,
171
+ t: 1024 ** 4
172
+ };
173
+ function parseDockerMemoryBytes(value) {
174
+ const m = /^\s*(\d+(?:\.\d+)?)\s*([kmgt]?)(?:i?b)?\s*$/i.exec(value);
175
+ if (!m) return null;
176
+ const bytes = Math.floor(Number(m[1]) * UNIT_BYTES[m[2].toLowerCase()]);
177
+ return Number.isSafeInteger(bytes) && bytes > 0 ? bytes : null;
178
+ }
179
+ var defaultDeps = (log2) => ({
180
+ exec: (args) => hardBoundedExecFile("docker", args, { timeoutMs: 15e3 }),
181
+ log: log2
182
+ });
183
+ var appliedBytesBySession = /* @__PURE__ */ new Map();
184
+ var lastLogBySession = /* @__PURE__ */ new Map();
185
+ function logOnce(deps, codeName, msg) {
186
+ if (lastLogBySession.get(codeName) === msg) return;
187
+ lastLogBySession.set(codeName, msg);
188
+ deps.log(msg);
189
+ }
190
+ function seedContainerMemory(codeName, memory) {
191
+ const bytes = parseDockerMemoryBytes(memory);
192
+ if (bytes === null) appliedBytesBySession.delete(codeName);
193
+ else appliedBytesBySession.set(codeName, bytes);
194
+ lastLogBySession.delete(codeName);
195
+ }
196
+ function syncContainerMemory(codeName, agentClass, log2, deps = defaultDeps(log2)) {
197
+ const desired = resolveContainerMemory(agentClass);
198
+ const desiredBytes = parseDockerMemoryBytes(desired);
199
+ const container = `agt-${codeName}`;
200
+ if (isInvalidMemoryOverride()) {
201
+ logOnce(
202
+ deps,
203
+ `${codeName}:override`,
204
+ `[isolation-memory] AGT_ISOLATION_MEMORY='${process.env.AGT_ISOLATION_MEMORY}' is not a valid Docker memory size (integer with optional b/k/m/g, at least 6m) \u2014 ignored; using the agent_class ceiling ${desired}`
205
+ );
206
+ }
207
+ if (desiredBytes === null) {
208
+ logOnce(deps, codeName, `[isolation-memory] '${codeName}': unparseable memory ceiling '${desired}' \u2014 not applied`);
209
+ return "failed";
210
+ }
211
+ let currentBytes = appliedBytesBySession.get(codeName);
212
+ if (currentBytes === void 0) {
213
+ const inspected = deps.exec(["inspect", "-f", "{{.HostConfig.Memory}}", container]);
214
+ const parsed = inspected.kind === "ok" ? Number(inspected.stdout.trim()) : NaN;
215
+ if (!Number.isSafeInteger(parsed) || parsed < 0) {
216
+ logOnce(
217
+ deps,
218
+ codeName,
219
+ `[isolation-memory] '${codeName}': could not read ${container}'s memory ceiling (${describe(inspected)}) \u2014 will retry`
220
+ );
221
+ return "failed";
222
+ }
223
+ currentBytes = parsed === 0 ? Number.POSITIVE_INFINITY : parsed;
224
+ appliedBytesBySession.set(codeName, currentBytes);
225
+ }
226
+ if (currentBytes === desiredBytes) {
227
+ lastLogBySession.delete(codeName);
228
+ return "unchanged";
229
+ }
230
+ if (currentBytes > desiredBytes) {
231
+ logOnce(
232
+ deps,
233
+ codeName,
234
+ `[isolation-memory] '${codeName}': ceiling ${formatBytes(currentBytes)} -> ${desired} (agent_class=${String(agentClass)}) is a decrease \u2014 deferred to the next respawn, not applied live (lowering memory.max under use OOM-kills inside the container)`
235
+ );
236
+ return "deferred-lower";
237
+ }
238
+ const updated = deps.exec(["update", "--memory", desired, "--memory-swap", desired, container]);
239
+ if (updated.kind !== "ok") {
240
+ logOnce(
241
+ deps,
242
+ codeName,
243
+ `[isolation-memory] '${codeName}': docker update to ${desired} failed (${describe(updated)}) \u2014 will retry`
244
+ );
245
+ return "failed";
246
+ }
247
+ appliedBytesBySession.set(codeName, desiredBytes);
248
+ lastLogBySession.delete(codeName);
249
+ deps.log(
250
+ `[isolation-memory] '${codeName}': ceiling ${formatBytes(currentBytes)} -> ${desired} (agent_class=${String(agentClass)}), applied live`
251
+ );
252
+ return "raised";
253
+ }
254
+ function describe(r) {
255
+ if (r.kind === "ok") return `unexpected output '${r.stdout.trim().slice(0, 80)}'`;
256
+ if (r.kind === "timed-out") return "timed out";
257
+ return r.message.trim().slice(0, 200) || `exit ${r.status}`;
258
+ }
259
+ function formatBytes(bytes) {
260
+ if (!Number.isFinite(bytes)) return "unlimited";
261
+ const gib = bytes / 1024 ** 3;
262
+ return Number.isInteger(gib) ? `${gib}g` : `${Math.round(bytes / 1024 ** 2)}m`;
263
+ }
264
+
149
265
  // src/lib/persistent-session.ts
150
266
  import { join as join7, dirname as dirname5 } from "path";
151
267
  import { homedir as homedir5, platform, userInfo as userInfo2 } from "os";
@@ -3735,7 +3851,7 @@ function ensureClaudeStateLayout(args) {
3735
3851
  mkdirSync6(join7(projects, encodeClaudeProjectPath(args.projectDir)), { recursive: true });
3736
3852
  }
3737
3853
  function buildDockerRunCommand(args) {
3738
- const { codeName, agentId, wrapperPath, projectDir, homeDir, runId, passApiKey, passOpenRouter, passModelPolicyGateway, passModelPolicyModel, egress, forwardSlackReplyBinding, forwardBlockTurnEndAllMarkers, forwardKanbanWaiting, forwardNotifyDispatch, forwardTurnFailureNotice, forwardChannelSecrets } = args;
3854
+ const { codeName, agentId, wrapperPath, projectDir, homeDir, runId, passApiKey, passOpenRouter, passModelPolicyGateway, passModelPolicyModel, egress, forwardSlackReplyBinding, forwardBlockTurnEndAllMarkers, forwardKanbanWaiting, forwardNotifyDispatch, forwardTurnFailureNotice, forwardChannelSecrets, agentClass } = args;
3739
3855
  const q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
3740
3856
  const agentDir2 = join7(homeDir, ".augmented", codeName);
3741
3857
  const agentIdDir = join7(homeDir, ".augmented", agentId);
@@ -3761,7 +3877,7 @@ function buildDockerRunCommand(args) {
3761
3877
  ...CLAUDE_NULLED_FILES.map((name) => `-v ${q(`/dev/null:${join7(claudeHome, name)}`)}`)
3762
3878
  ];
3763
3879
  const image = process.env.AGT_ISOLATION_IMAGE || "agt-runtime:latest";
3764
- const memory = process.env.AGT_ISOLATION_MEMORY || "2g";
3880
+ const memory = resolveContainerMemory(agentClass);
3765
3881
  const memorySwap = memory;
3766
3882
  const cpus = process.env.AGT_ISOLATION_CPUS || "1.0";
3767
3883
  const pids = process.env.AGT_ISOLATION_PIDS || "512";
@@ -4574,7 +4690,9 @@ function spawnSession(config, session) {
4574
4690
  // no longer carries them for the in-container wrapper to source, so
4575
4691
  // without the forward `${SLACK_BOT_TOKEN}` resolves empty and the
4576
4692
  // channel MCP dies. Empty/undef ⇒ no forwards (file-sourced as before).
4577
- forwardChannelSecrets: config.channelSpawnSecrets ? Object.keys(config.channelSpawnSecrets) : void 0
4693
+ forwardChannelSecrets: config.channelSpawnSecrets ? Object.keys(config.channelSpawnSecrets) : void 0,
4694
+ // ENG-10641: the memory ceiling follows the agent's class.
4695
+ agentClass: config.agentClass
4578
4696
  }) : JSON.stringify(wrapperPath);
4579
4697
  const tmuxEnv = buildSpawnEnv({
4580
4698
  base: process.env,
@@ -5357,6 +5475,9 @@ export {
5357
5475
  LATE_BOUND_VARS,
5358
5476
  expandTemplateVars,
5359
5477
  parseEnvIntegrations,
5478
+ resolveContainerMemory,
5479
+ seedContainerMemory,
5480
+ syncContainerMemory,
5360
5481
  agentRuntimeKey,
5361
5482
  log,
5362
5483
  sha256,
@@ -5454,4 +5575,4 @@ export {
5454
5575
  stopAllSessionsAndWait,
5455
5576
  getProjectDir
5456
5577
  };
5457
- //# sourceMappingURL=chunk-F47LQPDM.js.map
5578
+ //# sourceMappingURL=chunk-SQGW4VAW.js.map