@integrity-labs/agt-cli 0.7.9 → 0.7.10

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.
@@ -17,11 +17,11 @@ import {
17
17
  loadSchedulerState,
18
18
  markTaskFired,
19
19
  syncTasksToScheduler
20
- } from "../chunk-4I4QZRBQ.js";
20
+ } from "../chunk-5UGOY3IV.js";
21
21
 
22
22
  // src/lib/manager-worker.ts
23
23
  import { createHash } from "crypto";
24
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync2, rmSync, readdirSync, statSync, unlinkSync } from "fs";
24
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync2, rmSync, readdirSync, statSync, unlinkSync } from "fs";
25
25
  import https from "https";
26
26
  import { join as join2 } from "path";
27
27
 
@@ -327,55 +327,30 @@ var GatewayClientPool = class extends EventEmitter {
327
327
  };
328
328
 
329
329
  // src/lib/persistent-session.ts
330
- import { spawn, execSync } from "child_process";
330
+ import { execFileSync } from "child_process";
331
331
  import { join } from "path";
332
332
  import { homedir } from "os";
333
- import { existsSync, readFileSync as readFileSync2 } from "fs";
333
+ import { existsSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
334
334
  var sessions = /* @__PURE__ */ new Map();
335
- function startPersistentSession(config2) {
336
- const existing = sessions.get(config2.codeName);
337
- if (existing && existing.status === "running" && existing.process && !existing.process.killed) {
338
- return existing;
339
- }
340
- const session = {
341
- codeName: config2.codeName,
342
- process: null,
343
- taskChannelPort: null,
344
- startedAt: null,
345
- restartCount: existing?.restartCount ?? 0,
346
- status: "starting"
347
- };
348
- sessions.set(config2.codeName, session);
349
- spawnSession(config2, session);
350
- return session;
351
- }
352
- function spawnSession(config2, session) {
353
- const { codeName, projectDir, mcpConfigPath, claudeMdPath, channels, devChannels, apiHost, log: log2 } = config2;
354
- sanitizeMcpJson(mcpConfigPath, apiHost);
355
- const args = [];
335
+ function writeAcpxConfig(config2) {
336
+ const { projectDir, mcpConfigPath, claudeMdPath, channels, devChannels } = config2;
337
+ const claudeArgs = [];
356
338
  if (channels.length > 0) {
357
- args.push("--channels", ...channels);
339
+ claudeArgs.push("--channels", ...channels);
358
340
  }
359
341
  if (devChannels.length > 0) {
360
- args.push("--dangerously-load-development-channels", ...devChannels);
342
+ claudeArgs.push("--dangerously-load-development-channels", ...devChannels);
361
343
  }
362
- args.push("--mcp-config", mcpConfigPath);
344
+ claudeArgs.push("--mcp-config", mcpConfigPath);
363
345
  const channelsConfigPath = join(projectDir, ".mcp-channels.json");
364
346
  if (existsSync(channelsConfigPath)) {
365
- args.push("--mcp-config", channelsConfigPath);
347
+ claudeArgs.push("--mcp-config", channelsConfigPath);
366
348
  }
367
349
  if (existsSync(claudeMdPath)) {
368
- args.push("--system-prompt-file", claudeMdPath);
369
- }
370
- args.push("--allow-dangerously-skip-permissions");
371
- args.push("--dangerously-skip-permissions");
372
- args.push("--name", `agt-${codeName}`);
373
- log2(`[persistent-session] Starting session for '${codeName}' with args: ${args.join(" ")}`);
374
- const tmuxSession = `agt-${codeName}`;
375
- try {
376
- execSync(`tmux kill-session -t ${tmuxSession} 2>/dev/null`, { stdio: "ignore" });
377
- } catch {
350
+ claudeArgs.push("--system-prompt-file", claudeMdPath);
378
351
  }
352
+ claudeArgs.push("--allow-dangerously-skip-permissions");
353
+ claudeArgs.push("--dangerously-skip-permissions");
379
354
  const envIntegrationsPath = join(projectDir, ".env.integrations");
380
355
  let envPrefix = "";
381
356
  if (existsSync(envIntegrationsPath)) {
@@ -385,138 +360,154 @@ function spawnSession(config2, session) {
385
360
  const eqIdx = line.indexOf("=");
386
361
  const key = line.slice(0, eqIdx);
387
362
  const value = line.slice(eqIdx + 1);
388
- return `${key}=${JSON.stringify(value)}`;
363
+ return `${key}=${value.includes(" ") ? JSON.stringify(value) : value}`;
389
364
  }).join(" ");
390
365
  if (envVars) envPrefix = `env ${envVars} `;
391
366
  } catch {
392
367
  }
393
368
  }
394
- const initPrompt = "You are now online. Wait for messages from your channels (Telegram, Slack) and respond to them. Use your kanban tools to track work.";
395
- const claudeCmd = `${envPrefix}claude ${JSON.stringify(initPrompt)} ${args.map((a) => a.includes(" ") ? JSON.stringify(a) : a).join(" ")}`;
396
- const child = spawn("tmux", [
397
- "new-session",
398
- "-d",
399
- "-s",
400
- tmuxSession,
401
- "-c",
402
- projectDir,
403
- claudeCmd
404
- ], {
405
- cwd: projectDir,
406
- stdio: ["ignore", "pipe", "pipe"],
407
- env: process.env
408
- });
409
- session.process = child;
410
- session.startedAt = Date.now();
411
- session.status = "running";
412
- child.on("close", (code) => {
413
- if (code !== 0) {
414
- log2(`[persistent-session] Failed to create tmux session for '${codeName}' (exit ${code})`);
415
- session.status = "crashed";
416
- session.process = null;
417
- return;
418
- }
419
- log2(`[persistent-session] tmux session '${tmuxSession}' created for '${codeName}'`);
420
- const acceptDialogs = async () => {
421
- for (let i = 0; i < 15; i++) {
422
- await new Promise((r) => setTimeout(r, 2e3));
423
- try {
424
- const { execSync: es } = await import("child_process");
425
- const screen = es(`tmux capture-pane -t ${tmuxSession} -p 2>/dev/null`, { encoding: "utf-8" });
426
- if (screen.includes("Yes, I trust this folder")) {
427
- es(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
428
- log2(`[persistent-session] Auto-accepted workspace trust for '${codeName}'`);
429
- continue;
430
- }
431
- if (screen.includes("I am using this for local development")) {
432
- es(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
433
- log2(`[persistent-session] Auto-accepted dev channels for '${codeName}'`);
434
- continue;
435
- }
436
- if (screen.includes("Enter to confirm") && screen.includes("MCP")) {
437
- es(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
438
- log2(`[persistent-session] Auto-accepted MCP servers for '${codeName}'`);
439
- continue;
440
- }
441
- if (screen.includes("Yes, I accept") && screen.includes("Bypass Permissions")) {
442
- es(`tmux send-keys -t ${tmuxSession} 2`, { stdio: "ignore" });
443
- await new Promise((r) => setTimeout(r, 300));
444
- es(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
445
- log2(`[persistent-session] Auto-accepted bypass permissions for '${codeName}'`);
446
- continue;
447
- }
448
- if (screen.includes("\u276F") && !screen.includes("Enter to confirm")) {
449
- log2(`[persistent-session] Session ready for '${codeName}' \u2014 no more dialogs`);
450
- break;
451
- }
452
- } catch {
453
- break;
454
- }
369
+ const acpxConfig = {
370
+ defaultAgent: "claude",
371
+ defaultPermissions: "approve-all",
372
+ agents: {
373
+ claude: {
374
+ command: `${envPrefix}npx -y @agentclientprotocol/claude-agent-acp ${claudeArgs.join(" ")}`
455
375
  }
456
- };
457
- acceptDialogs().catch(() => {
376
+ }
377
+ };
378
+ writeFileSync2(join(projectDir, ".acpxrc.json"), JSON.stringify(acpxConfig, null, 2));
379
+ }
380
+ function startPersistentSession(config2) {
381
+ const existing = sessions.get(config2.codeName);
382
+ if (existing && existing.status === "running") {
383
+ return existing;
384
+ }
385
+ const restartCount = existing?.restartCount ?? 0;
386
+ if (existing?.status === "crashed" && existing.startedAt) {
387
+ const backoffMs = Math.min(5e3 * Math.pow(2, restartCount), 6e4);
388
+ if (Date.now() - existing.startedAt < backoffMs) {
389
+ return existing;
390
+ }
391
+ }
392
+ const session = {
393
+ codeName: config2.codeName,
394
+ startedAt: null,
395
+ restartCount,
396
+ status: "starting"
397
+ };
398
+ sessions.set(config2.codeName, session);
399
+ spawnSession(config2, session);
400
+ return session;
401
+ }
402
+ function spawnSession(config2, session) {
403
+ const { codeName, projectDir, mcpConfigPath, apiHost, log: log2 } = config2;
404
+ const sessionName = `agt-${codeName}`;
405
+ const initPrompt = "You are now online. Wait for messages from your channels (Telegram, Slack) and respond to them. Use your kanban tools to track work.";
406
+ log2(`[persistent-session] Starting acpx session '${sessionName}' for '${codeName}'`);
407
+ try {
408
+ sanitizeMcpJson(mcpConfigPath, apiHost);
409
+ writeAcpxConfig(config2);
410
+ execFileSync("acpx", [
411
+ "claude",
412
+ "sessions",
413
+ "ensure",
414
+ "--name",
415
+ sessionName
416
+ ], {
417
+ cwd: projectDir,
418
+ timeout: 3e4,
419
+ stdio: "ignore"
458
420
  });
459
- });
460
- child.on("error", (err) => {
461
- log2(`[persistent-session] Failed to start tmux for '${codeName}': ${err.message}`);
421
+ execFileSync("acpx", [
422
+ "claude",
423
+ "-s",
424
+ sessionName,
425
+ "--no-wait",
426
+ "--",
427
+ initPrompt
428
+ ], {
429
+ cwd: projectDir,
430
+ timeout: 3e4,
431
+ stdio: "ignore"
432
+ });
433
+ session.startedAt = Date.now();
434
+ session.status = "running";
435
+ session.restartCount = 0;
436
+ log2(`[persistent-session] Session '${sessionName}' started for '${codeName}'`);
437
+ } catch (err) {
438
+ log2(`[persistent-session] Failed to start acpx session for '${codeName}': ${err.message}`);
462
439
  session.status = "crashed";
463
- });
440
+ session.startedAt = Date.now();
441
+ session.restartCount++;
442
+ }
464
443
  }
465
444
  function stopPersistentSession(codeName, log2) {
466
445
  const session = sessions.get(codeName);
467
446
  if (!session) return;
468
447
  log2(`[persistent-session] Stopping session for '${codeName}'`);
469
448
  session.status = "stopped";
449
+ const projectDir = getProjectDir2(codeName);
470
450
  try {
471
- execSync(`tmux kill-session -t agt-${codeName} 2>/dev/null`, { stdio: "ignore" });
451
+ execFileSync("acpx", ["claude", "sessions", "close", `agt-${codeName}`], {
452
+ cwd: projectDir,
453
+ timeout: 1e4,
454
+ stdio: "ignore"
455
+ });
472
456
  } catch {
473
457
  }
474
- if (session.process && !session.process.killed) {
475
- session.process.kill("SIGTERM");
458
+ try {
459
+ execFileSync("tmux", ["kill-session", "-t", `agt-${codeName}`], { stdio: "ignore" });
460
+ } catch {
476
461
  }
477
- setTimeout(() => {
478
- if (session.process && !session.process.killed) {
479
- session.process.kill("SIGKILL");
480
- }
481
- }, 1e4);
482
462
  sessions.delete(codeName);
483
463
  }
484
464
  async function injectMessage(codeName, type, content, meta) {
485
465
  const session = sessions.get(codeName);
486
- if (!session || session.status !== "running" || !session.process) {
466
+ if (!session || session.status !== "running") {
487
467
  return false;
488
468
  }
489
- if (session.taskChannelPort) {
490
- try {
491
- const res = await fetch(`http://127.0.0.1:${session.taskChannelPort}`, {
492
- method: "POST",
493
- headers: { "Content-Type": "application/json" },
494
- body: JSON.stringify({ type, content, meta }),
495
- signal: AbortSignal.timeout(1e4)
496
- });
497
- if (res.ok) return true;
498
- } catch {
499
- }
500
- }
469
+ const projectDir = getProjectDir2(codeName);
470
+ const sessionName = `agt-${codeName}`;
471
+ const prefix = meta?.task_name ? `[Task: ${meta.task_name}] ` : "";
472
+ const text = prefix + content;
501
473
  try {
502
- const prefix = meta?.task_name ? `[Task: ${meta.task_name}] ` : "";
503
- const text = prefix + content;
504
- const escaped = text.replace(/'/g, "'\\''");
505
- execSync(`tmux send-keys -t agt-${codeName} '${escaped}' Enter`, { stdio: "ignore" });
474
+ execFileSync("acpx", [
475
+ "claude",
476
+ "-s",
477
+ sessionName,
478
+ "--no-wait",
479
+ "--",
480
+ text
481
+ ], {
482
+ cwd: projectDir,
483
+ timeout: 15e3,
484
+ stdio: "ignore"
485
+ });
506
486
  return true;
507
487
  } catch {
508
488
  return false;
509
489
  }
510
- return false;
511
490
  }
512
491
  function isSessionHealthy(codeName) {
513
492
  const session = sessions.get(codeName);
514
493
  if (!session || session.status !== "running") return false;
494
+ const projectDir = getProjectDir2(codeName);
515
495
  try {
516
- execSync(`tmux has-session -t agt-${codeName} 2>/dev/null`, { stdio: "ignore" });
517
- return true;
496
+ const output = execFileSync("acpx", [
497
+ "claude",
498
+ "-s",
499
+ `agt-${codeName}`,
500
+ "status"
501
+ ], {
502
+ cwd: projectDir,
503
+ timeout: 1e4,
504
+ encoding: "utf-8"
505
+ });
506
+ return output.includes("status: running") || output.includes("status: idle");
518
507
  } catch {
519
508
  session.status = "crashed";
509
+ session.startedAt = Date.now();
510
+ session.restartCount++;
520
511
  return false;
521
512
  }
522
513
  }
@@ -527,28 +518,10 @@ function resetRestartCount(codeName) {
527
518
  async function stopAllSessionsAndWait(log2, opts) {
528
519
  const codeNames = [...sessions.keys()];
529
520
  if (codeNames.length === 0) return;
530
- const exitPromises = [];
531
521
  for (const codeName of codeNames) {
532
- const session = sessions.get(codeName);
533
- const proc = session?.process;
534
522
  stopPersistentSession(codeName, log2);
535
- if (proc && !proc.killed && proc.exitCode === null) {
536
- exitPromises.push(
537
- new Promise((resolve) => {
538
- proc.on("close", resolve);
539
- proc.on("error", resolve);
540
- })
541
- );
542
- }
543
523
  }
544
- if (exitPromises.length === 0) return;
545
- await Promise.race([
546
- Promise.all(exitPromises),
547
- new Promise((resolve) => setTimeout(() => {
548
- log2(`[persistent-session] Shutdown timeout (${opts.timeoutMs}ms), force-killing remaining sessions`);
549
- resolve();
550
- }, opts.timeoutMs))
551
- ]);
524
+ await new Promise((resolve) => setTimeout(resolve, Math.min(opts.timeoutMs, 2e3)));
552
525
  }
553
526
  function getProjectDir2(codeName) {
554
527
  return join(homedir(), ".augmented", codeName, "project");
@@ -874,16 +847,16 @@ var lastVersionCheckAt = 0;
874
847
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
875
848
  async function ensureBrewDependency(binary, formula) {
876
849
  if (frameworkBinaryChecked.has(`dep:${binary}`)) return true;
877
- const { execFileSync } = await import("child_process");
850
+ const { execFileSync: execFileSync2 } = await import("child_process");
878
851
  try {
879
- execFileSync("which", [binary], { timeout: 5e3 });
852
+ execFileSync2("which", [binary], { timeout: 5e3 });
880
853
  frameworkBinaryChecked.add(`dep:${binary}`);
881
854
  return true;
882
855
  } catch {
883
856
  }
884
857
  let brewPath;
885
858
  try {
886
- brewPath = execFileSync("which", ["brew"], { timeout: 5e3 }).toString().trim();
859
+ brewPath = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
887
860
  } catch {
888
861
  log(`${binary} not found and Homebrew not available \u2014 install manually: brew install ${formula}`);
889
862
  frameworkBinaryChecked.add(`dep:${binary}`);
@@ -891,14 +864,14 @@ async function ensureBrewDependency(binary, formula) {
891
864
  }
892
865
  log(`${binary} not found \u2014 installing via Homebrew...`);
893
866
  try {
894
- execFileSync(brewPath, ["install", formula], { timeout: 12e4, stdio: "pipe" });
867
+ execFileSync2(brewPath, ["install", formula], { timeout: 12e4, stdio: "pipe" });
895
868
  } catch (err) {
896
869
  log(`Failed to install ${formula}: ${err.message}`);
897
870
  frameworkBinaryChecked.add(`dep:${binary}`);
898
871
  return false;
899
872
  }
900
873
  try {
901
- execFileSync("which", [binary], { timeout: 5e3 });
874
+ execFileSync2("which", [binary], { timeout: 5e3 });
902
875
  log(`${binary} installed successfully`);
903
876
  frameworkBinaryChecked.add(`dep:${binary}`);
904
877
  return true;
@@ -913,24 +886,24 @@ async function ensureFrameworkBinary(frameworkId) {
913
886
  if (frameworkBinaryChecked.has(frameworkId)) return;
914
887
  frameworkBinaryChecked.add(frameworkId);
915
888
  await ensureBrewDependency("tmux", "tmux");
916
- const { execFileSync } = await import("child_process");
889
+ const { execFileSync: execFileSync2 } = await import("child_process");
917
890
  let brewPath;
918
891
  try {
919
- brewPath = execFileSync("which", ["brew"], { timeout: 5e3 }).toString().trim();
892
+ brewPath = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
920
893
  } catch {
921
894
  log("Homebrew not found \u2014 cannot auto-install/upgrade Claude Code. Install manually: https://claude.ai/download");
922
895
  return;
923
896
  }
924
897
  let claudeExists = false;
925
898
  try {
926
- execFileSync("which", ["claude"], { timeout: 5e3 });
899
+ execFileSync2("which", ["claude"], { timeout: 5e3 });
927
900
  claudeExists = true;
928
901
  } catch {
929
902
  }
930
903
  if (!claudeExists) {
931
904
  log("Claude Code binary not found \u2014 installing via Homebrew...");
932
905
  try {
933
- execFileSync(brewPath, ["install", "--cask", "claude-code"], {
906
+ execFileSync2(brewPath, ["install", "--cask", "claude-code"], {
934
907
  timeout: 12e4,
935
908
  stdio: "pipe"
936
909
  });
@@ -939,7 +912,7 @@ async function ensureFrameworkBinary(frameworkId) {
939
912
  return;
940
913
  }
941
914
  try {
942
- execFileSync("which", ["claude"], { timeout: 5e3 });
915
+ execFileSync2("which", ["claude"], { timeout: 5e3 });
943
916
  log("Claude Code installed successfully");
944
917
  } catch {
945
918
  log("Claude Code install completed but binary not found on PATH \u2014 you may need to restart your terminal");
@@ -947,7 +920,7 @@ async function ensureFrameworkBinary(frameworkId) {
947
920
  } else {
948
921
  log("Checking for Claude Code updates...");
949
922
  try {
950
- const output = execFileSync(brewPath, ["upgrade", "--cask", "claude-code"], {
923
+ const output = execFileSync2(brewPath, ["upgrade", "--cask", "claude-code"], {
951
924
  timeout: 12e4,
952
925
  stdio: "pipe"
953
926
  }).toString();
@@ -965,11 +938,11 @@ async function ensureFrameworkBinary(frameworkId) {
965
938
  }
966
939
  }
967
940
  }
968
- agentRuntimeAuthenticated = checkClaudeAuth(execFileSync);
941
+ agentRuntimeAuthenticated = checkClaudeAuth(execFileSync2);
969
942
  }
970
- function checkClaudeAuth(execFileSync) {
943
+ function checkClaudeAuth(execFileSync2) {
971
944
  try {
972
- const authOutput = execFileSync("claude", ["auth", "status"], { timeout: 1e4, stdio: "pipe" }).toString().trim();
945
+ const authOutput = execFileSync2("claude", ["auth", "status"], { timeout: 1e4, stdio: "pipe" }).toString().trim();
973
946
  let loggedIn = null;
974
947
  try {
975
948
  const parsed = JSON.parse(authOutput);
@@ -1000,8 +973,8 @@ function loadGatewayPorts() {
1000
973
  }
1001
974
  }
1002
975
  function saveGatewayPorts(ports) {
1003
- mkdirSync(AUGMENTED_DIR, { recursive: true });
1004
- writeFileSync2(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
976
+ mkdirSync2(AUGMENTED_DIR, { recursive: true });
977
+ writeFileSync3(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
1005
978
  }
1006
979
  function allocatePort(codeName) {
1007
980
  const ports = loadGatewayPorts();
@@ -1027,7 +1000,7 @@ var STATE_FILE = join2(process.env["HOME"] ?? "/tmp", ".augmented", "manager-sta
1027
1000
  function send(msg) {
1028
1001
  if (msg.type === "state-update") {
1029
1002
  try {
1030
- writeFileSync2(STATE_FILE, JSON.stringify(msg.state, null, 2));
1003
+ writeFileSync3(STATE_FILE, JSON.stringify(msg.state, null, 2));
1031
1004
  } catch {
1032
1005
  }
1033
1006
  }
@@ -1083,9 +1056,9 @@ async function migrateToProfiles() {
1083
1056
  const profileAuthDir = join2(profileDir, "agents", codeName, "agent");
1084
1057
  const authFile = join2(sharedAuthDir, "auth-profiles.json");
1085
1058
  if (existsSync2(authFile)) {
1086
- mkdirSync(profileAuthDir, { recursive: true });
1059
+ mkdirSync2(profileAuthDir, { recursive: true });
1087
1060
  const authContent = readFileSync3(authFile, "utf-8");
1088
- writeFileSync2(join2(profileAuthDir, "auth-profiles.json"), authContent);
1061
+ writeFileSync3(join2(profileAuthDir, "auth-profiles.json"), authContent);
1089
1062
  }
1090
1063
  allocatePort(codeName);
1091
1064
  migrated++;
@@ -1180,7 +1153,7 @@ async function ensureGatewayRunning(codeName, adapter) {
1180
1153
  if (cfg.gateway?.port !== status.port) {
1181
1154
  if (!cfg.gateway) cfg.gateway = {};
1182
1155
  cfg.gateway.port = status.port;
1183
- writeFileSync2(configPath, JSON.stringify(cfg, null, 2));
1156
+ writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
1184
1157
  }
1185
1158
  }
1186
1159
  } catch {
@@ -1205,7 +1178,7 @@ async function ensureGatewayRunning(codeName, adapter) {
1205
1178
  const cfg = JSON.parse(readFileSync3(configPath, "utf-8"));
1206
1179
  if (!cfg.gateway) cfg.gateway = {};
1207
1180
  cfg.gateway.port = port;
1208
- writeFileSync2(configPath, JSON.stringify(cfg, null, 2));
1181
+ writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
1209
1182
  }
1210
1183
  } catch {
1211
1184
  }
@@ -1590,7 +1563,7 @@ async function processAgent(agent, agentStates) {
1590
1563
  try {
1591
1564
  const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
1592
1565
  const changedFiles = [];
1593
- mkdirSync(agentDir, { recursive: true });
1566
+ mkdirSync2(agentDir, { recursive: true });
1594
1567
  for (const artifact of artifacts) {
1595
1568
  const filePath = join2(agentDir, artifact.relativePath);
1596
1569
  const newHash = sha256(artifact.content);
@@ -1605,7 +1578,7 @@ async function processAgent(agent, agentStates) {
1605
1578
  const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
1606
1579
  log(`${verb} '${agent.code_name}': ${fileNames}`);
1607
1580
  for (const file of changedFiles) {
1608
- writeFileSync2(join2(agentDir, file.relativePath), file.content);
1581
+ writeFileSync3(join2(agentDir, file.relativePath), file.content);
1609
1582
  }
1610
1583
  lastProvisionAt = (/* @__PURE__ */ new Date()).toISOString();
1611
1584
  knownVersions.set(agent.agent_id, { charterVersion, toolsVersion });
@@ -1839,10 +1812,10 @@ async function processAgent(agent, agentStates) {
1839
1812
  const hasLcm = integrations.some((i) => i.definition_id === "lossless-claw");
1840
1813
  if (hasLcm && !losslessClawInstalled.get(agent.code_name)) {
1841
1814
  try {
1842
- const { execFileSync } = await import("child_process");
1815
+ const { execFileSync: execFileSync2 } = await import("child_process");
1843
1816
  let pluginList;
1844
1817
  try {
1845
- pluginList = execFileSync(
1818
+ pluginList = execFileSync2(
1846
1819
  "openclaw",
1847
1820
  ["--profile", agent.code_name, "plugins", "list", "--json"],
1848
1821
  { timeout: 15e3 }
@@ -1856,7 +1829,7 @@ async function processAgent(agent, agentStates) {
1856
1829
  );
1857
1830
  if (!alreadyInstalled) {
1858
1831
  log(`Installing lossless-claw plugin for '${agent.code_name}'...`);
1859
- execFileSync(
1832
+ execFileSync2(
1860
1833
  "openclaw",
1861
1834
  ["--profile", agent.code_name, "plugins", "install", "@martian-engineering/lossless-claw"],
1862
1835
  { stdio: "ignore", timeout: 6e4 }
@@ -1903,18 +1876,18 @@ async function processAgent(agent, agentStates) {
1903
1876
  "@tobilu/qmd"
1904
1877
  ]);
1905
1878
  if (intHash !== prevIntHash) {
1906
- const { execFileSync } = await import("child_process");
1879
+ const { execFileSync: execFileSync2 } = await import("child_process");
1907
1880
  for (const tool of capData.cliTools) {
1908
1881
  if (!ALLOWED_CLI_PACKAGES.has(tool.package)) {
1909
1882
  log(`Skipping CLI tool '${tool.package}' for '${agent.code_name}' \u2014 not on the allowed packages list`);
1910
1883
  continue;
1911
1884
  }
1912
1885
  try {
1913
- execFileSync("which", [tool.binary], { stdio: "ignore" });
1886
+ execFileSync2("which", [tool.binary], { stdio: "ignore" });
1914
1887
  } catch {
1915
1888
  log(`Installing CLI tool '${tool.package}' for '${agent.code_name}'...`);
1916
1889
  try {
1917
- execFileSync("npm", ["install", "-g", tool.package], { stdio: "ignore", timeout: 6e4 });
1890
+ execFileSync2("npm", ["install", "-g", tool.package], { stdio: "ignore", timeout: 6e4 });
1918
1891
  log(`CLI tool '${tool.binary}' installed successfully`);
1919
1892
  } catch (installErr) {
1920
1893
  log(`Failed to install CLI tool '${tool.package}': ${installErr.message}`);
@@ -1923,7 +1896,7 @@ async function processAgent(agent, agentStates) {
1923
1896
  if (tool.binary === "qmd") {
1924
1897
  try {
1925
1898
  const agentDir2 = join2(process.env["HOME"] ?? "/tmp", ".augmented", agent.code_name);
1926
- execFileSync("qmd", ["collection", "add", agent.code_name, "project"], {
1899
+ execFileSync2("qmd", ["collection", "add", agent.code_name, "project"], {
1927
1900
  stdio: "ignore",
1928
1901
  timeout: 3e4,
1929
1902
  cwd: agentDir2
@@ -2304,7 +2277,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
2304
2277
  }
2305
2278
  }
2306
2279
  }
2307
- writeFileSync2(indexPath, JSON.stringify(index));
2280
+ writeFileSync3(indexPath, JSON.stringify(index));
2308
2281
  if (toDelete.length > 0) {
2309
2282
  log(`Cleaned ${toDelete.length} cron session(s) and ${deletedFiles} file(s) from ${sessionsDir}`);
2310
2283
  }
@@ -2340,7 +2313,7 @@ function clearStaleCronRunState(jobsPath) {
2340
2313
  }
2341
2314
  }
2342
2315
  if (changed) {
2343
- writeFileSync2(jobsPath, JSON.stringify(data, null, 2));
2316
+ writeFileSync3(jobsPath, JSON.stringify(data, null, 2));
2344
2317
  }
2345
2318
  } catch {
2346
2319
  }
@@ -2563,8 +2536,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
2563
2536
  }
2564
2537
  }
2565
2538
  if (!agentRuntimeAuthenticated) {
2566
- const { execFileSync } = await import("child_process");
2567
- agentRuntimeAuthenticated = checkClaudeAuth(execFileSync);
2539
+ const { execFileSync: execFileSync2 } = await import("child_process");
2540
+ agentRuntimeAuthenticated = checkClaudeAuth(execFileSync2);
2568
2541
  if (!agentRuntimeAuthenticated) {
2569
2542
  log(`[persistent-session] Skipping '${codeName}' \u2014 Claude Code not authenticated`);
2570
2543
  return;
@@ -2594,6 +2567,32 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
2594
2567
  return;
2595
2568
  }
2596
2569
  resetRestartCount(codeName);
2570
+ const stableTasksHash = createHash("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
2571
+ const prevHash = knownTasksHashes.get(agent.agent_id);
2572
+ if (stableTasksHash !== prevHash) {
2573
+ const taskInputs = tasks.map((t) => ({
2574
+ id: t.id,
2575
+ template_id: t.template_id,
2576
+ name: t.name,
2577
+ schedule_kind: t.schedule_kind,
2578
+ schedule_expr: t.schedule_expr ?? null,
2579
+ schedule_every: t.schedule_every ?? null,
2580
+ schedule_at: t.schedule_at ?? null,
2581
+ timezone: t.timezone ?? "UTC",
2582
+ prompt: t.prompt ?? "",
2583
+ session_target: t.session_target ?? "isolated",
2584
+ delivery_mode: t.delivery_mode ?? "none",
2585
+ delivery_channel: t.delivery_channel ?? null,
2586
+ delivery_to: t.delivery_to ?? null,
2587
+ enabled: t.enabled ?? true
2588
+ }));
2589
+ const schedulerState = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
2590
+ claudeSchedulerStates.set(codeName, schedulerState);
2591
+ knownTasksHashes.set(agent.agent_id, stableTasksHash);
2592
+ log(`[persistent-session] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
2593
+ } else if (!claudeSchedulerStates.has(codeName)) {
2594
+ claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
2595
+ }
2597
2596
  const state2 = claudeSchedulerStates.get(codeName);
2598
2597
  if (state2) {
2599
2598
  const ready = getReadyTasks(state2);
@@ -2625,8 +2624,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
2625
2624
  const markerDir = join2(projectDir2, ".claude");
2626
2625
  const markerPath = join2(markerDir, ".agt-pending-task.json");
2627
2626
  try {
2628
- mkdirSync(markerDir, { recursive: true });
2629
- writeFileSync2(markerPath, JSON.stringify({
2627
+ mkdirSync2(markerDir, { recursive: true });
2628
+ writeFileSync3(markerPath, JSON.stringify({
2630
2629
  agent_id: agent.agent_id,
2631
2630
  task_id: task.taskId,
2632
2631
  template_id: task.templateId,
@@ -2644,35 +2643,13 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
2644
2643
  if (injected) {
2645
2644
  const updated = markTaskFired(codeName, task.taskId, "ok");
2646
2645
  claudeSchedulerStates.set(codeName, updated);
2647
- log(`[persistent-session] Task '${task.name}' injected, next fire at ${new Date(updated.tasks[task.taskId]?.nextFireAt ?? 0).toISOString()}`);
2646
+ log(`[persistent-session] Task '${task.name}' injected for '${codeName}', next fire at ${new Date(updated.tasks[task.taskId]?.nextFireAt ?? 0).toISOString()}`);
2647
+ } else {
2648
+ log(`[persistent-session] Task '${task.name}' injection FAILED for '${codeName}' \u2014 injectMessage returned false`);
2648
2649
  }
2649
2650
  break;
2650
2651
  }
2651
2652
  }
2652
- const stableTasksHash = createHash("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
2653
- const prevHash = knownTasksHashes.get(agent.agent_id);
2654
- if (stableTasksHash !== prevHash) {
2655
- const taskInputs = tasks.map((t) => ({
2656
- id: t.id,
2657
- template_id: t.template_id,
2658
- name: t.name,
2659
- schedule_kind: t.schedule_kind,
2660
- schedule_expr: t.schedule_expr ?? null,
2661
- schedule_every: t.schedule_every ?? null,
2662
- schedule_at: t.schedule_at ?? null,
2663
- timezone: t.timezone ?? "UTC",
2664
- prompt: t.prompt ?? "",
2665
- session_target: t.session_target ?? "isolated",
2666
- delivery_mode: t.delivery_mode ?? "none",
2667
- delivery_channel: t.delivery_channel ?? null,
2668
- delivery_to: t.delivery_to ?? null,
2669
- enabled: t.enabled ?? true
2670
- }));
2671
- const schedulerState = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
2672
- claudeSchedulerStates.set(codeName, schedulerState);
2673
- knownTasksHashes.set(agent.agent_id, stableTasksHash);
2674
- log(`[persistent-session] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
2675
- }
2676
2653
  }
2677
2654
  var realtimeStarted = false;
2678
2655
  var realtimeDriftStarted = false;
@@ -2887,7 +2864,7 @@ async function processDirectChatMessage(agent, msg) {
2887
2864
  try {
2888
2865
  let reply;
2889
2866
  if (fw === "claude-code") {
2890
- const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-APXMZEK4.js");
2867
+ const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-V2QNX3Z4.js");
2891
2868
  const projDir = ccProjectDir(agent.codeName);
2892
2869
  const chatArgs = [
2893
2870
  "-p",
@@ -3646,8 +3623,8 @@ var caffeinateProc = null;
3646
3623
  async function startCaffeinate() {
3647
3624
  if (process.platform !== "darwin") return;
3648
3625
  try {
3649
- const { spawn: spawn2 } = await import("child_process");
3650
- caffeinateProc = spawn2("caffeinate", ["-dims"], {
3626
+ const { spawn } = await import("child_process");
3627
+ caffeinateProc = spawn("caffeinate", ["-dims"], {
3651
3628
  stdio: "ignore",
3652
3629
  detached: false
3653
3630
  });