@integrity-labs/agt-cli 0.27.7-test.7 → 0.27.8-test.8

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.
@@ -9,7 +9,7 @@ import {
9
9
  parseDeliveryTarget,
10
10
  registerFramework,
11
11
  wrapScheduledTaskPrompt
12
- } from "./chunk-YSBGIXJG.js";
12
+ } from "./chunk-HT6EETEL.js";
13
13
 
14
14
  // ../../packages/core/dist/integrations/registry.js
15
15
  var INTEGRATION_REGISTRY = [
@@ -174,7 +174,15 @@ var INTEGRATION_REGISTRY = [
174
174
  { id: "qmd:search", name: "Search Memory", description: "Semantic + keyword search over indexed memory files", access: "read" },
175
175
  { id: "qmd:get", name: "Get Memory", description: "Read memory files by path and line range", access: "read" }
176
176
  ],
177
- beta: true
177
+ beta: true,
178
+ // ENG-5815: migrated from buildMcpJson's hardcoded if-block. qmd is
179
+ // the simplest of the four pre-data-driven entries — no env, no
180
+ // conditional logic, just `qmd mcp`. The byte-identical render is
181
+ // pinned by claudecode-qmd-data-driven.test.ts.
182
+ nativeMcp: {
183
+ command: "qmd",
184
+ args: ["mcp"]
185
+ }
178
186
  },
179
187
  {
180
188
  id: "v0",
@@ -310,6 +318,35 @@ var INTEGRATION_REGISTRY = [
310
318
  },
311
319
  docs_url: "https://www.coderabbit.ai/cli"
312
320
  },
321
+ {
322
+ id: "aws",
323
+ name: "AWS",
324
+ category: "infrastructure",
325
+ description: "Amazon Web Services \u2014 query AWS APIs (EC2, S3, IAM, Lambda, etc.) via AWS Labs' official AWS API MCP server",
326
+ supported_auth_types: ["api_key", "managed", "none"],
327
+ capabilities: [
328
+ { id: "aws:read", name: "Read AWS Resources", description: "List and describe AWS resources across services (EC2, S3, IAM, Lambda, \u2026)", access: "read" },
329
+ { id: "aws:write", name: "Write AWS Resources", description: "Create and update AWS resources. Pair with an aws-no-destructive-ops guardrail.", access: "write" }
330
+ ],
331
+ docs_url: "https://github.com/awslabs/mcp/tree/main/src/aws-api-mcp-server",
332
+ beta: true,
333
+ // ENG-5815: first integration shipped purely via the data-driven
334
+ // path — buildMcpJson never grew an `aws` if-block. The AWS Labs
335
+ // AWS API MCP server runs through uvx (Python tooling), which the
336
+ // host bootstrap installs alongside python3. Credentials are
337
+ // resolved via the standard AWS_* env / shared credentials file
338
+ // chain on the host; the spec doesn't override them.
339
+ nativeMcp: {
340
+ command: "uvx",
341
+ args: ["awslabs.aws-api-mcp-server@latest"],
342
+ env: {
343
+ AWS_REGION: "{{empty_if_no_env.AWS_REGION}}",
344
+ AWS_PROFILE: "{{empty_if_no_env.AWS_PROFILE}}",
345
+ PATH: "{{process_env.PATH}}",
346
+ HOME: "{{process_env.HOME}}"
347
+ }
348
+ }
349
+ },
313
350
  {
314
351
  id: "custom",
315
352
  name: "Custom Integration",
@@ -2644,42 +2681,6 @@ If your charter doesn't authorise team-scope skill writes
2644
2681
  refused server-side \u2014 surface that error to the user rather than
2645
2682
  falling back to a local-disk write.
2646
2683
 
2647
- `;
2648
- }
2649
- function buildProgressHeartbeatSection(resolvedChannels) {
2650
- const hasSlack = !!resolvedChannels?.includes("slack");
2651
- const hasDirectChat = !!resolvedChannels?.includes("direct-chat");
2652
- if (!hasSlack && !hasDirectChat)
2653
- return "";
2654
- const bullets = [];
2655
- if (hasSlack) {
2656
- bullets.push("- **Slack threads:** `slack_progress_start` (channel + thread_ts from the inbound `<channel>` tag, label = short task name) \u2192 `slack_progress_update` between steps (pass `step_label`, optional `step_current`/`step_total`) \u2192 `slack_progress_complete` (summary) or `slack_progress_fail` (one-sentence reason).");
2657
- }
2658
- if (hasDirectChat) {
2659
- bullets.push("- **Direct Chat sessions:** `direct_chat_progress_start` (session_id from the inbound tag, label) \u2192 `direct_chat_progress_update` \u2192 `direct_chat_progress_complete` / `_fail`.");
2660
- }
2661
- return `
2662
- ## Progress heartbeats during multi-step work
2663
-
2664
- When a task involves **\u22653 tool calls or is likely to take >5 seconds**
2665
- of tool work, open an in-place progress anchor instead of streaming
2666
- "working on\u2026" / "still working\u2026" / "done" messages. The operator sees
2667
- one message that ticks forward as you advance, not a thread flooded
2668
- with status pings.
2669
-
2670
- ${bullets.join("\n")}
2671
-
2672
- The state machine debounces updates to roughly one API call per second
2673
- per channel, so it's safe to call \`_update\` after every step rather
2674
- than batching. Always end with \`_complete\` or \`_fail\` \u2014 leaving an
2675
- anchor in the "working" state means a future stale-state sweep flips it
2676
- to \u26A0\uFE0F "agent unresponsive" once the heartbeat goes cold.
2677
-
2678
- **Skip the heartbeat for one-shot replies.** A single \`slack.reply\` /
2679
- \`direct_chat.reply\` is still the right pattern when there's no
2680
- multi-step work to surface. Heartbeats are for the case where you'd
2681
- otherwise emit several intermediate messages.
2682
-
2683
2684
  `;
2684
2685
  }
