@integrity-labs/agt-cli 0.28.131 → 0.28.133

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.
package/dist/bin/agt.js CHANGED
@@ -37,7 +37,7 @@ import {
37
37
  success,
38
38
  table,
39
39
  warn
40
- } from "../chunk-N5N3CA6W.js";
40
+ } from "../chunk-L3WP7Y5L.js";
41
41
  import {
42
42
  CHANNEL_REGISTRY,
43
43
  DEPLOYMENT_TEMPLATES,
@@ -4777,7 +4777,7 @@ import { execFileSync, execSync } from "child_process";
4777
4777
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4778
4778
  import chalk18 from "chalk";
4779
4779
  import ora16 from "ora";
4780
- var cliVersion = true ? "0.28.131" : "dev";
4780
+ var cliVersion = true ? "0.28.133" : "dev";
4781
4781
  async function fetchLatestVersion() {
4782
4782
  const host2 = getHost();
4783
4783
  if (!host2) return null;
@@ -5791,7 +5791,7 @@ function handleError(err) {
5791
5791
  }
5792
5792
 
5793
5793
  // src/bin/agt.ts
5794
- var cliVersion2 = true ? "0.28.131" : "dev";
5794
+ var cliVersion2 = true ? "0.28.133" : "dev";
5795
5795
  var program = new Command();
5796
5796
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
5797
5797
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -4577,7 +4577,23 @@ function provisionStopHook(codeName) {
4577
4577
  'SL_PENDING=$(pending_markers_count "$SL_MARKER_DIR")',
4578
4578
  'MS_PENDING=$(pending_markers_count "$MS_MARKER_DIR")',
4579
4579
  'if [ "$TG_PENDING" = "0" ] && [ "$SL_PENDING" = "0" ] && [ "$MS_PENDING" = "0" ]; then exit 0; fi',
4580
- `LAST_ASSISTANT=$(tail -200 "$TRANSCRIPT_PATH" | jq -cs '[.[] | select(.type == "assistant" or .role == "assistant")] | last // empty' 2>/dev/null || true)`,
4580
+ "# ENG-6727 (failure mode 3 \u2014 hook slowness): read the transcript tail ONCE",
4581
+ "# into a temp file and reuse it for every jq pass below (last-assistant text,",
4582
+ "# channel-tag scan, and the per-source recency check). The old hook re-ran",
4583
+ '# `tail -400 "$TRANSCRIPT_PATH" | jq` three to four times per Stop; on a long',
4584
+ "# session that repeated seek-to-end + pipe over a multi-MB transcript could",
4585
+ "# exceed Claude Code's stop-hook timeout, so the safety net was killed mid-run",
4586
+ '# (vera: "running stop hooks\u2026 0/2 \xB7 3m 15s"). One bounded read, many cheap',
4587
+ "# passes over the small tail, removes that failure mode.",
4588
+ 'RECENT_FILE="$(mktemp "${TMPDIR:-/tmp}/agt-ghost-recent.XXXXXX" 2>/dev/null || true)"',
4589
+ 'if [ -z "$RECENT_FILE" ]; then RECENT_FILE="${AGENT_DIR}/.ghost-recent.$$"; : > "$RECENT_FILE" 2>/dev/null || true; fi',
4590
+ "# Transcript text can contain user/secret content \u2014 keep the snapshot",
4591
+ "# owner-only (mktemp is already 0600; this covers the umask-dependent",
4592
+ "# fallback path), matching atomic_write_payload's posture.",
4593
+ 'chmod 600 "$RECENT_FILE" 2>/dev/null || true',
4594
+ `trap 'rm -f "$RECENT_FILE" 2>/dev/null || true' EXIT`,
4595
+ 'tail -400 "$TRANSCRIPT_PATH" > "$RECENT_FILE" 2>/dev/null || true',
4596
+ `LAST_ASSISTANT=$(jq -cs '[.[] | select(.type == "assistant" or .role == "assistant")] | last // empty' "$RECENT_FILE" 2>/dev/null || true)`,
4581
4597
  'if [ -z "$LAST_ASSISTANT" ] || [ "$LAST_ASSISTANT" = "null" ]; then exit 0; fi',
4582
4598
  "# Assistant content is array-shaped today; normalize anyway so a shape",
4583
4599
  "# change can never resurrect the swallowed-jq-error no-op (ENG-6288).",
@@ -4608,7 +4624,13 @@ function provisionStopHook(codeName) {
4608
4624
  "# thread_context can truncate the match early, but every scalar attr we read",
4609
4625
  "# (source/channel/thread_ts/message_ts/chat_id/conversation_id) precedes",
4610
4626
  "# thread_context, so the truncated tag still carries them.",
4611
- `CHANNEL_TAG=$(tail -400 "$TRANSCRIPT_PATH" | jq -r '${jqNormalizeContent} | map(select(type == "object" and .type == "text") | .text | gsub("[\\n\\r]+"; " ")) | join(" ")' 2>/dev/null | grep -oE '<channel [^>]+>' | tail -1 || true)`,
4627
+ "# ENG-6727: skip assistant-authored records when scanning for the inbound",
4628
+ "# <channel> tag. If the agent quotes/summarizes a <channel ...> tag in its",
4629
+ "# own final text, that assistant tag could otherwise become CHANNEL_TAG and",
4630
+ "# correlate recovery/block against the wrong conversation. Exclude assistant",
4631
+ "# turns (not an allowlist of user/notification, which could miss a real",
4632
+ "# notification record shape).",
4633
+ `CHANNEL_TAG=$(jq -r 'select((.type // "") != "assistant" and (.role // "") != "assistant") | ${jqNormalizeContent} | map(select(type == "object" and .type == "text") | .text | gsub("[\\n\\r]+"; " ")) | join(" ")' "$RECENT_FILE" 2>/dev/null | grep -oE '<channel [^>]+>' | tail -1 || true)`,
4612
4634
  'TAG_SOURCE=""',
4613
4635
  `if [ -n "$CHANNEL_TAG" ]; then TAG_SOURCE=$(echo "$CHANNEL_TAG" | grep -oE 'source="[^"]+"' | head -1 | sed 's/source="\\(.*\\)"/\\1/' || true); fi`,
4614
4636
  "# ENG-6567: one context line per Stop that reached here (non-empty assistant",
@@ -4635,22 +4657,45 @@ function provisionStopHook(codeName) {
4635
4657
  "# Sanitize an ID the same way the channel servers do (safeMarkerName /",
4636
4658
  "# safeSlackMarkerName): replace anything outside [A-Za-z0-9_-] with `_`.",
4637
4659
  `safe_id() { echo -n "$1" | sed -E 's|[^A-Za-z0-9_-]|_|g'; }`,
4660
+ '# ENG-6727 (failure mode 1 \u2014 over-suppression): per-conversation "did the',
4661
+ '# agent reply to THIS conversation in the final turn?" checks. The old',
4662
+ "# same-turn guards grepped TOOL_NAMES for ANY reply tool, so a multi-thread",
4663
+ "# agent that replied to thread B suppressed the owed reply/block for thread A",
4664
+ "# (the chronic sherlock symptom). These scope the check to the conversation",
4665
+ "# keyed by the inbound tag, comparing the reply tool's input against it.",
4666
+ '# Output "yes"/"no" (never a jq nonzero exit) to stay clear of the ERR trap.',
4667
+ "replied_this_conv_slack() {",
4668
+ ' local channel="$1" thread_ts="$2" hit',
4669
+ ` hit=$(printf '%s' "$LAST_ASSISTANT" | jq -r --arg ch "$channel" --arg th "$thread_ts" '(.message.content // .content // []) | (if type=="array" then . else [] end) | (if any(.[]; (type=="object") and (.type=="tool_use") and ((.name|tostring)|test("(^|__)slack[._]reply$")) and ((.input.channel // "") as $c | (.input.thread_ts // "") as $t | (.input.message_ts // "") as $m | (($th != "" and ($t==$th or $m==$th)) or ($th=="" and $c!="" and $c==$ch)))) then "yes" else "no" end)' 2>/dev/null || echo no)`,
4670
+ ' [ "$hit" = "yes" ]',
4671
+ "}",
4672
+ "replied_this_conv_telegram() {",
4673
+ ' local chat_id="$1" hit',
4674
+ ` hit=$(printf '%s' "$LAST_ASSISTANT" | jq -r --arg cid "$chat_id" '(.message.content // .content // []) | (if type=="array" then . else [] end) | (if any(.[]; (type=="object") and (.type=="tool_use") and ((.name|tostring)|test("(^|__)telegram[._](reply|send_message)$")) and (((.input.chat_id // "")|tostring)==$cid)) then "yes" else "no" end)' 2>/dev/null || echo no)`,
4675
+ ' [ "$hit" = "yes" ]',
4676
+ "}",
4677
+ "replied_this_conv_teams() {",
4678
+ ' local conversation_id="$1" hit',
4679
+ ` hit=$(printf '%s' "$LAST_ASSISTANT" | jq -r --arg cid "$conversation_id" '(.message.content // .content // []) | (if type=="array" then . else [] end) | (if any(.[]; (type=="object") and (.type=="tool_use") and ((.name|tostring)|test("(^|__)teams[._]reply$")) and (((.input.conversation_id // "")|tostring)==$cid)) then "yes" else "no" end)' 2>/dev/null || echo no)`,
4680
+ ' [ "$hit" = "yes" ]',
4681
+ "}",
4638
4682
  "recover_telegram_for() {",
4639
4683
  ' local chat_id="$1" msg_id="$2"',
4640
4684
  ' if [ -z "$chat_id" ] || [ -z "$msg_id" ]; then return; fi',
4641
- " # If the agent DID call telegram.reply this turn, no recovery needed.",
4642
- " # ENG-6405: match the namespaced transcript form (mcp__telegram__telegram_reply)",
4643
- " # as ENG-6387 did for Slack \u2014 the bare `^telegram\\.reply$` never matched a real",
4644
- " # transcript, so this same-turn guard was dead (masked only by the MCP-side",
4645
- " # marker clear). The `(^|__)` + `[._]` form catches both shapes.",
4646
- ` if echo "$TOOL_NAMES" | grep -qE '(^|__)telegram[._](reply|send_message)$'; then return; fi`,
4647
- " # ENG-6405 \u2014 correlation + fail-silent parity with Slack (ENG-6387). If the",
4648
- " # agent has telegram.replied/sent to a DIFFERENT chat_id anywhere in the recent",
4649
- " # window, it is conversing elsewhere and TEXT is not a reliably-correlated ghost",
4650
- " # of THIS inbound \u2014 recovering it would post mis-correlated text. Stay silent: a",
4651
- " # silent gap the operator re-pings beats wrong content that poisons trust.",
4685
+ " # ENG-6727 (mode 1): scoped same-turn guard. Suppress only if the agent",
4686
+ " # replied to THIS chat in the final turn (a final reply clears the marker",
4687
+ " # anyway; this catches the interim-ack case where the marker is downgraded",
4688
+ " # to seen, not deleted). A reply to a DIFFERENT chat no longer suppresses \u2014",
4689
+ " # marker existence + the recency guard below decide that. Was a global grep",
4690
+ " # over TOOL_NAMES that dropped owed replies on multi-chat agents.",
4691
+ ' if replied_this_conv_telegram "$chat_id"; then log_ghost "telegram skip=replied_this_turn chat=$(safe_id "$chat_id")"; return; fi',
4692
+ " # ENG-6405 / ENG-6467 \u2014 recency-aware koda suppression (KEPT). If the agent",
4693
+ " # has telegram.replied/sent to a DIFFERENT chat_id AFTER this inbound tag,",
4694
+ " # it moved on and TEXT is not a reliably-correlated ghost of THIS inbound \u2014",
4695
+ " # recovering it would post mis-correlated text. Stay silent: a silent gap the",
4696
+ " # operator re-pings beats wrong content that poisons trust.",
4652
4697
  " local replied_other",
4653
- ` replied_other=$(tail -400 "$TRANSCRIPT_PATH" 2>/dev/null | jq -s --arg cid "$chat_id" 'def ctext: (.message.content // .content // []) | if type=="string" then . elif type=="array" then (map(select(type=="object" and .type=="text")|.text)|join(" ")) else "" end; . as $all | ([ range(0; ($all|length)) | select(($all[.]|ctext)|test("<channel ")) ] | last) as $idx | [ $all[(($idx // -1)+1):][] | select((.type // .role)=="assistant") | (.message.content // .content // []) | (if type=="array" then . else [] end) | .[] | select((.type=="tool_use") and ((.name|tostring)|test("telegram[._](reply|send_message)$")) and (((.input.chat_id // "")) as $c | ($c != $cid and $c != ""))) ] | length' 2>/dev/null || true)`,
4698
+ ` replied_other=$(jq -s --arg cid "$chat_id" 'def ctext: (.message.content // .content // []) | if type=="string" then . elif type=="array" then (map(select(type=="object" and .type=="text")|.text)|join(" ")) else "" end; . as $all | ([ range(0; ($all|length)) | select((($all[.]|ctext)|test("<channel ")) and (($all[.]|(.type // .role // "")) != "assistant")) ] | last) as $idx | [ $all[(($idx // -1)+1):][] | select((.type // .role)=="assistant") | (.message.content // .content // []) | (if type=="array" then . else [] end) | .[] | select((.type=="tool_use") and ((.name|tostring)|test("telegram[._](reply|send_message)$")) and (((.input.chat_id // "")|tostring) as $c | ($c != $cid and $c != ""))) ] | length' "$RECENT_FILE" 2>/dev/null || true)`,
4654
4699
  ' if [ "${replied_other:-0}" -gt 0 ] 2>/dev/null; then return; fi',
4655
4700
  " local marker_name",
4656
4701
  ' marker_name="$(safe_id "$chat_id")__$(safe_id "$msg_id").json"',
@@ -4667,14 +4712,15 @@ function provisionStopHook(codeName) {
4667
4712
  "recover_slack_for() {",
4668
4713
  ' local channel="$1" thread_ts="$2"',
4669
4714
  ' if [ -z "$channel" ]; then return; fi',
4670
- " # If the agent DID call slack.reply this turn, no recovery needed.",
4671
- " # ENG-6387: match the namespaced transcript form mcp__slack__slack_reply",
4672
- " # (Claude Code rewrites the registered `slack.reply` tool name to",
4673
- " # `mcp__slack__slack_reply`). The bare `^slack\\.reply$` never matched a",
4674
- " # real transcript, so this same-turn guard was dead in prod and only",
4675
- " # masked by the MCP-side marker clear.",
4676
- ` if echo "$TOOL_NAMES" | grep -qE '(^|__)slack[._]reply$'; then log_ghost "slack skip=replied_this_turn thread=$(safe_id "$thread_ts")"; return; fi`,
4677
- " # ENG-6387 plus ENG-6467: recency-aware correlation + fail-silent. A reply and",
4715
+ " # ENG-6727 (mode 1): scoped same-turn guard. Suppress only if the agent",
4716
+ " # replied to THIS thread/channel in the final turn (a final reply clears the",
4717
+ " # marker anyway; this catches the interim-ack case where the marker is",
4718
+ " # downgraded to seen, not deleted). A reply to a DIFFERENT thread no longer",
4719
+ " # suppresses here \u2014 marker existence + the recency guard below decide that.",
4720
+ " # Was a global grep over TOOL_NAMES (any slack.reply), which dropped owed",
4721
+ " # replies on multi-thread agents (the chronic sherlock symptom).",
4722
+ ' if replied_this_conv_slack "$channel" "$thread_ts"; then log_ghost "slack skip=replied_this_turn thread=$(safe_id "$thread_ts")"; return; fi',
4723
+ " # ENG-6387 plus ENG-6467: recency-aware correlation + fail-silent (KEPT). A reply and",
4678
4724
  " # trailing narration can straddle turns, so a single-turn check is not",
4679
4725
  " # enough. ENG-6387 bailed on a reply to ANY other thread ANYWHERE in the",
4680
4726
  " # window; a multi-thread agent (sherlock) satisfies that on nearly every",
@@ -4686,7 +4732,7 @@ function provisionStopHook(codeName) {
4686
4732
  " # so answering an older thread then this one recovers correctly. Silence on",
4687
4733
  " # a genuine move-on still beats posting mis-correlated text.",
4688
4734
  " local replied_other",
4689
- ` replied_other=$(tail -400 "$TRANSCRIPT_PATH" 2>/dev/null | jq -s --arg th "$thread_ts" 'def ctext: (.message.content // .content // []) | if type=="string" then . elif type=="array" then (map(select(type=="object" and .type=="text")|.text)|join(" ")) else "" end; . as $all | ([ range(0; ($all|length)) | select(($all[.]|ctext)|test("<channel ")) ] | last) as $idx | [ $all[(($idx // -1)+1):][] | select((.type // .role)=="assistant") | (.message.content // .content // []) | (if type=="array" then . else [] end) | .[] | select((.type=="tool_use") and ((.name|tostring)|test("slack[._]reply$")) and (((.input.thread_ts // .input.message_ts // "")) as $t | ($t != $th and $t != ""))) ] | length' 2>/dev/null || true)`,
4735
+ ` replied_other=$(jq -s --arg th "$thread_ts" 'def ctext: (.message.content // .content // []) | if type=="string" then . elif type=="array" then (map(select(type=="object" and .type=="text")|.text)|join(" ")) else "" end; . as $all | ([ range(0; ($all|length)) | select((($all[.]|ctext)|test("<channel ")) and (($all[.]|(.type // .role // "")) != "assistant")) ] | last) as $idx | [ $all[(($idx // -1)+1):][] | select((.type // .role)=="assistant") | (.message.content // .content // []) | (if type=="array" then . else [] end) | .[] | select((.type=="tool_use") and ((.name|tostring)|test("slack[._]reply$")) and (((.input.thread_ts // .input.message_ts // "")) as $t | ($t != $th and $t != ""))) ] | length' "$RECENT_FILE" 2>/dev/null || true)`,
4690
4736
  ' if [ "${replied_other:-0}" -gt 0 ] 2>/dev/null; then log_ghost "slack skip=moved_on_after_tag thread=$(safe_id "$thread_ts")"; return; fi',
4691
4737
  " # Find any marker for this channel+thread (in busy threads multiple",
4692
4738
  " # message_ts entries can be pending \u2014 recover the oldest, that's the",
@@ -4709,17 +4755,17 @@ function provisionStopHook(codeName) {
4709
4755
  "recover_teams_for() {",
4710
4756
  ' local conversation_id="$1" reply_to_id="$2" service_url="$3"',
4711
4757
  ' if [ -z "$conversation_id" ] || [ -z "$service_url" ]; then return; fi',
4712
- " # If the agent DID call teams.reply this turn, no recovery needed.",
4713
- " # ENG-6405: namespaced transcript form (mcp__teams__teams_reply); the bare",
4714
- " # `^teams\\.reply$` was dead (masked by the MCP-side marker clear), same bug",
4715
- " # ENG-6387 fixed for Slack.",
4716
- ` if echo "$TOOL_NAMES" | grep -qE '(^|__)teams[._]reply$'; then return; fi`,
4717
- " # ENG-6405 \u2014 correlation + fail-silent parity. If the agent has teams.replied",
4718
- " # to a DIFFERENT conversation_id in the recent window, TEXT is not a reliably-",
4719
- " # correlated ghost of THIS inbound \u2014 stay silent rather than post mis-correlated",
4720
- " # text (silence the operator re-pings beats wrong content that poisons trust).",
4758
+ " # ENG-6727 (mode 1): scoped same-turn guard. Suppress only if the agent",
4759
+ " # replied to THIS conversation in the final turn. A reply to a DIFFERENT",
4760
+ " # conversation no longer suppresses \u2014 marker existence + the recency guard",
4761
+ " # below decide that. Was a global grep over TOOL_NAMES.",
4762
+ ' if replied_this_conv_teams "$conversation_id"; then log_ghost "teams skip=replied_this_turn conv=$(safe_id "$conversation_id")"; return; fi',
4763
+ " # ENG-6405 / ENG-6467 \u2014 recency-aware koda suppression (KEPT). If the agent has",
4764
+ " # teams.replied to a DIFFERENT conversation_id AFTER this inbound tag, TEXT is",
4765
+ " # not a reliably-correlated ghost of THIS inbound \u2014 stay silent rather than post",
4766
+ " # mis-correlated text (silence the operator re-pings beats wrong content).",
4721
4767
  " local replied_other",
4722
- ` replied_other=$(tail -400 "$TRANSCRIPT_PATH" 2>/dev/null | jq -s --arg cid "$conversation_id" 'def ctext: (.message.content // .content // []) | if type=="string" then . elif type=="array" then (map(select(type=="object" and .type=="text")|.text)|join(" ")) else "" end; . as $all | ([ range(0; ($all|length)) | select(($all[.]|ctext)|test("<channel ")) ] | last) as $idx | [ $all[(($idx // -1)+1):][] | select((.type // .role)=="assistant") | (.message.content // .content // []) | (if type=="array" then . else [] end) | .[] | select((.type=="tool_use") and ((.name|tostring)|test("teams[._]reply$")) and (((.input.conversation_id // "")) as $c | ($c != $cid and $c != ""))) ] | length' 2>/dev/null || true)`,
4768
+ ` replied_other=$(jq -s --arg cid "$conversation_id" 'def ctext: (.message.content // .content // []) | if type=="string" then . elif type=="array" then (map(select(type=="object" and .type=="text")|.text)|join(" ")) else "" end; . as $all | ([ range(0; ($all|length)) | select((($all[.]|ctext)|test("<channel ")) and (($all[.]|(.type // .role // "")) != "assistant")) ] | last) as $idx | [ $all[(($idx // -1)+1):][] | select((.type // .role)=="assistant") | (.message.content // .content // []) | (if type=="array" then . else [] end) | .[] | select((.type=="tool_use") and ((.name|tostring)|test("teams[._]reply$")) and (((.input.conversation_id // "")) as $c | ($c != $cid and $c != ""))) ] | length' "$RECENT_FILE" 2>/dev/null || true)`,
4723
4769
  ' if [ "${replied_other:-0}" -gt 0 ] 2>/dev/null; then return; fi',
4724
4770
  " # teams-channel.ts encodes the marker filename as",
4725
4771
  " # hex(conversation_id)[..64]--hex(activity_id)[..64].json",
@@ -4762,16 +4808,21 @@ function provisionStopHook(codeName) {
4762
4808
  "# GC stale ledger entries (>1 day) so the per-marker cap dir cannot grow unbounded.",
4763
4809
  'if [ -d "$BLOCK_LEDGER_DIR" ]; then find "$BLOCK_LEDGER_DIR" -type f -mtime +1 -delete 2>/dev/null || true; fi',
4764
4810
  "# Returns 0 (after printing the block JSON to stdout) when it blocked; 1 otherwise.",
4765
- "# $1 = reply-tool name regex (same shape the recover_* same-turn guards use)",
4811
+ '# $1 = "yes"/"no" \u2014 did the agent reply to THIS conversation in the final turn?',
4812
+ "# (ENG-6727: per-conversation, computed by the caller via replied_this_conv_*)",
4766
4813
  "# $2 = newline-separated candidate marker paths for the tagged conversation",
4767
4814
  "# $3 = reply-tool hint shown to the model in the block reason",
4768
4815
  "emit_block_if_obligated() {",
4769
- ' local reply_re="$1" candidates="$2" tool_hint="$3"',
4816
+ ' local replied_this_conv="$1" candidates="$2" tool_hint="$3"',
4770
4817
  ' if [ "$BLOCK_TURN_END_ON" != "1" ]; then return 1; fi',
4771
4818
  " # Belt: never block during a continuation Claude Code already flagged.",
4772
4819
  ' if [ "$STOP_ACTIVE" = "true" ]; then return 1; fi',
4773
- " # Already replied to THIS conversation this turn \u21D2 nothing owed.",
4774
- ' if echo "$TOOL_NAMES" | grep -qE "$reply_re"; then return 1; fi',
4820
+ " # ENG-6727 (mode 1): already replied to THIS conversation this turn \u21D2 nothing",
4821
+ " # owed. Was a global grep over TOOL_NAMES for ANY reply tool, so a reply to",
4822
+ " # thread B let an owed block for thread A fall through to recovery, which the",
4823
+ " # recency guard then suppressed \u2192 silent loss (the sherlock symptom, since",
4824
+ " # sherlock runs with block-turn-end ON). Now scoped to the tagged conversation.",
4825
+ ' if [ "$replied_this_conv" = "yes" ]; then return 1; fi',
4775
4826
  ' local m d u obligated=""',
4776
4827
  " while IFS= read -r m; do",
4777
4828
  ' if [ -z "$m" ] || [ ! -f "$m" ]; then continue; fi',
@@ -4810,7 +4861,8 @@ function provisionStopHook(codeName) {
4810
4861
  ' MSG_ID=$(extract_attr "$CHANNEL_TAG" "message_id")',
4811
4862
  ' if [ -n "$CHAT_ID" ] && [ -n "$MSG_ID" ]; then',
4812
4863
  ' TG_CAND="${TG_MARKER_DIR}/$(safe_id "$CHAT_ID")__$(safe_id "$MSG_ID").json"',
4813
- ` if emit_block_if_obligated '(^|__)telegram[._](reply|send_message)$' "$TG_CAND" 'telegram.reply'; then exit 0; fi`,
4864
+ ' TG_REPLIED=no; if replied_this_conv_telegram "$CHAT_ID"; then TG_REPLIED=yes; fi',
4865
+ ` if emit_block_if_obligated "$TG_REPLIED" "$TG_CAND" 'telegram.reply'; then exit 0; fi`,
4814
4866
  " fi",
4815
4867
  ' recover_telegram_for "$CHAT_ID" "$MSG_ID"',
4816
4868
  'elif [ "$TAG_SOURCE" = "slack" ]; then',
@@ -4820,7 +4872,8 @@ function provisionStopHook(codeName) {
4820
4872
  ' SL_PREFIX="${SL_MARKER_DIR}/$(safe_id "$CHANNEL")__$(safe_id "$THREAD_TS")__"',
4821
4873
  " shopt -s nullglob",
4822
4874
  ` SL_CAND=$(printf '%s\\n' "$SL_PREFIX"*.json)`,
4823
- ` if emit_block_if_obligated '(^|__)slack[._]reply$' "$SL_CAND" 'slack.reply'; then exit 0; fi`,
4875
+ ' SL_REPLIED=no; if replied_this_conv_slack "$CHANNEL" "$THREAD_TS"; then SL_REPLIED=yes; fi',
4876
+ ` if emit_block_if_obligated "$SL_REPLIED" "$SL_CAND" 'slack.reply'; then exit 0; fi`,
4824
4877
  " fi",
4825
4878
  ' recover_slack_for "$CHANNEL" "$THREAD_TS"',
4826
4879
  'elif [ "$TAG_SOURCE" = "msteams" ]; then',
@@ -4831,7 +4884,8 @@ function provisionStopHook(codeName) {
4831
4884
  ' HEX_CONV=$(printf %s "$CONVERSATION_ID" | od -An -tx1 | tr -d " \\n" | cut -c1-64)',
4832
4885
  " shopt -s nullglob",
4833
4886
  " MS_CAND=$(printf '%s\\n' \"${MS_MARKER_DIR}/${HEX_CONV}\"*.json)",
4834
- ` if emit_block_if_obligated '(^|__)teams[._]reply$' "$MS_CAND" 'teams.reply'; then exit 0; fi`,
4887
+ ' MS_REPLIED=no; if replied_this_conv_teams "$CONVERSATION_ID"; then MS_REPLIED=yes; fi',
4888
+ ` if emit_block_if_obligated "$MS_REPLIED" "$MS_CAND" 'teams.reply'; then exit 0; fi`,
4835
4889
  " fi",
4836
4890
  ' recover_teams_for "$CONVERSATION_ID" "$REPLY_TO_ID" "$SERVICE_URL"',
4837
4891
  'elif [ -z "$TAG_SOURCE" ]; then',
@@ -7692,7 +7746,7 @@ function requireHost() {
7692
7746
  }
7693
7747
 
7694
7748
  // src/lib/api-client.ts
7695
- var agtCliVersion = true ? "0.28.131" : "dev";
7749
+ var agtCliVersion = true ? "0.28.133" : "dev";
7696
7750
  var lastConfigHash = null;
7697
7751
  function setConfigHash(hash) {
7698
7752
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -8989,4 +9043,4 @@ export {
8989
9043
  managerInstallSystemUnitCommand,
8990
9044
  managerUninstallSystemUnitCommand
8991
9045
  };
8992
- //# sourceMappingURL=chunk-N5N3CA6W.js.map
9046
+ //# sourceMappingURL=chunk-L3WP7Y5L.js.map