@integrity-labs/agt-cli 0.28.269 → 0.28.270

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.
@@ -100,7 +100,7 @@ async function spawnPairSession(session) {
100
100
  return { ok: true };
101
101
  } catch {
102
102
  }
103
- const { resolveClaudeBinary } = await import("./persistent-session-ZQDTWV3D.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-FMFTNDJO.js");
104
104
  const claudeBin = resolveClaudeBinary();
105
105
  const pairEnv = {
106
106
  ...process.env,
@@ -373,4 +373,4 @@ export {
373
373
  startClaudePair,
374
374
  submitClaudePairCode
375
375
  };
376
- //# sourceMappingURL=claude-pair-runtime-CXM2IH7U.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-WZLKWP74.js.map
@@ -38,7 +38,7 @@ import {
38
38
  requireHost,
39
39
  safeWriteJsonAtomic,
40
40
  setConfigHash
41
- } from "../chunk-ZH3CEX2A.js";
41
+ } from "../chunk-ND4BBPV5.js";
42
42
  import {
43
43
  getProjectDir as getProjectDir2,
44
44
  getReadyTasks,
@@ -123,7 +123,7 @@ import {
123
123
  takeZombieDetection,
124
124
  transcriptActivityAgeSeconds,
125
125
  writeEgressAllowlist
126
- } from "../chunk-JW6CWBNF.js";
126
+ } from "../chunk-RH4FTCZK.js";
127
127
  import {
128
128
  reapOrphanChannelMcps
129
129
  } from "../chunk-XWVM4KPK.js";
@@ -6700,7 +6700,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
6700
6700
  var lastVersionCheckAt = 0;
6701
6701
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
6702
6702
  var lastResponsivenessProbeAt = 0;
6703
- var agtCliVersion = true ? "0.28.269" : "dev";
6703
+ var agtCliVersion = true ? "0.28.270" : "dev";
6704
6704
  function resolveBrewPath(execFileSync2) {
6705
6705
  try {
6706
6706
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -7550,7 +7550,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
7550
7550
  if (codeNames.length === 0) return;
7551
7551
  void (async () => {
7552
7552
  try {
7553
- const { collectDiagnostics } = await import("../persistent-session-ZQDTWV3D.js");
7553
+ const { collectDiagnostics } = await import("../persistent-session-FMFTNDJO.js");
7554
7554
  await api.post("/host/heartbeat", {
7555
7555
  host_id: hostId,
7556
7556
  agent_diagnostics: collectDiagnostics(codeNames)
@@ -7648,7 +7648,7 @@ async function pollCycle() {
7648
7648
  }
7649
7649
  try {
7650
7650
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
7651
- const { collectDiagnostics } = await import("../persistent-session-ZQDTWV3D.js");
7651
+ const { collectDiagnostics } = await import("../persistent-session-FMFTNDJO.js");
7652
7652
  const diagCodeNames = [...agentState.persistentSessionAgents];
7653
7653
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
7654
7654
  let tailscaleHostname;
@@ -7796,7 +7796,7 @@ async function pollCycle() {
7796
7796
  const {
7797
7797
  collectResponsivenessProbes,
7798
7798
  getResponsivenessIntervalMs
7799
- } = await import("../responsiveness-probe-74SDDDGN.js");
7799
+ } = await import("../responsiveness-probe-FPTBS6RZ.js");
7800
7800
  const probeIntervalMs = getResponsivenessIntervalMs();
7801
7801
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
7802
7802
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -7828,7 +7828,7 @@ async function pollCycle() {
7828
7828
  collectResponsivenessProbes,
7829
7829
  livePendingInboundOldestAgeSeconds,
7830
7830
  parkPendingInbound
7831
- } = await import("../responsiveness-probe-74SDDDGN.js");
7831
+ } = await import("../responsiveness-probe-FPTBS6RZ.js");
7832
7832
  const { getProjectDir: wedgeProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
7833
7833
  const wedgeNow = /* @__PURE__ */ new Date();
7834
7834
  const liveAgents = agentState.persistentSessionAgents;
@@ -10686,7 +10686,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
10686
10686
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
10687
10687
  void (async () => {
10688
10688
  try {
10689
- const { collectDiagnostics } = await import("../persistent-session-ZQDTWV3D.js");
10689
+ const { collectDiagnostics } = await import("../persistent-session-FMFTNDJO.js");
10690
10690
  await api.post("/host/heartbeat", {
10691
10691
  host_id: hostId,
10692
10692
  agent_diagnostics: collectDiagnostics([codeName])
@@ -10736,7 +10736,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
10736
10736
  }
10737
10737
  try {
10738
10738
  const hostId = await getHostId();
10739
- const { collectDiagnostics } = await import("../persistent-session-ZQDTWV3D.js");
10739
+ const { collectDiagnostics } = await import("../persistent-session-FMFTNDJO.js");
10740
10740
  await api.post("/host/heartbeat", {
10741
10741
  host_id: hostId,
10742
10742
  agent_diagnostics: collectDiagnostics([codeName])
@@ -11127,7 +11127,7 @@ async function processClaudePairSessions(agents) {
11127
11127
  killPairSession,
11128
11128
  pairTmuxSession,
11129
11129
  finalizeClaudePairOnboarding
11130
- } = await import("../claude-pair-runtime-CXM2IH7U.js");
11130
+ } = await import("../claude-pair-runtime-WZLKWP74.js");
11131
11131
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
11132
11132
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
11133
11133
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -38739,13 +38739,13 @@ var INTEGRATION_REGISTRY = [
38739
38739
  id: "image-gen",
38740
38740
  name: "Image Generation",
38741
38741
  category: "media",
38742
- description: "AI image generation for Augmented Live pages. An agent authoring a page passes a text prompt (agt_live.generate_image) and gets back a generated raster image, stored on the page asset CDN so there is no image to source or host. Augmented Team manages the model access for you - there is no key to enter. Generation runs through the Vercel AI Gateway (OpenAI gpt-image-2 today). It is a PREMIUM, usage-billed capability, separate from Augmented Live text and audio: gated on a per-org opt-in and a monthly USD budget, and metered per generated image.",
38742
+ description: "AI image generation from a text prompt. An agent passes a prompt to generate_image and gets back a generated raster image, which it can deliver to a chat channel or embed on an Augmented Live page (ENG-7535). Augmented Team manages the model access for you - there is no key to enter. Generation runs through the Vercel AI Gateway (OpenAI gpt-image-2 today). It is a PREMIUM, usage-billed capability: gated on a per-org opt-in and a monthly USD budget, and metered per generated image.",
38743
38743
  // No vendor key to enter: Augmented owns the shared Vercel AI Gateway virtual
38744
38744
  // key (AI_GATEWAY_API_KEY) that fronts the image providers, so auth is `none`
38745
38745
  // (customers never enter a key), same as ElevenLabs.
38746
38746
  supported_auth_types: ["none"],
38747
38747
  capabilities: [
38748
- { id: "image-gen:generate", name: "Generate Image", description: "Generate a raster image from a text prompt for an Augmented Live page (agt_live.generate_image)", access: "write" }
38748
+ { id: "image-gen:generate", name: "Generate Image", description: "Generate a raster image from a text prompt (generate_image), deliverable to a chat channel or an Augmented Live page", access: "write" }
38749
38749
  ],
38750
38750
  docs_url: "https://platform.openai.com/docs/guides/images",
38751
38751
  beta: true,
@@ -38762,7 +38762,7 @@ var INTEGRATION_REGISTRY = [
38762
38762
  // declared here; the handler fails CLOSED when the deployed model maps to an
38763
38763
  // undeclared meter, so a version bump without a rate card refuses rather than
38764
38764
  // silently disabling the budget. Adding a model = declare its meter here + seed
38765
- // its rate card + add a normalizer entry in augmented-live-image-gen.ts.
38765
+ // its rate card + add a normalizer entry in image-generation.ts.
38766
38766
  premium: {
38767
38767
  pricing: "usage",
38768
38768
  note: "Billed per generated image; the rate depends on the model/version used.",
@@ -40033,6 +40033,19 @@ var FLAG_REGISTRY = [
40033
40033
  // env var is retired.
40034
40034
  envVar: "AUGMENTED_CONNECTIVITY_ESCALATION_ENABLED"
40035
40035
  },
40036
+ {
40037
+ key: "managed-health-connectivity-flip",
40038
+ description: "Arm the managed-connection-health status flip on data-plane hard-down (ENG-7539). When on, the managed-connection-health cron flips a managed integration to needs_reauth when the host-side connectivity probe reports a sustained hard-down (consecutive_connectivity_failures at/over the hysteresis threshold with a down observation), even if Composio account-status still reads ACTIVE. When off it keeps the pre-ENG-7539 behaviour: status is driven only by the account-status probe, so a live 401 behind an ACTIVE account never flips status. Boolean gate; ships dark. Sibling of integration-connectivity-escalation: that flag arms the alert; this flag arms the status flip plus the reconnect notification.",
40039
+ flagType: "boolean",
40040
+ // Declared safe value is `false` (no auto status flip). It preserves the
40041
+ // pre-ENG-7539 fleet behaviour where managed status derives solely from the
40042
+ // account-status probe, so merging this dark changes nothing until an
40043
+ // operator arms it per-stage from the admin Feature Flags page. `false` is
40044
+ // also the fail-safe direction: a flag-DB read error must never start
40045
+ // flipping statuses (and firing reconnect notifications) on its own. No env
40046
+ // override - this is a net-new gate, so it is registry-only (ADR-0022).
40047
+ defaultValue: false
40048
+ },
40036
40049
  {
40037
40050
  key: "projects-menu",
40038
40051
  description: "Show the Projects nav item in the webapp (ADR-0017 Projects soft launch, ENG-6342). Replaces the isAdmin/adminOnly gate with a per-org rollout flag.",
@@ -2985,7 +2985,7 @@ var require_compile = __commonJS({
2985
2985
  const schOrFunc = root.refs[ref];
2986
2986
  if (schOrFunc)
2987
2987
  return schOrFunc;
2988
- let _sch = resolve2.call(this, root, ref);
2988
+ let _sch = resolve3.call(this, root, ref);
2989
2989
  if (_sch === void 0) {
2990
2990
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2991
2991
  const { schemaId } = this.opts;
@@ -3012,7 +3012,7 @@ var require_compile = __commonJS({
3012
3012
  function sameSchemaEnv(s1, s2) {
3013
3013
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3014
3014
  }
3015
- function resolve2(root, ref) {
3015
+ function resolve3(root, ref) {
3016
3016
  let sch;
3017
3017
  while (typeof (sch = this.refs[ref]) == "string")
3018
3018
  ref = sch;
@@ -3587,7 +3587,7 @@ var require_fast_uri = __commonJS({
3587
3587
  }
3588
3588
  return uri;
3589
3589
  }
3590
- function resolve2(baseURI, relativeURI, options) {
3590
+ function resolve3(baseURI, relativeURI, options) {
3591
3591
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3592
3592
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3593
3593
  schemelessOptions.skipEscape = true;
@@ -3814,7 +3814,7 @@ var require_fast_uri = __commonJS({
3814
3814
  var fastUri = {
3815
3815
  SCHEMES,
3816
3816
  normalize,
3817
- resolve: resolve2,
3817
+ resolve: resolve3,
3818
3818
  resolveComponent,
3819
3819
  equal,
3820
3820
  serialize,
@@ -13054,7 +13054,7 @@ var Protocol = class {
13054
13054
  return;
13055
13055
  }
13056
13056
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
13057
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
13057
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
13058
13058
  options?.signal?.throwIfAborted();
13059
13059
  }
13060
13060
  } catch (error2) {
@@ -13071,7 +13071,7 @@ var Protocol = class {
13071
13071
  */
13072
13072
  request(request, resultSchema, options) {
13073
13073
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
13074
- return new Promise((resolve2, reject) => {
13074
+ return new Promise((resolve3, reject) => {
13075
13075
  const earlyReject = (error2) => {
13076
13076
  reject(error2);
13077
13077
  };
@@ -13149,7 +13149,7 @@ var Protocol = class {
13149
13149
  if (!parseResult.success) {
13150
13150
  reject(parseResult.error);
13151
13151
  } else {
13152
- resolve2(parseResult.data);
13152
+ resolve3(parseResult.data);
13153
13153
  }
13154
13154
  } catch (error2) {
13155
13155
  reject(error2);
@@ -13410,12 +13410,12 @@ var Protocol = class {
13410
13410
  }
13411
13411
  } catch {
13412
13412
  }
13413
- return new Promise((resolve2, reject) => {
13413
+ return new Promise((resolve3, reject) => {
13414
13414
  if (signal.aborted) {
13415
13415
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
13416
13416
  return;
13417
13417
  }
13418
- const timeoutId = setTimeout(resolve2, interval);
13418
+ const timeoutId = setTimeout(resolve3, interval);
13419
13419
  signal.addEventListener("abort", () => {
13420
13420
  clearTimeout(timeoutId);
13421
13421
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -14476,12 +14476,12 @@ var StdioServerTransport = class {
14476
14476
  this.onclose?.();
14477
14477
  }
14478
14478
  send(message) {
14479
- return new Promise((resolve2) => {
14479
+ return new Promise((resolve3) => {
14480
14480
  const json = serializeMessage(message);
14481
14481
  if (this._stdout.write(json)) {
14482
- resolve2();
14482
+ resolve3();
14483
14483
  } else {
14484
- this._stdout.once("drain", resolve2);
14484
+ this._stdout.once("drain", resolve3);
14485
14485
  }
14486
14486
  });
14487
14487
  }
@@ -14499,6 +14499,7 @@ import {
14499
14499
  openSync,
14500
14500
  readFileSync as readFileSync11,
14501
14501
  readdirSync as readdirSync3,
14502
+ realpathSync,
14502
14503
  renameSync as renameSync6,
14503
14504
  statSync as statSync2,
14504
14505
  unlinkSync as unlinkSync6,
@@ -14507,7 +14508,7 @@ import {
14507
14508
  writeSync
14508
14509
  } from "fs";
14509
14510
  import { homedir as homedir4 } from "os";
14510
- import { join as join10 } from "path";
14511
+ import { basename, extname, join as join10, resolve as resolve2 } from "path";
14511
14512
 
14512
14513
  // src/channel-attachments.ts
14513
14514
  import { homedir } from "os";
@@ -16539,9 +16540,60 @@ var TELEGRAM_EGRESS_TOOLS = /* @__PURE__ */ new Set([
16539
16540
  "telegram.reply",
16540
16541
  "telegram.send_message",
16541
16542
  "telegram.react",
16543
+ "telegram.upload_file",
16542
16544
  "channel_request_input"
16543
16545
  ]);
16544
16546
 
16547
+ // src/telegram-upload.ts
16548
+ var TELEGRAM_PHOTO_MAX_BYTES = 10 * 1024 * 1024;
16549
+ var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
16550
+ var TELEGRAM_IMAGE_EXTS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
16551
+ var TELEGRAM_EXT_CONTENT_TYPE = {
16552
+ ".png": "image/png",
16553
+ ".jpg": "image/jpeg",
16554
+ ".jpeg": "image/jpeg",
16555
+ ".gif": "image/gif",
16556
+ ".webp": "image/webp",
16557
+ ".pdf": "application/pdf",
16558
+ ".mp4": "video/mp4",
16559
+ ".mp3": "audio/mpeg",
16560
+ ".txt": "text/plain",
16561
+ ".json": "application/json",
16562
+ ".csv": "text/csv"
16563
+ };
16564
+ function chooseTelegramUploadMethod(input) {
16565
+ const ext = input.ext.toLowerCase();
16566
+ const isImage = TELEGRAM_IMAGE_EXTS.has(ext);
16567
+ let sendAsPhoto = isImage && input.asDocument !== true;
16568
+ const downgradedToDocument = sendAsPhoto && input.sizeBytes > TELEGRAM_PHOTO_MAX_BYTES;
16569
+ if (downgradedToDocument) sendAsPhoto = false;
16570
+ return {
16571
+ method: sendAsPhoto ? "sendPhoto" : "sendDocument",
16572
+ fileField: sendAsPhoto ? "photo" : "document",
16573
+ contentType: TELEGRAM_EXT_CONTENT_TYPE[ext] ?? "application/octet-stream",
16574
+ downgradedToDocument
16575
+ };
16576
+ }
16577
+ function sanitizeMultipartFilename(name) {
16578
+ return name.replace(/[\r\n"]/g, "_");
16579
+ }
16580
+ function buildTelegramMultipartBody(boundary, fileField, file, fields) {
16581
+ const CRLF = "\r\n";
16582
+ const safeName = sanitizeMultipartFilename(file.filename);
16583
+ const parts = [];
16584
+ for (const [k, v] of Object.entries(fields)) {
16585
+ parts.push(Buffer.from(`--${boundary}${CRLF}Content-Disposition: form-data; name="${k}"${CRLF}${CRLF}${v}${CRLF}`));
16586
+ }
16587
+ parts.push(
16588
+ Buffer.from(
16589
+ `--${boundary}${CRLF}Content-Disposition: form-data; name="${fileField}"; filename="${safeName}"${CRLF}Content-Type: ${file.contentType}${CRLF}${CRLF}`
16590
+ ),
16591
+ Buffer.from(file.bytes),
16592
+ Buffer.from(`${CRLF}--${boundary}--${CRLF}`)
16593
+ );
16594
+ return Buffer.concat(parts);
16595
+ }
16596
+
16545
16597
  // src/strip-repost-preamble.ts
16546
16598
  var PRIOR_ATTEMPT_CUE = /\b(a moment ago|just now|a second ago|earlier|moments ago|previously|last (?:time|attempt)|drafted|dialled|dialed|typed|composed|wrote|sent) (?:this|that|it)?\b|\b(?:drafted|dialled|dialed|typed|composed)\b/i;
16547
16599
  var FAILURE_CUE = /\b(?:did(?:n['’]t| not)|could(?:n['’]t| not)|would(?:n['’]t| not)|wasn['’]t|fail(?:ed)?|never)\b[^.!?\n]*?\b(?:parse[d]?|send|sent|go through|went through|land|deliver(?:ed)?|post(?:ed)?|come through|came through)\b/i;
@@ -16751,7 +16803,7 @@ function ensureEmptyMcpConfig() {
16751
16803
  return p;
16752
16804
  }
16753
16805
  function runClaudeP(prompt, model) {
16754
- return new Promise((resolve2, reject) => {
16806
+ return new Promise((resolve3, reject) => {
16755
16807
  const args = [
16756
16808
  "-p",
16757
16809
  prompt,
@@ -16781,7 +16833,7 @@ function runClaudeP(prompt, model) {
16781
16833
  reject(new Error("claude -p returned no output"));
16782
16834
  return;
16783
16835
  }
16784
- resolve2(text);
16836
+ resolve3(text);
16785
16837
  }
16786
16838
  );
16787
16839
  });
@@ -17103,12 +17155,12 @@ function defaultIsPidAlive(pid) {
17103
17155
  }
17104
17156
  }
17105
17157
  function acquireMcpSpawnLock(args) {
17106
- const { agentDir, basename, options = {} } = args;
17158
+ const { agentDir, basename: basename2, options = {} } = args;
17107
17159
  if (!agentDir) return { kind: "no-agent-dir" };
17108
17160
  const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
17109
17161
  const selfPid = options.selfPid ?? process.pid;
17110
17162
  const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
17111
- const path = join8(agentDir, basename);
17163
+ const path = join8(agentDir, basename2);
17112
17164
  const existing = readLockHolder(path);
17113
17165
  if (existing) {
17114
17166
  if (existing.pid === selfPid) {
@@ -17530,7 +17582,7 @@ if (AGENT_CODE_NAME && AGENT_CODE_NAME !== "unknown") {
17530
17582
  }
17531
17583
  }
17532
17584
  function telegramApiCall(method, body, timeoutMs) {
17533
- return new Promise((resolve2, reject) => {
17585
+ return new Promise((resolve3, reject) => {
17534
17586
  const postData = JSON.stringify(body);
17535
17587
  const req = https.request(
17536
17588
  {
@@ -17552,7 +17604,7 @@ function telegramApiCall(method, body, timeoutMs) {
17552
17604
  });
17553
17605
  res.on("end", () => {
17554
17606
  try {
17555
- resolve2(JSON.parse(data));
17607
+ resolve3(JSON.parse(data));
17556
17608
  } catch {
17557
17609
  reject(new Error("Invalid JSON from Telegram API"));
17558
17610
  }
@@ -17568,6 +17620,46 @@ function telegramApiCall(method, body, timeoutMs) {
17568
17620
  req.end();
17569
17621
  });
17570
17622
  }
17623
+ function sendTelegramFileMultipart(method, fileField, file, fields, timeoutMs) {
17624
+ return new Promise((resolvePromise, reject) => {
17625
+ const boundary = `----augmented${randomUUID3().replace(/-/g, "")}`;
17626
+ const body = buildTelegramMultipartBody(boundary, fileField, file, fields);
17627
+ const req = https.request(
17628
+ {
17629
+ hostname: "api.telegram.org",
17630
+ port: 443,
17631
+ path: `/bot${BOT_TOKEN}/${method}`,
17632
+ method: "POST",
17633
+ family: 4,
17634
+ timeout: timeoutMs,
17635
+ headers: {
17636
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
17637
+ "Content-Length": body.length
17638
+ }
17639
+ },
17640
+ (res) => {
17641
+ let data = "";
17642
+ res.on("data", (d) => {
17643
+ data += d;
17644
+ });
17645
+ res.on("end", () => {
17646
+ try {
17647
+ resolvePromise(JSON.parse(data));
17648
+ } catch {
17649
+ reject(new Error("Invalid JSON from Telegram API"));
17650
+ }
17651
+ });
17652
+ }
17653
+ );
17654
+ req.on("error", reject);
17655
+ req.on("timeout", () => {
17656
+ req.destroy();
17657
+ reject(new Error("Telegram API timeout"));
17658
+ });
17659
+ req.write(body);
17660
+ req.end();
17661
+ });
17662
+ }
17571
17663
  var TELEGRAM_MAINTENANCE_NOTICE_CACHE = /* @__PURE__ */ new Map();
17572
17664
  var TELEGRAM_MAINTENANCE_COOLDOWN_MS = (() => {
17573
17665
  const raw = process.env["TELEGRAM_MAINTENANCE_NOTICE_COOLDOWN_SEC"];
@@ -18242,7 +18334,7 @@ function investigateNowUtcLabel() {
18242
18334
  return `${(/* @__PURE__ */ new Date()).toISOString().slice(11, 19)} UTC`;
18243
18335
  }
18244
18336
  function investigateSleep(ms) {
18245
- return new Promise((resolve2) => setTimeout(resolve2, ms));
18337
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
18246
18338
  }
18247
18339
  async function classifyInvestigateCommand(text) {
18248
18340
  const needsUsername = /@[A-Za-z0-9_]{1,64}(?:\s|$)/.test(text);
@@ -19494,6 +19586,40 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
19494
19586
  required: ["chat_id", "text"]
19495
19587
  }
19496
19588
  },
19589
+ {
19590
+ name: "telegram.upload_file",
19591
+ description: "Send a file from the agent project dir to a Telegram chat. Use this for images, PDFs, reports, and any binary deliverable the agent generates (e.g. an image returned by generate_image: download its URL to a file under ~/.augmented/<codeName>/project/ first, then upload it here). Images (png/jpg/gif/webp) are sent as photos with an inline preview; everything else is sent as a document. The path must be inside the agent project dir.",
19592
+ inputSchema: {
19593
+ type: "object",
19594
+ properties: {
19595
+ path: {
19596
+ type: "string",
19597
+ description: "Absolute path to the local file. Must be within the agent project dir (~/.augmented/<codeName>/project/)."
19598
+ },
19599
+ chat_id: {
19600
+ type: "string",
19601
+ description: "Telegram chat ID to send the file to (from the chat_id attribute in the <channel> tag)"
19602
+ },
19603
+ caption: {
19604
+ type: "string",
19605
+ description: "Optional caption shown with the file (plain text, up to 1024 chars)"
19606
+ },
19607
+ reply_to_message_id: {
19608
+ type: "string",
19609
+ description: "Optional Telegram message_id to send the file as a quote-reply to"
19610
+ },
19611
+ as_document: {
19612
+ type: "boolean",
19613
+ description: "Force sending as an uncompressed document even for images (preserves full quality / original file). Default: images are sent as photos, everything else as a document."
19614
+ },
19615
+ filename: {
19616
+ type: "string",
19617
+ description: "Override the filename shown in Telegram (defaults to the basename of path)"
19618
+ }
19619
+ },
19620
+ required: ["path", "chat_id"]
19621
+ }
19622
+ },
19497
19623
  {
19498
19624
  name: "telegram.download_attachment",
19499
19625
  description: "Download an inbound Telegram attachment on demand. Use this for non-image files (PDFs, docx, voice, video, audio, animations) that arrived via a <channel> tag \u2014 look for `file_id` in the `files` meta. Requires the `chat_id` from the same <channel> tag so the tool can verify the attachment was shared in the current conversation. Returns the local path where the file was written (inside ~/.augmented/<codeName>/telegram-inbound/). Images are auto-downloaded on receipt and don't need this tool.",
@@ -19544,6 +19670,119 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
19544
19670
  if (name === "channel_request_input") {
19545
19671
  return handleChannelRequestInput(args ?? {});
19546
19672
  }
19673
+ if (name === "telegram.upload_file") {
19674
+ const { path, chat_id, caption, reply_to_message_id, as_document, filename } = args;
19675
+ if (!AGENT_CODE_NAME || AGENT_CODE_NAME === "unknown") {
19676
+ return {
19677
+ content: [{
19678
+ type: "text",
19679
+ text: "Upload refused: AGT_AGENT_CODE_NAME not set in the MCP server env. Reprovision the telegram channel so the upload tool can scope paths."
19680
+ }],
19681
+ isError: true
19682
+ };
19683
+ }
19684
+ const allowedRoot = resolve2(homedir4(), ".augmented", AGENT_CODE_NAME, "project") + "/";
19685
+ const resolvedPath = resolve2(path);
19686
+ if (!resolvedPath.startsWith(allowedRoot)) {
19687
+ return {
19688
+ content: [{
19689
+ type: "text",
19690
+ text: `Upload refused: ${resolvedPath} is outside the agent's project dir (${allowedRoot}). Move the file under that dir first.`
19691
+ }],
19692
+ isError: true
19693
+ };
19694
+ }
19695
+ if (ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chat_id)) {
19696
+ return {
19697
+ content: [{ type: "text", text: `Chat ${chat_id} is not in TELEGRAM_ALLOWED_CHATS` }],
19698
+ isError: true
19699
+ };
19700
+ }
19701
+ const peerActiveOnChat = PEER_CLASSIFIER_CONFIG.peer_agent_mode !== "off" && PEER_CLASSIFIER_CONFIG.peer_group_ids.length > 0 && PEER_CLASSIFIER_CONFIG.peer_group_ids.includes(chat_id);
19702
+ if (peerActiveOnChat) {
19703
+ if (PEER_DISABLED_GLOBAL) {
19704
+ process.stderr.write(
19705
+ `telegram-channel(${AGENT_CODE_NAME}): outgress_blocked reason=peer_disabled_global tool=upload_file chat=${redactId(chat_id)}
19706
+ `
19707
+ );
19708
+ return {
19709
+ content: [{ type: "text", text: "Multi-agent collaboration is disabled (TELEGRAM_PEER_DISABLED). Cannot send files on this chat." }],
19710
+ isError: true
19711
+ };
19712
+ }
19713
+ if (peerPresence.shouldDropOutgress(chat_id, Date.now()) === "no_human_recent") {
19714
+ process.stderr.write(
19715
+ `telegram-channel(${AGENT_CODE_NAME}): outgress_blocked reason=no_human_recent tool=upload_file chat=${redactId(chat_id)}
19716
+ `
19717
+ );
19718
+ return {
19719
+ content: [{ type: "text", text: "No human message in this chat in the last 30 minutes. Peer-agent collaboration is paused for this chat until a human posts." }],
19720
+ isError: true
19721
+ };
19722
+ }
19723
+ }
19724
+ let realPath;
19725
+ try {
19726
+ realPath = realpathSync(resolvedPath);
19727
+ } catch (err) {
19728
+ return { content: [{ type: "text", text: `Failed to read file: ${err.message}` }], isError: true };
19729
+ }
19730
+ let realRoot = allowedRoot;
19731
+ try {
19732
+ realRoot = realpathSync(resolve2(homedir4(), ".augmented", AGENT_CODE_NAME, "project")) + "/";
19733
+ } catch {
19734
+ }
19735
+ if (!realPath.startsWith(realRoot)) {
19736
+ return {
19737
+ content: [{
19738
+ type: "text",
19739
+ text: `Upload refused: ${resolvedPath} resolves (via a symlink) outside the agent's project dir. Move a real file under ${allowedRoot}.`
19740
+ }],
19741
+ isError: true
19742
+ };
19743
+ }
19744
+ let bytes;
19745
+ let size;
19746
+ try {
19747
+ const st = statSync2(realPath);
19748
+ if (!st.isFile()) {
19749
+ return { content: [{ type: "text", text: `Upload refused: ${resolvedPath} is not a regular file.` }], isError: true };
19750
+ }
19751
+ size = st.size;
19752
+ if (size === 0) {
19753
+ return { content: [{ type: "text", text: "Upload refused: the file is empty." }], isError: true };
19754
+ }
19755
+ if (size > TELEGRAM_DOCUMENT_MAX_BYTES) {
19756
+ return {
19757
+ content: [{ type: "text", text: `Upload refused: the file is ${(size / 1024 / 1024).toFixed(1)} MB; Telegram's bot upload limit is 50 MB.` }],
19758
+ isError: true
19759
+ };
19760
+ }
19761
+ bytes = readFileSync11(realPath);
19762
+ } catch (err) {
19763
+ return { content: [{ type: "text", text: `Failed to read file: ${err.message}` }], isError: true };
19764
+ }
19765
+ const fname = (filename ?? basename(resolvedPath)).trim() || basename(resolvedPath);
19766
+ const { method, fileField, contentType } = chooseTelegramUploadMethod({
19767
+ ext: extname(fname),
19768
+ sizeBytes: size,
19769
+ asDocument: as_document
19770
+ });
19771
+ const fields = { chat_id };
19772
+ if (typeof caption === "string" && caption.trim().length > 0) fields.caption = caption.slice(0, 1024);
19773
+ if (reply_to_message_id) fields.reply_to_message_id = reply_to_message_id;
19774
+ try {
19775
+ const resp = await sendTelegramFileMultipart(method, fileField, { filename: fname, contentType, bytes }, fields, 6e4);
19776
+ if (!resp.ok) {
19777
+ return { content: [{ type: "text", text: `Telegram ${method} failed: ${resp.description ?? "unknown"}` }], isError: true };
19778
+ }
19779
+ return {
19780
+ content: [{ type: "text", text: `Sent ${fname} (${(size / 1024).toFixed(0)} KB) to chat ${chat_id} as a ${fileField === "photo" ? "photo" : "document"}.` }]
19781
+ };
19782
+ } catch (err) {
19783
+ return { content: [{ type: "text", text: `Telegram ${method} error: ${err.message}` }], isError: true };
19784
+ }
19785
+ }
19547
19786
  if (name === "telegram.reply" || name === "telegram.send_message") {
19548
19787
  const { chat_id, text, reply_to_message_id, interim } = args;
19549
19788
  if (ALLOWED_CHATS.size > 0 && !ALLOWED_CHATS.has(chat_id)) {
@@ -20158,7 +20397,7 @@ var telegramHttp = {
20158
20397
  return { file_path: resp.result.file_path, file_size: resp.result.file_size };
20159
20398
  },
20160
20399
  async fetchBytes(filePath, signal, maxBytes) {
20161
- return new Promise((resolve2, reject) => {
20400
+ return new Promise((resolve3, reject) => {
20162
20401
  const req = https.request(
20163
20402
  {
20164
20403
  hostname: "api.telegram.org",
@@ -20202,7 +20441,7 @@ var telegramHttp = {
20202
20441
  chunks.push(c);
20203
20442
  });
20204
20443
  res.on("end", () => {
20205
- if (!aborted2) resolve2(Buffer.concat(chunks));
20444
+ if (!aborted2) resolve3(Buffer.concat(chunks));
20206
20445
  });
20207
20446
  res.on("error", reject);
20208
20447
  }
@@ -20747,7 +20986,7 @@ async function pollLoop() {
20747
20986
  }
20748
20987
  }
20749
20988
  function sleep2(ms) {
20750
- return new Promise((resolve2) => setTimeout(resolve2, ms).unref());
20989
+ return new Promise((resolve3) => setTimeout(resolve3, ms).unref());
20751
20990
  }
20752
20991
  function shutdown(reason) {
20753
20992
  if (isShuttingDown) return;
@@ -36,7 +36,7 @@ import {
36
36
  writeDirectChatSessionState,
37
37
  writeEgressAllowlist,
38
38
  writePersistentClaudeWrapper
39
- } from "./chunk-JW6CWBNF.js";
39
+ } from "./chunk-RH4FTCZK.js";
40
40
  import "./chunk-XWVM4KPK.js";
41
41
  export {
42
42
  EGRESS_BASELINE_DOMAINS,
@@ -77,4 +77,4 @@ export {
77
77
  writeEgressAllowlist,
78
78
  writePersistentClaudeWrapper
79
79
  };
80
- //# sourceMappingURL=persistent-session-ZQDTWV3D.js.map
80
+ //# sourceMappingURL=persistent-session-FMFTNDJO.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-JW6CWBNF.js";
3
+ } from "./chunk-RH4FTCZK.js";
4
4
  import "./chunk-XWVM4KPK.js";
5
5
 
6
6
  // src/lib/responsiveness-probe.ts
@@ -418,4 +418,4 @@ export {
418
418
  readAndResetSlackReplyBindingClassifications,
419
419
  readAndResetSlackReplyTargetClassifications
420
420
  };
421
- //# sourceMappingURL=responsiveness-probe-74SDDDGN.js.map
421
+ //# sourceMappingURL=responsiveness-probe-FPTBS6RZ.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.269",
3
+ "version": "0.28.270",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {