@integrity-labs/agt-cli 0.28.1171 → 0.28.1173

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
@@ -40,7 +40,7 @@ import {
40
40
  success,
41
41
  table,
42
42
  warn
43
- } from "../chunk-TJ4MVWNH.js";
43
+ } from "../chunk-W34QK2EY.js";
44
44
  import {
45
45
  getProjectDir,
46
46
  isSessionResumeDisabled,
@@ -5513,7 +5513,7 @@ import { execFileSync, execSync } from "child_process";
5513
5513
  import { existsSync as existsSync11, realpathSync as realpathSync2 } from "fs";
5514
5514
  import chalk18 from "chalk";
5515
5515
  import ora16 from "ora";
5516
- var cliVersion = true ? "0.28.1171" : "dev";
5516
+ var cliVersion = true ? "0.28.1173" : "dev";
5517
5517
  async function fetchLatestVersion() {
5518
5518
  const host2 = getHost();
5519
5519
  if (!host2) return null;
@@ -6773,7 +6773,7 @@ function handleError(err) {
6773
6773
  }
6774
6774
 
6775
6775
  // src/bin/agt.ts
6776
- var cliVersion2 = true ? "0.28.1171" : "dev";
6776
+ var cliVersion2 = true ? "0.28.1173" : "dev";
6777
6777
  var program = new Command();
6778
6778
  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");
6779
6779
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -8078,7 +8078,7 @@ function exchangeFailureKind(err) {
8078
8078
  }
8079
8079
 
8080
8080
  // src/lib/api-client.ts
8081
- var agtCliVersion = true ? "0.28.1171" : "dev";
8081
+ var agtCliVersion = true ? "0.28.1173" : "dev";
8082
8082
  var lastConfigHash = null;
8083
8083
  function setConfigHash(hash) {
8084
8084
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -13235,4 +13235,4 @@ export {
13235
13235
  managerInstallSystemUnitCommand,
13236
13236
  managerUninstallSystemUnitCommand
13237
13237
  };
13238
- //# sourceMappingURL=chunk-TJ4MVWNH.js.map
13238
+ //# sourceMappingURL=chunk-W34QK2EY.js.map
@@ -77,7 +77,7 @@ import {
77
77
  setConfigHash,
78
78
  shellGuard,
79
79
  tripClass
80
- } from "../chunk-TJ4MVWNH.js";
80
+ } from "../chunk-W34QK2EY.js";
81
81
  import {
82
82
  CLAUDE_CODE_SECURITY_FLOOR,
83
83
  SELF_UPDATE_TIMEOUTS,
@@ -15928,7 +15928,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
15928
15928
  var lastVersionCheckAt = 0;
15929
15929
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
15930
15930
  var lastResponsivenessProbeAt = 0;
15931
- var agtCliVersion = true ? "0.28.1171" : "dev";
15931
+ var agtCliVersion = true ? "0.28.1173" : "dev";
15932
15932
  function resolveBrewPath(execFileSync3) {
15933
15933
  try {
15934
15934
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -47473,6 +47473,9 @@ function classifyUndeliverableCause(i) {
47473
47473
  return null;
47474
47474
  }
47475
47475
  var UNDELIVERABLE_NOTICE_THROTTLE_MS = 5 * 60 * 1e3;
47476
+ function markerOwesNoReply(m) {
47477
+ return m.discretionary === true || m.ingress_only === true;
47478
+ }
47476
47479
  function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
47477
47480
  return lastNoticeAtMs == null || nowMs - lastNoticeAtMs >= throttleMs;
47478
47481
  }
@@ -47644,7 +47647,7 @@ function oldestPendingMarkerAgeMs(dir, now = Date.now(), opts) {
47644
47647
  let receivedAt;
47645
47648
  try {
47646
47649
  const raw = JSON.parse(readFileSync9(join10(dir, name), "utf-8"));
47647
- if (raw.discretionary === true) continue;
47650
+ if (markerOwesNoReply(raw)) continue;
47648
47651
  if (!opts?.includeSeen && typeof raw.seen_at === "string" && raw.seen_at) continue;
47649
47652
  receivedAt = raw.received_at;
47650
47653
  } catch {
@@ -54079,6 +54082,7 @@ var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
54079
54082
  // ADR-0025 approval tools, registered by `approval-tools.ts` against the same
54080
54083
  // server object, so the wrapper in index.ts covers them too.
54081
54084
  "cancel_approval",
54085
+ "approval_annotate",
54082
54086
  // Status / identity.
54083
54087
  "status_standup",
54084
54088
  "status_update",
package/dist/mcp/index.js CHANGED
@@ -47352,6 +47352,7 @@ var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
47352
47352
  // ADR-0025 approval tools, registered by `approval-tools.ts` against the same
47353
47353
  // server object, so the wrapper in index.ts covers them too.
47354
47354
  "cancel_approval",
47355
+ "approval_annotate",
47355
47356
  // Status / identity.
47356
47357
  "status_standup",
47357
47358
  "status_update",
@@ -48694,6 +48695,22 @@ function describeKanbanLink(requestedCardId, resp) {
48694
48695
  }
48695
48696
  return ` Card ${requestedCardId} was NOT linked to this approval (${why}), so the card is not waiting on it and answering the card will not decide it. Leave the request as is (the approver can still decide it from the Approvals page) and do not park the card as a human Approve/Deny wait.`;
48696
48697
  }
48698
+ var APPROVAL_ANNOTATE_EVIDENCE_MAX = 1e3;
48699
+ function describeAnnotation(resp) {
48700
+ const head = `Recorded your recommendation (${resp.recommendation}) on request ${resp.request_id}. This is not a decision; the person still decides.`;
48701
+ switch (resp.card.outcome) {
48702
+ case "card_updated":
48703
+ return `${head} It now shows on their approval card.`;
48704
+ case "follow_up_posted":
48705
+ return `${head} It was posted as a follow-up beside their card. It also shows on the Approvals page.`;
48706
+ case "unchanged":
48707
+ return `${head} It is identical to your last one, which already reached their card, so nothing was re-sent.`;
48708
+ case "no_card":
48709
+ return `${head} No chat card was delivered for this request, so it shows on the Approvals page only.`;
48710
+ default:
48711
+ return `${head} It could NOT be added to their chat card (${resp.card.reason ?? "unknown"}), so it shows on the Approvals page only. Do not say they have seen it.`;
48712
+ }
48713
+ }
48697
48714
  function statusToOutcome(status) {
48698
48715
  switch (status) {
48699
48716
  case "active":
@@ -48803,6 +48820,31 @@ function registerApprovalTools(server2, deps) {
48803
48820
  }
48804
48821
  }
48805
48822
  );
48823
+ server2.tool(
48824
+ "approval_annotate",
48825
+ "Attach your recommendation and what you checked to an approval request from an agent that reports to you, directly or further down your reports-to chain. The person deciding sees it on their approval card, labelled as an agent recommendation. It is NOT a decision: it cannot approve, deny or change the request, and the human still decides. Use it after you have verified the facts yourself, never by repeating the requester's own words. Calling it again replaces your earlier recommendation. Only works while the request is pending.",
48826
+ {
48827
+ request_id: external_exports.string().min(1).describe("The request id from the notice telling you about your report's approval."),
48828
+ recommendation: external_exports.enum(["approve", "deny", "unsure"]).describe("What you recommend the person decides."),
48829
+ evidence: external_exports.string().min(1).max(APPROVAL_ANNOTATE_EVIDENCE_MAX).describe('What you checked and found, e.g. "tree clean, no stash, all commits on origin". Shown as one line of quoted text, attributed to you.')
48830
+ },
48831
+ async (params) => {
48832
+ try {
48833
+ const resp = await apiPost2(`/approvals/${encodeURIComponent(params.request_id)}/annotate`, {
48834
+ agent_id: agentId,
48835
+ recommendation: params.recommendation,
48836
+ evidence: params.evidence
48837
+ });
48838
+ return { content: [{ type: "text", text: describeAnnotation(resp) }] };
48839
+ } catch (err) {
48840
+ const msg = err instanceof Error ? err.message : String(err);
48841
+ return {
48842
+ content: [{ type: "text", text: `Could not attach the recommendation to ${params.request_id}: ${msg}` }],
48843
+ isError: true
48844
+ };
48845
+ }
48846
+ }
48847
+ );
48806
48848
  server2.tool(
48807
48849
  "cancel_approval",
48808
48850
  "Withdraw an approval request you no longer need (only works while it is still pending). The Manager stops being asked. Use when the underlying need went away.",
@@ -52424,6 +52466,8 @@ var LOCAL_TOOL_NAMES = /* @__PURE__ */ new Set([
52424
52466
  "request_approval",
52425
52467
  "check_approval",
52426
52468
  "cancel_approval",
52469
+ // ENG-11573: a managing agent's recommendation on a report's approval.
52470
+ "approval_annotate",
52427
52471
  "kanban_list",
52428
52472
  "kanban_search",
52429
52473
  "kanban_get",
@@ -31065,6 +31065,7 @@ var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
31065
31065
  // ADR-0025 approval tools, registered by `approval-tools.ts` against the same
31066
31066
  // server object, so the wrapper in index.ts covers them too.
31067
31067
  "cancel_approval",
31068
+ "approval_annotate",
31068
31069
  // Status / identity.
31069
31070
  "status_standup",
31070
31071
  "status_update",
@@ -41280,6 +41281,12 @@ var UNDELIVERABLE_NOTICE_THROTTLE_MS = 5 * 60 * 1e3;
41280
41281
  function undeliverableNoticeEligible(shouldEngage) {
41281
41282
  return shouldEngage;
41282
41283
  }
41284
+ function decideInboundReplyOwed(i) {
41285
+ return i.shouldEngage && !i.ingressOnlyThread;
41286
+ }
41287
+ function markerOwesNoReply(m) {
41288
+ return m.discretionary === true || m.ingress_only === true;
41289
+ }
41283
41290
  function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
41284
41291
  return lastNoticeAtMs == null || nowMs - lastNoticeAtMs >= throttleMs;
41285
41292
  }
@@ -41646,7 +41653,7 @@ function oldestPendingMarkerAgeMs(dir, now = Date.now(), opts) {
41646
41653
  let receivedAt;
41647
41654
  try {
41648
41655
  const raw = JSON.parse(readFileSync3(join3(dir, name), "utf-8"));
41649
- if (raw.discretionary === true) continue;
41656
+ if (markerOwesNoReply(raw)) continue;
41650
41657
  if (!opts?.includeSeen && typeof raw.seen_at === "string" && raw.seen_at) continue;
41651
41658
  receivedAt = raw.received_at;
41652
41659
  } catch {
@@ -43596,7 +43603,7 @@ function countOwedSlackRepliesForThread(dir, channel, threadTs) {
43596
43603
  try {
43597
43604
  const m = JSON.parse(readFileSync5(fullPath, "utf-8"));
43598
43605
  if (!m || typeof m !== "object") return;
43599
- if (m.discretionary === true || m.undeliverable === true) return;
43606
+ if (m.discretionary === true || m.undeliverable === true || m.ingress_only === true) return;
43600
43607
  owed += 1;
43601
43608
  } catch {
43602
43609
  }
@@ -43607,6 +43614,21 @@ function markSeenAllSlackPendingMarkersForThread(dir, channel, threadTs, markSee
43607
43614
  const prefix = `${sanitizeMarkerSegment(channel)}__${sanitizeMarkerSegment(threadTs)}__`;
43608
43615
  return applyToMatchingMarkers(dir, prefix, ".json", markSeen);
43609
43616
  }
43617
+ function clearIngressOnlySlackPendingMarkersForThread(dir, channel, threadTs, clear = defaultClearMarkerFile) {
43618
+ const cleared = [];
43619
+ markSeenAllSlackPendingMarkersForThread(dir, channel, threadTs, (fullPath) => {
43620
+ let marker;
43621
+ try {
43622
+ marker = JSON.parse(readFileSync5(fullPath, "utf-8"));
43623
+ } catch {
43624
+ return;
43625
+ }
43626
+ if (typeof marker !== "object" || marker === null || marker.ingress_only !== true) return;
43627
+ clear(fullPath);
43628
+ if (typeof marker.message_ts === "string") cleared.push(marker.message_ts);
43629
+ });
43630
+ return cleared;
43631
+ }
43610
43632
  function clearSlackPendingMarkerByMessageTs(dir, channel, messageTs, clear = defaultClearMarkerFile) {
43611
43633
  const channelPrefix = `${sanitizeMarkerSegment(channel)}__`;
43612
43634
  const messageSuffix = `__${sanitizeMarkerSegment(messageTs)}.json`;
@@ -47915,7 +47937,7 @@ function sweepSlackStaleMarkers(thresholdMs) {
47915
47937
  thresholdMs,
47916
47938
  hardCeilingMs: STALE_MARKER_MS,
47917
47939
  discretionaryThresholdMs: DISCRETIONARY_MARKER_MS,
47918
- discretionary: marker.discretionary,
47940
+ discretionary: markerOwesNoReply(marker),
47919
47941
  hasPayload: Boolean(marker.payload),
47920
47942
  replayCount: marker.replay_count ?? 0,
47921
47943
  replayEnabled
@@ -47923,7 +47945,7 @@ function sweepSlackStaleMarkers(thresholdMs) {
47923
47945
  const structurallyBroken = !channel || !thread_ts || !message_ts;
47924
47946
  const timeStale = isPendingMarkerStale(received_at, now, effectiveThresholdMs);
47925
47947
  if (structurallyBroken || timeStale) {
47926
- if (!marker.discretionary) {
47948
+ if (!markerOwesNoReply(marker)) {
47927
47949
  if (structurallyBroken || !isMarkerGenuinelyAged(received_at, now, effectiveThresholdMs)) {
47928
47950
  recordChannelDeflection(SLACK_AGENT_DIR, "slack", "marker_corrupt");
47929
47951
  } else if (marker.payload) {
@@ -48006,7 +48028,7 @@ function findSlackProgressTarget() {
48006
48028
  continue;
48007
48029
  }
48008
48030
  if (!m.channel || !m.thread_ts) continue;
48009
- if (m.discretionary === true || m.undeliverable === true) continue;
48031
+ if (markerOwesNoReply(m) || m.undeliverable === true) continue;
48010
48032
  const ms = Date.parse(m.received_at ?? "");
48011
48033
  if (!Number.isFinite(ms)) continue;
48012
48034
  if (ms < bestMs) {
@@ -48156,7 +48178,7 @@ function listPendingSlackConversations() {
48156
48178
  );
48157
48179
  if (typeof marker.channel !== "string" || !marker.channel) continue;
48158
48180
  if (typeof marker.thread_ts !== "string" || !marker.thread_ts) continue;
48159
- if (marker.discretionary === true) continue;
48181
+ if (markerOwesNoReply(marker)) continue;
48160
48182
  if (typeof marker.seen_at === "string" && marker.seen_at) continue;
48161
48183
  const isThreadReply = typeof marker.message_ts === "string" && marker.message_ts !== marker.thread_ts;
48162
48184
  const key2 = isThreadReply ? `${marker.channel}:${marker.thread_ts}` : marker.channel;
@@ -51315,7 +51337,8 @@ async function replayPendingSlackMarkers() {
51315
51337
  // ENG-6366: auto-followed un-mentioned thread inbound the agent may
51316
51338
  // legitimately silently skip — never re-deliver it (it never clears,
51317
51339
  // so it would otherwise re-push MAX_MARKER_REPLAYS times under replay).
51318
- discretionary: marker.discretionary,
51340
+ // ENG-11709: the same for a marker in a third-party-bot thread.
51341
+ discretionary: markerOwesNoReply(marker),
51319
51342
  // ENG-7736: bound the pane-fresh defer (clamped under the orphan
51320
51343
  // window) so a perpetually-fresh pane (respawn churn) can't hold off
51321
51344
  // replay until the orphan sweep GCs the marker - the Sherlock loss.
@@ -51774,6 +51797,16 @@ async function connectSocketMode() {
51774
51797
  }
51775
51798
  } else if (access.kind !== "drop" && (!isBot || peerClassification?.kind === "peer-ingress")) {
51776
51799
  ingressOnlyThreads.clear(evt.channel, ingressThreadTs);
51800
+ if (evt.channel && ingressThreadTs) {
51801
+ for (const clearedTs of clearIngressOnlySlackPendingMarkersForThread(
51802
+ SLACK_PENDING_INBOUND_DIR,
51803
+ evt.channel,
51804
+ ingressThreadTs,
51805
+ clearSlackMarkerFileWithHeal
51806
+ )) {
51807
+ slackInboundRegistry.markCleared(evt.channel, clearedTs);
51808
+ }
51809
+ }
51777
51810
  }
51778
51811
  if (access.kind === "drop") {
51779
51812
  const channelHash = createHash3("sha256").update(evt.channel ?? "").digest("hex").slice(0, 8);
@@ -52044,6 +52077,10 @@ async function connectSocketMode() {
52044
52077
  botPostedInThread,
52045
52078
  addressesAnotherAgent
52046
52079
  });
52080
+ const replyOwed = decideInboundReplyOwed({
52081
+ shouldEngage,
52082
+ ingressOnlyThread: ingressOnlyThreads.isIngressOnly(channel, threadTs)
52083
+ });
52047
52084
  const ackProbe = process.env.TMUX && AGENT_CODE_NAME ? probeAgentSessionCached(AGENT_CODE_NAME) : { tmux: "unknown", claude: "unknown" };
52048
52085
  let paneLogFreshAgeMs = null;
52049
52086
  if (SLACK_AGENT_DIR) {
@@ -52083,13 +52120,13 @@ async function connectSocketMode() {
52083
52120
  body: JSON.stringify({ channel, timestamp: ts, name: reactionName })
52084
52121
  }).catch(() => {
52085
52122
  });
52086
- if (ackDecision === "undeliverable" && channel && undeliverableNoticeEligible(shouldEngage)) {
52123
+ if (ackDecision === "undeliverable" && channel && undeliverableNoticeEligible(replyOwed)) {
52087
52124
  postUndeliverableNotice(channel, threadTs, isThreadReply, cause);
52088
52125
  }
52089
52126
  }
52090
52127
  if (channel && ts && armMarker) {
52091
- trackPendingMessage(channel, threadTs, ts, ackDecision === "undeliverable", !shouldEngage);
52092
- if (ackDecision === "ack" && shouldEngage) {
52128
+ trackPendingMessage(channel, threadTs, ts, ackDecision === "undeliverable", !replyOwed);
52129
+ if (ackDecision === "ack" && replyOwed) {
52093
52130
  scheduleBusyAck(
52094
52131
  channel,
52095
52132
  threadTs,
@@ -52168,7 +52205,7 @@ async function connectSocketMode() {
52168
52205
  // fork) - an auto-followed, un-addressed thread message (discretionary,
52169
52206
  // !shouldEngage) owes no reply. Additive only; no consumer gates on
52170
52207
  // these yet (Slice 2).
52171
- ...conversationalLaneMeta(shouldEngage),
52208
+ ...conversationalLaneMeta(replyOwed),
52172
52209
  // ENG-7919: surface the effective auto-follow state (already cleared when
52173
52210
  // the bot was @-mentioned) so the agent sees auto_followed="true" only
52174
52211
  // when the message genuinely requires discretionary engagement judgment.
@@ -52199,7 +52236,7 @@ async function connectSocketMode() {
52199
52236
  };
52200
52237
  recordLaneClassification(SLACK_AGENT_DIR, "slack", {
52201
52238
  lane: "conversational",
52202
- expectsReply: shouldEngage
52239
+ expectsReply: replyOwed
52203
52240
  });
52204
52241
  if (channel && ts && armMarker) {
52205
52242
  attachSlackReplayPayload(channel, threadTs, ts, replayPayload);
@@ -52250,7 +52287,7 @@ async function connectSocketMode() {
52250
52287
  sinceMs: dispatchedAtMs
52251
52288
  });
52252
52289
  }
52253
- if (shouldEngage) seedSlackProgressHeartbeat();
52290
+ if (replyOwed) seedSlackProgressHeartbeat();
52254
52291
  if (channel) {
52255
52292
  conversationIngestClient?.ingest({
52256
52293
  channel: "slack",
@@ -31282,6 +31282,7 @@ var MAINTENANCE_GATED_TOOLS = /* @__PURE__ */ new Set([
31282
31282
  // ADR-0025 approval tools, registered by `approval-tools.ts` against the same
31283
31283
  // server object, so the wrapper in index.ts covers them too.
31284
31284
  "cancel_approval",
31285
+ "approval_annotate",
31285
31286
  // Status / identity.
31286
31287
  "status_standup",
31287
31288
  "status_update",
@@ -44212,6 +44213,9 @@ function decideRecoveryHeal(i) {
44212
44213
  return i.wasUndeliverable && i.hasTarget ? "heal" : "none";
44213
44214
  }
44214
44215
  var UNDELIVERABLE_NOTICE_THROTTLE_MS = 5 * 60 * 1e3;
44216
+ function markerOwesNoReply(m) {
44217
+ return m.discretionary === true || m.ingress_only === true;
44218
+ }
44215
44219
  function shouldPostUndeliverableNotice(lastNoticeAtMs, nowMs, throttleMs = UNDELIVERABLE_NOTICE_THROTTLE_MS) {
44216
44220
  return lastNoticeAtMs == null || nowMs - lastNoticeAtMs >= throttleMs;
44217
44221
  }
@@ -44553,7 +44557,7 @@ function oldestPendingMarkerAgeMs(dir, now = Date.now(), opts) {
44553
44557
  let receivedAt;
44554
44558
  try {
44555
44559
  const raw = JSON.parse(readFileSync14(join17(dir, name), "utf-8"));
44556
- if (raw.discretionary === true) continue;
44560
+ if (markerOwesNoReply(raw)) continue;
44557
44561
  if (!opts?.includeSeen && typeof raw.seen_at === "string" && raw.seen_at) continue;
44558
44562
  receivedAt = raw.received_at;
44559
44563
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.1171",
3
+ "version": "0.28.1173",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {