@integrity-labs/agt-cli 0.19.14 → 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,38 +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);
3368
3503
  } catch {
3369
3504
  }
3370
3505
  renderChannelMessageHandlerForAgent(codeName);
3371
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
+ }
3372
3528
  function renderChannelMessageHandlerForAgent(codeName) {
3373
3529
  const agentDir = getAgentDir(codeName);
3374
3530
  const projectDir = getProjectDir(codeName);
3375
3531
  const provisionMcpPath = join4(agentDir, "provision", ".mcp.json");
3376
3532
  let mcpServerKeys;
3377
3533
  try {
3378
- const config = JSON.parse(readFileSync4(provisionMcpPath, "utf-8"));
3534
+ const config = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
3379
3535
  mcpServerKeys = Object.keys(config.mcpServers ?? {});
3380
3536
  } catch {
3381
3537
  return;
3382
3538
  }
3383
- const content = buildChannelMessageHandlerAgent({ mcpServerKeys });
3539
+ const integrations = readIntegrationsSummaryForAgent(codeName);
3540
+ const content = buildChannelMessageHandlerAgent({ mcpServerKeys, integrations });
3384
3541
  for (const baseDir of [agentDir, projectDir]) {
3385
3542
  const target = join4(baseDir, ".claude", "agents", "channel-message-handler.md");
3386
3543
  try {
3387
3544
  mkdirSync4(dirname4(target), { recursive: true });
3388
- writeFileSync4(target, content);
3545
+ writeFileSync5(target, content);
3389
3546
  } catch {
3390
3547
  }
3391
3548
  }
3392
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;
3556
+ }
3557
+ return true;
3558
+ }
3393
3559
  function readExistingMcpEnvVar(codeName, serverId, envKey) {
3394
3560
  const mcpJsonPath = join4(getAgentDir(codeName), "provision", ".mcp.json");
3395
3561
  try {
3396
- const raw = readFileSync4(mcpJsonPath, "utf-8");
3562
+ const raw = readFileSync5(mcpJsonPath, "utf-8");
3397
3563
  const config = JSON.parse(raw);
3398
3564
  const server = config.mcpServers?.[serverId];
3399
3565
  if (!server || typeof server !== "object")
@@ -3408,6 +3574,18 @@ function readExistingMcpEnvVar(codeName, serverId, envKey) {
3408
3574
  return void 0;
3409
3575
  }
3410
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
+ }
3411
3589
  function deployArtifactsToProject(codeName, provisionDir) {
3412
3590
  const projectDir = getProjectDir(codeName);
3413
3591
  mkdirSync4(projectDir, { recursive: true });
@@ -3418,27 +3596,27 @@ function deployArtifactsToProject(codeName, provisionDir) {
3418
3596
  const src = join4(provisionDir, file);
3419
3597
  const dest = join4(projectDir, file);
3420
3598
  try {
3421
- const srcContent = readFileSync4(src, "utf-8");
3422
- if (file === "CLAUDE.md" && existsSync4(dest)) {
3423
- 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");
3424
3602
  const stripIndex = (s) => s.replace(new RegExp(`${SKILLS_START}[\\s\\S]*?${SKILLS_END}`), "").trimEnd();
3425
3603
  if (stripIndex(srcContent) === stripIndex(destContent))
3426
3604
  continue;
3427
3605
  const indexMatch = destContent.match(new RegExp(`${SKILLS_START}[\\s\\S]*?${SKILLS_END}`));
3428
3606
  if (indexMatch) {
3429
- writeFileSync4(dest, srcContent.trimEnd() + "\n\n" + indexMatch[0] + "\n");
3607
+ writeFileSync5(dest, srcContent.trimEnd() + "\n\n" + indexMatch[0] + "\n");
3430
3608
  continue;
3431
3609
  }
3432
3610
  }
3433
- writeFileSync4(dest, srcContent);
3611
+ writeFileSync5(dest, srcContent);
3434
3612
  } catch {
3435
3613
  }
3436
3614
  }
3437
3615
  const skillsDir = join4(provisionDir, ".claude", "skills");
3438
3616
  const destSkillsDir = join4(projectDir, ".claude", "skills");
3439
3617
  try {
3440
- if (existsSync4(destSkillsDir)) {
3441
- 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();
3442
3620
  for (const folder of readdirSync(destSkillsDir)) {
3443
3621
  if (folder.startsWith("knowledge-") || folder === "core-knowledge" && !srcFolders.has(folder)) {
3444
3622
  try {
@@ -3448,21 +3626,21 @@ function deployArtifactsToProject(codeName, provisionDir) {
3448
3626
  }
3449
3627
  }
3450
3628
  }
3451
- if (existsSync4(skillsDir)) {
3629
+ if (existsSync5(skillsDir)) {
3452
3630
  for (const skillFolder of readdirSync(skillsDir)) {
3453
3631
  const srcSkillFile = join4(skillsDir, skillFolder, "SKILL.md");
3454
- if (!existsSync4(srcSkillFile))
3632
+ if (!existsSync5(srcSkillFile))
3455
3633
  continue;
3456
3634
  const destFolder = join4(destSkillsDir, skillFolder);
3457
3635
  const destFile = join4(destFolder, "SKILL.md");
3458
- const srcContent = readFileSync4(srcSkillFile, "utf-8");
3636
+ const srcContent = readFileSync5(srcSkillFile, "utf-8");
3459
3637
  try {
3460
- if (existsSync4(destFile) && readFileSync4(destFile, "utf-8") === srcContent)
3638
+ if (existsSync5(destFile) && readFileSync5(destFile, "utf-8") === srcContent)
3461
3639
  continue;
3462
3640
  } catch {
3463
3641
  }
3464
3642
  mkdirSync4(destFolder, { recursive: true });
3465
- writeFileSync4(destFile, srcContent);
3643
+ writeFileSync5(destFile, srcContent);
3466
3644
  }
3467
3645
  }
3468
3646
  } catch {
@@ -3470,9 +3648,9 @@ function deployArtifactsToProject(codeName, provisionDir) {
3470
3648
  const agentsDir = join4(provisionDir, ".claude", "agents");
3471
3649
  const destAgentsDir = join4(projectDir, ".claude", "agents");
3472
3650
  try {
3473
- if (existsSync4(agentsDir)) {
3651
+ if (existsSync5(agentsDir)) {
3474
3652
  const sourceAgentFiles = new Set(readdirSync(agentsDir).filter((f) => f.endsWith(".md")));
3475
- if (existsSync4(destAgentsDir)) {
3653
+ if (existsSync5(destAgentsDir)) {
3476
3654
  for (const destFile of readdirSync(destAgentsDir)) {
3477
3655
  if (!destFile.endsWith(".md"))
3478
3656
  continue;
@@ -3487,14 +3665,14 @@ function deployArtifactsToProject(codeName, provisionDir) {
3487
3665
  for (const agentFile of sourceAgentFiles) {
3488
3666
  const srcPath = join4(agentsDir, agentFile);
3489
3667
  const destPath = join4(destAgentsDir, agentFile);
3490
- const srcContent = readFileSync4(srcPath, "utf-8");
3668
+ const srcContent = readFileSync5(srcPath, "utf-8");
3491
3669
  try {
3492
- if (existsSync4(destPath) && readFileSync4(destPath, "utf-8") === srcContent)
3670
+ if (existsSync5(destPath) && readFileSync5(destPath, "utf-8") === srcContent)
3493
3671
  continue;
3494
3672
  } catch {
3495
3673
  }
3496
3674
  mkdirSync4(destAgentsDir, { recursive: true });
3497
- writeFileSync4(destPath, srcContent);
3675
+ writeFileSync5(destPath, srcContent);
3498
3676
  }
3499
3677
  }
3500
3678
  } catch {
@@ -3502,10 +3680,10 @@ function deployArtifactsToProject(codeName, provisionDir) {
3502
3680
  const agentMcpPath = join4(getAgentDir(codeName), "provision", ".mcp.json");
3503
3681
  const projectMcpPath = join4(projectDir, ".mcp.json");
3504
3682
  try {
3505
- const agentMcp = JSON.parse(readFileSync4(agentMcpPath, "utf-8"));
3683
+ const agentMcp = JSON.parse(readFileSync5(agentMcpPath, "utf-8"));
3506
3684
  let projectMcp;
3507
3685
  try {
3508
- projectMcp = JSON.parse(readFileSync4(projectMcpPath, "utf-8"));
3686
+ projectMcp = JSON.parse(readFileSync5(projectMcpPath, "utf-8"));
3509
3687
  } catch {
3510
3688
  projectMcp = { mcpServers: {} };
3511
3689
  }
@@ -3516,28 +3694,28 @@ function deployArtifactsToProject(codeName, provisionDir) {
3516
3694
  return !(entry && typeof entry["url"] === "string" && entry["url"].startsWith("/"));
3517
3695
  }));
3518
3696
  projectMcp["mcpServers"] = { ...stripRelativeUrls(projectServers), ...stripRelativeUrls(agentServers) };
3519
- writeFileSync4(projectMcpPath, JSON.stringify(projectMcp, null, 2));
3697
+ writeFileSync5(projectMcpPath, JSON.stringify(projectMcp, null, 2));
3520
3698
  } catch {
3521
3699
  }
3522
3700
  const agentDir = getAgentDir(codeName);
3523
3701
  for (const envFile of [".env", ".env.integrations"]) {
3524
3702
  try {
3525
- const content = readFileSync4(join4(agentDir, envFile), "utf-8");
3526
- writeFileSync4(join4(projectDir, envFile), content);
3703
+ const content = readFileSync5(join4(agentDir, envFile), "utf-8");
3704
+ writeFileSync5(join4(projectDir, envFile), content);
3527
3705
  } catch {
3528
3706
  }
3529
3707
  }
3530
3708
  try {
3531
3709
  const gitDir = join4(projectDir, ".git");
3532
3710
  const hookSrc = join4(provisionDir, ".git-hooks", "pre-commit");
3533
- if (existsSync4(gitDir) && existsSync4(hookSrc)) {
3711
+ if (existsSync5(gitDir) && existsSync5(hookSrc)) {
3534
3712
  const hooksDir = join4(gitDir, "hooks");
3535
3713
  mkdirSync4(hooksDir, { recursive: true });
3536
3714
  const hookDest = join4(hooksDir, "pre-commit");
3537
- const srcContent = readFileSync4(hookSrc, "utf-8");
3538
- 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;
3539
3717
  if (!upToDate)
3540
- writeFileSync4(hookDest, srcContent);
3718
+ writeFileSync5(hookDest, srcContent);
3541
3719
  chmodSync4(hookDest, 493);
3542
3720
  }
3543
3721
  } catch {
@@ -3578,7 +3756,7 @@ function provisionStopHook(codeName) {
3578
3756
  "esac",
3579
3757
  "exit 0"
3580
3758
  ].join("\n") + "\n";
3581
- writeFileSync4(hookScriptPath, hookScript, { mode: 493 });
3759
+ writeFileSync5(hookScriptPath, hookScript, { mode: 493 });
3582
3760
  const ghostHookPath = join4(claudeDir, "agt-ghost-reply-hook.sh");
3583
3761
  const ghostHookScript = [
3584
3762
  "#!/bin/bash",
@@ -3698,11 +3876,11 @@ function provisionStopHook(codeName) {
3698
3876
  "fi",
3699
3877
  "exit 0"
3700
3878
  ].join("\n") + "\n";
3701
- writeFileSync4(ghostHookPath, ghostHookScript, { mode: 493 });
3879
+ writeFileSync5(ghostHookPath, ghostHookScript, { mode: 493 });
3702
3880
  const settingsPath = join4(claudeDir, "settings.local.json");
3703
3881
  let settings = {};
3704
3882
  try {
3705
- settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
3883
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
3706
3884
  } catch {
3707
3885
  }
3708
3886
  const hooks = settings["hooks"] ?? {};
@@ -3715,7 +3893,7 @@ function provisionStopHook(codeName) {
3715
3893
  }
3716
3894
  ];
3717
3895
  settings["hooks"] = hooks;
3718
- writeFileSync4(settingsPath, JSON.stringify(settings, null, 2));
3896
+ writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
3719
3897
  }
3720
3898
  function provisionIsolationHook(codeName) {
3721
3899
  const projectDir = getProjectDir(codeName);
@@ -3778,11 +3956,11 @@ function provisionIsolationHook(codeName) {
3778
3956
  "",
3779
3957
  "exit 0"
3780
3958
  ].join("\n") + "\n";
3781
- writeFileSync4(hookScriptPath, hookScript, { mode: 493 });
3959
+ writeFileSync5(hookScriptPath, hookScript, { mode: 493 });
3782
3960
  const settingsPath = join4(claudeDir, "settings.local.json");
3783
3961
  let settings = {};
3784
3962
  try {
3785
- settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
3963
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
3786
3964
  } catch {
3787
3965
  }
3788
3966
  const hooks = settings["hooks"] ?? {};
@@ -3797,13 +3975,13 @@ function provisionIsolationHook(codeName) {
3797
3975
  }
3798
3976
  ];
3799
3977
  settings["hooks"] = hooks;
3800
- writeFileSync4(settingsPath, JSON.stringify(settings, null, 2));
3978
+ writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
3801
3979
  }
3802
3980
  function modifyJsonConfig(filePath, fn) {
3803
3981
  let originalContent;
3804
3982
  let config;
3805
3983
  try {
3806
- originalContent = readFileSync4(filePath, "utf-8");
3984
+ originalContent = readFileSync5(filePath, "utf-8");
3807
3985
  config = JSON.parse(originalContent);
3808
3986
  } catch {
3809
3987
  return;
@@ -3814,7 +3992,7 @@ function modifyJsonConfig(filePath, fn) {
3814
3992
  const newContent = JSON.stringify(config, null, 2);
3815
3993
  if (newContent === originalContent)
3816
3994
  return;
3817
- writeFileSync4(filePath, newContent);
3995
+ writeFileSync5(filePath, newContent);
3818
3996
  }
3819
3997
  var SECRETS_DENY_PERMISSIONS = [
3820
3998
  // Read blocks
@@ -3925,6 +4103,7 @@ function sanitizeMcpName(name) {
3925
4103
  }
3926
4104
  function buildChannelMessageHandlerAgent(args) {
3927
4105
  const mcpServerKeys = args?.mcpServerKeys ?? [];
4106
+ const integrations = args?.integrations ?? [];
3928
4107
  const mcpWildcards = Array.from(new Set(mcpServerKeys.map((k) => `mcp__${sanitizeMcpName(k)}__*`)));
3929
4108
  const tools = [
3930
4109
  "Bash",
@@ -3937,6 +4116,19 @@ function buildChannelMessageHandlerAgent(args) {
3937
4116
  "Agent",
3938
4117
  ...mcpWildcards
3939
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
+ `;
3940
4132
  return `---
3941
4133
  name: channel-message-handler
3942
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.
@@ -3958,7 +4150,7 @@ Your job:
3958
4150
  3. End. Do not do additional follow-up work; if more is needed, the user will send another message.
3959
4151
 
3960
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.
3961
- `;
4153
+ ${integrationsBlock}`;
3962
4154
  }
3963
4155
  function buildPostizMcpEntry(integration) {
3964
4156
  const rawBaseUrl = integration.config["base_url"];
@@ -4148,9 +4340,19 @@ var claudeCodeAdapter = {
4148
4340
  // turn return immediately on dispatch, so new inbound messages get a
4149
4341
  // fresh turn while the subagent does the work in parallel. Triggered
4150
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.
4151
4346
  {
4152
4347
  relativePath: ".claude/agents/channel-message-handler.md",
4153
- content: buildChannelMessageHandlerAgent({ mcpServerKeys: initialMcpServerKeys })
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)
4154
4356
  }
4155
4357
  ];
4156
4358
  const knowledgeEntries = input.knowledge ?? [];
@@ -4205,7 +4407,7 @@ ${sections}`
4205
4407
  } catch {
4206
4408
  continue;
4207
4409
  }
4208
- if (existsSync4(join4(agentRoot, "registration.json"))) {
4410
+ if (existsSync5(join4(agentRoot, "registration.json"))) {
4209
4411
  agents.add(entry);
4210
4412
  }
4211
4413
  }
@@ -4219,14 +4421,14 @@ ${sections}`
4219
4421
  const projectDir = getProjectDir(codeName);
4220
4422
  mkdirSync4(agentDir, { recursive: true });
4221
4423
  mkdirSync4(projectDir, { recursive: true });
4222
- writeFileSync4(join4(agentDir, "registration.json"), JSON.stringify({
4424
+ writeFileSync5(join4(agentDir, "registration.json"), JSON.stringify({
4223
4425
  code_name: codeName,
4224
4426
  team_dir: teamDir,
4225
4427
  project_dir: projectDir,
4226
4428
  framework: "claude-code",
4227
4429
  registered_at: (/* @__PURE__ */ new Date()).toISOString()
4228
4430
  }, null, 2));
4229
- if (existsSync4(teamDir)) {
4431
+ if (existsSync5(teamDir)) {
4230
4432
  deployArtifactsToProject(codeName, teamDir);
4231
4433
  }
4232
4434
  return true;
@@ -4238,9 +4440,9 @@ ${sections}`
4238
4440
  try {
4239
4441
  const agentDir = getAgentDir(codeName);
4240
4442
  const regFile = join4(agentDir, "registration.json");
4241
- if (existsSync4(regFile)) {
4242
- const { unlinkSync: unlinkSync4 } = await import("fs");
4243
- unlinkSync4(regFile);
4443
+ if (existsSync5(regFile)) {
4444
+ const { unlinkSync: unlinkSync5 } = await import("fs");
4445
+ unlinkSync5(regFile);
4244
4446
  }
4245
4447
  return true;
4246
4448
  } catch {
@@ -4264,7 +4466,7 @@ ${sections}`
4264
4466
  }
4265
4467
  if (envLines.length > 1) {
4266
4468
  const envPath = join4(agentDir, ".env");
4267
- writeFileSync4(envPath, envLines.join("\n") + "\n");
4469
+ writeFileSync5(envPath, envLines.join("\n") + "\n");
4268
4470
  chmodSync4(envPath, SECRET_FILE_MODE);
4269
4471
  }
4270
4472
  },
@@ -4313,13 +4515,15 @@ ${sections}`
4313
4515
  mkdirSync4(dirname4(provisionMcpPath), { recursive: true });
4314
4516
  let mcpConfig2 = { mcpServers: {} };
4315
4517
  try {
4316
- mcpConfig2 = JSON.parse(readFileSync4(provisionMcpPath, "utf-8"));
4518
+ mcpConfig2 = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
4317
4519
  if (!mcpConfig2.mcpServers)
4318
4520
  mcpConfig2.mcpServers = {};
4319
4521
  } catch {
4320
4522
  }
4321
4523
  mcpConfig2.mcpServers["telegram"] = telegramEntry;
4322
- writeFileSync4(provisionMcpPath, JSON.stringify(mcpConfig2, null, 2));
4524
+ if (!writeMcpJsonGuarded(codeName, provisionMcpPath, mcpConfig2)) {
4525
+ return;
4526
+ }
4323
4527
  syncMcpToProject(codeName);
4324
4528
  return;
4325
4529
  }
@@ -4330,7 +4534,7 @@ ${sections}`
4330
4534
  if (channelId === "discord") {
4331
4535
  const botToken = config["bot_token"];
4332
4536
  if (botToken) {
4333
- writeFileSync4(join4(channelDir, ".env"), `DISCORD_BOT_TOKEN=${botToken}
4537
+ writeFileSync5(join4(channelDir, ".env"), `DISCORD_BOT_TOKEN=${botToken}
4334
4538
  `);
4335
4539
  }
4336
4540
  } else if (channelId === "slack") {
@@ -4353,8 +4557,8 @@ ${sections}`
4353
4557
  if (botToken) {
4354
4558
  const localSlackChannel = join4(getHomeDir3(), ".augmented", "_mcp", "slack-channel.js");
4355
4559
  const slackEntry = {
4356
- command: existsSync4(localSlackChannel) ? "node" : "npx",
4357
- 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"],
4358
4562
  env: {
4359
4563
  SLACK_BOT_TOKEN: botToken,
4360
4564
  ...appToken ? { SLACK_APP_TOKEN: appToken } : {},
@@ -4372,16 +4576,18 @@ ${sections}`
4372
4576
  mkdirSync4(dirname4(provisionMcpPath), { recursive: true });
4373
4577
  let mcpConfig2 = { mcpServers: {} };
4374
4578
  try {
4375
- mcpConfig2 = JSON.parse(readFileSync4(provisionMcpPath, "utf-8"));
4579
+ mcpConfig2 = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
4376
4580
  if (!mcpConfig2.mcpServers)
4377
4581
  mcpConfig2.mcpServers = {};
4378
4582
  } catch {
4379
4583
  }
4380
4584
  mcpConfig2.mcpServers["slack"] = slackEntry;
4381
- writeFileSync4(provisionMcpPath, JSON.stringify(mcpConfig2, null, 2));
4585
+ if (!writeMcpJsonGuarded(codeName, provisionMcpPath, mcpConfig2)) {
4586
+ return;
4587
+ }
4382
4588
  syncMcpToProject(codeName);
4383
4589
  const staleChannelsPath = join4(getProjectDir(codeName), ".mcp-channels.json");
4384
- if (existsSync4(staleChannelsPath)) {
4590
+ if (existsSync5(staleChannelsPath)) {
4385
4591
  try {
4386
4592
  rmSync(staleChannelsPath, { force: true });
4387
4593
  } catch {
@@ -4394,7 +4600,7 @@ ${sections}`
4394
4600
  const mcpJsonPath = join4(agentDir, "provision", ".mcp.json");
4395
4601
  let mcpConfig;
4396
4602
  try {
4397
- mcpConfig = JSON.parse(readFileSync4(mcpJsonPath, "utf-8"));
4603
+ mcpConfig = JSON.parse(readFileSync5(mcpJsonPath, "utf-8"));
4398
4604
  } catch {
4399
4605
  mcpConfig = { mcpServers: {} };
4400
4606
  }
@@ -4430,7 +4636,7 @@ ${sections}`
4430
4636
  ...oneshotBlockKitAskUserEnabled && process.env["AGT_API_KEY"] ? { AGT_API_KEY: process.env["AGT_API_KEY"] } : {},
4431
4637
  ...oneshotBlockKitAskUserEnabled && options?.agentId ? { AGT_AGENT_ID: options.agentId } : {}
4432
4638
  } : {};
4433
- if (isPersistent && existsSync4(localSlackChannel)) {
4639
+ if (isPersistent && existsSync5(localSlackChannel)) {
4434
4640
  mcpServers["slack"] = {
4435
4641
  command: "node",
4436
4642
  args: [localSlackChannel],
@@ -4456,15 +4662,16 @@ ${sections}`
4456
4662
  };
4457
4663
  }
4458
4664
  }
4459
- writeFileSync4(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
4460
- syncMcpToProject(codeName);
4665
+ if (writeMcpJsonGuarded(codeName, mcpJsonPath, mcpConfig)) {
4666
+ syncMcpToProject(codeName);
4667
+ }
4461
4668
  },
4462
4669
  hasChannelCredentials(codeName, channelId) {
4463
4670
  const provisionMcpPath = join4(getAgentDir(codeName), "provision", ".mcp.json");
4464
- if (!existsSync4(provisionMcpPath))
4671
+ if (!existsSync5(provisionMcpPath))
4465
4672
  return false;
4466
4673
  try {
4467
- const parsed = JSON.parse(readFileSync4(provisionMcpPath, "utf-8"));
4674
+ const parsed = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
4468
4675
  return Boolean(parsed.mcpServers?.[channelId]);
4469
4676
  } catch {
4470
4677
  return false;
@@ -4504,12 +4711,22 @@ ${sections}`
4504
4711
  const schedulesPath = join4(agentDir, "schedules.json");
4505
4712
  const mapped = mapScheduledTasks(tasks);
4506
4713
  mkdirSync4(agentDir, { recursive: true });
4507
- writeFileSync4(schedulesPath, JSON.stringify({ schedules: mapped }, null, 2));
4714
+ writeFileSync5(schedulesPath, JSON.stringify({ schedules: mapped }, null, 2));
4508
4715
  return Promise.resolve();
4509
4716
  },
4510
4717
  writeIntegrations(codeName, integrations) {
4511
4718
  const agentDir = getAgentDir(codeName);
4512
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);
4513
4730
  const decryptedIntegrations = integrations.map((integration) => ({
4514
4731
  ...integration,
4515
4732
  credentials: decryptIntegrationCredentials(integration.credentials)
@@ -4542,7 +4759,7 @@ ${sections}`
4542
4759
  }
4543
4760
  if (envLines.length > 1) {
4544
4761
  const envPath = join4(agentDir, ".env.integrations");
4545
- writeFileSync4(envPath, envLines.join("\n") + "\n");
4762
+ writeFileSync5(envPath, envLines.join("\n") + "\n");
4546
4763
  chmodSync4(envPath, SECRET_FILE_MODE);
4547
4764
  }
4548
4765
  writeXurlStoreForIntegrations(decryptedIntegrations);
@@ -4574,53 +4791,50 @@ ${sections}`
4574
4791
  }
4575
4792
  const hasCloudBroker = integrations.some((i) => i.definition_id === "cloud-broker");
4576
4793
  if (hasCloudBroker) {
4577
- const existingAgentId = readExistingMcpEnvVar(codeName, "cloud-broker", "AGT_AGENT_ID");
4578
- this.writeMcpServer(codeName, "cloud-broker", {
4579
- command: "npx",
4580
- args: ["-y", "@integrity-labs/cloud-broker@latest"],
4581
- env: {
4582
- AGT_HOST: "${AGT_HOST}",
4583
- AGT_API_KEY: "${AGT_API_KEY}",
4584
- AGT_AGENT_ID: existingAgentId ?? "${AGT_AGENT_ID}",
4585
- // ENG-4788: cloud-broker@0.6+ exits at startup if AGT_RUN_ID
4586
- // is missing. Mirror buildMcpJson's entry on incremental sync
4587
- // so an agent that connects AWS post-provision boots cleanly.
4588
- AGT_RUN_ID: "${AGT_RUN_ID}",
4589
- PATH: process.env["PATH"] ?? "",
4590
- HOME: process.env["HOME"] ?? ""
4591
- }
4592
- });
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
+ }
4593
4815
  }
4594
4816
  const projectDir = getProjectDir(codeName);
4595
4817
  const claudeMdPath = join4(projectDir, "CLAUDE.md");
4596
4818
  try {
4597
- const existing = readFileSync4(claudeMdPath, "utf-8");
4598
- const summaries = integrations.map((i) => {
4599
- const def = INTEGRATION_REGISTRY.find((d) => d.id === i.definition_id);
4600
- return {
4601
- id: i.definition_id,
4602
- name: def?.name || i.display_name || i.definition_id,
4603
- cliBinary: def?.cli_tool?.binary,
4604
- description: def?.description || (i.auth_type === "managed" ? "Managed integration via Composio" : void 0)
4605
- };
4606
- });
4607
- const newSection = buildIntegrationsSection(summaries);
4819
+ const existing = readFileSync5(claudeMdPath, "utf-8");
4820
+ const newSection = buildIntegrationsSection(summariesForSidecar);
4608
4821
  let updated;
4609
4822
  if (existing.includes("## Integrations")) {
4610
4823
  updated = existing.replace(/## Integrations[\s\S]*?(?=## Rules)/, newSection);
4611
4824
  } else {
4612
4825
  updated = existing.replace("## Rules", `${newSection}## Rules`);
4613
4826
  }
4614
- writeFileSync4(claudeMdPath, updated);
4827
+ writeFileSync5(claudeMdPath, updated);
4615
4828
  const agentDir2 = getAgentDir(codeName);
4616
4829
  const envSrc = join4(agentDir2, ".env.integrations");
4617
4830
  try {
4618
- const envContent = readFileSync4(envSrc, "utf-8");
4619
- writeFileSync4(join4(projectDir, ".env.integrations"), envContent);
4831
+ const envContent = readFileSync5(envSrc, "utf-8");
4832
+ writeFileSync5(join4(projectDir, ".env.integrations"), envContent);
4620
4833
  } catch {
4621
4834
  }
4622
4835
  } catch {
4623
4836
  }
4837
+ renderChannelMessageHandlerForAgent(codeName);
4624
4838
  },
4625
4839
  writeMcpServer(codeName, serverId, config) {
4626
4840
  const agentDir = getAgentDir(codeName);
@@ -4628,7 +4842,7 @@ ${sections}`
4628
4842
  mkdirSync4(join4(agentDir, "provision"), { recursive: true });
4629
4843
  let mcpConfig;
4630
4844
  try {
4631
- mcpConfig = JSON.parse(readFileSync4(mcpJsonPath, "utf-8"));
4845
+ mcpConfig = JSON.parse(readFileSync5(mcpJsonPath, "utf-8"));
4632
4846
  } catch {
4633
4847
  mcpConfig = { mcpServers: {} };
4634
4848
  }
@@ -4680,8 +4894,9 @@ ${sections}`
4680
4894
  serverEntry["env"] = config.env;
4681
4895
  }
4682
4896
  mcpServers[serverId] = serverEntry;
4683
- writeFileSync4(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
4684
- syncMcpToProject(codeName);
4897
+ if (writeMcpJsonGuarded(codeName, mcpJsonPath, mcpConfig)) {
4898
+ syncMcpToProject(codeName);
4899
+ }
4685
4900
  },
4686
4901
  getMcpPath(codeName) {
4687
4902
  return join4(getAgentDir(codeName), "provision", ".mcp.json");
@@ -4691,7 +4906,7 @@ ${sections}`
4691
4906
  const mcpJsonPath = join4(agentDir, "provision", ".mcp.json");
4692
4907
  let mcpConfig;
4693
4908
  try {
4694
- mcpConfig = JSON.parse(readFileSync4(mcpJsonPath, "utf-8"));
4909
+ mcpConfig = JSON.parse(readFileSync5(mcpJsonPath, "utf-8"));
4695
4910
  } catch {
4696
4911
  return;
4697
4912
  }
@@ -4699,8 +4914,9 @@ ${sections}`
4699
4914
  if (!mcpServers || !(serverId in mcpServers))
4700
4915
  return;
4701
4916
  delete mcpServers[serverId];
4702
- writeFileSync4(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
4703
- syncMcpToProject(codeName);
4917
+ if (writeMcpJsonGuarded(codeName, mcpJsonPath, mcpConfig)) {
4918
+ syncMcpToProject(codeName);
4919
+ }
4704
4920
  },
4705
4921
  installSkillFiles(codeName, skillId, files) {
4706
4922
  assertValidCodeName(skillId);
@@ -4720,13 +4936,13 @@ ${sections}`
4720
4936
  throw new Error(`Path traversal detected: ${file.relativePath} resolves outside ${skillDir}`);
4721
4937
  }
4722
4938
  mkdirSync4(join4(filePath, ".."), { recursive: true });
4723
- if (isPluginManaged && existsSync4(filePath)) {
4939
+ if (isPluginManaged && existsSync5(filePath)) {
4724
4940
  try {
4725
4941
  chmodSync4(filePath, READ_WRITE_MODE);
4726
4942
  } catch {
4727
4943
  }
4728
4944
  }
4729
- writeFileSync4(filePath, file.content);
4945
+ writeFileSync5(filePath, file.content);
4730
4946
  if (isPluginManaged) {
4731
4947
  try {
4732
4948
  chmodSync4(filePath, READ_ONLY_MODE);
@@ -4742,7 +4958,7 @@ ${sections}`
4742
4958
  mkdirSync4(agentDir, { recursive: true });
4743
4959
  let pluginsConfig;
4744
4960
  try {
4745
- pluginsConfig = JSON.parse(readFileSync4(pluginsJsonPath, "utf-8"));
4961
+ pluginsConfig = JSON.parse(readFileSync5(pluginsJsonPath, "utf-8"));
4746
4962
  } catch {
4747
4963
  pluginsConfig = { plugins: {} };
4748
4964
  }
@@ -4755,7 +4971,7 @@ ${sections}`
4755
4971
  installed_at: (/* @__PURE__ */ new Date()).toISOString(),
4756
4972
  ...pluginConfig ? { config: pluginConfig } : {}
4757
4973
  };
4758
- writeFileSync4(pluginsJsonPath, JSON.stringify(pluginsConfig, null, 2));
4974
+ writeFileSync5(pluginsJsonPath, JSON.stringify(pluginsConfig, null, 2));
4759
4975
  },
4760
4976
  /**
4761
4977
  * Full plugin provisioning: install scripts, register hooks, apply permissions,
@@ -4790,7 +5006,7 @@ ${sections}`
4790
5006
  const settingsPath = join4(claudeDir, "settings.local.json");
4791
5007
  let settings = {};
4792
5008
  try {
4793
- settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
5009
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
4794
5010
  } catch {
4795
5011
  }
4796
5012
  const existingHooks = settings["hooks"] ?? {};
@@ -4824,13 +5040,13 @@ ${sections}`
4824
5040
  }
4825
5041
  }
4826
5042
  settings["hooks"] = existingHooks;
4827
- writeFileSync4(settingsPath, JSON.stringify(settings, null, 2));
5043
+ writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
4828
5044
  }
4829
5045
  if (plugin.allowed_tools.length > 0) {
4830
5046
  const settingsPath = join4(claudeDir, "settings.local.json");
4831
5047
  let settings = {};
4832
5048
  try {
4833
- settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
5049
+ settings = JSON.parse(readFileSync5(settingsPath, "utf-8"));
4834
5050
  } catch {
4835
5051
  }
4836
5052
  const existingPerms = settings["permissions"] ?? {};
@@ -4842,12 +5058,12 @@ ${sections}`
4842
5058
  }
4843
5059
  existingPerms["allow"] = allowList;
4844
5060
  settings["permissions"] = existingPerms;
4845
- writeFileSync4(settingsPath, JSON.stringify(settings, null, 2));
5061
+ writeFileSync5(settingsPath, JSON.stringify(settings, null, 2));
4846
5062
  }
4847
5063
  if (contextValues && Object.keys(contextValues).length > 0) {
4848
5064
  const configDir = join4(projectDir, `.${plugin.slug}`);
4849
5065
  mkdirSync4(configDir, { recursive: true });
4850
- writeFileSync4(join4(configDir, "config.json"), JSON.stringify(contextValues, null, 2));
5066
+ writeFileSync5(join4(configDir, "config.json"), JSON.stringify(contextValues, null, 2));
4851
5067
  }
4852
5068
  },
4853
5069
  executePluginHook(ctx) {
@@ -4909,7 +5125,7 @@ ${sections}`
4909
5125
  if (Object.keys(tokens).length === 0)
4910
5126
  return;
4911
5127
  const tokenPath = join4(agentDir, ".tokens.json");
4912
- writeFileSync4(tokenPath, JSON.stringify(tokens, null, 2));
5128
+ writeFileSync5(tokenPath, JSON.stringify(tokens, null, 2));
4913
5129
  chmodSync4(tokenPath, SECRET_FILE_MODE);
4914
5130
  }
4915
5131
  };
@@ -5199,13 +5415,13 @@ function jsonOutput(data) {
5199
5415
  }
5200
5416
 
5201
5417
  // src/lib/config.ts
5202
- 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";
5203
5419
  import { join as join6 } from "path";
5204
5420
  import { homedir as homedir5 } from "os";
5205
5421
  var AUGMENTED_DIR2 = join6(homedir5(), ".augmented");
5206
5422
  var CONFIG_PATH = join6(AUGMENTED_DIR2, "config.json");
5207
5423
  function ensureAugmentedDir() {
5208
- if (!existsSync5(AUGMENTED_DIR2)) {
5424
+ if (!existsSync6(AUGMENTED_DIR2)) {
5209
5425
  mkdirSync5(AUGMENTED_DIR2, { recursive: true });
5210
5426
  }
5211
5427
  }
@@ -5219,7 +5435,7 @@ function loadFromShellProfile(force = false) {
5219
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")];
5220
5436
  for (const profile of candidates) {
5221
5437
  try {
5222
- const content = readFileSync5(profile, "utf-8");
5438
+ const content = readFileSync6(profile, "utf-8");
5223
5439
  for (const key of ["AGT_HOST", "AGT_API_KEY", "AGT_TEAM"]) {
5224
5440
  if (!force && process.env[key]) continue;
5225
5441
  const match = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map(
@@ -5244,7 +5460,7 @@ function getApiKey() {
5244
5460
  }
5245
5461
  function getConfig() {
5246
5462
  try {
5247
- const raw = readFileSync5(CONFIG_PATH, "utf-8");
5463
+ const raw = readFileSync6(CONFIG_PATH, "utf-8");
5248
5464
  return JSON.parse(raw);
5249
5465
  } catch {
5250
5466
  return {};
@@ -5252,7 +5468,7 @@ function getConfig() {
5252
5468
  }
5253
5469
  function saveConfig(config) {
5254
5470
  ensureAugmentedDir();
5255
- writeFileSync5(CONFIG_PATH, JSON.stringify(config, null, 2));
5471
+ writeFileSync6(CONFIG_PATH, JSON.stringify(config, null, 2));
5256
5472
  }
5257
5473
  function getActiveTeam() {
5258
5474
  const envTeam = process.env["AGT_TEAM"];
@@ -5599,7 +5815,12 @@ var SLACK_SCOPE_REGISTRY = [
5599
5815
  name: "Write Bot Profile",
5600
5816
  description: "Update the bot's own profile (status emoji + status text). Used to surface live/offline state to operators without polling.",
5601
5817
  category: "users",
5602
- 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"
5603
5824
  },
5604
5825
  // ── Channel Management ───────────────────────────────────────────────────
5605
5826
  {
@@ -5741,6 +5962,9 @@ function getScopesByCategory() {
5741
5962
  }
5742
5963
  return map;
5743
5964
  }
5965
+ function getSlackScopeDefinition(scope) {
5966
+ return SLACK_SCOPE_REGISTRY.find((s) => s.scope === scope);
5967
+ }
5744
5968
  var SLACK_SCOPE_PRESETS = {
5745
5969
  minimal: [
5746
5970
  "app_mentions:read",
@@ -5826,9 +6050,23 @@ function generateSlackAppManifest(input) {
5826
6050
  },
5827
6051
  oauth_config: {
5828
6052
  ...redirect_urls && redirect_urls.length > 0 ? { redirect_urls } : {},
5829
- scopes: {
5830
- bot: [...scopes]
5831
- }
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
+ })()
5832
6070
  },
5833
6071
  settings: {
5834
6072
  ...botEvents.size > 0 ? { event_subscriptions: { bot_events: [...botEvents].sort() } } : {},
@@ -7599,13 +7837,13 @@ function provision(input, frameworkId = "openclaw") {
7599
7837
 
7600
7838
  // src/commands/manager.ts
7601
7839
  import chalk3 from "chalk";
7602
- import { existsSync as existsSync7, realpathSync } from "fs";
7840
+ import { existsSync as existsSync8, realpathSync } from "fs";
7603
7841
  import { join as join8 } from "path";
7604
7842
  import { homedir as homedir6, userInfo } from "os";
7605
7843
  import { spawn as spawn3 } from "child_process";
7606
7844
 
7607
7845
  // src/lib/watchdog.ts
7608
- 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";
7609
7847
  import { join as join7 } from "path";
7610
7848
  import { spawn as spawn2, execFileSync } from "child_process";
7611
7849
  var DEFAULT_CONFIG_DIR = join7(process.env["HOME"] ?? "/tmp", ".augmented");
@@ -7617,17 +7855,17 @@ function getManagerPaths(configDir) {
7617
7855
  };
7618
7856
  }
7619
7857
  function ensureDir2(configDir) {
7620
- if (!existsSync6(configDir)) {
7858
+ if (!existsSync7(configDir)) {
7621
7859
  mkdirSync6(configDir, { recursive: true });
7622
7860
  }
7623
7861
  }
7624
7862
  function writePidFile(configDir, pid) {
7625
7863
  ensureDir2(configDir);
7626
- writeFileSync6(getManagerPaths(configDir).pidFile, String(pid), { mode: 384 });
7864
+ writeFileSync7(getManagerPaths(configDir).pidFile, String(pid), { mode: 384 });
7627
7865
  }
7628
7866
  function readPidFile(configDir) {
7629
7867
  try {
7630
- const raw = readFileSync6(getManagerPaths(configDir).pidFile, "utf-8").trim();
7868
+ const raw = readFileSync7(getManagerPaths(configDir).pidFile, "utf-8").trim();
7631
7869
  const pid = parseInt(raw, 10);
7632
7870
  return isNaN(pid) ? null : pid;
7633
7871
  } catch {
@@ -7636,7 +7874,7 @@ function readPidFile(configDir) {
7636
7874
  }
7637
7875
  function removePidFile(configDir) {
7638
7876
  try {
7639
- unlinkSync3(getManagerPaths(configDir).pidFile);
7877
+ unlinkSync4(getManagerPaths(configDir).pidFile);
7640
7878
  } catch {
7641
7879
  }
7642
7880
  }
@@ -7665,7 +7903,7 @@ function defaultPgrep() {
7665
7903
  }
7666
7904
  function readStateFile(configDir) {
7667
7905
  try {
7668
- const raw = readFileSync6(getManagerPaths(configDir).stateFile, "utf-8");
7906
+ const raw = readFileSync7(getManagerPaths(configDir).stateFile, "utf-8");
7669
7907
  return JSON.parse(raw);
7670
7908
  } catch {
7671
7909
  return null;
@@ -7673,7 +7911,7 @@ function readStateFile(configDir) {
7673
7911
  }
7674
7912
  function removeStateFile(configDir) {
7675
7913
  try {
7676
- unlinkSync3(getManagerPaths(configDir).stateFile);
7914
+ unlinkSync4(getManagerPaths(configDir).stateFile);
7677
7915
  } catch {
7678
7916
  }
7679
7917
  }
@@ -7721,7 +7959,7 @@ function startWatchdog(opts) {
7721
7959
  const deadline = Date.now() + 5e3;
7722
7960
  const sleepBuf = new Int32Array(new SharedArrayBuffer(4));
7723
7961
  while (Date.now() < deadline) {
7724
- if (existsSync6(pidFile)) {
7962
+ if (existsSync7(pidFile)) {
7725
7963
  return { pid: child.pid };
7726
7964
  }
7727
7965
  if (child.exitCode !== null) {
@@ -8045,7 +8283,7 @@ function resolveStableAgtBin(rawPath) {
8045
8283
  if (!prefix || !formula) return rawPath;
8046
8284
  const candidates = [`${prefix}/bin/${formula}`, `${prefix}/bin/agt`];
8047
8285
  for (const candidate of candidates) {
8048
- if (!existsSync7(candidate)) continue;
8286
+ if (!existsSync8(candidate)) continue;
8049
8287
  try {
8050
8288
  realpathSync(candidate);
8051
8289
  return candidate;
@@ -8276,4 +8514,4 @@ export {
8276
8514
  managerInstallSystemUnitCommand,
8277
8515
  managerUninstallSystemUnitCommand
8278
8516
  };
8279
- //# sourceMappingURL=chunk-KLNFWXOI.js.map
8517
+ //# sourceMappingURL=chunk-DVWBVANP.js.map