2685
2686
  function buildPersonalitySection(seed) {
@@ -2900,6 +2901,27 @@ function formatConfigLines(config) {
2900
2901
  return ` - ${k}: ${rendered}`;
2901
2902
  });
2902
2903
  }
2904
+ var EMAIL_DOMAIN_RESTRICT_DEF = "email.domain_restrict";
2905
+ function stringList(value) {
2906
+ return Array.isArray(value) ? value.filter((d) => typeof d === "string") : [];
2907
+ }
2908
+ function formatEmailDomainConfigLines(config) {
2909
+ const mode = typeof config.mode === "string" ? config.mode : void 0;
2910
+ const lines = [];
2911
+ if (mode)
2912
+ lines.push(` - mode: ${mode}`);
2913
+ if (mode === "allowlist") {
2914
+ const allowed = stringList(config.allowed_domains);
2915
+ lines.push(` - allowed_domains: ${allowed.length ? allowed.join(", ") : "(none \u2014 no external email permitted)"}`);
2916
+ } else if (mode === "blocklist") {
2917
+ const blocked = stringList(config.blocked_domains);
2918
+ lines.push(` - blocked_domains: ${blocked.length ? blocked.join(", ") : "(none)"}`);
2919
+ } else if (mode === "internal_only") {
2920
+ lines.push(` - only your organization's own email domain is permitted`);
2921
+ }
2922
+ lines.push(` *Advisory: honor this restriction \u2014 it is guidance in your instructions, not a hard block at send time.*`);
2923
+ return lines;
2924
+ }
2903
2925
  function renderGuardrailBullet(g) {
2904
2926
  const lines = [];
2905
2927
  const header = `- **${g.displayName}** (${g.category}, from ${g.source})`;
@@ -2907,7 +2929,7 @@ function renderGuardrailBullet(g) {
2907
2929
  if (g.description?.trim()) {
2908
2930
  lines.push(` ${g.description.trim()}`);
2909
2931
  }
2910
- lines.push(...formatConfigLines(g.config));
2932
+ lines.push(...g.definitionId === EMAIL_DOMAIN_RESTRICT_DEF ? formatEmailDomainConfigLines(g.config) : formatConfigLines(g.config));
2911
2933
  if (g.overrideApplied && g.overrideReason?.trim()) {
2912
2934
  lines.push(` *Override applied: ${g.overrideReason.trim()}*`);
2913
2935
  }
@@ -3019,7 +3041,7 @@ inline replies \u2014 they do NOT replace this dispatch decision.
3019
3041
  If the work turns out to be unexpectedly slow after you started inline,
3020
3042
  finish the current sub-step, then dispatch the rest. Don't apologise for
3021
3043
  mid-task switching \u2014 operators care about responsiveness, not consistency.
3022
- ${buildProgressHeartbeatSection(resolvedChannels)}
3044
+
3023
3045
  ${activeTasksSection}${personalitySection}## Identity
3024
3046
 
3025
3047
  - Code Name: ${frontmatter.code_name}
@@ -3494,6 +3516,66 @@ function buildRemoteMcpEntry(definitionId) {
3494
3516
  };
3495
3517
  }
3496
3518
 
3519
+ // ../../packages/core/dist/provisioning/native-mcp.js
3520
+ function buildNativeMcpEntry(spec, ctx) {
3521
+ const resolvedCommand = resolveTemplate(spec.command, ctx);
3522
+ if (resolvedCommand.omit) {
3523
+ throw new Error("NativeMcpSpec: empty_if_no_env is only valid in env values (not in `command`)");
3524
+ }
3525
+ const command = resolvedCommand.value;
3526
+ const args = spec.args.map((a, i) => {
3527
+ const resolved = resolveTemplate(a, ctx);
3528
+ if (resolved.omit) {
3529
+ throw new Error(`NativeMcpSpec: empty_if_no_env is only valid in env values (not in args[${i}])`);
3530
+ }
3531
+ return resolved.value;
3532
+ });
3533
+ if (spec.env === void 0) {
3534
+ return { command, args };
3535
+ }
3536
+ const env = {};
3537
+ for (const [k, raw] of Object.entries(spec.env)) {
3538
+ const { value, omit } = resolveTemplate(raw, ctx);
3539
+ if (omit)
3540
+ continue;
3541
+ env[k] = value;
3542
+ }
3543
+ return { command, args, env };
3544
+ }
3545
+ function resolveTemplate(input, ctx) {
3546
+ const TOKEN = /\{\{([^}]+)\}\}/g;
3547
+ const hasEmptyIfNoEnv = /\{\{\s*empty_if_no_env\./.test(input);
3548
+ const isWholeValueEmptyIfNoEnv = /^\{\{\s*empty_if_no_env\.[^}]+\}\}$/.test(input);
3549
+ if (hasEmptyIfNoEnv && !isWholeValueEmptyIfNoEnv) {
3550
+ throw new Error(`NativeMcpSpec: empty_if_no_env must be the sole content of the value, never mixed with literal text or other tokens (value: ${JSON.stringify(input)})`);
3551
+ }
3552
+ let omit = false;
3553
+ const value = input.replace(TOKEN, (whole, expr) => {
3554
+ const trimmed = expr.trim();
3555
+ if (trimmed === "agent_id")
3556
+ return ctx.agentId;
3557
+ if (trimmed === "agent_code_name")
3558
+ return ctx.agentCodeName;
3559
+ if (trimmed === "integration_id")
3560
+ return ctx.integration?.id ?? "";
3561
+ if (trimmed.startsWith("process_env.")) {
3562
+ const name = trimmed.slice("process_env.".length);
3563
+ return process.env[name] ?? "";
3564
+ }
3565
+ if (trimmed.startsWith("empty_if_no_env.")) {
3566
+ const name = trimmed.slice("empty_if_no_env.".length);
3567
+ const v = process.env[name] ?? "";
3568
+ if (v.length === 0) {
3569
+ omit = true;
3570
+ return "";
3571
+ }
3572
+ return v;
3573
+ }
3574
+ return whole;
3575
+ });
3576
+ return { value, omit };
3577
+ }
3578
+
3497
3579
  // ../../packages/core/dist/provisioning/frameworks/claudecode/index.js
3498
3580
  var VALID_CODE_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
3499
3581
  var SECRET_FILE_MODE = 384;
@@ -4353,7 +4435,7 @@ The parent passes you in the task description:
4353
4435
  Your job:
4354
4436
 
4355
4437
  1. Do the work the message asks for (Xero pull, research, multi-step skill, etc.). Use whichever tools you need.
4356
- 2. Post the reply yourself via the matching channel tool \u2014 slack.reply for Slack, telegram.reply for Telegram, teams.reply for Microsoft Teams, directchat.reply for Direct Chat. Use the metadata the parent gave you to address the right thread/conversation.
4438
+ 2. Post the reply yourself via the matching channel tool \u2014 slack.reply for Slack, telegram.reply for Telegram, teams.reply for Microsoft Teams, direct_chat.reply for Direct Chat. Use the metadata the parent gave you to address the right thread/conversation.
4357
4439
  3. End. Do not do additional follow-up work; if more is needed, the user will send another message.
4358
4440
 
4359
4441
  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.
@@ -4405,12 +4487,16 @@ function buildMcpJson(input) {
4405
4487
  HOME: process.env["HOME"] ?? ""
4406
4488
  }
4407
4489
  };
4408
- const hasQmd = input.integrations?.some((i) => i.definition_id === "qmd");
4409
- if (hasQmd) {
4410
- mcpServers["qmd"] = {
4411
- command: "qmd",
4412
- args: ["mcp"]
4413
- };
4490
+ for (const integration of input.integrations ?? []) {
4491
+ const def = INTEGRATION_REGISTRY.find((d) => d.id === integration.definition_id);
4492
+ if (!def?.nativeMcp)
4493
+ continue;
4494
+ const key = def.nativeMcp.key ?? integration.definition_id;
4495
+ mcpServers[key] = buildNativeMcpEntry(def.nativeMcp, {
4496
+ agentId: input.agent.agent_id,
4497
+ agentCodeName: input.agent.code_name,
4498
+ integration
4499
+ });
4414
4500
  }
4415
4501
  const xeroIntegration = input.integrations?.find((i) => i.definition_id === "xero");
4416
4502
  if (xeroIntegration) {
@@ -4750,6 +4836,20 @@ ${sections}`
4750
4836
  },
4751
4837
  writeChannelCredentials(codeName, channelId, config, options) {
4752
4838
  const tzEnv = options?.agentTimezone && options.agentTimezone.trim() !== "" ? { TZ: options.agentTimezone.trim() } : {};
4839
+ const senderPolicyEnv = (() => {
4840
+ const sp = options?.senderPolicy;
4841
+ if (!sp)
4842
+ return {};
4843
+ return {
4844
+ // Channel-specific var name to match what the consumer MCP reads;
4845
+ // spread below selects the right one per channelId.
4846
+ ...sp.mode === "team_agents_only" && sp.team_id ? { AGT_TEAM_ID: sp.team_id } : {},
4847
+ // sentinel keys consumed by the per-channel spread below
4848
+ _SENDER_POLICY_MODE: sp.mode
4849
+ };
4850
+ })();
4851
+ const senderPolicyMode = senderPolicyEnv["_SENDER_POLICY_MODE"];
4852
+ delete senderPolicyEnv["_SENDER_POLICY_MODE"];
4753
4853
  const agentDir = getAgentDir(codeName);
4754
4854
  mkdirSync4(agentDir, { recursive: true });
4755
4855
  const isPersistent = options?.sessionMode === "persistent";
@@ -4901,7 +5001,14 @@ ${sections}`
4901
5001
  // (ENG-4936) honours PEER_DISABLED with identical
4902
5002
  // semantics. Only emit when non-default so the env block
4903
5003
  // stays clean for the common case.
4904
- ...peerDisabledMode !== "off" ? { PEER_DISABLED: peerDisabledMode } : {}
5004
+ ...peerDisabledMode !== "off" ? { PEER_DISABLED: peerDisabledMode } : {},
5005
+ // ENG-5841: SLACK_SENDER_POLICY drives slack-inbound-filter.ts.
5006
+ // Only emitted when the effective mode is restrictive (default
5007
+ // 'all' is the absence of the env var — same convention the
5008
+ // MCP filter uses on its own end).
5009
+ ...senderPolicyMode ? { SLACK_SENDER_POLICY: senderPolicyMode } : {},
5010
+ ...senderPolicyEnv
5011
+ // AGT_TEAM_ID when team_agents_only
4905
5012
  }
4906
5013
  };
4907
5014
  const provisionMcpPath = join4(agentDir, "provision", ".mcp.json");
@@ -5077,7 +5184,12 @@ ${sections}`
5077
5184
  ...peerTeamIds.length > 0 ? { MSTEAMS_PEER_TEAM_IDS: peerTeamIds.join(",") } : {},
5078
5185
  ...knownPeerBotIds.length > 0 ? { MSTEAMS_KNOWN_PEER_BOT_IDS: knownPeerBotIds.join(",") } : {},
5079
5186
  ...msteamsAgtAuthEnv,
5080
- ...tzEnv
5187
+ ...tzEnv,
5188
+ // ENG-5841: MSTEAMS_SENDER_POLICY drives teams-inbound-filter.ts.
5189
+ // Mirrors the Slack branch above — only emitted when restrictive.
5190
+ ...senderPolicyMode ? { MSTEAMS_SENDER_POLICY: senderPolicyMode } : {},
5191
+ ...senderPolicyEnv
5192
+ // AGT_TEAM_ID when team_agents_only
5081
5193
  };
5082
5194
  if (isPersistent && existsSync5(localTeamsChannel)) {
5083
5195
  mcpServers["msteams"] = {
@@ -5194,9 +5306,16 @@ ${sections}`
5194
5306
  chmodSync4(envPath, SECRET_FILE_MODE);
5195
5307
  }
5196
5308
  writeXurlStoreForIntegrations(decryptedIntegrations);
5197
- const hasQmd = integrations.some((i) => i.definition_id === "qmd");
5198
- if (hasQmd) {
5199
- this.writeMcpServer(codeName, "qmd", { command: "qmd", args: ["mcp"] });
5309
+ for (const integration of decryptedIntegrations) {
5310
+ const def = INTEGRATION_REGISTRY.find((d) => d.id === integration.definition_id);
5311
+ if (!def?.nativeMcp)
5312
+ continue;
5313
+ const key = def.nativeMcp.key ?? integration.definition_id;
5314
+ this.writeMcpServer(codeName, key, buildNativeMcpEntry(def.nativeMcp, {
5315
+ agentId: agentId ?? "",
5316
+ agentCodeName: codeName,
5317
+ integration
5318
+ }));
5200
5319
  }
5201
5320
  const xeroIntegration = integrations.find((i) => i.definition_id === "xero");
5202
5321
  if (xeroIntegration) {
@@ -5255,16 +5374,15 @@ ${sections}`
5255
5374
  }
5256
5375
  }
5257
5376
  if (this.removeMcpServer) {
5377
+ const nativeMcpKeys = INTEGRATION_REGISTRY.filter((d) => d.nativeMcp !== void 0).map((d) => d.nativeMcp.key ?? d.id);
5258
5378
  const integrationDerivedKeys = /* @__PURE__ */ new Set([
5259
- "qmd",
5260
5379
  "xero",
5261
5380
  "postiz",
5262
5381
  "cloud-broker",
5382
+ ...nativeMcpKeys,
5263
5383
  ...Object.entries(OAUTH_PROVIDERS).filter(([, provider]) => Boolean(provider.mcpUrl)).map(([id]) => id)
5264
5384
  ]);
5265
5385
  const expectedKeys = /* @__PURE__ */ new Set();
5266
- if (hasQmd)
5267
- expectedKeys.add("qmd");
5268
5386
  if (xeroIntegration)
5269
5387
  expectedKeys.add("xero");
5270
5388
  if (postizIntegration)
@@ -5272,6 +5390,10 @@ ${sections}`
5272
5390
  if (hasCloudBroker)
5273
5391
  expectedKeys.add("cloud-broker");
5274
5392
  for (const integration of integrations) {
5393
+ const def = INTEGRATION_REGISTRY.find((d) => d.id === integration.definition_id);
5394
+ if (def?.nativeMcp) {
5395
+ expectedKeys.add(def.nativeMcp.key ?? integration.definition_id);
5396
+ }
5275
5397
  if (buildRemoteMcpEntry(integration.definition_id)) {
5276
5398
  expectedKeys.add(integration.definition_id);
5277
5399
  }
@@ -5933,12 +6055,15 @@ function setActiveTeam(slug) {
5933
6055
  config.active_team = slug;
5934
6056
  saveConfig(config);
5935
6057
  }
5936
- var DEFAULT_AGT_HOST = "https://api.augmented.team";
6058
+ var PROD_AGT_HOST = "https://api.augmented.team";
6059
+ var AGT_HOST_UNSET_MESSAGE = "AGT_HOST is not set. Set it to the API URL for your stage before running this command:\n Production: export AGT_HOST=https://api.augmented.team\n Non-prod stage: export AGT_HOST=https://<stage>.api.staging.augmented.team\n Local development: export AGT_HOST=http://api.agt.localhost:1355\n\nFor first-time host setup, pass --api-host <url> to `agt setup` instead \u2014 the setup command does NOT silently default to prod (ENG-5831).";
5937
6060
  function getHost() {
5938
6061
  const envHost = process.env["AGT_HOST"]?.trim();
5939
- return envHost ? envHost : DEFAULT_AGT_HOST;
6062
+ if (!envHost) {
6063
+ throw new Error(AGT_HOST_UNSET_MESSAGE);
6064
+ }
6065
+ return envHost;
5940
6066
  }
5941
- var AGT_HOST = getHost();
5942
6067
  function requireHost() {
5943
6068
  return getHost();
5944
6069
  }
@@ -6761,7 +6886,7 @@ export {
6761
6886
  getApiKey,
6762
6887
  getActiveTeam,
6763
6888
  setActiveTeam,
6764
- DEFAULT_AGT_HOST,
6889
+ PROD_AGT_HOST,
6765
6890
  getHost,
6766
6891
  requireHost,
6767
6892
  exchangeApiKey,
@@ -6785,4 +6910,4 @@ export {
6785
6910
  managerInstallSystemUnitCommand,
6786
6911
  managerUninstallSystemUnitCommand
6787
6912
  };
6788
- //# sourceMappingURL=chunk-2G35ES7G.js.map
6913
+ //# sourceMappingURL=chunk-DHF75YYA.js.map