@integrity-labs/agt-cli 0.19.13 → 0.19.15

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.
@@ -2632,11 +2632,146 @@ function extractAllowedDomains(input) {
2632
2632
  registerFramework(nemoClawAdapter);
2633
2633
 
2634
2634
  // ../../packages/core/dist/provisioning/frameworks/claudecode/index.js
2635
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync4, chmodSync as chmodSync4, readdirSync, rmSync, copyFileSync } from "fs";
2635
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync5, chmodSync as chmodSync4, readdirSync, rmSync, copyFileSync } from "fs";
2636
2636
  import { join as join4, relative, dirname as dirname4 } from "path";
2637
2637
  import { homedir as homedir3 } from "os";
2638
2638
  import { execFile as execFile3 } from "child_process";
2639
2639
 
2640
+ // ../../packages/core/dist/provisioning/mcp-config-guards.js
2641
+ import { existsSync as existsSync4, readFileSync as readFileSync4, renameSync as renameSync3, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3 } from "fs";
2642
+ var REQUIRED_ENV_RULES_BY_SERVER = {
2643
+ "cloud-broker": [
2644
+ { key: "AGT_HOST", mustBeConcrete: false },
2645
+ { key: "AGT_API_KEY", mustBeConcrete: false },
2646
+ // ENG-4744: this is the bug shape — writer used to omit this
2647
+ // entirely, or render it as a literal `${AGT_AGENT_ID}` instead
2648
+ // of the agent's real UUID. The broker has no way to substitute
2649
+ // it post-spawn, so a placeholder here = silently broken agent.
2650
+ { key: "AGT_AGENT_ID", mustBeConcrete: true }
2651
+ ]
2652
+ };
2653
+ var PLACEHOLDER_RE = /\$\{[^}]+\}/;
2654
+ function validateRenderedMcpConfig(config) {
2655
+ const errors = [];
2656
+ if (typeof config !== "object" || config === null || Array.isArray(config)) {
2657
+ return {
2658
+ ok: false,
2659
+ errors: [
2660
+ {
2661
+ kind: "invalid_json_shape",
2662
+ server: "*",
2663
+ message: "config root must be a non-null object"
2664
+ }
2665
+ ]
2666
+ };
2667
+ }
2668
+ const root = config;
2669
+ if (root.mcpServers === void 0) {
2670
+ return { ok: true };
2671
+ }
2672
+ if (typeof root.mcpServers !== "object" || root.mcpServers === null) {
2673
+ return {
2674
+ ok: false,
2675
+ errors: [
2676
+ {
2677
+ kind: "invalid_json_shape",
2678
+ server: "*",
2679
+ message: "mcpServers must be an object"
2680
+ }
2681
+ ]
2682
+ };
2683
+ }
2684
+ if (Array.isArray(root.mcpServers)) {
2685
+ return {
2686
+ ok: false,
2687
+ errors: [
2688
+ {
2689
+ kind: "invalid_json_shape",
2690
+ server: "*",
2691
+ message: "mcpServers must be an object"
2692
+ }
2693
+ ]
2694
+ };
2695
+ }
2696
+ for (const [serverKey, raw] of Object.entries(root.mcpServers)) {
2697
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
2698
+ errors.push({
2699
+ kind: "invalid_json_shape",
2700
+ server: serverKey,
2701
+ message: `entry must be an object`
2702
+ });
2703
+ continue;
2704
+ }
2705
+ const entry = raw;
2706
+ const rules = REQUIRED_ENV_RULES_BY_SERVER[serverKey];
2707
+ if (rules) {
2708
+ const env2 = entry.env ?? {};
2709
+ for (const rule of rules) {
2710
+ const value = env2[rule.key];
2711
+ if (typeof value !== "string" || value.length === 0) {
2712
+ errors.push({
2713
+ kind: "missing_required_env",
2714
+ server: serverKey,
2715
+ message: `missing required env key: ${rule.key}`
2716
+ });
2717
+ continue;
2718
+ }
2719
+ if (rule.mustBeConcrete && PLACEHOLDER_RE.test(value)) {
2720
+ errors.push({
2721
+ kind: "unexpanded_placeholder",
2722
+ server: serverKey,
2723
+ message: `env.${rule.key} contains an unexpanded \${...} placeholder; expected a concrete value`
2724
+ });
2725
+ }
2726
+ }
2727
+ }
2728
+ }
2729
+ return errors.length === 0 ? { ok: true } : { ok: false, errors };
2730
+ }
2731
+ function formatValidationErrors(errors) {
2732
+ return errors.map((e) => `${e.server}:${e.kind}=${e.message}`).join("; ");
2733
+ }
2734
+ function safeWriteJsonAtomic(path, content, opts = {}) {
2735
+ const keepBackup = opts.keepBackup !== false;
2736
+ const rename = opts.renamer ?? renameSync3;
2737
+ const tmpPath = `${path}.new`;
2738
+ const bakPath = `${path}.bak`;
2739
+ let movedOriginalToBackup = false;
2740
+ try {
2741
+ writeFileSync4(tmpPath, content);
2742
+ } catch (err) {
2743
+ throw err;
2744
+ }
2745
+ try {
2746
+ if (keepBackup && existsSync4(path)) {
2747
+ rename(path, bakPath);
2748
+ movedOriginalToBackup = true;
2749
+ }
2750
+ rename(tmpPath, path);
2751
+ } catch (err) {
2752
+ try {
2753
+ if (existsSync4(tmpPath))
2754
+ unlinkSync3(tmpPath);
2755
+ } catch {
2756
+ }
2757
+ if (movedOriginalToBackup && !existsSync4(path) && existsSync4(bakPath)) {
2758
+ try {
2759
+ renameSync3(bakPath, path);
2760
+ } catch {
2761
+ }
2762
+ }
2763
+ throw err;
2764
+ }
2765
+ }
2766
+ function safeWriteMcpJson(path, config) {
2767
+ const validation = validateRenderedMcpConfig(config);
2768
+ if (!validation.ok) {
2769
+ return { written: false, errors: validation.errors };
2770
+ }
2771
+ safeWriteJsonAtomic(path, JSON.stringify(config, null, 2));
2772
+ return { written: true, errors: [] };
2773
+ }
2774
+
2640
2775
  // ../../packages/core/dist/provisioning/frameworks/claudecode/identity.js
2641
2776
  function buildMemorySection(hasQmd) {
2642
2777
  const recall = hasQmd ? `### Recall
@@ -3300,7 +3435,7 @@ function migrateLegacyClaudecodeDir(codeName, log) {
3300
3435
  if (migratedCodeNames.has(codeName))
3301
3436
  return;
3302
3437
  const legacyRoot = join4(getHomeDir3(), ".augmented", codeName, "claudecode");
3303
- if (!existsSync4(legacyRoot)) {
3438
+ if (!existsSync5(legacyRoot)) {
3304
3439
  migratedCodeNames.add(codeName);
3305
3440
  return;
3306
3441
  }
@@ -3318,25 +3453,25 @@ function migrateLegacyClaudecodeDir(codeName, log) {
3318
3453
  walkAndMigrate(src, dest);
3319
3454
  continue;
3320
3455
  }
3321
- if (entry.name === ".mcp.json" && existsSync4(dest)) {
3456
+ if (entry.name === ".mcp.json" && existsSync5(dest)) {
3322
3457
  try {
3323
- const oldCfg = JSON.parse(readFileSync4(src, "utf-8"));
3324
- const newCfg = JSON.parse(readFileSync4(dest, "utf-8"));
3458
+ const oldCfg = JSON.parse(readFileSync5(src, "utf-8"));
3459
+ const newCfg = JSON.parse(readFileSync5(dest, "utf-8"));
3325
3460
  const merged = { mcpServers: { ...oldCfg.mcpServers ?? {}, ...newCfg.mcpServers ?? {} } };
3326
- writeFileSync4(dest, JSON.stringify(merged, null, 2));
3461
+ writeFileSync5(dest, JSON.stringify(merged, null, 2));
3327
3462
  emit(`[migrate] '${codeName}' merged .mcp.json (${Object.keys(merged.mcpServers).length} servers)`);
3328
3463
  } catch (err) {
3329
3464
  throw new Error(`Failed merging .mcp.json (${src} \u2192 ${dest}): ${err.message}`);
3330
3465
  }
3331
3466
  continue;
3332
3467
  }
3333
- if (!existsSync4(dest)) {
3468
+ if (!existsSync5(dest)) {
3334
3469
  copyFileSync(src, dest);
3335
3470
  continue;
3336
3471
  }
3337
3472
  try {
3338
- const srcStat = readFileSync4(src);
3339
- const destStat = readFileSync4(dest);
3473
+ const srcStat = readFileSync5(src);
3474
+ const destStat = readFileSync5(dest);
3340
3475
  if (!srcStat.equals(destStat)) {
3341
3476
  }
3342
3477
  } catch (err) {
@@ -3362,16 +3497,69 @@ function syncMcpToProject(codeName) {
3362
3497
  const provisionMcpPath = join4(agentDir, "provision", ".mcp.json");
3363
3498
  const projectMcpPath = join4(projectDir, ".mcp.json");
3364
3499
  try {
3365
- const content = readFileSync4(provisionMcpPath, "utf-8");
3500
+ const content = readFileSync5(provisionMcpPath, "utf-8");
3366
3501
  mkdirSync4(projectDir, { recursive: true });
3367
- writeFileSync4(projectMcpPath, content);
3502
+ writeFileSync5(projectMcpPath, content);
3503
+ } catch {
3504
+ }
3505
+ renderChannelMessageHandlerForAgent(codeName);
3506
+ }
3507
+ var INTEGRATIONS_SUMMARY_FILE = "integrations-summary.json";
3508
+ function integrationsSummaryPath(codeName) {
3509
+ return join4(getAgentDir(codeName), "provision", INTEGRATIONS_SUMMARY_FILE);
3510
+ }
3511
+ function writeIntegrationsSummaryForAgent(codeName, summaries) {
3512
+ const target = integrationsSummaryPath(codeName);
3513
+ try {
3514
+ mkdirSync4(dirname4(target), { recursive: true });
3515
+ writeFileSync5(target, JSON.stringify(summaries, null, 2));
3516
+ } catch {
3517
+ }
3518
+ }
3519
+ function readIntegrationsSummaryForAgent(codeName) {
3520
+ try {
3521
+ const raw = readFileSync5(integrationsSummaryPath(codeName), "utf-8");
3522
+ const parsed = JSON.parse(raw);
3523
+ return Array.isArray(parsed) ? parsed : [];
3524
+ } catch {
3525
+ return [];
3526
+ }
3527
+ }
3528
+ function renderChannelMessageHandlerForAgent(codeName) {
3529
+ const agentDir = getAgentDir(codeName);
3530
+ const projectDir = getProjectDir(codeName);
3531
+ const provisionMcpPath = join4(agentDir, "provision", ".mcp.json");
3532
+ let mcpServerKeys;
3533
+ try {
3534
+ const config = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
3535
+ mcpServerKeys = Object.keys(config.mcpServers ?? {});
3368
3536
  } catch {
3537
+ return;
3538
+ }
3539
+ const integrations = readIntegrationsSummaryForAgent(codeName);
3540
+ const content = buildChannelMessageHandlerAgent({ mcpServerKeys, integrations });
3541
+ for (const baseDir of [agentDir, projectDir]) {
3542
+ const target = join4(baseDir, ".claude", "agents", "channel-message-handler.md");
3543
+ try {
3544
+ mkdirSync4(dirname4(target), { recursive: true });
3545
+ writeFileSync5(target, content);
3546
+ } catch {
3547
+ }
3548
+ }
3549
+ }
3550
+ function writeMcpJsonGuarded(codeName, path, config) {
3551
+ const result = safeWriteMcpJson(path, config);
3552
+ if (!result.written) {
3553
+ process.stderr.write(`[manager-worker] [mcp-validate] skipping write for '${codeName}': ${formatValidationErrors(result.errors)}
3554
+ `);
3555
+ return false;
3369
3556
  }
3557
+ return true;
3370
3558
  }
3371
3559
  function readExistingMcpEnvVar(codeName, serverId, envKey) {
3372
3560
  const mcpJsonPath = join4(getAgentDir(codeName), "provision", ".mcp.json");
3373
3561
  try {
3374
- const raw = readFileSync4(mcpJsonPath, "utf-8");
3562
+ const raw = readFileSync5(mcpJsonPath, "utf-8");
3375
3563
  const config = JSON.parse(raw);
3376
3564
  const server = config.mcpServers?.[serverId];
3377
3565
  if (!server || typeof server !== "object")
@@ -3386,6 +3574,18 @@ function readExistingMcpEnvVar(codeName, serverId, envKey) {
3386
3574
  return void 0;
3387
3575
  }
3388
3576
  }
3577
+ var PLACEHOLDER_LITERAL_RE = /\$\{[^}]+\}/;
3578
+ function resolveBrokerAgentId(codeName, fallback) {
3579
+ const existing = readExistingMcpEnvVar(codeName, "cloud-broker", "AGT_AGENT_ID");
3580
+ if (existing && !PLACEHOLDER_LITERAL_RE.test(existing))
3581
+ return existing;
3582
+ const fromAugmented = readExistingMcpEnvVar(codeName, "augmented", "AGT_AGENT_ID");
3583
+ if (fromAugmented && !PLACEHOLDER_LITERAL_RE.test(fromAugmented))
3584
+ return fromAugmented;
3585
+ if (fallback && !PLACEHOLDER_LITERAL_RE.test(fallback))
3586
+ return fallback;
3587
+ return void 0;
3588
+ }
3389
3589
  function deployArtifactsToProject(codeName, provisionDir) {
3390
3590
  const projectDir = getProjectDir(codeName);
3391
3591
  mkdirSync4(projectDir, { recursive: true });
@@ -3396,27 +3596,27 @@ function deployArtifactsToProject(codeName, provisionDir) {
3396
3596
  const src = join4(provisionDir, file);
3397
3597
  const dest = join4(projectDir, file);
3398
3598
  try {
3399
- const srcContent = readFileSync4(src, "utf-8");
3400
- if (file === "CLAUDE.md" && existsSync4(dest)) {
3401
- const destContent = readFileSync4(dest, "utf-8");
3599
+ const srcContent = readFileSync5(src, "utf-8");
3600
+ if (file === "CLAUDE.md" && existsSync5(dest)) {
3601
+ const destContent = readFileSync5(dest, "utf-8");
3402
3602
  const stripIndex = (s) => s.replace(new RegExp(`${SKILLS_START}[\\s\\S]*?${SKILLS_END}`), "").trimEnd();
3403
3603
  if (stripIndex(srcContent) === stripIndex(destContent))
3404
3604
  continue;
3405
3605
  const indexMatch = destContent.match(new RegExp(`${SKILLS_START}[\\s\\S]*?${SKILLS_END}`));
3406
3606
  if (indexMatch) {
3407
- writeFileSync4(dest, srcContent.trimEnd() + "\n\n" + indexMatch[0] + "\n");
3607
+ writeFileSync5(dest, srcContent.trimEnd() + "\n\n" + indexMatch[0] + "\n");
3408
3608
  continue;
3409
3609
  }
3410
3610
  }
3411
- writeFileSync4(dest, srcContent);
3611
+ writeFileSync5(dest, srcContent);
3412
3612
  } catch {
3413
3613
  }
3414
3614
  }
3415
3615
  const skillsDir = join4(provisionDir, ".claude", "skills");
3416
3616
  const destSkillsDir = join4(projectDir, ".claude", "skills");
3417
3617
  try {
3418
- if (existsSync4(destSkillsDir)) {
3419
- const srcFolders = existsSync4(skillsDir) ? new Set(readdirSync(skillsDir)) : /* @__PURE__ */ new Set();
3618
+ if (existsSync5(destSkillsDir)) {
3619
+ const srcFolders = existsSync5(skillsDir) ? new Set(readdirSync(skillsDir)) : /* @__PURE__ */ new Set();
3420
3620
  for (const folder of readdirSync(destSkillsDir)) {
3421
3621
  if (folder.startsWith("knowledge-") || folder === "core-knowledge" && !srcFolders.has(folder)) {
3422
3622
  try {
@@ -3426,21 +3626,21 @@ function deployArtifactsToProject(codeName, provisionDir) {
3426
3626
  }
3427
3627
  }
3428
3628
  }
3429
- if (existsSync4(skillsDir)) {
3629
+ if (existsSync5(skillsDir)) {
3430
3630
  for (const skillFolder of readdirSync(skillsDir)) {
3431
3631
  const srcSkillFile = join4(skillsDir, skillFolder, "SKILL.md");
3432
- if (!existsSync4(srcSkillFile))
3632
+ if (!existsSync5(srcSkillFile))
3433
3633
  continue;
3434
3634
  const destFolder = join4(destSkillsDir, skillFolder);
3435
3635
  const destFile = join4(destFolder, "SKILL.md");
3436
- const srcContent = readFileSync4(srcSkillFile, "utf-8");
3636
+ const srcContent = readFileSync5(srcSkillFile, "utf-8");
3437
3637
  try {
3438
- if (existsSync4(destFile) && readFileSync4(destFile, "utf-8") === srcContent)
3638
+ if (existsSync5(destFile) && readFileSync5(destFile, "utf-8") === srcContent)
3439
3639
  continue;
3440
3640
  } catch {
3441
3641
  }
3442
3642
  mkdirSync4(destFolder, { recursive: true });
3443
- writeFileSync4(destFile, srcContent);
3643
+ writeFileSync5(destFile, srcContent);
3444
3644
  }
3445
3645
  }
3446
3646
  } catch {
@@ -3448,9 +3648,9 @@ function deployArtifactsToProject(codeName, provisionDir) {
3448
3648
  const agentsDir = join4(provisionDir, ".claude", "agents");
3449
3649
  const destAgentsDir = join4(projectDir, ".claude", "agents");
3450
3650
  try {
3451
- if (existsSync4(agentsDir)) {
3651
+ if (existsSync5(agentsDir)) {
3452
3652
  const sourceAgentFiles = new Set(readdirSync(agentsDir).filter((f) => f.endsWith(".md")));
3453
- if (existsSync4(destAgentsDir)) {
3653
+ if (existsSync5(destAgentsDir)) {
3454
3654
  for (const destFile of readdirSync(destAgentsDir)) {
3455
3655
  if (!destFile.endsWith(".md"))
3456
3656
  continue;
@@ -3465,14 +3665,14 @@ function deployArtifactsToProject(codeName, provisionDir) {
3465
3665
  for (const agentFile of sourceAgentFiles) {
3466
3666
  const srcPath = join4(agentsDir, agentFile);
3467
3667
  const destPath = join4(destAgentsDir, agentFile);
3468
- const srcContent = readFileSync4(srcPath, "utf-8");
3668
+ const srcContent = readFileSync5(srcPath, "utf-8");
3469
3669
  try {
3470
- if (existsSync4(destPath) && readFileSync4(destPath, "utf-8") === srcContent)
3670
+ if (existsSync5(destPath) && readFileSync5(destPath, "utf-8") === srcContent)
3471
3671
  continue;
3472
3672
  } catch {
3473
3673
  }
3474
3674
  mkdirSync4(destAgentsDir, { recursive: true });
3475
- writeFileSync4(destPath, srcContent);
3675
+ writeFileSync5(destPath, srcContent);
3476
3676
  }
3477
3677
  }
3478
3678
  } catch {
@@ -3480,10 +3680,10 @@ function deployArtifactsToProject(codeName, provisionDir) {
3480
3680
  const agentMcpPath = join4(getAgentDir(codeName), "provision", ".mcp.json");
3481
3681
  const projectMcpPath = join4(projectDir, ".mcp.json");
3482
3682
  try {
3483
- const agentMcp = JSON.parse(readFileSync4(agentMcpPath, "utf-8"));
3683
+ const agentMcp = JSON.parse(readFileSync5(agentMcpPath, "utf-8"));
3484
3684
  let projectMcp;
3485
3685
  try {
3486
- projectMcp = JSON.parse(readFileSync4(projectMcpPath, "utf-8"));
3686
+ projectMcp = JSON.parse(readFileSync5(projectMcpPath, "utf-8"));
3487
3687
  } catch {
3488
3688
  projectMcp = { mcpServers: {} };
3489
3689
  }
@@ -3494,28 +3694,28 @@ function deployArtifactsToProject(codeName, provisionDir) {
3494
3694
  return !(entry && typeof entry["url"] === "string" && entry["url"].startsWith("/"));
3495
3695
  }));
3496
3696
  projectMcp["mcpServers"] = { ...stripRelativeUrls(projectServers), ...stripRelativeUrls(agentServers) };
3497
- writeFileSync4(projectMcpPath, JSON.stringify(projectMcp, null, 2));
3697
+ writeFileSync5(projectMcpPath, JSON.stringify(projectMcp, null, 2));
3498
3698
  } catch {
3499
3699
  }
3500
3700
  const agentDir = getAgentDir(codeName);
3501
3701
  for (const envFile of [".env", ".env.integrations"]) {
3502
3702
  try {
3503
- const content = readFileSync4(join4(agentDir, envFile), "utf-8");
3504
- writeFileSync4(join4(projectDir, envFile), content);
3703
+ const content = readFileSync5(join4(agentDir, envFile), "utf-8");
3704
+ writeFileSync5(join4(projectDir, envFile), content);
3505
3705
  } catch {
3506
3706
  }
3507
3707
  }
3508
3708
  try {
3509
3709
  const gitDir = join4(projectDir, ".git");
3510
3710
  const hookSrc = join4(provisionDir, ".git-hooks", "pre-commit");
3511
- if (existsSync4(gitDir) && existsSync4(hookSrc)) {
3711
+ if (existsSync5(gitDir) && existsSync5(hookSrc)) {
3512
3712
  const hooksDir = join4(gitDir, "hooks");
3513
3713
  mkdirSync4(hooksDir, { recursive: true });
3514
3714
  const hookDest = join4(hooksDir, "pre-commit");
3515
- const srcContent = readFileSync4(hookSrc, "utf-8");
3516
- const upToDate = existsSync4(hookDest) && readFileSync4(hookDest, "utf-8") === srcContent;
3715
+ const srcContent = readFileSync5(hookSrc, "utf-8");
3716
+ const upToDate = existsSync5(hookDest) && readFileSync5(hookDest, "utf-8") === srcContent;
3517
3717
  if (!upToDate)
3518
- writeFileSync4(hookDest, srcContent);
3718
+ writeFileSync5(hookDest, srcContent);
3519
3719
  chmodSync4(hookDest, 493);
3520
3720
  }
3521
3721
  } catch {
@@ -3556,7 +3756,7 @@ function provisionStopHook(codeName) {
3556
3756
  "esac",
3557
3757
  "exit 0"
3558
3758
  ].join("\n") + "\n";
3559
- writeFileSync4(hookScriptPath, hookScript, { mode: 493 });
3759
+ writeFileSync5(hookScriptPath, hookScript, { mode: 493 });
3560
3760
  const ghostHookPath = join4(claudeDir, "agt-ghost-reply-hook.sh");
3561
3761
  const ghostHookScript = [
3562
3762
  "#!/bin/bash",
@@ -3676,11 +3876,11 @@ function provisionStopHook(codeName) {
3676
3876
  "fi",
3677
3877
  "exit 0"
3678
3878
  ].join("\n") + "\n";
3679
- writeFileSync4(ghostHookPath, ghostHookScript, { mode: 493 });
3879
+ writeFileSync5(ghostHookPath, ghostHookScript, { mode: 493 });
3680
3880
  const settingsPath = join4(claudeDir, "settings.local.json");
3681
3881
  let settings = {};
3682
3882
  try {
3683
- settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
3883
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
3684
3884
  } catch {
3685
3885
  }
3686
3886
  const hooks = settings["hooks"] ?? {};
@@ -3693,7 +3893,7 @@ function provisionStopHook(codeName) {
3693
3893
  }
3694
3894
  ];
3695
3895
  settings["hooks"] = hooks;
3696
- writeFileSync4(settingsPath, JSON.stringify(settings, null, 2));
3896
+ writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
3697
3897
  }
3698
3898
  function provisionIsolationHook(codeName) {
3699
3899
  const projectDir = getProjectDir(codeName);
@@ -3756,11 +3956,11 @@ function provisionIsolationHook(codeName) {
3756
3956
  "",
3757
3957
  "exit 0"
3758
3958
  ].join("\n") + "\n";
3759
- writeFileSync4(hookScriptPath, hookScript, { mode: 493 });
3959
+ writeFileSync5(hookScriptPath, hookScript, { mode: 493 });
3760
3960
  const settingsPath = join4(claudeDir, "settings.local.json");
3761
3961
  let settings = {};
3762
3962
  try {
3763
- settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
3963
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
3764
3964
  } catch {
3765
3965
  }
3766
3966
  const hooks = settings["hooks"] ?? {};
@@ -3775,13 +3975,13 @@ function provisionIsolationHook(codeName) {
3775
3975
  }
3776
3976
  ];
3777
3977
  settings["hooks"] = hooks;
3778
- writeFileSync4(settingsPath, JSON.stringify(settings, null, 2));
3978
+ writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
3779
3979
  }
3780
3980
  function modifyJsonConfig(filePath, fn) {
3781
3981
  let originalContent;
3782
3982
  let config;
3783
3983
  try {
3784
- originalContent = readFileSync4(filePath, "utf-8");
3984
+ originalContent = readFileSync5(filePath, "utf-8");
3785
3985
  config = JSON.parse(originalContent);
3786
3986
  } catch {
3787
3987
  return;
@@ -3792,7 +3992,7 @@ function modifyJsonConfig(filePath, fn) {
3792
3992
  const newContent = JSON.stringify(config, null, 2);
3793
3993
  if (newContent === originalContent)
3794
3994
  return;
3795
- writeFileSync4(filePath, newContent);
3995
+ writeFileSync5(filePath, newContent);
3796
3996
  }
3797
3997
  var SECRETS_DENY_PERMISSIONS = [
3798
3998
  // Read blocks
@@ -3898,12 +4098,42 @@ function buildSettingsJson(input) {
3898
4098
  settings["permissions"] = { deny: SECRETS_DENY_PERMISSIONS };
3899
4099
  return settings;
3900
4100
  }
3901
- function buildChannelMessageHandlerAgent() {
4101
+ function sanitizeMcpName(name) {
4102
+ return name.replace(/-/g, "_");
4103
+ }
4104
+ function buildChannelMessageHandlerAgent(args) {
4105
+ const mcpServerKeys = args?.mcpServerKeys ?? [];
4106
+ const integrations = args?.integrations ?? [];
4107
+ const mcpWildcards = Array.from(new Set(mcpServerKeys.map((k) => `mcp__${sanitizeMcpName(k)}__*`)));
4108
+ const tools = [
4109
+ "Bash",
4110
+ "Read",
4111
+ "Write",
4112
+ "Edit",
4113
+ "Grep",
4114
+ "Glob",
4115
+ "Skill",
4116
+ "Agent",
4117
+ ...mcpWildcards
4118
+ ].join(", ");
4119
+ const integrationsBlock = integrations.length === 0 ? "" : `
4120
+ ## Integrations available
4121
+
4122
+ You inherit the parent agent's environment, including credentials for the integrations below. Env vars follow the convention \`<DEFINITION_ID>_ACCESS_TOKEN\` for OAuth and \`<DEFINITION_ID>_API_KEY\` for API-key integrations (e.g. \`GITHUB_ACCESS_TOKEN\`, \`POSTIZ_API_KEY\`). Where a CLI is listed, prefer it over raw curl \u2014 the CLI handles auth automatically.
4123
+
4124
+ ${integrations.map((i) => {
4125
+ const cli = i.cliBinary ? ` \u2014 use the \`${i.cliBinary}\` CLI` : "";
4126
+ const desc = i.description ? `. ${i.description}` : "";
4127
+ return `- **${i.name}**${cli}${desc}`;
4128
+ }).join("\n")}
4129
+
4130
+ If asked about your capabilities, **check first** (run the CLI, echo the env var, or invoke an MCP tool) before answering. Do not claim a capability is absent without verifying \u2014 the parent's environment is yours.
4131
+ `;
3902
4132
  return `---
3903
4133
  name: channel-message-handler
3904
4134
  description: Handles a single inbound Slack/Telegram/Direct-Chat message end to end. Posts the reply itself via the matching channel tool. The parent agent dispatches to this subagent only for slow requests (\u2265 ~60s) so the parent's listener turn stays free for new inbound work.
3905
4135
  background: true
3906
- tools: Bash, Read, Write, Edit, Grep, Glob, Skill, Agent, mcp__augmented__*, mcp__slack__*, mcp__telegram__*, mcp__direct_chat__*, mcp__xero__*, mcp__granola__*, mcp__composio_attio__*, mcp__composio_canva__*, mcp__composio_gmail__*, mcp__composio_googlecalendar__*, mcp__composio_googledocs__*, mcp__composio_googledrive__*, mcp__composio_googlesheets__*
4136
+ tools: ${tools}
3907
4137
  ---
3908
4138
 
3909
4139
  You are dispatched by the parent agent to handle one channel message.
@@ -3920,7 +4150,7 @@ Your job:
3920
4150
  3. End. Do not do additional follow-up work; if more is needed, the user will send another message.
3921
4151
 
3922
4152
  Do NOT post intermediate progress updates unless the work spans 5+ minutes \u2014 keep noise low. The parent already sent a single-line acknowledgement before dispatching you.
3923
- `;
4153
+ ${integrationsBlock}`;
3924
4154
  }
3925
4155
  function buildPostizMcpEntry(integration) {
3926
4156
  const rawBaseUrl = integration.config["base_url"];
@@ -4097,10 +4327,12 @@ var claudeCodeAdapter = {
4097
4327
  teamMembers: input.teamMembers,
4098
4328
  people: input.people
4099
4329
  };
4330
+ const mcpJson = buildMcpJson(input);
4331
+ const initialMcpServerKeys = Object.keys(mcpJson.mcpServers ?? {});
4100
4332
  const artifacts = [
4101
4333
  { relativePath: "CLAUDE.md", content: generateClaudeMd(claudeMdInput) },
4102
4334
  { relativePath: "settings.json", content: JSON.stringify(buildSettingsJson(input), null, 2) },
4103
- { relativePath: ".mcp.json", content: JSON.stringify(buildMcpJson(input), null, 2) },
4335
+ { relativePath: ".mcp.json", content: JSON.stringify(mcpJson, null, 2) },
4104
4336
  { relativePath: "CHARTER.md", content: input.charterContent },
4105
4337
  { relativePath: "TOOLS.md", content: input.toolsContent },
4106
4338
  // ENG-4684: named subagent the parent uses for slow channel-message
@@ -4108,9 +4340,19 @@ var claudeCodeAdapter = {
4108
4340
  // turn return immediately on dispatch, so new inbound messages get a
4109
4341
  // fresh turn while the subagent does the work in parallel. Triggered
4110
4342
  // by the "Channel message triage" instruction in CLAUDE.md.
4343
+ // ENG-4821: integrations are rendered into the subagent body so it
4344
+ // stops claiming "no creds" for capabilities the parent has — and the
4345
+ // sidecar JSON keeps incremental writeIntegrations syncs in lockstep.
4111
4346
  {
4112
4347
  relativePath: ".claude/agents/channel-message-handler.md",
4113
- content: buildChannelMessageHandlerAgent()
4348
+ content: buildChannelMessageHandlerAgent({
4349
+ mcpServerKeys: initialMcpServerKeys,
4350
+ integrations: integrationSummaries
4351
+ })
4352
+ },
4353
+ {
4354
+ relativePath: `provision/${INTEGRATIONS_SUMMARY_FILE}`,
4355
+ content: JSON.stringify(integrationSummaries, null, 2)
4114
4356
  }
4115
4357
  ];
4116
4358
  const knowledgeEntries = input.knowledge ?? [];
@@ -4165,7 +4407,7 @@ ${sections}`
4165
4407
  } catch {
4166
4408
  continue;
4167
4409
  }
4168
- if (existsSync4(join4(agentRoot, "registration.json"))) {
4410
+ if (existsSync5(join4(agentRoot, "registration.json"))) {
4169
4411
  agents.add(entry);
4170
4412
  }
4171
4413
  }
@@ -4179,14 +4421,14 @@ ${sections}`
4179
4421
  const projectDir = getProjectDir(codeName);
4180
4422
  mkdirSync4(agentDir, { recursive: true });
4181
4423
  mkdirSync4(projectDir, { recursive: true });
4182
- writeFileSync4(join4(agentDir, "registration.json"), JSON.stringify({
4424
+ writeFileSync5(join4(agentDir, "registration.json"), JSON.stringify({
4183
4425
  code_name: codeName,
4184
4426
  team_dir: teamDir,
4185
4427
  project_dir: projectDir,
4186
4428
  framework: "claude-code",
4187
4429
  registered_at: (/* @__PURE__ */ new Date()).toISOString()
4188
4430
  }, null, 2));
4189
- if (existsSync4(teamDir)) {
4431
+ if (existsSync5(teamDir)) {
4190
4432
  deployArtifactsToProject(codeName, teamDir);
4191
4433
  }
4192
4434
  return true;
@@ -4198,9 +4440,9 @@ ${sections}`
4198
4440
  try {
4199
4441
  const agentDir = getAgentDir(codeName);
4200
4442
  const regFile = join4(agentDir, "registration.json");
4201
- if (existsSync4(regFile)) {
4202
- const { unlinkSync: unlinkSync4 } = await import("fs");
4203
- unlinkSync4(regFile);
4443
+ if (existsSync5(regFile)) {
4444
+ const { unlinkSync: unlinkSync5 } = await import("fs");
4445
+ unlinkSync5(regFile);
4204
4446
  }
4205
4447
  return true;
4206
4448
  } catch {
@@ -4224,7 +4466,7 @@ ${sections}`
4224
4466
  }
4225
4467
  if (envLines.length > 1) {
4226
4468
  const envPath = join4(agentDir, ".env");
4227
- writeFileSync4(envPath, envLines.join("\n") + "\n");
4469
+ writeFileSync5(envPath, envLines.join("\n") + "\n");
4228
4470
  chmodSync4(envPath, SECRET_FILE_MODE);
4229
4471
  }
4230
4472
  },
@@ -4273,13 +4515,15 @@ ${sections}`
4273
4515
  mkdirSync4(dirname4(provisionMcpPath), { recursive: true });
4274
4516
  let mcpConfig2 = { mcpServers: {} };
4275
4517
  try {
4276
- mcpConfig2 = JSON.parse(readFileSync4(provisionMcpPath, "utf-8"));
4518
+ mcpConfig2 = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
4277
4519
  if (!mcpConfig2.mcpServers)
4278
4520
  mcpConfig2.mcpServers = {};
4279
4521
  } catch {
4280
4522
  }
4281
4523
  mcpConfig2.mcpServers["telegram"] = telegramEntry;
4282
- writeFileSync4(provisionMcpPath, JSON.stringify(mcpConfig2, null, 2));
4524
+ if (!writeMcpJsonGuarded(codeName, provisionMcpPath, mcpConfig2)) {
4525
+ return;
4526
+ }
4283
4527
  syncMcpToProject(codeName);
4284
4528
  return;
4285
4529
  }
@@ -4290,7 +4534,7 @@ ${sections}`
4290
4534
  if (channelId === "discord") {
4291
4535
  const botToken = config["bot_token"];
4292
4536
  if (botToken) {
4293
- writeFileSync4(join4(channelDir, ".env"), `DISCORD_BOT_TOKEN=${botToken}
4537
+ writeFileSync5(join4(channelDir, ".env"), `DISCORD_BOT_TOKEN=${botToken}
4294
4538
  `);
4295
4539
  }
4296
4540
  } else if (channelId === "slack") {
@@ -4313,8 +4557,8 @@ ${sections}`
4313
4557
  if (botToken) {
4314
4558
  const localSlackChannel = join4(getHomeDir3(), ".augmented", "_mcp", "slack-channel.js");
4315
4559
  const slackEntry = {
4316
- command: existsSync4(localSlackChannel) ? "node" : "npx",
4317
- args: existsSync4(localSlackChannel) ? [localSlackChannel] : ["-y", "@augmented/claude-code-channel-slack"],
4560
+ command: existsSync5(localSlackChannel) ? "node" : "npx",
4561
+ args: existsSync5(localSlackChannel) ? [localSlackChannel] : ["-y", "@augmented/claude-code-channel-slack"],
4318
4562
  env: {
4319
4563
  SLACK_BOT_TOKEN: botToken,
4320
4564
  ...appToken ? { SLACK_APP_TOKEN: appToken } : {},
@@ -4332,16 +4576,18 @@ ${sections}`
4332
4576
  mkdirSync4(dirname4(provisionMcpPath), { recursive: true });
4333
4577
  let mcpConfig2 = { mcpServers: {} };
4334
4578
  try {
4335
- mcpConfig2 = JSON.parse(readFileSync4(provisionMcpPath, "utf-8"));
4579
+ mcpConfig2 = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
4336
4580
  if (!mcpConfig2.mcpServers)
4337
4581
  mcpConfig2.mcpServers = {};
4338
4582
  } catch {
4339
4583
  }
4340
4584
  mcpConfig2.mcpServers["slack"] = slackEntry;
4341
- writeFileSync4(provisionMcpPath, JSON.stringify(mcpConfig2, null, 2));
4585
+ if (!writeMcpJsonGuarded(codeName, provisionMcpPath, mcpConfig2)) {
4586
+ return;
4587
+ }
4342
4588
  syncMcpToProject(codeName);
4343
4589
  const staleChannelsPath = join4(getProjectDir(codeName), ".mcp-channels.json");
4344
- if (existsSync4(staleChannelsPath)) {
4590
+ if (existsSync5(staleChannelsPath)) {
4345
4591
  try {
4346
4592
  rmSync(staleChannelsPath, { force: true });
4347
4593
  } catch {
@@ -4354,7 +4600,7 @@ ${sections}`
4354
4600
  const mcpJsonPath = join4(agentDir, "provision", ".mcp.json");
4355
4601
  let mcpConfig;
4356
4602
  try {
4357
- mcpConfig = JSON.parse(readFileSync4(mcpJsonPath, "utf-8"));
4603
+ mcpConfig = JSON.parse(readFileSync5(mcpJsonPath, "utf-8"));
4358
4604
  } catch {
4359
4605
  mcpConfig = { mcpServers: {} };
4360
4606
  }
@@ -4390,7 +4636,7 @@ ${sections}`
4390
4636
  ...oneshotBlockKitAskUserEnabled && process.env["AGT_API_KEY"] ? { AGT_API_KEY: process.env["AGT_API_KEY"] } : {},
4391
4637
  ...oneshotBlockKitAskUserEnabled && options?.agentId ? { AGT_AGENT_ID: options.agentId } : {}
4392
4638
  } : {};
4393
- if (isPersistent && existsSync4(localSlackChannel)) {
4639
+ if (isPersistent && existsSync5(localSlackChannel)) {
4394
4640
  mcpServers["slack"] = {
4395
4641
  command: "node",
4396
4642
  args: [localSlackChannel],
@@ -4416,15 +4662,16 @@ ${sections}`
4416
4662
  };
4417
4663
  }
4418
4664
  }
4419
- writeFileSync4(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
4420
- syncMcpToProject(codeName);
4665
+ if (writeMcpJsonGuarded(codeName, mcpJsonPath, mcpConfig)) {
4666
+ syncMcpToProject(codeName);
4667
+ }
4421
4668
  },
4422
4669
  hasChannelCredentials(codeName, channelId) {
4423
4670
  const provisionMcpPath = join4(getAgentDir(codeName), "provision", ".mcp.json");
4424
- if (!existsSync4(provisionMcpPath))
4671
+ if (!existsSync5(provisionMcpPath))
4425
4672
  return false;
4426
4673
  try {
4427
- const parsed = JSON.parse(readFileSync4(provisionMcpPath, "utf-8"));
4674
+ const parsed = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
4428
4675
  return Boolean(parsed.mcpServers?.[channelId]);
4429
4676
  } catch {
4430
4677
  return false;
@@ -4464,12 +4711,22 @@ ${sections}`
4464
4711
  const schedulesPath = join4(agentDir, "schedules.json");
4465
4712
  const mapped = mapScheduledTasks(tasks);
4466
4713
  mkdirSync4(agentDir, { recursive: true });
4467
- writeFileSync4(schedulesPath, JSON.stringify({ schedules: mapped }, null, 2));
4714
+ writeFileSync5(schedulesPath, JSON.stringify({ schedules: mapped }, null, 2));
4468
4715
  return Promise.resolve();
4469
4716
  },
4470
4717
  writeIntegrations(codeName, integrations) {
4471
4718
  const agentDir = getAgentDir(codeName);
4472
4719
  mkdirSync4(agentDir, { recursive: true });
4720
+ const summariesForSidecar = integrations.map((i) => {
4721
+ const def = INTEGRATION_REGISTRY.find((d) => d.id === i.definition_id);
4722
+ return {
4723
+ id: i.definition_id,
4724
+ name: def?.name || i.display_name || i.definition_id,
4725
+ cliBinary: def?.cli_tool?.binary,
4726
+ description: def?.description || (i.auth_type === "managed" ? "Managed integration via Composio" : void 0)
4727
+ };
4728
+ });
4729
+ writeIntegrationsSummaryForAgent(codeName, summariesForSidecar);
4473
4730
  const decryptedIntegrations = integrations.map((integration) => ({
4474
4731
  ...integration,
4475
4732
  credentials: decryptIntegrationCredentials(integration.credentials)
@@ -4502,7 +4759,7 @@ ${sections}`
4502
4759
  }
4503
4760
  if (envLines.length > 1) {
4504
4761
  const envPath = join4(agentDir, ".env.integrations");
4505
- writeFileSync4(envPath, envLines.join("\n") + "\n");
4762
+ writeFileSync5(envPath, envLines.join("\n") + "\n");
4506
4763
  chmodSync4(envPath, SECRET_FILE_MODE);
4507
4764
  }
4508
4765
  writeXurlStoreForIntegrations(decryptedIntegrations);
@@ -4534,53 +4791,50 @@ ${sections}`
4534
4791
  }
4535
4792
  const hasCloudBroker = integrations.some((i) => i.definition_id === "cloud-broker");
4536
4793
  if (hasCloudBroker) {
4537
- const existingAgentId = readExistingMcpEnvVar(codeName, "cloud-broker", "AGT_AGENT_ID");
4538
- this.writeMcpServer(codeName, "cloud-broker", {
4539
- command: "npx",
4540
- args: ["-y", "@integrity-labs/cloud-broker@latest"],
4541
- env: {
4542
- AGT_HOST: "${AGT_HOST}",
4543
- AGT_API_KEY: "${AGT_API_KEY}",
4544
- AGT_AGENT_ID: existingAgentId ?? "${AGT_AGENT_ID}",
4545
- // ENG-4788: cloud-broker@0.6+ exits at startup if AGT_RUN_ID
4546
- // is missing. Mirror buildMcpJson's entry on incremental sync
4547
- // so an agent that connects AWS post-provision boots cleanly.
4548
- AGT_RUN_ID: "${AGT_RUN_ID}",
4549
- PATH: process.env["PATH"] ?? "",
4550
- HOME: process.env["HOME"] ?? ""
4551
- }
4552
- });
4794
+ const brokerAgentId = resolveBrokerAgentId(codeName);
4795
+ if (!brokerAgentId) {
4796
+ process.stderr.write(`[manager-worker] [cloud-broker] skipping write for '${codeName}': no real AGT_AGENT_ID available (no existing broker entry, no augmented entry to copy from). The full-provision artifact pipeline will recreate this on the next cycle.
4797
+ `);
4798
+ } else {
4799
+ this.writeMcpServer(codeName, "cloud-broker", {
4800
+ command: "npx",
4801
+ args: ["-y", "@integrity-labs/cloud-broker@latest"],
4802
+ env: {
4803
+ AGT_HOST: "${AGT_HOST}",
4804
+ AGT_API_KEY: "${AGT_API_KEY}",
4805
+ AGT_AGENT_ID: brokerAgentId,
4806
+ // ENG-4788: cloud-broker@0.6+ exits at startup if AGT_RUN_ID
4807
+ // is missing. Mirror buildMcpJson's entry on incremental sync
4808
+ // so an agent that connects AWS post-provision boots cleanly.
4809
+ AGT_RUN_ID: "${AGT_RUN_ID}",
4810
+ PATH: process.env["PATH"] ?? "",
4811
+ HOME: process.env["HOME"] ?? ""
4812
+ }
4813
+ });
4814
+ }
4553
4815
  }
4554
4816
  const projectDir = getProjectDir(codeName);
4555
4817
  const claudeMdPath = join4(projectDir, "CLAUDE.md");
4556
4818
  try {
4557
- const existing = readFileSync4(claudeMdPath, "utf-8");
4558
- const summaries = integrations.map((i) => {
4559
- const def = INTEGRATION_REGISTRY.find((d) => d.id === i.definition_id);
4560
- return {
4561
- id: i.definition_id,
4562
- name: def?.name || i.display_name || i.definition_id,
4563
- cliBinary: def?.cli_tool?.binary,
4564
- description: def?.description || (i.auth_type === "managed" ? "Managed integration via Composio" : void 0)
4565
- };
4566
- });
4567
- const newSection = buildIntegrationsSection(summaries);
4819
+ const existing = readFileSync5(claudeMdPath, "utf-8");
4820
+ const newSection = buildIntegrationsSection(summariesForSidecar);
4568
4821
  let updated;
4569
4822
  if (existing.includes("## Integrations")) {
4570
4823
  updated = existing.replace(/## Integrations[\s\S]*?(?=## Rules)/, newSection);
4571
4824
  } else {
4572
4825
  updated = existing.replace("## Rules", `${newSection}## Rules`);
4573
4826
  }
4574
- writeFileSync4(claudeMdPath, updated);
4827
+ writeFileSync5(claudeMdPath, updated);
4575
4828
  const agentDir2 = getAgentDir(codeName);
4576
4829
  const envSrc = join4(agentDir2, ".env.integrations");
4577
4830
  try {
4578
- const envContent = readFileSync4(envSrc, "utf-8");
4579
- writeFileSync4(join4(projectDir, ".env.integrations"), envContent);
4831
+ const envContent = readFileSync5(envSrc, "utf-8");
4832
+ writeFileSync5(join4(projectDir, ".env.integrations"), envContent);
4580
4833
  } catch {
4581
4834
  }
4582
4835
  } catch {
4583
4836
  }
4837
+ renderChannelMessageHandlerForAgent(codeName);
4584
4838
  },
4585
4839
  writeMcpServer(codeName, serverId, config) {
4586
4840
  const agentDir = getAgentDir(codeName);
@@ -4588,7 +4842,7 @@ ${sections}`
4588
4842
  mkdirSync4(join4(agentDir, "provision"), { recursive: true });
4589
4843
  let mcpConfig;
4590
4844
  try {
4591
- mcpConfig = JSON.parse(readFileSync4(mcpJsonPath, "utf-8"));
4845
+ mcpConfig = JSON.parse(readFileSync5(mcpJsonPath, "utf-8"));
4592
4846
  } catch {
4593
4847
  mcpConfig = { mcpServers: {} };
4594
4848
  }
@@ -4640,8 +4894,9 @@ ${sections}`
4640
4894
  serverEntry["env"] = config.env;
4641
4895
  }
4642
4896
  mcpServers[serverId] = serverEntry;
4643
- writeFileSync4(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
4644
- syncMcpToProject(codeName);
4897
+ if (writeMcpJsonGuarded(codeName, mcpJsonPath, mcpConfig)) {
4898
+ syncMcpToProject(codeName);
4899
+ }
4645
4900
  },
4646
4901
  getMcpPath(codeName) {
4647
4902
  return join4(getAgentDir(codeName), "provision", ".mcp.json");
@@ -4651,7 +4906,7 @@ ${sections}`
4651
4906
  const mcpJsonPath = join4(agentDir, "provision", ".mcp.json");
4652
4907
  let mcpConfig;
4653
4908
  try {
4654
- mcpConfig = JSON.parse(readFileSync4(mcpJsonPath, "utf-8"));
4909
+ mcpConfig = JSON.parse(readFileSync5(mcpJsonPath, "utf-8"));
4655
4910
  } catch {
4656
4911
  return;
4657
4912
  }
@@ -4659,8 +4914,9 @@ ${sections}`
4659
4914
  if (!mcpServers || !(serverId in mcpServers))
4660
4915
  return;
4661
4916
  delete mcpServers[serverId];
4662
- writeFileSync4(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
4663
- syncMcpToProject(codeName);
4917
+ if (writeMcpJsonGuarded(codeName, mcpJsonPath, mcpConfig)) {
4918
+ syncMcpToProject(codeName);
4919
+ }
4664
4920
  },
4665
4921
  installSkillFiles(codeName, skillId, files) {
4666
4922
  assertValidCodeName(skillId);
@@ -4680,13 +4936,13 @@ ${sections}`
4680
4936
  throw new Error(`Path traversal detected: ${file.relativePath} resolves outside ${skillDir}`);
4681
4937
  }
4682
4938
  mkdirSync4(join4(filePath, ".."), { recursive: true });
4683
- if (isPluginManaged && existsSync4(filePath)) {
4939
+ if (isPluginManaged && existsSync5(filePath)) {
4684
4940
  try {
4685
4941
  chmodSync4(filePath, READ_WRITE_MODE);
4686
4942
  } catch {
4687
4943
  }
4688
4944
  }
4689
- writeFileSync4(filePath, file.content);
4945
+ writeFileSync5(filePath, file.content);
4690
4946
  if (isPluginManaged) {
4691
4947
  try {
4692
4948
  chmodSync4(filePath, READ_ONLY_MODE);
@@ -4702,7 +4958,7 @@ ${sections}`
4702
4958
  mkdirSync4(agentDir, { recursive: true });
4703
4959
  let pluginsConfig;
4704
4960
  try {
4705
- pluginsConfig = JSON.parse(readFileSync4(pluginsJsonPath, "utf-8"));
4961
+ pluginsConfig = JSON.parse(readFileSync5(pluginsJsonPath, "utf-8"));
4706
4962
  } catch {
4707
4963
  pluginsConfig = { plugins: {} };
4708
4964
  }
@@ -4715,7 +4971,7 @@ ${sections}`
4715
4971
  installed_at: (/* @__PURE__ */ new Date()).toISOString(),
4716
4972
  ...pluginConfig ? { config: pluginConfig } : {}
4717
4973
  };
4718
- writeFileSync4(pluginsJsonPath, JSON.stringify(pluginsConfig, null, 2));
4974
+ writeFileSync5(pluginsJsonPath, JSON.stringify(pluginsConfig, null, 2));
4719
4975
  },
4720
4976
  /**
4721
4977
  * Full plugin provisioning: install scripts, register hooks, apply permissions,
@@ -4750,7 +5006,7 @@ ${sections}`
4750
5006
  const settingsPath = join4(claudeDir, "settings.local.json");
4751
5007
  let settings = {};
4752
5008
  try {
4753
- settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
5009
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
4754
5010
  } catch {
4755
5011
  }
4756
5012
  const existingHooks = settings["hooks"] ?? {};
@@ -4784,13 +5040,13 @@ ${sections}`
4784
5040
  }
4785
5041
  }
4786
5042
  settings["hooks"] = existingHooks;
4787
- writeFileSync4(settingsPath, JSON.stringify(settings, null, 2));
5043
+ writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
4788
5044
  }
4789
5045
  if (plugin.allowed_tools.length > 0) {
4790
5046
  const settingsPath = join4(claudeDir, "settings.local.json");
4791
5047
  let settings = {};
4792
5048
  try {
4793
- settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
5049
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
4794
5050
  } catch {
4795
5051
  }
4796
5052
  const existingPerms = settings["permissions"] ?? {};
@@ -4802,12 +5058,12 @@ ${sections}`
4802
5058
  }
4803
5059
  existingPerms["allow"] = allowList;
4804
5060
  settings["permissions"] = existingPerms;
4805
- writeFileSync4(settingsPath, JSON.stringify(settings, null, 2));
5061
+ writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
4806
5062
  }
4807
5063
  if (contextValues && Object.keys(contextValues).length > 0) {
4808
5064
  const configDir = join4(projectDir, `.${plugin.slug}`);
4809
5065
  mkdirSync4(configDir, { recursive: true });
4810
- writeFileSync4(join4(configDir, "config.json"), JSON.stringify(contextValues, null, 2));
5066
+ writeFileSync5(join4(configDir, "config.json"), JSON.stringify(contextValues, null, 2));
4811
5067
  }
4812
5068
  },
4813
5069
  executePluginHook(ctx) {
@@ -4869,7 +5125,7 @@ ${sections}`
4869
5125
  if (Object.keys(tokens).length === 0)
4870
5126
  return;
4871
5127
  const tokenPath = join4(agentDir, ".tokens.json");
4872
- writeFileSync4(tokenPath, JSON.stringify(tokens, null, 2));
5128
+ writeFileSync5(tokenPath, JSON.stringify(tokens, null, 2));
4873
5129
  chmodSync4(tokenPath, SECRET_FILE_MODE);
4874
5130
  }
4875
5131
  };
@@ -5159,13 +5415,13 @@ function jsonOutput(data) {
5159
5415
  }
5160
5416
 
5161
5417
  // src/lib/config.ts
5162
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync5 } from "fs";
5418
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
5163
5419
  import { join as join6 } from "path";
5164
5420
  import { homedir as homedir5 } from "os";
5165
5421
  var AUGMENTED_DIR2 = join6(homedir5(), ".augmented");
5166
5422
  var CONFIG_PATH = join6(AUGMENTED_DIR2, "config.json");
5167
5423
  function ensureAugmentedDir() {
5168
- if (!existsSync5(AUGMENTED_DIR2)) {
5424
+ if (!existsSync6(AUGMENTED_DIR2)) {
5169
5425
  mkdirSync5(AUGMENTED_DIR2, { recursive: true });
5170
5426
  }
5171
5427
  }
@@ -5179,7 +5435,7 @@ function loadFromShellProfile(force = false) {
5179
5435
  const candidates = shell.includes("zsh") ? [join6(home, ".zshrc"), join6(home, ".zprofile")] : shell.includes("fish") ? [join6(home, ".config", "fish", "config.fish")] : [join6(home, ".bashrc"), join6(home, ".bash_profile")];
5180
5436
  for (const profile of candidates) {
5181
5437
  try {
5182
- const content = readFileSync5(profile, "utf-8");
5438
+ const content = readFileSync6(profile, "utf-8");
5183
5439
  for (const key of ["AGT_HOST", "AGT_API_KEY", "AGT_TEAM"]) {
5184
5440
  if (!force && process.env[key]) continue;
5185
5441
  const match = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map(
@@ -5204,7 +5460,7 @@ function getApiKey() {
5204
5460
  }
5205
5461
  function getConfig() {
5206
5462
  try {
5207
- const raw = readFileSync5(CONFIG_PATH, "utf-8");
5463
+ const raw = readFileSync6(CONFIG_PATH, "utf-8");
5208
5464
  return JSON.parse(raw);
5209
5465
  } catch {
5210
5466
  return {};
@@ -5212,7 +5468,7 @@ function getConfig() {
5212
5468
  }
5213
5469
  function saveConfig(config) {
5214
5470
  ensureAugmentedDir();
5215
- writeFileSync5(CONFIG_PATH, JSON.stringify(config, null, 2));
5471
+ writeFileSync6(CONFIG_PATH, JSON.stringify(config, null, 2));
5216
5472
  }
5217
5473
  function getActiveTeam() {
5218
5474
  const envTeam = process.env["AGT_TEAM"];
@@ -5559,7 +5815,12 @@ var SLACK_SCOPE_REGISTRY = [
5559
5815
  name: "Write Bot Profile",
5560
5816
  description: "Update the bot's own profile (status emoji + status text). Used to surface live/offline state to operators without polling.",
5561
5817
  category: "users",
5562
- risk: "low"
5818
+ risk: "low",
5819
+ // ENG-4812: Slack rejects this scope under oauth_config.scopes.bot
5820
+ // with `illegal_bot_scopes`. It must be granted via a user token —
5821
+ // which is what `setBotStatus()` (calling users.profile.set in
5822
+ // packages/mcp/src/slack-channel.ts) actually requires anyway.
5823
+ token_type: "user"
5563
5824
  },
5564
5825
  // ── Channel Management ───────────────────────────────────────────────────
5565
5826
  {
@@ -5701,6 +5962,9 @@ function getScopesByCategory() {
5701
5962
  }
5702
5963
  return map;
5703
5964
  }
5965
+ function getSlackScopeDefinition(scope) {
5966
+ return SLACK_SCOPE_REGISTRY.find((s) => s.scope === scope);
5967
+ }
5704
5968
  var SLACK_SCOPE_PRESETS = {
5705
5969
  minimal: [
5706
5970
  "app_mentions:read",
@@ -5786,9 +6050,23 @@ function generateSlackAppManifest(input) {
5786
6050
  },
5787
6051
  oauth_config: {
5788
6052
  ...redirect_urls && redirect_urls.length > 0 ? { redirect_urls } : {},
5789
- scopes: {
5790
- bot: [...scopes]
5791
- }
6053
+ // ENG-4812: partition by token_type so user-only scopes
6054
+ // (e.g. users.profile:write) don't end up under `bot` and
6055
+ // trigger Slack's `illegal_bot_scopes` rejection. Scopes
6056
+ // without an explicit token_type default to 'bot' — matches
6057
+ // pre-fix behaviour for the registry's standard-token-set.
6058
+ scopes: (() => {
6059
+ const botScopes = [];
6060
+ const userScopes = [];
6061
+ for (const scope of scopes) {
6062
+ const def = getSlackScopeDefinition(scope);
6063
+ if (def?.token_type === "user")
6064
+ userScopes.push(scope);
6065
+ else
6066
+ botScopes.push(scope);
6067
+ }
6068
+ return userScopes.length > 0 ? { bot: botScopes, user: userScopes } : { bot: botScopes };
6069
+ })()
5792
6070
  },
5793
6071
  settings: {
5794
6072
  ...botEvents.size > 0 ? { event_subscriptions: { bot_events: [...botEvents].sort() } } : {},
@@ -7559,13 +7837,13 @@ function provision(input, frameworkId = "openclaw") {
7559
7837
 
7560
7838
  // src/commands/manager.ts
7561
7839
  import chalk3 from "chalk";
7562
- import { existsSync as existsSync7, realpathSync } from "fs";
7840
+ import { existsSync as existsSync8, realpathSync } from "fs";
7563
7841
  import { join as join8 } from "path";
7564
7842
  import { homedir as homedir6, userInfo } from "os";
7565
7843
  import { spawn as spawn3 } from "child_process";
7566
7844
 
7567
7845
  // src/lib/watchdog.ts
7568
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3, existsSync as existsSync6, mkdirSync as mkdirSync6, openSync, closeSync, chmodSync as chmodSync5 } from "fs";
7846
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, unlinkSync as unlinkSync4, existsSync as existsSync7, mkdirSync as mkdirSync6, openSync, closeSync, chmodSync as chmodSync5 } from "fs";
7569
7847
  import { join as join7 } from "path";
7570
7848
  import { spawn as spawn2, execFileSync } from "child_process";
7571
7849
  var DEFAULT_CONFIG_DIR = join7(process.env["HOME"] ?? "/tmp", ".augmented");
@@ -7577,17 +7855,17 @@ function getManagerPaths(configDir) {
7577
7855
  };
7578
7856
  }
7579
7857
  function ensureDir2(configDir) {
7580
- if (!existsSync6(configDir)) {
7858
+ if (!existsSync7(configDir)) {
7581
7859
  mkdirSync6(configDir, { recursive: true });
7582
7860
  }
7583
7861
  }
7584
7862
  function writePidFile(configDir, pid) {
7585
7863
  ensureDir2(configDir);
7586
- writeFileSync6(getManagerPaths(configDir).pidFile, String(pid), { mode: 384 });
7864
+ writeFileSync7(getManagerPaths(configDir).pidFile, String(pid), { mode: 384 });
7587
7865
  }
7588
7866
  function readPidFile(configDir) {
7589
7867
  try {
7590
- const raw = readFileSync6(getManagerPaths(configDir).pidFile, "utf-8").trim();
7868
+ const raw = readFileSync7(getManagerPaths(configDir).pidFile, "utf-8").trim();
7591
7869
  const pid = parseInt(raw, 10);
7592
7870
  return isNaN(pid) ? null : pid;
7593
7871
  } catch {
@@ -7596,7 +7874,7 @@ function readPidFile(configDir) {
7596
7874
  }
7597
7875
  function removePidFile(configDir) {
7598
7876
  try {
7599
- unlinkSync3(getManagerPaths(configDir).pidFile);
7877
+ unlinkSync4(getManagerPaths(configDir).pidFile);
7600
7878
  } catch {
7601
7879
  }
7602
7880
  }
@@ -7625,7 +7903,7 @@ function defaultPgrep() {
7625
7903
  }
7626
7904
  function readStateFile(configDir) {
7627
7905
  try {
7628
- const raw = readFileSync6(getManagerPaths(configDir).stateFile, "utf-8");
7906
+ const raw = readFileSync7(getManagerPaths(configDir).stateFile, "utf-8");
7629
7907
  return JSON.parse(raw);
7630
7908
  } catch {
7631
7909
  return null;
@@ -7633,7 +7911,7 @@ function readStateFile(configDir) {
7633
7911
  }
7634
7912
  function removeStateFile(configDir) {
7635
7913
  try {
7636
- unlinkSync3(getManagerPaths(configDir).stateFile);
7914
+ unlinkSync4(getManagerPaths(configDir).stateFile);
7637
7915
  } catch {
7638
7916
  }
7639
7917
  }
@@ -7681,7 +7959,7 @@ function startWatchdog(opts) {
7681
7959
  const deadline = Date.now() + 5e3;
7682
7960
  const sleepBuf = new Int32Array(new SharedArrayBuffer(4));
7683
7961
  while (Date.now() < deadline) {
7684
- if (existsSync6(pidFile)) {
7962
+ if (existsSync7(pidFile)) {
7685
7963
  return { pid: child.pid };
7686
7964
  }
7687
7965
  if (child.exitCode !== null) {
@@ -8005,7 +8283,7 @@ function resolveStableAgtBin(rawPath) {
8005
8283
  if (!prefix || !formula) return rawPath;
8006
8284
  const candidates = [`${prefix}/bin/${formula}`, `${prefix}/bin/agt`];
8007
8285
  for (const candidate of candidates) {
8008
- if (!existsSync7(candidate)) continue;
8286
+ if (!existsSync8(candidate)) continue;
8009
8287
  try {
8010
8288
  realpathSync(candidate);
8011
8289
  return candidate;
@@ -8236,4 +8514,4 @@ export {
8236
8514
  managerInstallSystemUnitCommand,
8237
8515
  managerUninstallSystemUnitCommand
8238
8516
  };
8239
- //# sourceMappingURL=chunk-SUUTWC6M.js.map
8517
+ //# sourceMappingURL=chunk-DVWBVANP.js.map