@integrity-labs/agt-cli 0.28.367 → 0.28.369

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-O2RYNO42.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-3PAEXZHX.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-SNLRINSI.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-NNGG572N.js.map
@@ -43,7 +43,7 @@ import {
43
43
  resolveEffectivePinRaw,
44
44
  safeWriteJsonAtomic,
45
45
  setConfigHash
46
- } from "../chunk-3FUABX65.js";
46
+ } from "../chunk-JJX2AH5K.js";
47
47
  import {
48
48
  getProjectDir as getProjectDir2,
49
49
  getReadyTasks,
@@ -147,7 +147,7 @@ import {
147
147
  toOpencodeModel,
148
148
  transcriptActivityAgeSeconds,
149
149
  writeEgressAllowlist
150
- } from "../chunk-COOBM6RF.js";
150
+ } from "../chunk-RIWZIYJ7.js";
151
151
  import {
152
152
  reapOrphanChannelMcps
153
153
  } from "../chunk-XWVM4KPK.js";
@@ -9072,7 +9072,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
9072
9072
  var lastVersionCheckAt = 0;
9073
9073
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
9074
9074
  var lastResponsivenessProbeAt = 0;
9075
- var agtCliVersion = true ? "0.28.367" : "dev";
9075
+ var agtCliVersion = true ? "0.28.369" : "dev";
9076
9076
  function resolveBrewPath(execFileSync2) {
9077
9077
  try {
9078
9078
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -10091,7 +10091,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
10091
10091
  if (codeNames.length === 0) return;
10092
10092
  void (async () => {
10093
10093
  try {
10094
- const { collectDiagnostics } = await import("../persistent-session-O2RYNO42.js");
10094
+ const { collectDiagnostics } = await import("../persistent-session-3PAEXZHX.js");
10095
10095
  await api.post("/host/heartbeat", {
10096
10096
  host_id: hostId,
10097
10097
  agent_diagnostics: collectDiagnostics(codeNames)
@@ -10190,7 +10190,7 @@ async function pollCycle() {
10190
10190
  }
10191
10191
  try {
10192
10192
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
10193
- const { collectDiagnostics } = await import("../persistent-session-O2RYNO42.js");
10193
+ const { collectDiagnostics } = await import("../persistent-session-3PAEXZHX.js");
10194
10194
  const diagCodeNames = [...agentState.persistentSessionAgents];
10195
10195
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
10196
10196
  let tailscaleHostname;
@@ -10345,7 +10345,7 @@ async function pollCycle() {
10345
10345
  const {
10346
10346
  collectResponsivenessProbes,
10347
10347
  getResponsivenessIntervalMs
10348
- } = await import("../responsiveness-probe-IZJNJO2C.js");
10348
+ } = await import("../responsiveness-probe-UEOKWHTD.js");
10349
10349
  const probeIntervalMs = getResponsivenessIntervalMs();
10350
10350
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
10351
10351
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -10377,7 +10377,7 @@ async function pollCycle() {
10377
10377
  collectResponsivenessProbes,
10378
10378
  livePendingInboundOldestAgeSeconds,
10379
10379
  parkPendingInbound
10380
- } = await import("../responsiveness-probe-IZJNJO2C.js");
10380
+ } = await import("../responsiveness-probe-UEOKWHTD.js");
10381
10381
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-HYOOQV4A.js");
10382
10382
  const wedgeNow = /* @__PURE__ */ new Date();
10383
10383
  const liveAgents = agentState.persistentSessionAgents;
@@ -13542,7 +13542,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
13542
13542
  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}`));
13543
13543
  void (async () => {
13544
13544
  try {
13545
- const { collectDiagnostics } = await import("../persistent-session-O2RYNO42.js");
13545
+ const { collectDiagnostics } = await import("../persistent-session-3PAEXZHX.js");
13546
13546
  await api.post("/host/heartbeat", {
13547
13547
  host_id: hostId,
13548
13548
  agent_diagnostics: collectDiagnostics([codeName])
@@ -13592,7 +13592,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
13592
13592
  }
13593
13593
  try {
13594
13594
  const hostId = await getHostId();
13595
- const { collectDiagnostics } = await import("../persistent-session-O2RYNO42.js");
13595
+ const { collectDiagnostics } = await import("../persistent-session-3PAEXZHX.js");
13596
13596
  await api.post("/host/heartbeat", {
13597
13597
  host_id: hostId,
13598
13598
  agent_diagnostics: collectDiagnostics([codeName])
@@ -14090,7 +14090,7 @@ async function processClaudePairSessions(agents) {
14090
14090
  killPairSession,
14091
14091
  pairTmuxSession,
14092
14092
  finalizeClaudePairOnboarding
14093
- } = await import("../claude-pair-runtime-SNLRINSI.js");
14093
+ } = await import("../claude-pair-runtime-NNGG572N.js");
14094
14094
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
14095
14095
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
14096
14096
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -29827,6 +29827,7 @@ import {
29827
29827
  existsSync as existsSync2,
29828
29828
  mkdirSync as mkdirSync2,
29829
29829
  readdirSync,
29830
+ readFileSync as readFileSync3,
29830
29831
  renameSync as renameSync2,
29831
29832
  unlinkSync,
29832
29833
  writeFileSync as writeFileSync2
@@ -29882,6 +29883,15 @@ function clearDirectChatPendingMarkersForSession(dir, sessionId, op = defaultCle
29882
29883
  }
29883
29884
  return cleared;
29884
29885
  }
29886
+ function readDirectChatPendingInboundMarker(dir, sessionId, messageId, readFileImpl = (path) => readFileSync3(path, "utf8")) {
29887
+ if (!dir || !sessionId || !messageId) return null;
29888
+ try {
29889
+ const raw = readFileImpl(join3(dir, directChatMarkerName(sessionId, messageId)));
29890
+ return JSON.parse(raw);
29891
+ } catch {
29892
+ return null;
29893
+ }
29894
+ }
29885
29895
  function sweepAgedDirectChatMarkers(dir, deps) {
29886
29896
  const result = { cleared: 0, orphaned: 0 };
29887
29897
  if (!dir) return result;
@@ -29982,7 +29992,7 @@ async function consumeDirectChatRecoveryFile(fullPath, filename, deps) {
29982
29992
  }
29983
29993
 
29984
29994
  // src/inbound-delivery-ledger.ts
29985
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "fs";
29995
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync4, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "fs";
29986
29996
  import { join as join4 } from "path";
29987
29997
  function safeInboundId(inboundId) {
29988
29998
  return inboundId.replace(/[^A-Za-z0-9_-]/g, "_");
@@ -29992,7 +30002,7 @@ var defaultDeps = {
29992
30002
  writeFile: (path, data) => writeFileSync3(path, data, "utf8"),
29993
30003
  rename: (from, to) => renameSync3(from, to),
29994
30004
  readdir: (dir) => readdirSync2(dir),
29995
- readFile: (path) => readFileSync3(path, "utf8"),
30005
+ readFile: (path) => readFileSync4(path, "utf8"),
29996
30006
  exists: (path) => existsSync3(path)
29997
30007
  };
29998
30008
  function writeInboundDeliveryLedgerEntry(dir, record2, deps = defaultDeps) {
@@ -30020,11 +30030,11 @@ function inboundDeliveryLedgerHas(dir, inboundId, deps = defaultDeps) {
30020
30030
  }
30021
30031
 
30022
30032
  // src/ack-reaction.ts
30023
- import { readdirSync as readdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
30033
+ import { readdirSync as readdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
30024
30034
  import { join as join6 } from "path";
30025
30035
 
30026
30036
  // src/flags-cache-read.ts
30027
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
30037
+ import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
30028
30038
  import { homedir } from "os";
30029
30039
  import { join as join5 } from "path";
30030
30040
  function defaultFlagsCachePath() {
@@ -30041,7 +30051,7 @@ function envBoolean(raw) {
30041
30051
  function cachedBoolean(key, path) {
30042
30052
  try {
30043
30053
  if (!existsSync4(path)) return void 0;
30044
- const parsed = JSON.parse(readFileSync4(path, "utf8"));
30054
+ const parsed = JSON.parse(readFileSync5(path, "utf8"));
30045
30055
  if (!parsed || typeof parsed !== "object") return void 0;
30046
30056
  const flags = parsed.flags;
30047
30057
  if (!flags || typeof flags !== "object") return void 0;
@@ -30062,9 +30072,90 @@ function resolveHostBooleanFlag(opts) {
30062
30072
 
30063
30073
  // src/ack-reaction.ts
30064
30074
  var REPLY_WEDGED_THRESHOLD_MS = 5 * 60 * 1e3;
30075
+ var ACK_PANE_FRESH_THRESHOLD_MS = 6e4;
30076
+ function classifyUndeliverableCause(i) {
30077
+ if (!i.hasTarget) return null;
30078
+ if (!i.integrationReady) return "integration_down";
30079
+ if (i.tmux === "dead") return "session_dead";
30080
+ if (!i.withinStartupGrace && i.claude === "dead") return "session_dead";
30081
+ const threshold = i.pendingStaleThresholdMs ?? REPLY_WEDGED_THRESHOLD_MS;
30082
+ if (i.oldestPendingAgeMs != null && i.oldestPendingAgeMs > threshold) {
30083
+ const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
30084
+ const paneIsFresh = i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
30085
+ if (paneIsFresh && i.tmux === "alive" && i.claude === "alive") {
30086
+ return null;
30087
+ }
30088
+ return "wedged";
30089
+ }
30090
+ return null;
30091
+ }
30065
30092
  var UNDELIVERABLE_NOTICE_THROTTLE_MS = 5 * 60 * 1e3;
30093
+ function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
30094
+ return lastNoticeAtMs == null || nowMs - lastNoticeAtMs >= throttleMs;
30095
+ }
30096
+ function undeliverableNoticeText(replayEnabled = channelReplayEnabled()) {
30097
+ return replayEnabled ? "\u23F3 I can't get to this right now \u2014 no need to resend; I'll pick it up automatically as soon as I'm free." : "\u23F3 I can't get to this right now. Please resend in a few minutes \u2014 I may not see this one.";
30098
+ }
30099
+ var BUSY_ACK_THRESHOLD_MS = 12e4;
30066
30100
  var BUSY_ACK_NOTICE_THROTTLE_MS = 20 * 60 * 1e3;
30101
+ function channelBusyAckEnabled() {
30102
+ return resolveHostBooleanFlag({
30103
+ key: "channel-busy-ack",
30104
+ envVar: "AGT_CHANNEL_BUSY_ACK_ENABLED",
30105
+ defaultValue: false
30106
+ });
30107
+ }
30108
+ function channelBusyAckThresholdMs() {
30109
+ const raw = parseInt(process.env.AGT_CHANNEL_BUSY_ACK_THRESHOLD_MS ?? "", 10);
30110
+ return Number.isFinite(raw) && raw > 0 ? raw : BUSY_ACK_THRESHOLD_MS;
30111
+ }
30112
+ function decideBusyAck(i) {
30113
+ if (!i.hasTarget) return false;
30114
+ if (!i.arrivedWhileBusy) return false;
30115
+ if (!i.stillPending) return false;
30116
+ if (!Number.isFinite(i.pendingAgeMs)) return false;
30117
+ const threshold = i.thresholdMs ?? channelBusyAckThresholdMs();
30118
+ if (i.pendingAgeMs < threshold) return false;
30119
+ if (!i.sessionAlive) return false;
30120
+ const paneFreshThreshold = i.paneFreshThresholdMs ?? ACK_PANE_FRESH_THRESHOLD_MS;
30121
+ return i.paneLogFreshAgeMs != null && i.paneLogFreshAgeMs <= paneFreshThreshold;
30122
+ }
30123
+ function busyAckNoticeText() {
30124
+ return "\u{1F6E0}\uFE0F I'm in the middle of something right now \u2014 I'll follow up on this as soon as I'm free.";
30125
+ }
30067
30126
  var GIVE_UP_SIGNAL_MAX_AGE_MS = 30 * 60 * 1e3;
30127
+ function oldestPendingMarkerAgeMs(dir, now = Date.now(), opts) {
30128
+ if (!dir) return null;
30129
+ let names;
30130
+ try {
30131
+ names = readdirSync3(dir);
30132
+ } catch {
30133
+ return null;
30134
+ }
30135
+ let oldest = null;
30136
+ for (const name of names) {
30137
+ if (!name.endsWith(".json")) continue;
30138
+ let receivedAt;
30139
+ try {
30140
+ const raw = JSON.parse(readFileSync6(join6(dir, name), "utf-8"));
30141
+ if (raw.discretionary === true) continue;
30142
+ if (!opts?.includeSeen && typeof raw.seen_at === "string" && raw.seen_at) continue;
30143
+ receivedAt = raw.received_at;
30144
+ } catch {
30145
+ continue;
30146
+ }
30147
+ if (!receivedAt) continue;
30148
+ const t = Date.parse(receivedAt);
30149
+ if (Number.isNaN(t)) continue;
30150
+ const age = now - t;
30151
+ if (age < 0) continue;
30152
+ if (oldest == null || age > oldest) oldest = age;
30153
+ }
30154
+ return oldest;
30155
+ }
30156
+ function hasInFlightInbound(dir, now = Date.now()) {
30157
+ return oldestPendingMarkerAgeMs(dir, now, { includeSeen: true }) != null;
30158
+ }
30068
30159
  function channelOrphanMarkerMs() {
30069
30160
  const raw = parseInt(process.env.AGT_CHANNEL_ORPHAN_MARKER_MS ?? "", 10);
30070
30161
  return Number.isFinite(raw) && raw > 0 ? Math.max(raw, 12e4) : 30 * 6e4;
@@ -30074,6 +30165,13 @@ function orphanSweepIntervalMs() {
30074
30165
  return Math.max(6e4, Math.min(ORPHAN_SWEEP_INTERVAL_MS, channelOrphanMarkerMs()));
30075
30166
  }
30076
30167
  var PANE_FRESH_DEFER_MAX_MS = 10 * 60 * 1e3;
30168
+ function channelReplayEnabled() {
30169
+ return resolveHostBooleanFlag({
30170
+ key: "channel-replay",
30171
+ envVar: "AGT_CHANNEL_REPLAY_ENABLED",
30172
+ defaultValue: true
30173
+ });
30174
+ }
30077
30175
  function isMarkerGenuinelyAged(receivedAt, nowMs, thresholdMs) {
30078
30176
  const t = Date.parse(receivedAt ?? "");
30079
30177
  return Number.isFinite(t) && t <= nowMs && nowMs - t >= thresholdMs;
@@ -30087,7 +30185,7 @@ function recordChannelDeflection(agentDir, channel, cause) {
30087
30185
  const path = deflectionCounterPath(agentDir, channel);
30088
30186
  let counts = {};
30089
30187
  try {
30090
- const parsed = JSON.parse(readFileSync5(path, "utf-8"));
30188
+ const parsed = JSON.parse(readFileSync6(path, "utf-8"));
30091
30189
  if (parsed && typeof parsed === "object") counts = parsed;
30092
30190
  } catch {
30093
30191
  }
@@ -30174,7 +30272,7 @@ async function fetchWithTimeout(input, init, timeoutMs, fetchImpl = fetch) {
30174
30272
  import {
30175
30273
  existsSync as existsSync5,
30176
30274
  mkdirSync as mkdirSync4,
30177
- readFileSync as readFileSync6,
30275
+ readFileSync as readFileSync7,
30178
30276
  renameSync as renameSync4,
30179
30277
  unlinkSync as unlinkSync2,
30180
30278
  writeFileSync as writeFileSync5
@@ -30228,7 +30326,7 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
30228
30326
  function readLockHolder(path) {
30229
30327
  if (!existsSync5(path)) return null;
30230
30328
  try {
30231
- const raw = readFileSync6(path, "utf8");
30329
+ const raw = readFileSync7(path, "utf8");
30232
30330
  const parsed = JSON.parse(raw);
30233
30331
  const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
30234
30332
  if (!Number.isFinite(pid) || pid <= 0) return null;
@@ -30247,11 +30345,12 @@ import {
30247
30345
  watch,
30248
30346
  mkdirSync as mkdirSync5,
30249
30347
  writeFileSync as writeFileSync6,
30250
- readFileSync as readFileSync8,
30348
+ readFileSync as readFileSync9,
30251
30349
  readdirSync as readdirSync4,
30252
30350
  existsSync as existsSync6,
30253
30351
  renameSync as renameSync5,
30254
30352
  unlinkSync as unlinkSync3,
30353
+ statSync,
30255
30354
  createWriteStream
30256
30355
  } from "fs";
30257
30356
 
@@ -30678,7 +30777,7 @@ function isMaintenanceModeActive(opts) {
30678
30777
  }
30679
30778
 
30680
30779
  // src/usage-limit-notice.ts
30681
- import { readFileSync as readFileSync7 } from "fs";
30780
+ import { readFileSync as readFileSync8 } from "fs";
30682
30781
  import { homedir as homedir2 } from "os";
30683
30782
  import { join as join9 } from "path";
30684
30783
 
@@ -32922,7 +33021,7 @@ var INTEGRATION_REGISTRY = [
32922
33021
  id: "brand-ninja",
32923
33022
  name: "Brand Ninja",
32924
33023
  category: "social",
32925
- description: "Brand-aligned content generation: submit async content requests and track them, search transcripts and assemble ranked timelines from source clips, and discover the account catalog (channels, brands, topics, templates, content types, skills). Wired as the hosted Brand Ninja External-Content MCP at https://ext-api.app.brandninja.ai/v1/mcp.",
33024
+ description: "Brand-aligned content generation: submit async content requests and track them, search transcripts and assemble ranked timelines from source clips, discover the account catalog (channels, brands, topics, templates, content types, skills), and author catalog entries and knowledge (topics, timeline templates, knowledge items). Wired as the hosted Brand Ninja External-Content MCP at https://ext-api.app.brandninja.ai/v1/mcp.",
32926
33025
  // ENG-6820: same remote streamable-HTTP MCP + OAuth pattern as Granola.
32927
33026
  // Brand Ninja's server implements the full MCP discovery chain (RFC
32928
33027
  // 9728/8414/7591); auth is OAuth 2.0 authorization-code with PKCE (S256)
@@ -32940,6 +33039,8 @@ var INTEGRATION_REGISTRY = [
32940
33039
  { id: "brand-ninja:list-catalog", name: "List Catalog", description: "Discover account catalog metadata: brands, topics, timeline templates, content types, and source, output and conversion skills (list_brands, list_topics, list_timeline_templates, list_content_types, list_source_skills, list_output_skills, list_conversion_skills)", access: "read" },
32941
33040
  { id: "brand-ninja:search-transcripts", name: "Search Transcripts", description: "Search the transcripts available to the account to source or reference when generating content, text and metadata (search_transcripts)", access: "read" },
32942
33041
  { id: "brand-ninja:assemble-timeline", name: "Assemble Timeline", description: "Turn transcript spans into topic-tagged source clips and rank a topic's clips into a ranked timeline via an async LLM job (create_source_clips, create_timeline_ranking)", access: "write" },
33042
+ { id: "brand-ninja:manage-catalog", name: "Manage Catalog", description: "Author catalog entries the other Brand Ninja tools then reference: create a topic or a timeline template (create_topic, create_timeline_template)", access: "write" },
33043
+ { id: "brand-ninja:manage-knowledge", name: "Manage Knowledge", description: "Build the account knowledge base: create a knowledge item and link it to a topic or brand to ground generated content (create_knowledge, link_knowledge)", access: "write" },
32943
33044
  { id: "brand-ninja:read-credentials", name: "Read Credentials", description: "Read-only External-API credential metadata, secrets stripped. Requires the elevated external-api/admin scope (list_credentials)", access: "admin" }
32944
33045
  ],
32945
33046
  docs_url: "https://ext-api.app.brandninja.ai/v1/mcp",
@@ -35075,7 +35176,7 @@ function readUsageLimitUntil(opts) {
35075
35176
  if (!path) return null;
35076
35177
  let raw;
35077
35178
  try {
35078
- raw = readFileSync7(path, "utf-8");
35179
+ raw = readFileSync8(path, "utf-8");
35079
35180
  } catch {
35080
35181
  return null;
35081
35182
  }
@@ -35173,7 +35274,7 @@ var directChatKanbanCardClient = createKanbanCardActiveClient({
35173
35274
  function readProgressHeartbeat() {
35174
35275
  if (!PROGRESS_HEARTBEAT_PATH || !existsSync6(PROGRESS_HEARTBEAT_PATH)) return null;
35175
35276
  try {
35176
- return parseProgressHeartbeat(readFileSync8(PROGRESS_HEARTBEAT_PATH, "utf-8"));
35277
+ return parseProgressHeartbeat(readFileSync9(PROGRESS_HEARTBEAT_PATH, "utf-8"));
35177
35278
  } catch {
35178
35279
  return null;
35179
35280
  }
@@ -35600,6 +35701,119 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
35600
35701
  });
35601
35702
  await mcp.connect(new StdioServerTransport());
35602
35703
  var processedIds = /* @__PURE__ */ new Set();
35704
+ var lastBusyAckNoticeAt = /* @__PURE__ */ new Map();
35705
+ var lastUndeliverableNoticeAt = /* @__PURE__ */ new Map();
35706
+ async function postDirectChatNoticeMessage(sessionId, content) {
35707
+ try {
35708
+ const res = await apiPost("/host/direct-chat/reply", {
35709
+ agent_id: AGT_AGENT_ID,
35710
+ session_id: sessionId,
35711
+ content,
35712
+ liveness_stamp: false
35713
+ });
35714
+ if (!res.ok) return false;
35715
+ const data = await res.json().catch(() => ({}));
35716
+ return data.error == null;
35717
+ } catch {
35718
+ return false;
35719
+ }
35720
+ }
35721
+ function postDirectChatBusyAckNotice(sessionId) {
35722
+ const now = Date.now();
35723
+ if (!shouldPostUndeliverableNotice(lastBusyAckNoticeAt.get(sessionId), now, BUSY_ACK_NOTICE_THROTTLE_MS)) {
35724
+ return;
35725
+ }
35726
+ lastBusyAckNoticeAt.set(sessionId, now);
35727
+ void (async () => {
35728
+ const ok = await postDirectChatNoticeMessage(sessionId, busyAckNoticeText());
35729
+ if (!ok) {
35730
+ lastBusyAckNoticeAt.delete(sessionId);
35731
+ process.stderr.write(
35732
+ `direct-chat-channel(${AGT_AGENT_CODE_NAME}): busy-ack notice failed (session=${sessionId})
35733
+ `
35734
+ );
35735
+ }
35736
+ })();
35737
+ }
35738
+ function postDirectChatUndeliverableNotice(sessionId) {
35739
+ const now = Date.now();
35740
+ if (!shouldPostUndeliverableNotice(lastUndeliverableNoticeAt.get(sessionId), now)) {
35741
+ return;
35742
+ }
35743
+ lastUndeliverableNoticeAt.set(sessionId, now);
35744
+ void (async () => {
35745
+ const ok = await postDirectChatNoticeMessage(sessionId, undeliverableNoticeText());
35746
+ if (!ok) {
35747
+ lastUndeliverableNoticeAt.delete(sessionId);
35748
+ process.stderr.write(
35749
+ `direct-chat-channel(${AGT_AGENT_CODE_NAME}): undeliverable notice failed (session=${sessionId})
35750
+ `
35751
+ );
35752
+ }
35753
+ })();
35754
+ }
35755
+ function __resetDirectChatBusyAckThrottles() {
35756
+ lastBusyAckNoticeAt.clear();
35757
+ lastUndeliverableNoticeAt.clear();
35758
+ }
35759
+ function scheduleDirectChatBusyAck(sessionId, messageId, arrivedWhileBusy) {
35760
+ if (!channelBusyAckEnabled()) return;
35761
+ if (!arrivedWhileBusy) return;
35762
+ if (!DIRECT_CHAT_PENDING_INBOUND_DIR) return;
35763
+ const thresholdMs = channelBusyAckThresholdMs();
35764
+ const timer = setTimeout(() => {
35765
+ const probe = process.env.TMUX && AGT_AGENT_CODE_NAME ? probeAgentSessionCached(AGT_AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
35766
+ let paneLogFreshAgeMs = null;
35767
+ if (DIRECT_CHAT_CODE_NAME_AGENT_DIR) {
35768
+ try {
35769
+ const paneMtimeMs = statSync(join10(DIRECT_CHAT_CODE_NAME_AGENT_DIR, "pane.log")).mtimeMs;
35770
+ paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
35771
+ } catch {
35772
+ }
35773
+ }
35774
+ const marker = readDirectChatPendingInboundMarker(DIRECT_CHAT_PENDING_INBOUND_DIR, sessionId, messageId);
35775
+ if (!marker) return;
35776
+ const sessionAlive = probe.tmux === "alive" && probe.claude === "alive";
35777
+ const pendingAgeMs = Math.max(0, Date.now() - Date.parse(marker.received_at));
35778
+ if (decideBusyAck({
35779
+ hasTarget: Boolean(sessionId && messageId),
35780
+ arrivedWhileBusy,
35781
+ stillPending: true,
35782
+ pendingAgeMs,
35783
+ sessionAlive,
35784
+ paneLogFreshAgeMs,
35785
+ thresholdMs
35786
+ })) {
35787
+ recordChannelDeflection(DIRECT_CHAT_CODE_NAME_AGENT_DIR, "direct-chat", "busy");
35788
+ process.stderr.write(
35789
+ `direct-chat-channel(${AGT_AGENT_CODE_NAME}): [channel-deflection] cause=busy channel=direct-chat session=${sessionId}
35790
+ `
35791
+ );
35792
+ postDirectChatBusyAckNotice(sessionId);
35793
+ return;
35794
+ }
35795
+ const cause = classifyUndeliverableCause({
35796
+ hasTarget: true,
35797
+ integrationReady: true,
35798
+ // direct-chat egress is the API - always reachable
35799
+ tmux: probe.tmux,
35800
+ claude: probe.claude,
35801
+ withinStartupGrace: false,
35802
+ // 2min past an in-flight inbound ⇒ well clear of boot grace
35803
+ oldestPendingAgeMs: pendingAgeMs,
35804
+ paneLogFreshAgeMs
35805
+ });
35806
+ if (cause) {
35807
+ recordChannelDeflection(DIRECT_CHAT_CODE_NAME_AGENT_DIR, "direct-chat", cause);
35808
+ process.stderr.write(
35809
+ `direct-chat-channel(${AGT_AGENT_CODE_NAME}): [channel-deflection] cause=${cause} channel=direct-chat session=${sessionId}
35810
+ `
35811
+ );
35812
+ postDirectChatUndeliverableNotice(sessionId);
35813
+ }
35814
+ }, thresholdMs);
35815
+ timer.unref();
35816
+ }
35603
35817
  async function pollForMessages(sinceMs) {
35604
35818
  try {
35605
35819
  const res = await apiPost("/host/direct-chat/poll", {
@@ -35720,11 +35934,13 @@ async function pollForMessages(sinceMs) {
35720
35934
  }
35721
35935
  });
35722
35936
  if (!isNotice) {
35937
+ const arrivedWhileBusy = hasInFlightInbound(DIRECT_CHAT_PENDING_INBOUND_DIR);
35723
35938
  writeDirectChatPendingInboundMarker(
35724
35939
  DIRECT_CHAT_PENDING_INBOUND_DIR,
35725
35940
  msg.session_id,
35726
35941
  msg.id
35727
35942
  );
35943
+ scheduleDirectChatBusyAck(msg.session_id, msg.id, arrivedWhileBusy);
35728
35944
  }
35729
35945
  if (!isNotice && msg.sender_id) {
35730
35946
  writeTurnInitiatorMarker({
@@ -35864,7 +36080,7 @@ function sweepAgedDirectChatMarkersNow(thresholdMs) {
35864
36080
  const now = Date.now();
35865
36081
  const res = sweepAgedDirectChatMarkers(DIRECT_CHAT_PENDING_INBOUND_DIR, {
35866
36082
  readdir: (dir) => readdirSync4(dir),
35867
- readFile: (p2) => readFileSync8(p2, "utf8"),
36083
+ readFile: (p2) => readFileSync9(p2, "utf8"),
35868
36084
  unlink: (p2) => {
35869
36085
  if (existsSync6(p2)) unlinkSync3(p2);
35870
36086
  },
@@ -35900,7 +36116,7 @@ function sanitizeRecoveryText(text) {
35900
36116
  }
35901
36117
  function directChatRecoveryDeps() {
35902
36118
  return {
35903
- readFile: (p2) => readFileSync8(p2, "utf-8"),
36119
+ readFile: (p2) => readFileSync9(p2, "utf-8"),
35904
36120
  renameFile: (from, to) => renameSync5(from, to),
35905
36121
  unlinkFile: (p2) => {
35906
36122
  if (existsSync6(p2)) unlinkSync3(p2);
@@ -36012,6 +36228,9 @@ process.stdin.on("end", () => shutdown("stdin ended"));
36012
36228
  process.on("SIGTERM", () => shutdown("SIGTERM"));
36013
36229
  process.on("SIGINT", () => shutdown("SIGINT"));
36014
36230
  process.on("SIGHUP", () => shutdown("SIGHUP"));
36231
+ export {
36232
+ __resetDirectChatBusyAckThrottles
36233
+ };
36015
36234
  /*! Bundled license information:
36016
36235
 
36017
36236
  chokidar/index.js:
@@ -38721,7 +38721,7 @@ var INTEGRATION_REGISTRY = [
38721
38721
  id: "brand-ninja",
38722
38722
  name: "Brand Ninja",
38723
38723
  category: "social",
38724
- description: "Brand-aligned content generation: submit async content requests and track them, search transcripts and assemble ranked timelines from source clips, and discover the account catalog (channels, brands, topics, templates, content types, skills). Wired as the hosted Brand Ninja External-Content MCP at https://ext-api.app.brandninja.ai/v1/mcp.",
38724
+ description: "Brand-aligned content generation: submit async content requests and track them, search transcripts and assemble ranked timelines from source clips, discover the account catalog (channels, brands, topics, templates, content types, skills), and author catalog entries and knowledge (topics, timeline templates, knowledge items). Wired as the hosted Brand Ninja External-Content MCP at https://ext-api.app.brandninja.ai/v1/mcp.",
38725
38725
  // ENG-6820: same remote streamable-HTTP MCP + OAuth pattern as Granola.
38726
38726
  // Brand Ninja's server implements the full MCP discovery chain (RFC
38727
38727
  // 9728/8414/7591); auth is OAuth 2.0 authorization-code with PKCE (S256)
@@ -38739,6 +38739,8 @@ var INTEGRATION_REGISTRY = [
38739
38739
  { id: "brand-ninja:list-catalog", name: "List Catalog", description: "Discover account catalog metadata: brands, topics, timeline templates, content types, and source, output and conversion skills (list_brands, list_topics, list_timeline_templates, list_content_types, list_source_skills, list_output_skills, list_conversion_skills)", access: "read" },
38740
38740
  { id: "brand-ninja:search-transcripts", name: "Search Transcripts", description: "Search the transcripts available to the account to source or reference when generating content, text and metadata (search_transcripts)", access: "read" },
38741
38741
  { id: "brand-ninja:assemble-timeline", name: "Assemble Timeline", description: "Turn transcript spans into topic-tagged source clips and rank a topic's clips into a ranked timeline via an async LLM job (create_source_clips, create_timeline_ranking)", access: "write" },
38742
+ { id: "brand-ninja:manage-catalog", name: "Manage Catalog", description: "Author catalog entries the other Brand Ninja tools then reference: create a topic or a timeline template (create_topic, create_timeline_template)", access: "write" },
38743
+ { id: "brand-ninja:manage-knowledge", name: "Manage Knowledge", description: "Build the account knowledge base: create a knowledge item and link it to a topic or brand to ground generated content (create_knowledge, link_knowledge)", access: "write" },
38742
38744
  { id: "brand-ninja:read-credentials", name: "Read Credentials", description: "Read-only External-API credential metadata, secrets stripped. Requires the elevated external-api/admin scope (list_credentials)", access: "admin" }
38743
38745
  ],
38744
38746
  docs_url: "https://ext-api.app.brandninja.ai/v1/mcp",
@@ -32604,7 +32604,7 @@ var INTEGRATION_REGISTRY = [
32604
32604
  id: "brand-ninja",
32605
32605
  name: "Brand Ninja",
32606
32606
  category: "social",
32607
- description: "Brand-aligned content generation: submit async content requests and track them, search transcripts and assemble ranked timelines from source clips, and discover the account catalog (channels, brands, topics, templates, content types, skills). Wired as the hosted Brand Ninja External-Content MCP at https://ext-api.app.brandninja.ai/v1/mcp.",
32607
+ description: "Brand-aligned content generation: submit async content requests and track them, search transcripts and assemble ranked timelines from source clips, discover the account catalog (channels, brands, topics, templates, content types, skills), and author catalog entries and knowledge (topics, timeline templates, knowledge items). Wired as the hosted Brand Ninja External-Content MCP at https://ext-api.app.brandninja.ai/v1/mcp.",
32608
32608
  // ENG-6820: same remote streamable-HTTP MCP + OAuth pattern as Granola.
32609
32609
  // Brand Ninja's server implements the full MCP discovery chain (RFC
32610
32610
  // 9728/8414/7591); auth is OAuth 2.0 authorization-code with PKCE (S256)
@@ -32622,6 +32622,8 @@ var INTEGRATION_REGISTRY = [
32622
32622
  { id: "brand-ninja:list-catalog", name: "List Catalog", description: "Discover account catalog metadata: brands, topics, timeline templates, content types, and source, output and conversion skills (list_brands, list_topics, list_timeline_templates, list_content_types, list_source_skills, list_output_skills, list_conversion_skills)", access: "read" },
32623
32623
  { id: "brand-ninja:search-transcripts", name: "Search Transcripts", description: "Search the transcripts available to the account to source or reference when generating content, text and metadata (search_transcripts)", access: "read" },
32624
32624
  { id: "brand-ninja:assemble-timeline", name: "Assemble Timeline", description: "Turn transcript spans into topic-tagged source clips and rank a topic's clips into a ranked timeline via an async LLM job (create_source_clips, create_timeline_ranking)", access: "write" },
32625
+ { id: "brand-ninja:manage-catalog", name: "Manage Catalog", description: "Author catalog entries the other Brand Ninja tools then reference: create a topic or a timeline template (create_topic, create_timeline_template)", access: "write" },
32626
+ { id: "brand-ninja:manage-knowledge", name: "Manage Knowledge", description: "Build the account knowledge base: create a knowledge item and link it to a topic or brand to ground generated content (create_knowledge, link_knowledge)", access: "write" },
32625
32627
  { id: "brand-ninja:read-credentials", name: "Read Credentials", description: "Read-only External-API credential metadata, secrets stripped. Requires the elevated external-api/admin scope (list_credentials)", access: "admin" }
32626
32628
  ],
32627
32629
  docs_url: "https://ext-api.app.brandninja.ai/v1/mcp",
@@ -32954,7 +32954,7 @@ var INTEGRATION_REGISTRY = [
32954
32954
  id: "brand-ninja",
32955
32955
  name: "Brand Ninja",
32956
32956
  category: "social",
32957
- description: "Brand-aligned content generation: submit async content requests and track them, search transcripts and assemble ranked timelines from source clips, and discover the account catalog (channels, brands, topics, templates, content types, skills). Wired as the hosted Brand Ninja External-Content MCP at https://ext-api.app.brandninja.ai/v1/mcp.",
32957
+ description: "Brand-aligned content generation: submit async content requests and track them, search transcripts and assemble ranked timelines from source clips, discover the account catalog (channels, brands, topics, templates, content types, skills), and author catalog entries and knowledge (topics, timeline templates, knowledge items). Wired as the hosted Brand Ninja External-Content MCP at https://ext-api.app.brandninja.ai/v1/mcp.",
32958
32958
  // ENG-6820: same remote streamable-HTTP MCP + OAuth pattern as Granola.
32959
32959
  // Brand Ninja's server implements the full MCP discovery chain (RFC
32960
32960
  // 9728/8414/7591); auth is OAuth 2.0 authorization-code with PKCE (S256)
@@ -32972,6 +32972,8 @@ var INTEGRATION_REGISTRY = [
32972
32972
  { id: "brand-ninja:list-catalog", name: "List Catalog", description: "Discover account catalog metadata: brands, topics, timeline templates, content types, and source, output and conversion skills (list_brands, list_topics, list_timeline_templates, list_content_types, list_source_skills, list_output_skills, list_conversion_skills)", access: "read" },
32973
32973
  { id: "brand-ninja:search-transcripts", name: "Search Transcripts", description: "Search the transcripts available to the account to source or reference when generating content, text and metadata (search_transcripts)", access: "read" },
32974
32974
  { id: "brand-ninja:assemble-timeline", name: "Assemble Timeline", description: "Turn transcript spans into topic-tagged source clips and rank a topic's clips into a ranked timeline via an async LLM job (create_source_clips, create_timeline_ranking)", access: "write" },
32975
+ { id: "brand-ninja:manage-catalog", name: "Manage Catalog", description: "Author catalog entries the other Brand Ninja tools then reference: create a topic or a timeline template (create_topic, create_timeline_template)", access: "write" },
32976
+ { id: "brand-ninja:manage-knowledge", name: "Manage Knowledge", description: "Build the account knowledge base: create a knowledge item and link it to a topic or brand to ground generated content (create_knowledge, link_knowledge)", access: "write" },
32975
32977
  { id: "brand-ninja:read-credentials", name: "Read Credentials", description: "Read-only External-API credential metadata, secrets stripped. Requires the elevated external-api/admin scope (list_credentials)", access: "admin" }
32976
32978
  ],
32977
32979
  docs_url: "https://ext-api.app.brandninja.ai/v1/mcp",
@@ -36,7 +36,7 @@ import {
36
36
  writeDirectChatSessionState,
37
37
  writeEgressAllowlist,
38
38
  writePersistentClaudeWrapper
39
- } from "./chunk-COOBM6RF.js";
39
+ } from "./chunk-RIWZIYJ7.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-O2RYNO42.js.map
80
+ //# sourceMappingURL=persistent-session-3PAEXZHX.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-COOBM6RF.js";
3
+ } from "./chunk-RIWZIYJ7.js";
4
4
  import "./chunk-XWVM4KPK.js";
5
5
 
6
6
  // src/lib/responsiveness-probe.ts
@@ -437,4 +437,4 @@ export {
437
437
  readAndResetSlackReplyBindingClassifications,
438
438
  readAndResetSlackReplyTargetClassifications
439
439
  };
440
- //# sourceMappingURL=responsiveness-probe-IZJNJO2C.js.map
440
+ //# sourceMappingURL=responsiveness-probe-UEOKWHTD.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.367",
3
+ "version": "0.28.369",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {