@integrity-labs/agt-cli 0.15.37 → 0.16.0

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.
@@ -14799,6 +14799,7 @@ async function processSlackRecoveryOutboxFile(filename) {
14799
14799
  const data = await res.json();
14800
14800
  if (data.ok) {
14801
14801
  sendSucceeded = true;
14802
+ recordActivity("reply");
14802
14803
  process.stderr.write(
14803
14804
  `slack-channel(${AGENT_CODE_NAME}): ghost-reply recovery sent (channel=${redactSlackId(payload.channel)} thread=${redactSlackId(payload.thread_ts)})
14804
14805
  `
@@ -15000,6 +15001,7 @@ function buildSlackHelpMessage(codeName) {
15000
15001
  }
15001
15002
  var lastActivityAt = null;
15002
15003
  var lastActivityKind = null;
15004
+ var setBotStatusWarnedErrors = /* @__PURE__ */ new Set();
15003
15005
  function recordActivity(kind) {
15004
15006
  lastActivityAt = Date.now();
15005
15007
  lastActivityKind = kind;
@@ -15017,10 +15019,21 @@ async function setBotStatus(status_emoji, status_text) {
15017
15019
  });
15018
15020
  const data = await res.json();
15019
15021
  if (!data.ok) {
15020
- process.stderr.write(
15021
- `slack-channel: users.profile.set failed (${data.error ?? "unknown"}) \u2014 bot presence indicator disabled
15022
+ const code = data.error ?? "unknown";
15023
+ if (code === "missing_scope") {
15024
+ if (!setBotStatusWarnedErrors.has(code)) {
15025
+ setBotStatusWarnedErrors.add(code);
15026
+ process.stderr.write(
15027
+ `slack-channel: users.profile.set failed (missing_scope) \u2014 bot presence indicator disabled until users.profile:write is granted (re-OAuth required)
15022
15028
  `
15023
- );
15029
+ );
15030
+ }
15031
+ } else {
15032
+ process.stderr.write(
15033
+ `slack-channel: users.profile.set failed (${code}) \u2014 bot presence indicator disabled
15034
+ `
15035
+ );
15036
+ }
15024
15037
  }
15025
15038
  } catch (err) {
15026
15039
  process.stderr.write(
@@ -15054,6 +15067,84 @@ function buildAgentStatusReply(codeName) {
15054
15067
  const state = connected ? "online" : "offline";
15055
15068
  return `${dot} \`${codeName}\` is *${state}* \u2014 Socket Mode ${connected ? "connected" : "disconnected"}. Last activity: ${formatLastActivity()}.`;
15056
15069
  }
15070
+ async function postEphemeralViaResponseUrl(responseUrl, text, logTag) {
15071
+ try {
15072
+ await fetch(responseUrl, {
15073
+ method: "POST",
15074
+ headers: { "Content-Type": "application/json; charset=utf-8" },
15075
+ body: JSON.stringify({ response_type: "ephemeral", text }),
15076
+ // Match the rest of the Slack API call sites in this file. Without
15077
+ // this, a hung response_url request can stall the live Socket Mode
15078
+ // event loop indefinitely (CodeRabbit feedback on PR #578).
15079
+ signal: AbortSignal.timeout(SLACK_DOWNLOAD_TIMEOUT_MS)
15080
+ });
15081
+ } catch (err) {
15082
+ process.stderr.write(
15083
+ `slack-channel(${logTag}): response_url POST failed: ${err.message}
15084
+ `
15085
+ );
15086
+ }
15087
+ }
15088
+ async function handleSlashCommandEnvelope(payload) {
15089
+ const command = payload.command;
15090
+ const responseUrl = payload.response_url;
15091
+ const codeName = AGENT_CODE_NAME ?? "unknown";
15092
+ if (!command || !responseUrl) return;
15093
+ if (command === "/agent-status") {
15094
+ await postEphemeralViaResponseUrl(responseUrl, buildAgentStatusReply(codeName), codeName);
15095
+ return;
15096
+ }
15097
+ if (command === "/kill" || command === "/unkill") {
15098
+ if (!AGT_HOST || !AGT_API_KEY || !AGT_AGENT_ID) {
15099
+ await postEphemeralViaResponseUrl(
15100
+ responseUrl,
15101
+ ":warning: This agent has no host API wiring \u2014 `/kill` / `/unkill` need the host runtime to be reachable.",
15102
+ codeName
15103
+ );
15104
+ return;
15105
+ }
15106
+ if (!payload.thread_ts || !payload.channel_id || !payload.user_id) {
15107
+ await postEphemeralViaResponseUrl(
15108
+ responseUrl,
15109
+ `:warning: \`${command}\` only works inside a thread. Invoke it as a thread reply.`,
15110
+ codeName
15111
+ );
15112
+ return;
15113
+ }
15114
+ try {
15115
+ const res = await fetch(`${AGT_HOST}/host/slack/thread-kill`, {
15116
+ method: "POST",
15117
+ headers: {
15118
+ "Content-Type": "application/json; charset=utf-8",
15119
+ Authorization: `Bearer ${AGT_API_KEY}`
15120
+ },
15121
+ body: JSON.stringify({
15122
+ agent_id: AGT_AGENT_ID,
15123
+ op: command === "/kill" ? "kill" : "unkill",
15124
+ channel_id: payload.channel_id,
15125
+ thread_ts: payload.thread_ts,
15126
+ user_id: payload.user_id,
15127
+ user_name: payload.user_name
15128
+ }),
15129
+ signal: AbortSignal.timeout(SLACK_DOWNLOAD_TIMEOUT_MS)
15130
+ });
15131
+ const data = await res.json();
15132
+ const text = data.message ?? (data.ok ? command === "/kill" ? ":lock: Kill engaged for this thread." : ":unlock: Kill cleared for this thread." : `:x: ${command} failed.`);
15133
+ await postEphemeralViaResponseUrl(responseUrl, text, codeName);
15134
+ } catch (err) {
15135
+ process.stderr.write(
15136
+ `slack-channel(${codeName}): ${command} forward failed: ${err.message}
15137
+ `
15138
+ );
15139
+ await postEphemeralViaResponseUrl(
15140
+ responseUrl,
15141
+ `:x: \`${command}\` forwarding failed \u2014 host runtime unreachable. Try again in a moment.`,
15142
+ codeName
15143
+ );
15144
+ }
15145
+ return;
15146
+ }
15147
+ }
15057
15148
  async function handleHelpCommand(opts) {
15058
15149
  const codeName = AGENT_CODE_NAME ?? "unknown";
15059
15150
  const replyThread = opts.threadTs ?? opts.ts;
@@ -15490,6 +15581,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
15490
15581
  };
15491
15582
  }
15492
15583
  recordReply(channel, throttleKey, throttleNow, throttleCfg);
15584
+ recordActivity("reply");
15493
15585
  if (THREAD_AUTO_FOLLOW !== "off") {
15494
15586
  const trackTs = thread_ts ?? data.ts ?? void 0;
15495
15587
  rememberThread(channel, trackTs, thread_ts ? "mentioned" : "started");
@@ -15789,7 +15881,9 @@ async function postSlackMessageWithTs(payload) {
15789
15881
  body: JSON.stringify(payload),
15790
15882
  signal: AbortSignal.timeout(SLACK_DOWNLOAD_TIMEOUT_MS)
15791
15883
  });
15792
- return await res.json();
15884
+ const data = await res.json();
15885
+ if (data.ok) recordActivity("reply");
15886
+ return data;
15793
15887
  } catch (err) {
15794
15888
  const isTimeout = err.name === "TimeoutError" || err.name === "AbortError";
15795
15889
  return { ok: false, error: isTimeout ? "timeout" : err.message };
@@ -16256,24 +16350,7 @@ async function connectSocketMode() {
16256
16350
  ws.send(JSON.stringify({ envelope_id: msg.envelope_id }));
16257
16351
  }
16258
16352
  if (msg.type === "slash_commands" && msg.payload?.command) {
16259
- const command = msg.payload.command;
16260
- const responseUrl = msg.payload.response_url;
16261
- if (command === "/agent-status" && responseUrl) {
16262
- const codeName = AGENT_CODE_NAME ?? "unknown";
16263
- fetch(responseUrl, {
16264
- method: "POST",
16265
- headers: { "Content-Type": "application/json; charset=utf-8" },
16266
- body: JSON.stringify({
16267
- response_type: "ephemeral",
16268
- text: buildAgentStatusReply(codeName)
16269
- })
16270
- }).catch((err) => {
16271
- process.stderr.write(
16272
- `slack-channel(${codeName}): /agent-status response_url POST failed: ${err.message}
16273
- `
16274
- );
16275
- });
16276
- }
16353
+ void handleSlashCommandEnvelope(msg.payload);
16277
16354
  return;
16278
16355
  }
16279
16356
  if (msg.type === "interactive" && msg.payload?.type === "block_actions") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.15.37",
3
+ "version": "0.16.0",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {