@integrity-labs/agt-cli 0.28.165 → 0.28.166

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-KBFXXA5U.js";
40
+ } from "../chunk-HWVCQ6PC.js";
41
41
  import {
42
42
  CHANNEL_REGISTRY,
43
43
  DEFAULT_FRAMEWORK,
@@ -4778,7 +4778,7 @@ import { execFileSync, execSync } from "child_process";
4778
4778
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4779
4779
  import chalk18 from "chalk";
4780
4780
  import ora16 from "ora";
4781
- var cliVersion = true ? "0.28.165" : "dev";
4781
+ var cliVersion = true ? "0.28.166" : "dev";
4782
4782
  async function fetchLatestVersion() {
4783
4783
  const host2 = getHost();
4784
4784
  if (!host2) return null;
@@ -5792,7 +5792,7 @@ function handleError(err) {
5792
5792
  }
5793
5793
 
5794
5794
  // src/bin/agt.ts
5795
- var cliVersion2 = true ? "0.28.165" : "dev";
5795
+ var cliVersion2 = true ? "0.28.166" : "dev";
5796
5796
  var program = new Command();
5797
5797
  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");
5798
5798
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -7948,7 +7948,7 @@ function requireHost() {
7948
7948
  }
7949
7949
 
7950
7950
  // src/lib/api-client.ts
7951
- var agtCliVersion = true ? "0.28.165" : "dev";
7951
+ var agtCliVersion = true ? "0.28.166" : "dev";
7952
7952
  var lastConfigHash = null;
7953
7953
  function setConfigHash(hash) {
7954
7954
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -9253,4 +9253,4 @@ export {
9253
9253
  managerInstallSystemUnitCommand,
9254
9254
  managerUninstallSystemUnitCommand
9255
9255
  };
9256
- //# sourceMappingURL=chunk-KBFXXA5U.js.map
9256
+ //# sourceMappingURL=chunk-HWVCQ6PC.js.map
@@ -28,7 +28,7 @@ import {
28
28
  requireHost,
29
29
  safeWriteJsonAtomic,
30
30
  setConfigHash
31
- } from "../chunk-KBFXXA5U.js";
31
+ } from "../chunk-HWVCQ6PC.js";
32
32
  import {
33
33
  getProjectDir as getProjectDir2,
34
34
  getReadyTasks,
@@ -6878,7 +6878,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
6878
6878
  var lastVersionCheckAt = 0;
6879
6879
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
6880
6880
  var lastResponsivenessProbeAt = 0;
6881
- var agtCliVersion = true ? "0.28.165" : "dev";
6881
+ var agtCliVersion = true ? "0.28.166" : "dev";
6882
6882
  function resolveBrewPath(execFileSync4) {
6883
6883
  try {
6884
6884
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -15035,6 +15035,141 @@ function buildAgentConfigReport(opts) {
15035
15035
  return lines.join("\n");
15036
15036
  }
15037
15037
 
15038
+ // src/agent-status-report.ts
15039
+ var STATUS_REPORT_SCHEMA_VERSION = 1;
15040
+ var FRESHNESS_VALUES = /* @__PURE__ */ new Set(["cached", "live", "unknown"]);
15041
+ var LEVEL_VALUES = /* @__PURE__ */ new Set(["ok", "degraded", "down", "error", "unknown"]);
15042
+ function coerceLevel(value) {
15043
+ return typeof value === "string" && LEVEL_VALUES.has(value) ? value : "unknown";
15044
+ }
15045
+ function coerceFreshness(value) {
15046
+ return typeof value === "string" && FRESHNESS_VALUES.has(value) ? value : "unknown";
15047
+ }
15048
+ function coerceCheckedAt(value) {
15049
+ return typeof value === "string" && value.length > 0 ? value : null;
15050
+ }
15051
+ function parseAgentStatusReport(raw) {
15052
+ if (!raw || typeof raw !== "object") return null;
15053
+ const obj = raw;
15054
+ if (!Array.isArray(obj.channels) || !Array.isArray(obj.integrations)) return null;
15055
+ const channels = obj.channels.filter((c) => !!c && typeof c === "object").map((c) => ({
15056
+ channel_id: typeof c.channel_id === "string" ? c.channel_id : "unknown",
15057
+ level: coerceLevel(c.level),
15058
+ freshness: coerceFreshness(c.freshness),
15059
+ checked_at: coerceCheckedAt(c.checked_at),
15060
+ ...typeof c.reason === "string" && c.reason ? { reason: c.reason } : {}
15061
+ }));
15062
+ const integrations = obj.integrations.filter((i) => !!i && typeof i === "object").map((i) => ({
15063
+ id: typeof i.id === "string" ? i.id : "unknown",
15064
+ definition_id: typeof i.definition_id === "string" ? i.definition_id : "unknown",
15065
+ display_name: typeof i.display_name === "string" && i.display_name ? i.display_name : typeof i.definition_id === "string" ? i.definition_id : "unknown",
15066
+ scope: i.scope === "team" || i.scope === "organization" ? i.scope : "agent",
15067
+ level: coerceLevel(i.level),
15068
+ freshness: coerceFreshness(i.freshness),
15069
+ checked_at: coerceCheckedAt(i.checked_at),
15070
+ ...typeof i.consecutive_failures === "number" ? { consecutive_failures: i.consecutive_failures } : {},
15071
+ ...typeof i.reason === "string" && i.reason ? { reason: i.reason } : {}
15072
+ }));
15073
+ return {
15074
+ schema_version: typeof obj.schema_version === "number" ? obj.schema_version : 0,
15075
+ agent_id: typeof obj.agent_id === "string" ? obj.agent_id : "",
15076
+ generated_at: typeof obj.generated_at === "string" ? obj.generated_at : "",
15077
+ manager_last_seen: coerceCheckedAt(obj.manager_last_seen),
15078
+ channels,
15079
+ integrations
15080
+ };
15081
+ }
15082
+ function mergeOwnChannelLiveStatus(report, channelId, live) {
15083
+ const liveEntry = {
15084
+ channel_id: channelId,
15085
+ level: live.level,
15086
+ freshness: "live",
15087
+ checked_at: live.checked_at,
15088
+ ...live.reason ? { reason: live.reason } : {}
15089
+ };
15090
+ const existingIndex = report.channels.findIndex((ch) => ch.channel_id === channelId);
15091
+ const channels = existingIndex === -1 ? [...report.channels, liveEntry] : report.channels.map((ch, i) => i === existingIndex ? liveEntry : ch);
15092
+ return { ...report, channels };
15093
+ }
15094
+ function statusLevelGlyph(level) {
15095
+ switch (level) {
15096
+ case "ok":
15097
+ return "\u{1F7E2}";
15098
+ case "degraded":
15099
+ return "\u{1F7E1}";
15100
+ case "down":
15101
+ case "error":
15102
+ return "\u{1F534}";
15103
+ default:
15104
+ return "\u26AA";
15105
+ }
15106
+ }
15107
+ function formatStatusAge(checkedAt, nowMs) {
15108
+ if (!checkedAt) return "never";
15109
+ const t = Date.parse(checkedAt);
15110
+ if (Number.isNaN(t)) return "unknown";
15111
+ const diffMs = nowMs - t;
15112
+ if (diffMs < 45e3) return "just now";
15113
+ const mins = Math.round(diffMs / 6e4);
15114
+ if (mins < 60) return `${mins}m ago`;
15115
+ const hours = Math.round(diffMs / 36e5);
15116
+ if (hours < 24) return `${hours}h ago`;
15117
+ const days = Math.round(diffMs / 864e5);
15118
+ return `${days}d ago`;
15119
+ }
15120
+
15121
+ // src/slack-status-render.ts
15122
+ var FRESHNESS_LABEL = {
15123
+ live: "live",
15124
+ cached: "cached",
15125
+ unknown: "unknown"
15126
+ };
15127
+ function freshnessLabel(freshness) {
15128
+ return FRESHNESS_LABEL[freshness] ?? freshness;
15129
+ }
15130
+ function renderChannelLine(ch, nowMs) {
15131
+ const glyph = statusLevelGlyph(ch.level);
15132
+ const fresh = freshnessLabel(ch.freshness);
15133
+ const age = formatStatusAge(ch.checked_at, nowMs);
15134
+ const detail = ch.level !== "ok" && ch.reason ? ` (${ch.reason})` : "";
15135
+ return `${glyph} \`${ch.channel_id}\` \xB7 ${ch.level} \xB7 ${fresh} \xB7 ${age}${detail}`;
15136
+ }
15137
+ function renderIntegrationLine(it, nowMs) {
15138
+ const glyph = statusLevelGlyph(it.level);
15139
+ const fresh = freshnessLabel(it.freshness);
15140
+ const age = formatStatusAge(it.checked_at, nowMs);
15141
+ const bits = [];
15142
+ if (it.level !== "ok" && it.reason) bits.push(it.reason);
15143
+ if (typeof it.consecutive_failures === "number" && it.consecutive_failures > 0) {
15144
+ bits.push(`${it.consecutive_failures} fail${it.consecutive_failures === 1 ? "" : "s"}`);
15145
+ }
15146
+ const detail = bits.length ? ` (${bits.join(", ")})` : "";
15147
+ return `${glyph} \`${it.display_name}\` \xB7 ${it.level} \xB7 ${fresh} \xB7 ${age}${detail}`;
15148
+ }
15149
+ function renderSlackStatusSections(report, opts) {
15150
+ const { probing, nowMs } = opts;
15151
+ const lines = [];
15152
+ lines.push(probing ? "*Channels* (probing...)" : "*Channels*");
15153
+ if (report.channels.length === 0) {
15154
+ lines.push("_No channels configured._");
15155
+ } else {
15156
+ for (const ch of report.channels) lines.push(renderChannelLine(ch, nowMs));
15157
+ }
15158
+ lines.push("");
15159
+ lines.push("*Integrations*");
15160
+ if (report.integrations.length === 0) {
15161
+ lines.push("_No integrations configured._");
15162
+ } else {
15163
+ for (const it of report.integrations) lines.push(renderIntegrationLine(it, nowMs));
15164
+ }
15165
+ lines.push("");
15166
+ const seen = report.manager_last_seen ? `Manager last seen ${formatStatusAge(report.manager_last_seen, nowMs)}` : "Manager has never checked in";
15167
+ const tail = probing ? "Integration health is cached; confirming this channel's own status..." : "Integration health is cached from the last probe.";
15168
+ const skew = report.schema_version !== STATUS_REPORT_SCHEMA_VERSION ? ` (report schema v${report.schema_version}; update this agent for full detail)` : "";
15169
+ lines.push(`_${seen}. ${tail}${skew}_`);
15170
+ return lines.join("\n");
15171
+ }
15172
+
15038
15173
  // src/pane-tail.ts
15039
15174
  import { execFile } from "child_process";
15040
15175
  import { promisify } from "util";
@@ -18352,7 +18487,7 @@ function buildSlackHelpMessage(codeName) {
18352
18487
  `\u2022 \`${agentSlashCommand("/restart")}\` \u2014 restart this agent`,
18353
18488
  `\u2022 \`${agentSlashCommand("/onboard")}\` \u2014 re-run this agent's onboarding: re-interview, existing config kept (allowlisted users)`,
18354
18489
  `\u2022 \`${agentSlashCommand("/resume-onboarding")}\` \u2014 resume this agent's onboarding where it left off (allowlisted users)`,
18355
- `\u2022 \`${agentSlashCommand("/status")}\` \u2014 this agent's model, session origin, uptime + connectivity`,
18490
+ `\u2022 \`${agentSlashCommand("/status")}\` - full health report: model, session, uptime + per-channel and per-integration status (cached snapshot, then this channel's own live check)`,
18356
18491
  `\u2022 \`${agentSlashCommand("/ping")}\` - confirm this agent's channel is connected: posts a visible pong (team + manager only)`,
18357
18492
  "\u2022 `/watch <google-doc-url> [duration]` (type it in chat) \u2014 watch a Google Doc for comments that mention me (default 2h, max 7d; auto-pauses when the window ends). In a shared channel, address me as `/watch-<my-name>`.",
18358
18493
  "\u2022 `/kill` \u2014 silence all agents in this thread for 6h (use as a thread reply)",
@@ -18443,17 +18578,101 @@ function buildAgentStatusReply(codeName) {
18443
18578
  const sessionState = readAgentSessionState(SLACK_AGENT_DIR);
18444
18579
  return buildAgentConfigReport({ codeName, connectivityLine, state: sessionState });
18445
18580
  }
18446
- async function postEphemeralViaResponseUrl(responseUrl, text, logTag) {
18581
+ var SLACK_OWN_CHANNEL_ID = "slack";
18582
+ function statusRuntimeConfig() {
18583
+ if (!AGT_HOST || !AGT_API_KEY || !AGT_AGENT_ID) return null;
18584
+ return { apiHost: AGT_HOST, apiKey: AGT_API_KEY, agentId: AGT_AGENT_ID };
18585
+ }
18586
+ async function computeSlackOwnChannelLiveStatus() {
18587
+ const checked_at = (/* @__PURE__ */ new Date()).toISOString();
18588
+ const connected = currentWs != null && !isShuttingDown;
18589
+ if (!connected) {
18590
+ return { level: "down", checked_at, reason: "Socket Mode disconnected" };
18591
+ }
18592
+ const { id, authFailed } = await getBotUserId();
18593
+ if (id) {
18594
+ return { level: "ok", checked_at, reason: "Socket Mode connected, auth.test ok" };
18595
+ }
18596
+ if (authFailed) {
18597
+ return { level: "error", checked_at, reason: "auth.test failed (token invalid)" };
18598
+ }
18599
+ return { level: "degraded", checked_at, reason: "Socket Mode connected, auth.test inconclusive" };
18600
+ }
18601
+ async function handleStatusCommand(responseUrl, codeName) {
18602
+ const configBlock = buildAgentStatusReply(codeName);
18603
+ const cfg = statusRuntimeConfig();
18604
+ let report = null;
18605
+ if (cfg) {
18606
+ try {
18607
+ const { apiCall: apiCall2 } = await Promise.resolve().then(() => (init_ask_user_runtime(), ask_user_runtime_exports));
18608
+ const res = await apiCall2(
18609
+ cfg,
18610
+ "GET",
18611
+ `/host/status?agent_id=${encodeURIComponent(cfg.agentId)}`
18612
+ );
18613
+ if (res.ok) {
18614
+ report = parseAgentStatusReport(await res.json());
18615
+ } else {
18616
+ process.stderr.write(
18617
+ `slack-channel(${codeName}): /status host fetch returned ${res.status}
18618
+ `
18619
+ );
18620
+ }
18621
+ } catch (err) {
18622
+ process.stderr.write(
18623
+ `slack-channel(${codeName}): /status host fetch failed: ${err.message}
18624
+ `
18625
+ );
18626
+ }
18627
+ }
18628
+ if (!report) {
18629
+ await postEphemeralViaResponseUrl(
18630
+ responseUrl,
18631
+ `${configBlock}
18632
+
18633
+ _Channel + integration health is unavailable right now (host unreachable). Try again shortly._`,
18634
+ codeName
18635
+ );
18636
+ return;
18637
+ }
18638
+ const snapshot = `${configBlock}
18639
+
18640
+ ${renderSlackStatusSections(report, {
18641
+ probing: true,
18642
+ nowMs: Date.now()
18643
+ })}`;
18644
+ await postEphemeralViaResponseUrl(responseUrl, snapshot, codeName);
18645
+ const live = await computeSlackOwnChannelLiveStatus();
18646
+ const merged = mergeOwnChannelLiveStatus(report, SLACK_OWN_CHANNEL_ID, live);
18647
+ const finalReport = `${configBlock}
18648
+
18649
+ ${renderSlackStatusSections(merged, {
18650
+ probing: false,
18651
+ nowMs: Date.now()
18652
+ })}`;
18653
+ await postEphemeralViaResponseUrl(responseUrl, finalReport, codeName, { replaceOriginal: true });
18654
+ }
18655
+ async function postEphemeralViaResponseUrl(responseUrl, text, logTag, opts = {}) {
18447
18656
  try {
18448
- await fetch(responseUrl, {
18657
+ const res = await fetch(responseUrl, {
18449
18658
  method: "POST",
18450
18659
  headers: { "Content-Type": "application/json; charset=utf-8" },
18451
- body: JSON.stringify({ response_type: "ephemeral", text }),
18660
+ body: JSON.stringify({
18661
+ response_type: "ephemeral",
18662
+ text,
18663
+ ...opts.replaceOriginal ? { replace_original: true } : {}
18664
+ }),
18452
18665
  // Match the rest of the Slack API call sites in this file. Without
18453
18666
  // this, a hung response_url request can stall the live Socket Mode
18454
18667
  // event loop indefinitely (CodeRabbit feedback on PR #578).
18455
18668
  signal: AbortSignal.timeout(SLACK_DOWNLOAD_TIMEOUT_MS)
18456
18669
  });
18670
+ if (!res.ok) {
18671
+ process.stderr.write(
18672
+ `slack-channel(${logTag}): response_url POST returned ${res.status}
18673
+ `
18674
+ );
18675
+ }
18457
18676
  } catch (err) {
18458
18677
  process.stderr.write(
18459
18678
  `slack-channel(${logTag}): response_url POST failed: ${err.message}
@@ -18701,7 +18920,7 @@ async function handleSlashCommandEnvelope(payload) {
18701
18920
  const codeName = AGENT_CODE_NAME ?? "unknown";
18702
18921
  if (!command || !responseUrl) return;
18703
18922
  if (matchesAgentCommand(command, "/status") || matchesAgentCommand(command, "/agent-status")) {
18704
- await postEphemeralViaResponseUrl(responseUrl, buildAgentStatusReply(codeName), codeName);
18923
+ await handleStatusCommand(responseUrl, codeName);
18705
18924
  return;
18706
18925
  }
18707
18926
  if (matchesAgentCommand(command, "/help")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.165",
3
+ "version": "0.28.166",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {