@integrity-labs/agt-cli 0.28.707 → 0.28.709

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-TPP35O6H.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-7SZRZLPC.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-HPYSFXPP.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-P2VVOI6B.js.map
@@ -54,7 +54,7 @@ import {
54
54
  safeWriteJsonAtomic,
55
55
  setConfigHash,
56
56
  tripClass
57
- } from "../chunk-5U45RQEM.js";
57
+ } from "../chunk-EPBHR2XN.js";
58
58
  import {
59
59
  getProjectDir as getProjectDir2,
60
60
  getReadyTasks,
@@ -112,7 +112,7 @@ import {
112
112
  takeZombieDetection,
113
113
  toOpencodeModel,
114
114
  writeEgressAllowlist
115
- } from "../chunk-326G325O.js";
115
+ } from "../chunk-QKEWEV6X.js";
116
116
  import {
117
117
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
118
118
  AnchorSessionClient,
@@ -197,7 +197,7 @@ import {
197
197
  subagentActivityAgeSeconds,
198
198
  sumTranscriptUsageInWindow,
199
199
  transcriptActivityAgeSeconds
200
- } from "../chunk-QRGNDW6C.js";
200
+ } from "../chunk-NOGQSRQM.js";
201
201
  import {
202
202
  reapOrphanChannelMcps
203
203
  } from "../chunk-XWVM4KPK.js";
@@ -12057,7 +12057,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
12057
12057
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
12058
12058
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
12059
12059
  async function channelInboundActivityAgeSecondsFor(codeName) {
12060
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-3HITHSYU.js");
12060
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-5YVJXKWN.js");
12061
12061
  const newest = newestPendingInboundActivityMtimeMs(dirname10(paneLogPath(codeName)));
12062
12062
  if (newest === null) return null;
12063
12063
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -12705,7 +12705,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
12705
12705
  var lastVersionCheckAt = 0;
12706
12706
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
12707
12707
  var lastResponsivenessProbeAt = 0;
12708
- var agtCliVersion = true ? "0.28.707" : "dev";
12708
+ var agtCliVersion = true ? "0.28.709" : "dev";
12709
12709
  function resolveBrewPath(execFileSync3) {
12710
12710
  try {
12711
12711
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -14232,7 +14232,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
14232
14232
  if (codeNames.length === 0) return;
14233
14233
  void (async () => {
14234
14234
  try {
14235
- const { collectDiagnostics } = await import("../persistent-session-TPP35O6H.js");
14235
+ const { collectDiagnostics } = await import("../persistent-session-7SZRZLPC.js");
14236
14236
  await api.post("/host/heartbeat", {
14237
14237
  host_id: hostId,
14238
14238
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -14350,7 +14350,7 @@ async function pollCycleInner() {
14350
14350
  }
14351
14351
  try {
14352
14352
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
14353
- const { collectDiagnostics } = await import("../persistent-session-TPP35O6H.js");
14353
+ const { collectDiagnostics } = await import("../persistent-session-7SZRZLPC.js");
14354
14354
  const diagCodeNames = [...agentState.persistentSessionAgents];
14355
14355
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
14356
14356
  let tailscaleHostname;
@@ -14519,7 +14519,7 @@ async function pollCycleInner() {
14519
14519
  collectPanelessActivityProbes,
14520
14520
  getResponsivenessIntervalMs,
14521
14521
  occupancyQualificationClassifications
14522
- } = await import("../responsiveness-probe-3HITHSYU.js");
14522
+ } = await import("../responsiveness-probe-5YVJXKWN.js");
14523
14523
  const probeIntervalMs = getResponsivenessIntervalMs();
14524
14524
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
14525
14525
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -14629,7 +14629,7 @@ async function pollCycleInner() {
14629
14629
  collectResponsivenessProbes,
14630
14630
  livePendingInboundOldestAgeSeconds,
14631
14631
  parkPendingInbound
14632
- } = await import("../responsiveness-probe-3HITHSYU.js");
14632
+ } = await import("../responsiveness-probe-5YVJXKWN.js");
14633
14633
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
14634
14634
  const wedgeNow = /* @__PURE__ */ new Date();
14635
14635
  const liveAgents = agentState.persistentSessionAgents;
@@ -14785,7 +14785,7 @@ async function pollCycleInner() {
14785
14785
  }
14786
14786
  try {
14787
14787
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
14788
- const { probeSessionAuth } = await import("../session-auth-dead-LY5264IN.js");
14788
+ const { probeSessionAuth } = await import("../session-auth-dead-PYWTMI43.js");
14789
14789
  const observations = [];
14790
14790
  const pendingCacheCommits = [];
14791
14791
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -18587,7 +18587,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
18587
18587
  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}`));
18588
18588
  void (async () => {
18589
18589
  try {
18590
- const { collectDiagnostics } = await import("../persistent-session-TPP35O6H.js");
18590
+ const { collectDiagnostics } = await import("../persistent-session-7SZRZLPC.js");
18591
18591
  await api.post("/host/heartbeat", {
18592
18592
  host_id: hostId,
18593
18593
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -18638,7 +18638,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
18638
18638
  }
18639
18639
  try {
18640
18640
  const hostId = await getHostId();
18641
- const { collectDiagnostics } = await import("../persistent-session-TPP35O6H.js");
18641
+ const { collectDiagnostics } = await import("../persistent-session-7SZRZLPC.js");
18642
18642
  await api.post("/host/heartbeat", {
18643
18643
  host_id: hostId,
18644
18644
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -19270,7 +19270,7 @@ async function processClaudePairSessions(agents) {
19270
19270
  killPairSession,
19271
19271
  pairTmuxSession,
19272
19272
  finalizeClaudePairOnboarding
19273
- } = await import("../claude-pair-runtime-HPYSFXPP.js");
19273
+ } = await import("../claude-pair-runtime-P2VVOI6B.js");
19274
19274
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
19275
19275
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
19276
19276
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -7647,6 +7647,31 @@ var init_snooze = __esm({
7647
7647
  });
7648
7648
 
7649
7649
  // ../core/dist/channels/slack-block-kit.js
7650
+ function isFetchableHttpUrl(value) {
7651
+ let parsed;
7652
+ try {
7653
+ parsed = new URL(value);
7654
+ } catch {
7655
+ return false;
7656
+ }
7657
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
7658
+ return false;
7659
+ return parsed.hostname.length > 0;
7660
+ }
7661
+ function isSvgImageUrl(value) {
7662
+ let parsed;
7663
+ try {
7664
+ parsed = new URL(value);
7665
+ } catch {
7666
+ return false;
7667
+ }
7668
+ let pathname = parsed.pathname;
7669
+ try {
7670
+ pathname = decodeURIComponent(pathname);
7671
+ } catch {
7672
+ }
7673
+ return pathname.toLowerCase().endsWith(".svg");
7674
+ }
7650
7675
  function decodeActionId(actionId) {
7651
7676
  const m = ACTION_ID_RE.exec(actionId);
7652
7677
  if (!m)
@@ -7659,11 +7684,12 @@ function isRoutableActionId(actionId) {
7659
7684
  function isLinkButton(element) {
7660
7685
  return typeof element.url === "string" && element.url.length > 0;
7661
7686
  }
7662
- var ACTION_ID_RE, UNROUTABLE_ACTION_ID_MESSAGE;
7687
+ var SVG_IMAGE_URL_MESSAGE, ACTION_ID_RE, UNROUTABLE_ACTION_ID_MESSAGE;
7663
7688
  var init_slack_block_kit = __esm({
7664
7689
  "../core/dist/channels/slack-block-kit.js"() {
7665
7690
  "use strict";
7666
7691
  init_snooze();
7692
+ SVG_IMAGE_URL_MESSAGE = "Slack cannot render SVG in an image element \u2014 use a PNG, JPG or GIF (our mirrored logos at logos.augmented.team are PNGs)";
7667
7693
  ACTION_ID_RE = /^aug:([0-9a-f-]{36}):([A-Za-z0-9_-]{1,200})$/;
7668
7694
  UNROUTABLE_ACTION_ID_MESSAGE = "interactive buttons must be created with the `request_buttons` tool \u2014 it mints the `aug:<callback-id>:<token>` action_id that makes a tap routable. A hand-written action_id renders fine but its click is silently dropped: the agent is never notified and the buttons are never swapped for the chosen value. For a non-interactive button, set `url` to make it a link button.";
7669
7695
  }
@@ -22683,10 +22709,37 @@ function validateContextBlock(b, path, push) {
22683
22709
  push(`${path}.elements`, "context requires non-empty `elements` array");
22684
22710
  return;
22685
22711
  }
22712
+ if (elements.length > SLACK_LIMITS.contextElements) {
22713
+ push(`${path}.elements`, `max ${SLACK_LIMITS.contextElements} context elements (got ${elements.length})`);
22714
+ }
22686
22715
  elements.forEach((el, i) => {
22687
- validateTextObject(el, `${path}.elements[${i}]`, SLACK_LIMITS.sectionTextChars, ["plain_text", "mrkdwn"], push);
22716
+ const elPath = `${path}.elements[${i}]`;
22717
+ const element = el ?? {};
22718
+ if (element.type === "image") {
22719
+ validateContextImageElement(element, elPath, push);
22720
+ return;
22721
+ }
22722
+ validateTextObject(el, elPath, SLACK_LIMITS.sectionTextChars, ["plain_text", "mrkdwn"], push);
22688
22723
  });
22689
22724
  }
22725
+ function validateContextImageElement(element, elPath, push) {
22726
+ if (typeof element.image_url !== "string" || element.image_url.length === 0) {
22727
+ push(`${elPath}.image_url`, "image element requires a non-empty image_url");
22728
+ } else {
22729
+ if (!isFetchableHttpUrl(element.image_url)) {
22730
+ push(`${elPath}.image_url`, "image_url must be an http(s) url Slack can fetch");
22731
+ }
22732
+ if (element.image_url.length > SLACK_LIMITS.imageUrlChars) {
22733
+ push(`${elPath}.image_url`, `max ${SLACK_LIMITS.imageUrlChars} chars in image_url (got ${element.image_url.length})`);
22734
+ }
22735
+ if (isSvgImageUrl(element.image_url)) {
22736
+ push(`${elPath}.image_url`, SVG_IMAGE_URL_MESSAGE);
22737
+ }
22738
+ }
22739
+ if (typeof element.alt_text !== "string" || element.alt_text.trim().length === 0) {
22740
+ push(`${elPath}.alt_text`, "image element requires non-empty alt_text");
22741
+ }
22742
+ }
22690
22743
  function validateActionsBlock(b, path, push) {
22691
22744
  const elements = b.elements;
22692
22745
  if (!Array.isArray(elements) || elements.length === 0) {
@@ -22860,7 +22913,11 @@ var init_slack_block_kit_runtime = __esm({
22860
22913
  buttonLabelChars: 75,
22861
22914
  identifierChars: 255,
22862
22915
  optionValueChars: 2e3,
22863
- headerTextChars: 150
22916
+ headerTextChars: 150,
22917
+ // ENG-9548: context limits, matching core's SLACK_LIMITS. Pinned equal by
22918
+ // slack-context-image-validation.test.ts so the two cannot drift silently.
22919
+ contextElements: 10,
22920
+ imageUrlChars: 3e3
22864
22921
  };
22865
22922
  SUPPORTED_BLOCK_TYPES = ["header", "section", "divider", "context", "actions"];
22866
22923
  SUPPORTED_ACTION_ELEMENTS = ["button"];
@@ -39133,6 +39190,131 @@ function emitUrlAsteriskStripTelemetry(channel, count = 0) {
39133
39190
  }
39134
39191
  }
39135
39192
 
39193
+ // src/markdown-to-mrkdwn.ts
39194
+ function segmentByCode(text) {
39195
+ const segments = [];
39196
+ let plainStart = 0;
39197
+ let i = 0;
39198
+ const pushPlain = (end) => {
39199
+ if (end > plainStart) segments.push({ text: text.slice(plainStart, end), code: false });
39200
+ };
39201
+ while (i < text.length) {
39202
+ if (text[i] !== "`") {
39203
+ i += 1;
39204
+ continue;
39205
+ }
39206
+ let backslashes = 0;
39207
+ for (let j = i - 1; j >= 0 && text[j] === "\\"; j -= 1) backslashes += 1;
39208
+ if (backslashes % 2 === 1) {
39209
+ i += 1;
39210
+ continue;
39211
+ }
39212
+ let runLen = 1;
39213
+ while (text[i + runLen] === "`") runLen += 1;
39214
+ if (runLen >= 3) {
39215
+ const close = text.indexOf("```", i + runLen);
39216
+ const end2 = close === -1 ? text.length : close + 3;
39217
+ pushPlain(i);
39218
+ segments.push({ text: text.slice(i, end2), code: true });
39219
+ plainStart = end2;
39220
+ i = end2;
39221
+ continue;
39222
+ }
39223
+ const afterOpen = i + runLen;
39224
+ const lineEnd = text.indexOf("\n", afterOpen);
39225
+ const windowEnd = lineEnd === -1 ? text.length : lineEnd;
39226
+ const closeIdx = text.slice(afterOpen, windowEnd).indexOf("`".repeat(runLen));
39227
+ if (closeIdx === -1) {
39228
+ i = afterOpen;
39229
+ continue;
39230
+ }
39231
+ const end = afterOpen + closeIdx + runLen;
39232
+ pushPlain(i);
39233
+ segments.push({ text: text.slice(i, end), code: true });
39234
+ plainStart = end;
39235
+ i = end;
39236
+ }
39237
+ pushPlain(text.length);
39238
+ return segments;
39239
+ }
39240
+ var TARGET = String.raw`(<[^<>]*>|[^()\s]+(?:\([^()\s]*\)[^()\s]*)*)`;
39241
+ var TITLE = String.raw`(?:\s+"[^"]*")?`;
39242
+ var WHOLE_LINE_BLANK_IMAGE = new RegExp(
39243
+ String.raw`^[ \t]*!\[[ \t]*\]\(\s*${TARGET}${TITLE}\s*\)[ \t]*\r?\n?`,
39244
+ "gm"
39245
+ );
39246
+ var INLINE_BLANK_IMAGE = new RegExp(
39247
+ String.raw`!\[[ \t]*\]\(\s*${TARGET}${TITLE}\s*\)[ \t]*`,
39248
+ "g"
39249
+ );
39250
+ var IMAGE = new RegExp(String.raw`!\[([^\]]*)\]\(\s*${TARGET}${TITLE}\s*\)`, "g");
39251
+ var LINK = new RegExp(String.raw`(?<!!)\[([^\]]*)\]\(\s*${TARGET}${TITLE}\s*\)`, "g");
39252
+ function normalizeTarget(raw) {
39253
+ let url = raw.trim();
39254
+ const slackWrapped = /^<(https?:\/\/[^|>\s]+)(?:\|[^>]*)?>$/i.exec(url);
39255
+ if (slackWrapped?.[1]) {
39256
+ url = slackWrapped[1];
39257
+ } else if (url.startsWith("<") && url.endsWith(">")) {
39258
+ url = url.slice(1, -1).trim();
39259
+ }
39260
+ if (!/^(?:https?:\/\/|mailto:)/i.test(url)) return null;
39261
+ if (/[\s<>|]/.test(url)) return null;
39262
+ return url;
39263
+ }
39264
+ function escapeLabel(label) {
39265
+ return label.trim().replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
39266
+ }
39267
+ function convertProse(input, tally) {
39268
+ let out = input;
39269
+ out = out.replace(WHOLE_LINE_BLANK_IMAGE, () => {
39270
+ tally.images += 1;
39271
+ return "";
39272
+ });
39273
+ out = out.replace(INLINE_BLANK_IMAGE, () => {
39274
+ tally.images += 1;
39275
+ return "";
39276
+ });
39277
+ out = out.replace(IMAGE, (match, alt, target) => {
39278
+ const label = escapeLabel(alt);
39279
+ const url = normalizeTarget(target);
39280
+ tally.images += 1;
39281
+ if (!url) return label;
39282
+ return `<${url}|${label}>`;
39283
+ });
39284
+ out = out.replace(LINK, (match, label, target) => {
39285
+ const url = normalizeTarget(target);
39286
+ if (!url) return match;
39287
+ tally.links += 1;
39288
+ const clean = escapeLabel(label);
39289
+ return clean ? `<${url}|${clean}>` : url;
39290
+ });
39291
+ return out;
39292
+ }
39293
+ function markdownToMrkdwn(text) {
39294
+ if (!text || !text.includes("](")) {
39295
+ return { text, converted: false, links: 0, images: 0 };
39296
+ }
39297
+ const tally = { links: 0, images: 0 };
39298
+ const out = segmentByCode(text).map((segment) => segment.code ? segment.text : convertProse(segment.text, tally)).join("");
39299
+ const converted = tally.links > 0 || tally.images > 0;
39300
+ return { text: converted ? out : text, converted, links: tally.links, images: tally.images };
39301
+ }
39302
+ function emitMarkdownToMrkdwnTelemetry(channel, links = 0, images = 0) {
39303
+ const agentCode = process.env.AGT_AGENT_CODE_NAME ?? "unknown";
39304
+ try {
39305
+ process.stderr.write(
39306
+ `agt.egress.markdown_to_mrkdwn_converted ${JSON.stringify({
39307
+ channel,
39308
+ agent_code: agentCode,
39309
+ links,
39310
+ images
39311
+ })}
39312
+ `
39313
+ );
39314
+ } catch {
39315
+ }
39316
+ }
39317
+
39136
39318
  // src/slack-pending-inbound-cleanup.ts
39137
39319
  import { existsSync as existsSync4, readdirSync as readdirSync4, statSync as statSync3, unlinkSync } from "fs";
39138
39320
  import { join as join9 } from "path";
@@ -42739,7 +42921,9 @@ async function processSlackRecoveryOutboxFile(filename) {
42739
42921
  if (recoveredGuarded.redacted) emitToolCallMarkupRedactionTelemetry("slack");
42740
42922
  const apiErr = rewriteTransientApiError(recoveredGuarded.text);
42741
42923
  if (apiErr.rewritten) emitTransientApiErrorTelemetry("slack", apiErr.match, apiErr.original);
42742
- const urlFixed = stripUrlWrappingAsterisks(apiErr.text);
42924
+ const mrkdwn = markdownToMrkdwn(apiErr.text);
42925
+ if (mrkdwn.converted) emitMarkdownToMrkdwnTelemetry("slack", mrkdwn.links, mrkdwn.images);
42926
+ const urlFixed = stripUrlWrappingAsterisks(mrkdwn.text);
42743
42927
  if (urlFixed.stripped) emitUrlAsteriskStripTelemetry("slack", urlFixed.count);
42744
42928
  const text = urlFixed.text;
42745
42929
  const ghostReplyMode = resolveGhostReplyMode();
@@ -44911,7 +45095,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
44911
45095
  channel: { type: "string", description: "Slack channel ID" },
44912
45096
  blocks: {
44913
45097
  type: "array",
44914
- description: "Array of Block Kit blocks. Supported block types: header, section, divider, context, actions. Supported interactive elements (inside actions blocks): button. Hard limits: 50 blocks per message, 5 elements per actions block, 3000 chars per section text, 75 chars per button label."
45098
+ description: 'Array of Block Kit blocks. Supported block types: header, section, divider, context, actions. Supported interactive elements (inside actions blocks): button. IMAGES: a context block is the only way to show one \u2014 message text is mrkdwn, which has no inline image syntax, so `![](url)` never renders. Use { type: "image", image_url, alt_text } (alt_text required) alongside an mrkdwn element, one context block per row, since elements flow as a single wrapping line. image_url must be a PNG, JPG or GIF: Slack does not render SVG. For vendor/product logos use our mirror at logos.augmented.team rather than hotlinking the upstream source (upload.wikimedia.org and friends) \u2014 those are the sync scripts\' INPUT, and three had already rotted before the mirror existed. Hard limits: 50 blocks per message, 10 elements per context block, 5 elements per actions block, 3000 chars per section text, 3000 chars per image_url, 75 chars per button label.'
44915
45099
  },
44916
45100
  text: { type: "string", description: "Plain-text fallback for push notifications and unfurls. Required." },
44917
45101
  thread_ts: { type: "string", description: "Thread timestamp for an existing thread reply (from the thread_ts attribute). Safe to omit: for a channel message the server automatically threads your reply off the message you are answering (via message_ts), and a DM reply posts inline." },
@@ -45317,7 +45501,9 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
45317
45501
  if (sanitizedGuarded.redacted) emitToolCallMarkupRedactionTelemetry("slack");
45318
45502
  const apiErr = rewriteTransientApiError(sanitizedGuarded.text);
45319
45503
  if (apiErr.rewritten) emitTransientApiErrorTelemetry("slack", apiErr.match, apiErr.original);
45320
- const urlFixed = stripUrlWrappingAsterisks(apiErr.text);
45504
+ const mrkdwn = markdownToMrkdwn(apiErr.text);
45505
+ if (mrkdwn.converted) emitMarkdownToMrkdwnTelemetry("slack", mrkdwn.links, mrkdwn.images);
45506
+ const urlFixed = stripUrlWrappingAsterisks(mrkdwn.text);
45321
45507
  if (urlFixed.stripped) emitUrlAsteriskStripTelemetry("slack", urlFixed.count);
45322
45508
  try {
45323
45509
  const res = await fetch("https://slack.com/api/chat.postMessage", {
@@ -7647,6 +7647,31 @@ var init_snooze = __esm({
7647
7647
  });
7648
7648
 
7649
7649
  // ../core/dist/channels/slack-block-kit.js
7650
+ function isFetchableHttpUrl(value) {
7651
+ let parsed;
7652
+ try {
7653
+ parsed = new URL(value);
7654
+ } catch {
7655
+ return false;
7656
+ }
7657
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
7658
+ return false;
7659
+ return parsed.hostname.length > 0;
7660
+ }
7661
+ function isSvgImageUrl(value) {
7662
+ let parsed;
7663
+ try {
7664
+ parsed = new URL(value);
7665
+ } catch {
7666
+ return false;
7667
+ }
7668
+ let pathname = parsed.pathname;
7669
+ try {
7670
+ pathname = decodeURIComponent(pathname);
7671
+ } catch {
7672
+ }
7673
+ return pathname.toLowerCase().endsWith(".svg");
7674
+ }
7650
7675
  function decodeActionId(actionId) {
7651
7676
  const m = ACTION_ID_RE.exec(actionId);
7652
7677
  if (!m)
@@ -7659,11 +7684,12 @@ function isRoutableActionId(actionId) {
7659
7684
  function isLinkButton(element) {
7660
7685
  return typeof element.url === "string" && element.url.length > 0;
7661
7686
  }
7662
- var ACTION_ID_RE, UNROUTABLE_ACTION_ID_MESSAGE;
7687
+ var SVG_IMAGE_URL_MESSAGE, ACTION_ID_RE, UNROUTABLE_ACTION_ID_MESSAGE;
7663
7688
  var init_slack_block_kit = __esm({
7664
7689
  "../core/dist/channels/slack-block-kit.js"() {
7665
7690
  "use strict";
7666
7691
  init_snooze();
7692
+ SVG_IMAGE_URL_MESSAGE = "Slack cannot render SVG in an image element \u2014 use a PNG, JPG or GIF (our mirrored logos at logos.augmented.team are PNGs)";
7667
7693
  ACTION_ID_RE = /^aug:([0-9a-f-]{36}):([A-Za-z0-9_-]{1,200})$/;
7668
7694
  UNROUTABLE_ACTION_ID_MESSAGE = "interactive buttons must be created with the `request_buttons` tool \u2014 it mints the `aug:<callback-id>:<token>` action_id that makes a tap routable. A hand-written action_id renders fine but its click is silently dropped: the agent is never notified and the buttons are never swapped for the chosen value. For a non-interactive button, set `url` to make it a link button.";
7669
7695
  }
@@ -22683,10 +22709,37 @@ function validateContextBlock(b, path, push) {
22683
22709
  push(`${path}.elements`, "context requires non-empty `elements` array");
22684
22710
  return;
22685
22711
  }
22712
+ if (elements.length > SLACK_LIMITS.contextElements) {
22713
+ push(`${path}.elements`, `max ${SLACK_LIMITS.contextElements} context elements (got ${elements.length})`);
22714
+ }
22686
22715
  elements.forEach((el, i) => {
22687
- validateTextObject(el, `${path}.elements[${i}]`, SLACK_LIMITS.sectionTextChars, ["plain_text", "mrkdwn"], push);
22716
+ const elPath = `${path}.elements[${i}]`;
22717
+ const element = el ?? {};
22718
+ if (element.type === "image") {
22719
+ validateContextImageElement(element, elPath, push);
22720
+ return;
22721
+ }
22722
+ validateTextObject(el, elPath, SLACK_LIMITS.sectionTextChars, ["plain_text", "mrkdwn"], push);
22688
22723
  });
22689
22724
  }
22725
+ function validateContextImageElement(element, elPath, push) {
22726
+ if (typeof element.image_url !== "string" || element.image_url.length === 0) {
22727
+ push(`${elPath}.image_url`, "image element requires a non-empty image_url");
22728
+ } else {
22729
+ if (!isFetchableHttpUrl(element.image_url)) {
22730
+ push(`${elPath}.image_url`, "image_url must be an http(s) url Slack can fetch");
22731
+ }
22732
+ if (element.image_url.length > SLACK_LIMITS.imageUrlChars) {
22733
+ push(`${elPath}.image_url`, `max ${SLACK_LIMITS.imageUrlChars} chars in image_url (got ${element.image_url.length})`);
22734
+ }
22735
+ if (isSvgImageUrl(element.image_url)) {
22736
+ push(`${elPath}.image_url`, SVG_IMAGE_URL_MESSAGE);
22737
+ }
22738
+ }
22739
+ if (typeof element.alt_text !== "string" || element.alt_text.trim().length === 0) {
22740
+ push(`${elPath}.alt_text`, "image element requires non-empty alt_text");
22741
+ }
22742
+ }
22690
22743
  function validateActionsBlock(b, path, push) {
22691
22744
  const elements = b.elements;
22692
22745
  if (!Array.isArray(elements) || elements.length === 0) {
@@ -22860,7 +22913,11 @@ var init_slack_block_kit_runtime = __esm({
22860
22913
  buttonLabelChars: 75,
22861
22914
  identifierChars: 255,
22862
22915
  optionValueChars: 2e3,
22863
- headerTextChars: 150
22916
+ headerTextChars: 150,
22917
+ // ENG-9548: context limits, matching core's SLACK_LIMITS. Pinned equal by
22918
+ // slack-context-image-validation.test.ts so the two cannot drift silently.
22919
+ contextElements: 10,
22920
+ imageUrlChars: 3e3
22864
22921
  };
22865
22922
  SUPPORTED_BLOCK_TYPES = ["header", "section", "divider", "context", "actions"];
22866
22923
  SUPPORTED_ACTION_ELEMENTS = ["button"];
@@ -43,8 +43,8 @@ import {
43
43
  writeDirectChatSessionState,
44
44
  writeEgressAllowlist,
45
45
  writePersistentClaudeWrapper
46
- } from "./chunk-326G325O.js";
47
- import "./chunk-QRGNDW6C.js";
46
+ } from "./chunk-QKEWEV6X.js";
47
+ import "./chunk-NOGQSRQM.js";
48
48
  import "./chunk-XWVM4KPK.js";
49
49
  export {
50
50
  EGRESS_BASELINE_DOMAINS,
@@ -92,4 +92,4 @@ export {
92
92
  writeEgressAllowlist,
93
93
  writePersistentClaudeWrapper
94
94
  };
95
- //# sourceMappingURL=persistent-session-TPP35O6H.js.map
95
+ //# sourceMappingURL=persistent-session-7SZRZLPC.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-326G325O.js";
4
- import "./chunk-QRGNDW6C.js";
3
+ } from "./chunk-QKEWEV6X.js";
4
+ import "./chunk-NOGQSRQM.js";
5
5
  import "./chunk-XWVM4KPK.js";
6
6
 
7
7
  // src/lib/responsiveness-probe.ts
@@ -745,4 +745,4 @@ export {
745
745
  readAndResetSlackReplyBindingClassifications,
746
746
  readAndResetSlackReplyTargetClassifications
747
747
  };
748
- //# sourceMappingURL=responsiveness-probe-3HITHSYU.js.map
748
+ //# sourceMappingURL=responsiveness-probe-5YVJXKWN.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionTranscriptDir
3
- } from "./chunk-QRGNDW6C.js";
3
+ } from "./chunk-NOGQSRQM.js";
4
4
 
5
5
  // src/lib/session-auth-dead.ts
6
6
  import { closeSync, openSync, readSync, readdirSync, statSync } from "fs";
@@ -203,4 +203,4 @@ export {
203
203
  decideSessionAuthState,
204
204
  probeSessionAuth
205
205
  };
206
- //# sourceMappingURL=session-auth-dead-LY5264IN.js.map
206
+ //# sourceMappingURL=session-auth-dead-PYWTMI43.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.707",
3
+ "version": "0.28.709",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {