@integrity-labs/agt-cli 0.15.8 → 0.15.10
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 +3 -3
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-32D5TUSD.js → chunk-EC6FSHOW.js} +117 -5
- package/dist/{chunk-32D5TUSD.js.map → chunk-EC6FSHOW.js.map} +1 -1
- package/dist/lib/manager-worker.js +348 -177
- package/dist/lib/manager-worker.js.map +1 -1
- package/mcp/slack-channel.js +388 -11
- package/mcp/telegram-channel.js +386 -8
- package/package.json +1 -1
|
@@ -3117,6 +3117,120 @@ function provisionStopHook(codeName) {
|
|
|
3117
3117
|
"exit 0"
|
|
3118
3118
|
].join("\n") + "\n";
|
|
3119
3119
|
writeFileSync4(hookScriptPath, hookScript, { mode: 493 });
|
|
3120
|
+
const ghostHookPath = join4(claudeDir, "agt-ghost-reply-hook.sh");
|
|
3121
|
+
const ghostHookScript = [
|
|
3122
|
+
"#!/bin/bash",
|
|
3123
|
+
"# Auto-generated by Augmented (ENG-4569) \u2014 detects ghost replies and",
|
|
3124
|
+
"# drops recovery files in the channel outbox dirs. Runs on every Stop.",
|
|
3125
|
+
"set -euo pipefail",
|
|
3126
|
+
"INPUT=$(cat)",
|
|
3127
|
+
`TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty')`,
|
|
3128
|
+
'[ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ] && exit 0',
|
|
3129
|
+
`CWD=$(echo "$INPUT" | jq -r '.cwd // empty')`,
|
|
3130
|
+
`CODE_NAME=$(echo "$CWD" | sed -nE 's|.*/\\.augmented/([^/]+)/project/?$|\\1|p')`,
|
|
3131
|
+
'[ -z "$CODE_NAME" ] && exit 0',
|
|
3132
|
+
'AGENT_DIR="$(dirname "$CWD")"',
|
|
3133
|
+
'TG_MARKER_DIR="${AGENT_DIR}/telegram-pending-inbound"',
|
|
3134
|
+
'SL_MARKER_DIR="${AGENT_DIR}/slack-pending-inbound"',
|
|
3135
|
+
"# CodeRabbit ENG-4569 round-3: latest-marker correlation could leak chat",
|
|
3136
|
+
"# A's composed reply into chat B if B arrived later. Now correlate by",
|
|
3137
|
+
"# scanning the transcript for the LAST channel-source <channel ...> tag",
|
|
3138
|
+
"# in user/notification turns and routing recovery to the EXACT marker",
|
|
3139
|
+
"# whose chat_id+message_id (Telegram) / channel+thread_ts (Slack) match.",
|
|
3140
|
+
"# If the tag isn't found, skip recovery \u2014 the timeout will catch it.",
|
|
3141
|
+
"pending_markers_count() {",
|
|
3142
|
+
' local dir="$1"',
|
|
3143
|
+
' [ ! -d "$dir" ] && echo 0 && return',
|
|
3144
|
+
" shopt -s nullglob",
|
|
3145
|
+
' local files=("$dir"/*.json)',
|
|
3146
|
+
' echo "${#files[@]}"',
|
|
3147
|
+
"}",
|
|
3148
|
+
'TG_PENDING=$(pending_markers_count "$TG_MARKER_DIR")',
|
|
3149
|
+
'SL_PENDING=$(pending_markers_count "$SL_MARKER_DIR")',
|
|
3150
|
+
'[ "$TG_PENDING" = "0" ] && [ "$SL_PENDING" = "0" ] && exit 0',
|
|
3151
|
+
`LAST_ASSISTANT=$(tail -200 "$TRANSCRIPT_PATH" | jq -cs '[.[] | select(.type == "assistant" or .role == "assistant")] | last // empty' 2>/dev/null)`,
|
|
3152
|
+
'[ -z "$LAST_ASSISTANT" ] || [ "$LAST_ASSISTANT" = "null" ] && exit 0',
|
|
3153
|
+
`TEXT=$(echo "$LAST_ASSISTANT" | jq -r '(.message.content // .content // []) | map(select(.type == "text") | .text) | join("\\n\\n")' 2>/dev/null)`,
|
|
3154
|
+
"# Strip whitespace and bail only on truly-empty (vs the previous <4-char",
|
|
3155
|
+
'# threshold that dropped legit short replies like "ok", "yes", emoji).',
|
|
3156
|
+
'[ -z "${TEXT//[[:space:]]/}" ] && exit 0',
|
|
3157
|
+
`TOOL_NAMES=$(echo "$LAST_ASSISTANT" | jq -r '(.message.content // .content // []) | map(select(.type == "tool_use") | .name) | .[]' 2>/dev/null || true)`,
|
|
3158
|
+
"# Find the LAST <channel ...> tag in user/notification turns. Channel",
|
|
3159
|
+
"# notifications are forwarded into the session as text containing this",
|
|
3160
|
+
"# tag (slack-channel.ts:1247 / telegram-channel.ts:776 emit content +",
|
|
3161
|
+
"# meta which Claude Code wraps in a <channel ...> preamble). Searching",
|
|
3162
|
+
"# the raw text gives us the exact pending conversation key.",
|
|
3163
|
+
`CHANNEL_TAG=$(tail -400 "$TRANSCRIPT_PATH" | jq -r '(.message.content // .content // []) | map(select(.type == "text") | .text) | join(" ")' 2>/dev/null | grep -oE '<channel [^>]+>' | tail -1)`,
|
|
3164
|
+
'TAG_SOURCE=""',
|
|
3165
|
+
`[ -n "$CHANNEL_TAG" ] && TAG_SOURCE=$(echo "$CHANNEL_TAG" | grep -oE 'source="[^"]+"' | head -1 | sed 's/source="\\(.*\\)"/\\1/')`,
|
|
3166
|
+
'extract_attr() { echo "$1" | grep -oE "$2=\\"[^\\"]+\\"" | head -1 | sed -E "s/$2=\\"(.*)\\"/\\\\1/"; }',
|
|
3167
|
+
"# Atomic write helper: jq \u2192 tmp file in same dir, then rename. The",
|
|
3168
|
+
"# channel-side fs.watch only fires on rename, so the file is never",
|
|
3169
|
+
"# observed mid-write.",
|
|
3170
|
+
"atomic_write_payload() {",
|
|
3171
|
+
' local out_dir="$1" final="$2" jq_expr="$3"; shift 3',
|
|
3172
|
+
' mkdir -p "$out_dir"',
|
|
3173
|
+
' local tmp="$out_dir/.${final##*/}.tmp"',
|
|
3174
|
+
' jq -n "$jq_expr" "$@" > "$tmp"',
|
|
3175
|
+
' chmod 600 "$tmp" 2>/dev/null || true',
|
|
3176
|
+
' mv -f "$tmp" "$out_dir/$final"',
|
|
3177
|
+
"}",
|
|
3178
|
+
"# Sanitize an ID the same way the channel servers do (safeMarkerName /",
|
|
3179
|
+
"# safeSlackMarkerName): replace anything outside [A-Za-z0-9_-] with `_`.",
|
|
3180
|
+
`safe_id() { echo -n "$1" | sed -E 's|[^A-Za-z0-9_-]|_|g'; }`,
|
|
3181
|
+
"recover_telegram_for() {",
|
|
3182
|
+
' local chat_id="$1" msg_id="$2"',
|
|
3183
|
+
' [ -z "$chat_id" ] || [ -z "$msg_id" ] && return',
|
|
3184
|
+
" # If the agent DID call telegram.reply this turn, no recovery needed.",
|
|
3185
|
+
` echo "$TOOL_NAMES" | grep -qE '^(telegram\\.reply|telegram\\.send_message)$' && return`,
|
|
3186
|
+
" local marker_name",
|
|
3187
|
+
' marker_name="$(safe_id "$chat_id")__$(safe_id "$msg_id").json"',
|
|
3188
|
+
' local marker_path="${TG_MARKER_DIR}/${marker_name}"',
|
|
3189
|
+
' [ ! -f "$marker_path" ] && return',
|
|
3190
|
+
" local TS",
|
|
3191
|
+
" TS=$(date -u +%Y%m%dT%H%M%S%N)",
|
|
3192
|
+
' atomic_write_payload "${AGENT_DIR}/telegram-recovery-outbox" "${TS}.json" \\',
|
|
3193
|
+
` '{chat_id:$c, message_id:$m, text:$t, source:"ghost-reply-recovery"}' \\`,
|
|
3194
|
+
' --arg c "$chat_id" --arg m "$msg_id" --arg t "$TEXT"',
|
|
3195
|
+
' rm -f "$marker_path" 2>/dev/null || true',
|
|
3196
|
+
"}",
|
|
3197
|
+
"recover_slack_for() {",
|
|
3198
|
+
' local channel="$1" thread_ts="$2"',
|
|
3199
|
+
' [ -z "$channel" ] && return',
|
|
3200
|
+
" # If the agent DID call slack.reply this turn, no recovery needed.",
|
|
3201
|
+
` echo "$TOOL_NAMES" | grep -qE '^slack\\.reply$' && return`,
|
|
3202
|
+
" # Find any marker for this channel+thread (in busy threads multiple",
|
|
3203
|
+
" # message_ts entries can be pending \u2014 recover the oldest, that's the",
|
|
3204
|
+
" # one closest to firing the timeout).",
|
|
3205
|
+
' local prefix="$(safe_id "$channel")__$(safe_id "$thread_ts")__"',
|
|
3206
|
+
' local marker_path=""',
|
|
3207
|
+
" shopt -s nullglob",
|
|
3208
|
+
' for f in "$SL_MARKER_DIR"/${prefix}*.json; do',
|
|
3209
|
+
' [ -z "$marker_path" ] && marker_path="$f"',
|
|
3210
|
+
" done",
|
|
3211
|
+
' [ -z "$marker_path" ] && return',
|
|
3212
|
+
" local TS",
|
|
3213
|
+
" TS=$(date -u +%Y%m%dT%H%M%S%N)",
|
|
3214
|
+
' atomic_write_payload "${AGENT_DIR}/slack-recovery-outbox" "${TS}.json" \\',
|
|
3215
|
+
` '{channel:$c, thread_ts:$th, text:$t, source:"ghost-reply-recovery"}' \\`,
|
|
3216
|
+
' --arg c "$channel" --arg th "$thread_ts" --arg t "$TEXT"',
|
|
3217
|
+
' rm -f "$marker_path" 2>/dev/null || true',
|
|
3218
|
+
"}",
|
|
3219
|
+
"# Strict correlation: only recover if the last channel tag in the",
|
|
3220
|
+
"# transcript points at a channel/key that has an exact-match pending",
|
|
3221
|
+
"# marker. No tag found \u2192 skip; let timeout handle it.",
|
|
3222
|
+
'if [ "$TAG_SOURCE" = "telegram" ]; then',
|
|
3223
|
+
' CHAT_ID=$(extract_attr "$CHANNEL_TAG" "chat_id")',
|
|
3224
|
+
' MSG_ID=$(extract_attr "$CHANNEL_TAG" "message_id")',
|
|
3225
|
+
' recover_telegram_for "$CHAT_ID" "$MSG_ID"',
|
|
3226
|
+
'elif [ "$TAG_SOURCE" = "slack" ]; then',
|
|
3227
|
+
' CHANNEL=$(extract_attr "$CHANNEL_TAG" "channel")',
|
|
3228
|
+
' THREAD_TS=$(extract_attr "$CHANNEL_TAG" "thread_ts")',
|
|
3229
|
+
' recover_slack_for "$CHANNEL" "$THREAD_TS"',
|
|
3230
|
+
"fi",
|
|
3231
|
+
"exit 0"
|
|
3232
|
+
].join("\n") + "\n";
|
|
3233
|
+
writeFileSync4(ghostHookPath, ghostHookScript, { mode: 493 });
|
|
3120
3234
|
const settingsPath = join4(claudeDir, "settings.local.json");
|
|
3121
3235
|
let settings = {};
|
|
3122
3236
|
try {
|
|
@@ -3127,10 +3241,8 @@ function provisionStopHook(codeName) {
|
|
|
3127
3241
|
hooks["Stop"] = [
|
|
3128
3242
|
{
|
|
3129
3243
|
hooks: [
|
|
3130
|
-
{
|
|
3131
|
-
|
|
3132
|
-
command: hookScriptPath
|
|
3133
|
-
}
|
|
3244
|
+
{ type: "command", command: hookScriptPath },
|
|
3245
|
+
{ type: "command", command: ghostHookPath }
|
|
3134
3246
|
]
|
|
3135
3247
|
}
|
|
3136
3248
|
];
|
|
@@ -7122,4 +7234,4 @@ export {
|
|
|
7122
7234
|
managerStopCommand,
|
|
7123
7235
|
managerStatusCommand
|
|
7124
7236
|
};
|
|
7125
|
-
//# sourceMappingURL=chunk-
|
|
7237
|
+
//# sourceMappingURL=chunk-EC6FSHOW.js.map
|