@integrity-labs/agt-cli 0.28.328 → 0.28.329

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-5IV24YKT.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-ENQXJNBJ.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-LTZRJHKO.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-T7EFAWS5.js.map
@@ -43,7 +43,7 @@ import {
43
43
  requireHost,
44
44
  safeWriteJsonAtomic,
45
45
  setConfigHash
46
- } from "../chunk-XRO2EOEM.js";
46
+ } from "../chunk-AOSBSVNB.js";
47
47
  import {
48
48
  getProjectDir as getProjectDir2,
49
49
  getReadyTasks,
@@ -132,7 +132,7 @@ import {
132
132
  takeZombieDetection,
133
133
  transcriptActivityAgeSeconds,
134
134
  writeEgressAllowlist
135
- } from "../chunk-GAWNDPGM.js";
135
+ } from "../chunk-J2WYEOBH.js";
136
136
  import {
137
137
  reapOrphanChannelMcps
138
138
  } from "../chunk-XWVM4KPK.js";
@@ -8386,7 +8386,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
8386
8386
  var lastVersionCheckAt = 0;
8387
8387
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
8388
8388
  var lastResponsivenessProbeAt = 0;
8389
- var agtCliVersion = true ? "0.28.328" : "dev";
8389
+ var agtCliVersion = true ? "0.28.329" : "dev";
8390
8390
  function resolveBrewPath(execFileSync2) {
8391
8391
  try {
8392
8392
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -9282,7 +9282,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
9282
9282
  if (codeNames.length === 0) return;
9283
9283
  void (async () => {
9284
9284
  try {
9285
- const { collectDiagnostics } = await import("../persistent-session-5IV24YKT.js");
9285
+ const { collectDiagnostics } = await import("../persistent-session-ENQXJNBJ.js");
9286
9286
  await api.post("/host/heartbeat", {
9287
9287
  host_id: hostId,
9288
9288
  agent_diagnostics: collectDiagnostics(codeNames)
@@ -9381,7 +9381,7 @@ async function pollCycle() {
9381
9381
  }
9382
9382
  try {
9383
9383
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
9384
- const { collectDiagnostics } = await import("../persistent-session-5IV24YKT.js");
9384
+ const { collectDiagnostics } = await import("../persistent-session-ENQXJNBJ.js");
9385
9385
  const diagCodeNames = [...agentState.persistentSessionAgents];
9386
9386
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
9387
9387
  let tailscaleHostname;
@@ -9530,7 +9530,7 @@ async function pollCycle() {
9530
9530
  const {
9531
9531
  collectResponsivenessProbes,
9532
9532
  getResponsivenessIntervalMs
9533
- } = await import("../responsiveness-probe-TTGD44MW.js");
9533
+ } = await import("../responsiveness-probe-OJJESVVX.js");
9534
9534
  const probeIntervalMs = getResponsivenessIntervalMs();
9535
9535
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
9536
9536
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -9562,7 +9562,7 @@ async function pollCycle() {
9562
9562
  collectResponsivenessProbes,
9563
9563
  livePendingInboundOldestAgeSeconds,
9564
9564
  parkPendingInbound
9565
- } = await import("../responsiveness-probe-TTGD44MW.js");
9565
+ } = await import("../responsiveness-probe-OJJESVVX.js");
9566
9566
  const { getProjectDir: wedgeProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
9567
9567
  const wedgeNow = /* @__PURE__ */ new Date();
9568
9568
  const liveAgents = agentState.persistentSessionAgents;
@@ -12660,7 +12660,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
12660
12660
  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}`));
12661
12661
  void (async () => {
12662
12662
  try {
12663
- const { collectDiagnostics } = await import("../persistent-session-5IV24YKT.js");
12663
+ const { collectDiagnostics } = await import("../persistent-session-ENQXJNBJ.js");
12664
12664
  await api.post("/host/heartbeat", {
12665
12665
  host_id: hostId,
12666
12666
  agent_diagnostics: collectDiagnostics([codeName])
@@ -12710,7 +12710,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
12710
12710
  }
12711
12711
  try {
12712
12712
  const hostId = await getHostId();
12713
- const { collectDiagnostics } = await import("../persistent-session-5IV24YKT.js");
12713
+ const { collectDiagnostics } = await import("../persistent-session-ENQXJNBJ.js");
12714
12714
  await api.post("/host/heartbeat", {
12715
12715
  host_id: hostId,
12716
12716
  agent_diagnostics: collectDiagnostics([codeName])
@@ -13208,7 +13208,7 @@ async function processClaudePairSessions(agents) {
13208
13208
  killPairSession,
13209
13209
  pairTmuxSession,
13210
13210
  finalizeClaudePairOnboarding
13211
- } = await import("../claude-pair-runtime-LTZRJHKO.js");
13211
+ } = await import("../claude-pair-runtime-T7EFAWS5.js");
13212
13212
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
13213
13213
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
13214
13214
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -14316,6 +14316,68 @@ function clearDirectChatPendingMarkersForSession(dir, sessionId, op = defaultCle
14316
14316
  return cleared;
14317
14317
  }
14318
14318
 
14319
+ // src/direct-chat-recovery-outbox.ts
14320
+ async function consumeDirectChatRecoveryFile(fullPath, filename, deps) {
14321
+ if (filename.endsWith(".tmp") || filename.endsWith(".poison")) return "skipped";
14322
+ let payload;
14323
+ try {
14324
+ payload = JSON.parse(deps.readFile(fullPath));
14325
+ } catch (err) {
14326
+ deps.log(`recovery outbox parse failed (${filename}): ${err.message}`);
14327
+ try {
14328
+ deps.renameFile(fullPath, `${fullPath}.parse-error.poison`);
14329
+ } catch {
14330
+ }
14331
+ return "poison";
14332
+ }
14333
+ if (!payload || !payload.session_id || !payload.text) {
14334
+ deps.log(`recovery outbox malformed (${filename}): missing session_id or text`);
14335
+ if (payload && payload.marker_name) {
14336
+ try {
14337
+ deps.removeLedgerEntry(payload.marker_name);
14338
+ } catch {
14339
+ }
14340
+ }
14341
+ try {
14342
+ deps.renameFile(fullPath, `${fullPath}.malformed.poison`);
14343
+ } catch {
14344
+ }
14345
+ return "poison";
14346
+ }
14347
+ const content = deps.sanitize(payload.text);
14348
+ const messageIds = deps.peekClaims(payload.session_id);
14349
+ let result;
14350
+ try {
14351
+ result = await deps.deliver({
14352
+ sessionId: payload.session_id,
14353
+ content,
14354
+ messageIds
14355
+ });
14356
+ } catch (err) {
14357
+ result = { ok: false, error: err.message };
14358
+ }
14359
+ if (result.ok && !result.error) {
14360
+ deps.clearClaims(payload.session_id, messageIds);
14361
+ deps.clearMarkers(payload.session_id);
14362
+ if (payload.marker_name) deps.removeLedgerEntry(payload.marker_name);
14363
+ try {
14364
+ deps.unlinkFile(fullPath);
14365
+ } catch {
14366
+ }
14367
+ deps.log(`ghost-reply recovery sent (session=${payload.session_id})`);
14368
+ return "delivered";
14369
+ }
14370
+ if (payload.marker_name) deps.removeLedgerEntry(payload.marker_name);
14371
+ try {
14372
+ deps.unlinkFile(fullPath);
14373
+ } catch {
14374
+ }
14375
+ deps.log(
14376
+ `ghost-reply recovery send failed (session=${payload.session_id}): ${result.error ?? "unknown"} - re-armed for retry`
14377
+ );
14378
+ return "failed";
14379
+ }
14380
+
14319
14381
  // src/direct-chat-poll-guard.ts
14320
14382
  function evaluatePollGuard(state, nowMs, stuckMs) {
14321
14383
  if (!state.inFlight) return { run: true, stuck: false };
@@ -14414,6 +14476,7 @@ import {
14414
14476
  mkdirSync as mkdirSync4,
14415
14477
  writeFileSync as writeFileSync4,
14416
14478
  readFileSync as readFileSync5,
14479
+ readdirSync as readdirSync2,
14417
14480
  existsSync as existsSync5,
14418
14481
  renameSync as renameSync4,
14419
14482
  unlinkSync as unlinkSync3
@@ -14897,6 +14960,8 @@ var INBOUND_ATTACHMENTS_DIR = resolveInboundAttachmentsDir({
14897
14960
  var AGT_AGENT_CODE_NAME = process.env.AGT_AGENT_CODE_NAME;
14898
14961
  var PROGRESS_HEARTBEAT_PATH = AGT_AGENT_CODE_NAME ? join7(homedir2(), ".augmented", AGT_AGENT_CODE_NAME, "channel-progress-heartbeat.json") : null;
14899
14962
  var DIRECT_CHAT_PENDING_INBOUND_DIR = AGT_AGENT_CODE_NAME ? join7(homedir2(), ".augmented", AGT_AGENT_CODE_NAME, "direct-chat-pending-inbound") : null;
14963
+ var DIRECT_CHAT_RECOVERY_OUTBOX_DIR = AGT_AGENT_CODE_NAME ? join7(homedir2(), ".augmented", AGT_AGENT_CODE_NAME, "direct-chat-recovery-outbox") : null;
14964
+ var DIRECT_CHAT_RECOVERY_LEDGER_DIR = AGT_AGENT_CODE_NAME ? join7(homedir2(), ".augmented", AGT_AGENT_CODE_NAME, ".agt-direct-chat-recovery-ledger") : null;
14900
14965
  var progressReceivedAt = /* @__PURE__ */ new Map();
14901
14966
  var directChatProgressState = { tracked: null };
14902
14967
  var directChatProgressTickRunning = false;
@@ -15566,6 +15631,106 @@ if (PROGRESS_HEARTBEAT_PATH) {
15566
15631
  }, PROGRESS_TICK_MS);
15567
15632
  progressTickTimer.unref?.();
15568
15633
  }
15634
+ function sanitizeRecoveryText(text) {
15635
+ const preamble = stripRepostPreambleWithMeta(text);
15636
+ if (preamble.stripped) emitPreambleStripTelemetry("direct_chat", preamble.shape);
15637
+ const guarded = redactToolCallMarkup(preamble.text);
15638
+ if (guarded.redacted) emitToolCallMarkupRedactionTelemetry("direct_chat");
15639
+ const apiErr = rewriteTransientApiError(guarded.text);
15640
+ if (apiErr.rewritten) emitTransientApiErrorTelemetry("direct_chat", apiErr.match, apiErr.original);
15641
+ return apiErr.text;
15642
+ }
15643
+ function directChatRecoveryDeps() {
15644
+ return {
15645
+ readFile: (p) => readFileSync5(p, "utf-8"),
15646
+ renameFile: (from, to) => renameSync4(from, to),
15647
+ unlinkFile: (p) => {
15648
+ if (existsSync5(p)) unlinkSync3(p);
15649
+ },
15650
+ sanitize: sanitizeRecoveryText,
15651
+ peekClaims: (sessionId) => claimTracker.peek(sessionId),
15652
+ clearClaims: (sessionId, ids) => claimTracker.clear(sessionId, ids),
15653
+ clearMarkers: (sessionId) => clearDirectChatPendingMarkersForSession(DIRECT_CHAT_PENDING_INBOUND_DIR, sessionId),
15654
+ removeLedgerEntry: (markerName) => {
15655
+ if (!DIRECT_CHAT_RECOVERY_LEDGER_DIR) return;
15656
+ if (markerName.includes("/") || markerName.includes("\\") || markerName.includes("..")) return;
15657
+ try {
15658
+ const p = join7(DIRECT_CHAT_RECOVERY_LEDGER_DIR, markerName);
15659
+ if (existsSync5(p)) unlinkSync3(p);
15660
+ } catch {
15661
+ }
15662
+ },
15663
+ deliver: async ({ sessionId, content, messageIds }) => {
15664
+ try {
15665
+ const res = await apiPost("/host/direct-chat/reply", {
15666
+ agent_id: AGT_AGENT_ID,
15667
+ session_id: sessionId,
15668
+ content,
15669
+ message_ids: messageIds
15670
+ });
15671
+ const data = await res.json().catch(() => ({}));
15672
+ if (!res.ok || data.error) return { ok: false, error: data.error ?? `HTTP ${res.status}` };
15673
+ return { ok: true };
15674
+ } catch (err) {
15675
+ return { ok: false, error: err.message };
15676
+ }
15677
+ },
15678
+ log: (line) => process.stderr.write(`direct-chat-channel(${AGT_AGENT_CODE_NAME}): ${line}
15679
+ `)
15680
+ };
15681
+ }
15682
+ var directChatRecoveryInFlight = /* @__PURE__ */ new Set();
15683
+ async function processDirectChatRecoveryOutboxFile(filename) {
15684
+ if (!DIRECT_CHAT_RECOVERY_OUTBOX_DIR) return;
15685
+ if (directChatRecoveryInFlight.has(filename)) return;
15686
+ const enabled = resolveHostBooleanFlag({
15687
+ key: "direct-chat-recovery",
15688
+ envVar: "AGT_DIRECT_CHAT_RECOVERY_ENABLED",
15689
+ defaultValue: false
15690
+ });
15691
+ if (!enabled) return;
15692
+ directChatRecoveryInFlight.add(filename);
15693
+ try {
15694
+ const fullPath = join7(DIRECT_CHAT_RECOVERY_OUTBOX_DIR, filename);
15695
+ await consumeDirectChatRecoveryFile(fullPath, filename, directChatRecoveryDeps());
15696
+ } catch (err) {
15697
+ process.stderr.write(
15698
+ `direct-chat-channel(${AGT_AGENT_CODE_NAME}): recovery consume failed (${filename}): ${err.message}
15699
+ `
15700
+ );
15701
+ } finally {
15702
+ directChatRecoveryInFlight.delete(filename);
15703
+ }
15704
+ }
15705
+ var directChatRecoveryWatcher = null;
15706
+ if (DIRECT_CHAT_RECOVERY_OUTBOX_DIR) {
15707
+ try {
15708
+ mkdirSync4(DIRECT_CHAT_RECOVERY_OUTBOX_DIR, { recursive: true, mode: 448 });
15709
+ } catch {
15710
+ }
15711
+ try {
15712
+ for (const f of readdirSync2(DIRECT_CHAT_RECOVERY_OUTBOX_DIR)) {
15713
+ if (f.endsWith(".json")) void processDirectChatRecoveryOutboxFile(f);
15714
+ }
15715
+ } catch {
15716
+ }
15717
+ try {
15718
+ directChatRecoveryWatcher = watch(DIRECT_CHAT_RECOVERY_OUTBOX_DIR, (_event, filename) => {
15719
+ if (!filename) return;
15720
+ const name = filename.toString();
15721
+ if (!name.endsWith(".json")) return;
15722
+ if (existsSync5(join7(DIRECT_CHAT_RECOVERY_OUTBOX_DIR, name))) {
15723
+ void processDirectChatRecoveryOutboxFile(name);
15724
+ }
15725
+ });
15726
+ directChatRecoveryWatcher.unref?.();
15727
+ } catch (err) {
15728
+ process.stderr.write(
15729
+ `direct-chat-channel(${AGT_AGENT_CODE_NAME}): recovery watcher failed (${err.message})
15730
+ `
15731
+ );
15732
+ }
15733
+ }
15569
15734
  var isShuttingDown = false;
15570
15735
  function shutdown(reason) {
15571
15736
  if (isShuttingDown) return;
@@ -40108,6 +40108,16 @@ var FLAG_REGISTRY = [
40108
40108
  // canary is a high-blast-radius change worth an explicit confirm.
40109
40109
  sensitive: true
40110
40110
  },
40111
+ {
40112
+ key: "direct-chat-recovery",
40113
+ description: "Always-on direct-chat ghost-reply recovery (ENG-7814, ENG-6722 slice E). direct-chat has no recovery-outbox by default: block-turn-end (channel-block-turn-end) only makes the MODEL re-send and is dark/canary, so a direct-chat turn answered as plain text with no direct_chat.reply is lost. When ON, the ghost-reply Stop hook, for an owed and unanswered direct-chat inbound where block-turn-end did not fire, writes the last assistant text to a direct-chat-recovery-outbox that the direct-chat MCP consumes and POSTs to /host/direct-chat/reply for the session, re-sending the reply the agent produced but never delivered. session_id is unambiguous (one session per marker), so there is no cross-thread mis-correlation risk (why direct-chat can safely re-send where Slack needs the ENG-7806 ledger). Confirm-before-clear: the pending marker is cleared only after the POST confirms; a per-marker recovery ledger caps one in-flight recovery per inbound and re-arms on failure. Independent of channel-block-turn-end (either can be on). The env var AGT_DIRECT_CHAT_RECOVERY_ENABLED is the operator/host override the bash hook reads directly (materialized by the manager) and the MCP resolves via the flags cache. Boolean gate; ships dark, flip per host/org to canary before fleet rollout.",
40114
+ flagType: "boolean",
40115
+ defaultValue: false,
40116
+ envVar: "AGT_DIRECT_CHAT_RECOVERY_ENABLED",
40117
+ // Re-sends a reply on the agent behalf (a customer-visible send); flipping it
40118
+ // beyond a canary is a blast-radius change worth an explicit confirm.
40119
+ sensitive: true
40120
+ },
40111
40121
  {
40112
40122
  key: "ghost-reply-intent-classifier",
40113
40123
  description: `Intentional-non-reply suppression on the ghost-reply RECOVERY path (ENG-7096, ENG-7478). The recovery-outbox consumer in the channel MCP only ever fires for turns where the agent ended with text but did NOT call the matching reply tool for that conversation - a set that includes DELIBERATE non-replies and internal third-person self-status narration ("Filed CS-1443... standing by"). A cheap model (the conversation-eval backend, AGT_CONV_EVAL_*) classifies the recovered text as a real message for the user (deliver) or internal not-replying / self-status narration (suppress), keyed on grammatical person, biased to DELIVER and fail-open (a classifier outage degrades to today's behaviour, never a dropped real reply). off = classifier never runs, every recovered reply is posted exactly as today (ships dark, == the old boolean-false). shadow = classify + log the would-suppress verdict for operator adjudication but STILL DELIVER (measure the suppression rate before acting). enforce = actually suppress on a clean high-confidence suppress verdict. The channel server reads this live from the heartbeat flags-cache (or the env override); enforce is a deliberate, audited per-org flip after a shadow soak.`,
@@ -36,7 +36,7 @@ import {
36
36
  writeDirectChatSessionState,
37
37
  writeEgressAllowlist,
38
38
  writePersistentClaudeWrapper
39
- } from "./chunk-GAWNDPGM.js";
39
+ } from "./chunk-J2WYEOBH.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-5IV24YKT.js.map
80
+ //# sourceMappingURL=persistent-session-ENQXJNBJ.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-GAWNDPGM.js";
3
+ } from "./chunk-J2WYEOBH.js";
4
4
  import "./chunk-XWVM4KPK.js";
5
5
 
6
6
  // src/lib/responsiveness-probe.ts
@@ -427,4 +427,4 @@ export {
427
427
  readAndResetSlackReplyBindingClassifications,
428
428
  readAndResetSlackReplyTargetClassifications
429
429
  };
430
- //# sourceMappingURL=responsiveness-probe-TTGD44MW.js.map
430
+ //# sourceMappingURL=responsiveness-probe-OJJESVVX.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.328",
3
+ "version": "0.28.329",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {