@integrity-labs/agt-cli 0.26.2-eng5706.1 → 0.27.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.
@@ -13982,6 +13982,75 @@ function createDirectChatProgressFlush(opts) {
13982
13982
  };
13983
13983
  }
13984
13984
 
13985
+ // src/inbound-context-client.ts
13986
+ var REQUEST_TIMEOUT_MS = 1e4;
13987
+ function createInboundContextClient(args) {
13988
+ if (!args.agtHost || !args.agtApiKey || !args.agentId) return null;
13989
+ const fetchImpl = args.fetchImpl ?? fetch;
13990
+ const log = args.log ?? (() => {
13991
+ });
13992
+ const base = args.agtHost.replace(/\/+$/, "");
13993
+ const agentId = args.agentId;
13994
+ const apiKey = args.agtApiKey;
13995
+ let cachedToken = null;
13996
+ let cachedTokenExpiresAt = 0;
13997
+ async function getToken() {
13998
+ if (cachedToken && Date.now() < cachedTokenExpiresAt) return cachedToken;
13999
+ const resp = await fetchImpl(`${base}/host/exchange`, {
14000
+ method: "POST",
14001
+ headers: { "Content-Type": "application/json" },
14002
+ body: JSON.stringify({ host_key: apiKey }),
14003
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
14004
+ });
14005
+ if (!resp.ok) {
14006
+ const body = await resp.text().catch(() => "");
14007
+ throw new Error(`/host/exchange failed (${resp.status}): ${body.slice(0, 200)}`);
14008
+ }
14009
+ const data = await resp.json();
14010
+ cachedToken = data.token;
14011
+ cachedTokenExpiresAt = data.expires_at ? new Date(data.expires_at).getTime() - 12e4 : Date.now() + 55 * 6e4;
14012
+ return cachedToken;
14013
+ }
14014
+ async function postOnce(body) {
14015
+ const token = await getToken();
14016
+ return fetchImpl(`${base}/host/inbound-context`, {
14017
+ method: "POST",
14018
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
14019
+ body: JSON.stringify(body),
14020
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
14021
+ });
14022
+ }
14023
+ async function emit(args2) {
14024
+ const body = {
14025
+ agent_id: agentId,
14026
+ source_integration: args2.sourceIntegration,
14027
+ source_external_id: args2.sourceExternalId,
14028
+ ...args2.sourceUrl ? { source_url: args2.sourceUrl } : {}
14029
+ };
14030
+ try {
14031
+ let resp = await postOnce(body);
14032
+ if (resp.status === 401) {
14033
+ cachedToken = null;
14034
+ cachedTokenExpiresAt = 0;
14035
+ resp = await postOnce(body);
14036
+ }
14037
+ if (!resp.ok) {
14038
+ const text = await resp.text().catch(() => "");
14039
+ log(
14040
+ `inbound-context: POST failed (${resp.status}) integration=${args2.sourceIntegration}: ${text.slice(0, 200)}`
14041
+ );
14042
+ }
14043
+ } catch (err) {
14044
+ log(`inbound-context: emit threw integration=${args2.sourceIntegration}: ${err.message}`);
14045
+ }
14046
+ }
14047
+ return {
14048
+ recordInbound(args2) {
14049
+ void emit(args2);
14050
+ }
14051
+ };
14052
+ }
14053
+
13985
14054
  // src/progress-tools.ts
13986
14055
  import { randomUUID } from "crypto";
13987
14056
 
@@ -14299,10 +14368,89 @@ function isMode(value) {
14299
14368
  return value === "thinking" || value === "working" || value === "waiting";
14300
14369
  }
14301
14370
 
14371
+ // src/mcp-spawn-lock.ts
14372
+ import {
14373
+ existsSync,
14374
+ mkdirSync,
14375
+ readFileSync,
14376
+ renameSync,
14377
+ unlinkSync,
14378
+ writeFileSync
14379
+ } from "fs";
14380
+ import { join } from "path";
14381
+ function defaultIsPidAlive(pid) {
14382
+ if (!Number.isFinite(pid) || pid <= 0) return false;
14383
+ try {
14384
+ process.kill(pid, 0);
14385
+ return true;
14386
+ } catch (err) {
14387
+ const code = err.code;
14388
+ if (code === "ESRCH") return false;
14389
+ return true;
14390
+ }
14391
+ }
14392
+ function acquireMcpSpawnLock(args) {
14393
+ const { agentDir, basename, options = {} } = args;
14394
+ if (!agentDir) return { kind: "no-agent-dir" };
14395
+ const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
14396
+ const selfPid = options.selfPid ?? process.pid;
14397
+ const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
14398
+ const path = join(agentDir, basename);
14399
+ const existing = readLockHolder(path);
14400
+ if (existing) {
14401
+ if (existing.pid === selfPid) {
14402
+ return { kind: "acquired", path };
14403
+ }
14404
+ if (isPidAlive(existing.pid)) {
14405
+ return { kind: "blocked", path, holder: existing };
14406
+ }
14407
+ }
14408
+ mkdirSync(agentDir, { recursive: true, mode: 448 });
14409
+ const tmpPath = `${path}.${selfPid}.tmp`;
14410
+ const payload = { pid: selfPid, started_at: now() };
14411
+ writeFileSync(tmpPath, JSON.stringify(payload), { mode: 384 });
14412
+ renameSync(tmpPath, path);
14413
+ return { kind: "acquired", path };
14414
+ }
14415
+ function releaseMcpSpawnLock(lockPath, opts = {}) {
14416
+ if (!lockPath) return;
14417
+ const selfPid = opts.selfPid ?? process.pid;
14418
+ const existing = readLockHolder(lockPath);
14419
+ if (!existing) return;
14420
+ if (existing.pid !== selfPid) return;
14421
+ try {
14422
+ unlinkSync(lockPath);
14423
+ } catch {
14424
+ }
14425
+ }
14426
+ function readLockHolder(path) {
14427
+ if (!existsSync(path)) return null;
14428
+ try {
14429
+ const raw = readFileSync(path, "utf8");
14430
+ const parsed = JSON.parse(raw);
14431
+ const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
14432
+ if (!Number.isFinite(pid) || pid <= 0) return null;
14433
+ const startedAt = typeof parsed.started_at === "string" ? parsed.started_at : "";
14434
+ return { pid, started_at: startedAt };
14435
+ } catch {
14436
+ return null;
14437
+ }
14438
+ }
14439
+
14302
14440
  // src/direct-chat-channel.ts
14441
+ import { homedir } from "os";
14442
+ import { join as join2 } from "path";
14303
14443
  var AGT_HOST = process.env.AGT_HOST;
14304
14444
  var AGT_API_KEY = process.env.AGT_API_KEY;
14305
14445
  var AGT_AGENT_ID = process.env.AGT_AGENT_ID;
14446
+ var DIRECT_CHAT_AGENT_DIR = AGT_AGENT_ID ? join2(homedir(), ".augmented", AGT_AGENT_ID) : null;
14447
+ var inboundContextClient = createInboundContextClient({
14448
+ agtHost: AGT_HOST ?? null,
14449
+ agtApiKey: AGT_API_KEY ?? null,
14450
+ agentId: AGT_AGENT_ID ?? null,
14451
+ log: (line) => process.stderr.write(`direct-chat-channel: ${line}
14452
+ `)
14453
+ });
14306
14454
  if (!AGT_HOST || !AGT_API_KEY || !AGT_AGENT_ID) {
14307
14455
  process.stderr.write(
14308
14456
  "direct-chat-channel: Missing AGT_HOST, AGT_API_KEY, or AGT_AGENT_ID. Cannot start.\n"
@@ -14488,6 +14636,22 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
14488
14636
  throw new Error(`Unknown tool: ${name}`);
14489
14637
  });
14490
14638
  await mcp.connect(new StdioServerTransport());
14639
+ var acquiredLockPath = null;
14640
+ {
14641
+ const lockResult = acquireMcpSpawnLock({
14642
+ agentDir: DIRECT_CHAT_AGENT_DIR,
14643
+ basename: "direct-chat-channel.lock"
14644
+ });
14645
+ if (lockResult.kind === "blocked") {
14646
+ process.stderr.write(
14647
+ `direct-chat-channel: another instance pid=${lockResult.holder.pid} already running (since ${lockResult.holder.started_at}), exiting
14648
+ `
14649
+ );
14650
+ process.exit(0);
14651
+ } else if (lockResult.kind === "acquired") {
14652
+ acquiredLockPath = lockResult.path;
14653
+ }
14654
+ }
14491
14655
  process.stderr.write(
14492
14656
  `direct-chat-channel: Started (agent=${AGT_AGENT_ID}, polling=disabled \u2014 using Realtime)
14493
14657
  `
@@ -14498,6 +14662,10 @@ function shutdown(reason) {
14498
14662
  isShuttingDown = true;
14499
14663
  process.stderr.write(`direct-chat-channel: ${reason} \u2014 exiting
14500
14664
  `);
14665
+ try {
14666
+ releaseMcpSpawnLock(acquiredLockPath);
14667
+ } catch {
14668
+ }
14501
14669
  setTimeout(() => process.exit(0), 100).unref();
14502
14670
  }
14503
14671
  process.stdin.on("close", () => shutdown("stdin closed"));
@@ -15495,8 +15495,77 @@ function createConversationIngestClient(args) {
15495
15495
  };
15496
15496
  }
15497
15497
 
15498
- // src/slack-bot-user-id-client.ts
15498
+ // src/inbound-context-client.ts
15499
15499
  var REQUEST_TIMEOUT_MS3 = 1e4;
15500
+ function createInboundContextClient(args) {
15501
+ if (!args.agtHost || !args.agtApiKey || !args.agentId) return null;
15502
+ const fetchImpl = args.fetchImpl ?? fetch;
15503
+ const log = args.log ?? (() => {
15504
+ });
15505
+ const base = args.agtHost.replace(/\/+$/, "");
15506
+ const agentId = args.agentId;
15507
+ const apiKey = args.agtApiKey;
15508
+ let cachedToken = null;
15509
+ let cachedTokenExpiresAt = 0;
15510
+ async function getToken() {
15511
+ if (cachedToken && Date.now() < cachedTokenExpiresAt) return cachedToken;
15512
+ const resp = await fetchImpl(`${base}/host/exchange`, {
15513
+ method: "POST",
15514
+ headers: { "Content-Type": "application/json" },
15515
+ body: JSON.stringify({ host_key: apiKey }),
15516
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
15517
+ });
15518
+ if (!resp.ok) {
15519
+ const body = await resp.text().catch(() => "");
15520
+ throw new Error(`/host/exchange failed (${resp.status}): ${body.slice(0, 200)}`);
15521
+ }
15522
+ const data = await resp.json();
15523
+ cachedToken = data.token;
15524
+ cachedTokenExpiresAt = data.expires_at ? new Date(data.expires_at).getTime() - 12e4 : Date.now() + 55 * 6e4;
15525
+ return cachedToken;
15526
+ }
15527
+ async function postOnce(body) {
15528
+ const token = await getToken();
15529
+ return fetchImpl(`${base}/host/inbound-context`, {
15530
+ method: "POST",
15531
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
15532
+ body: JSON.stringify(body),
15533
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
15534
+ });
15535
+ }
15536
+ async function emit(args2) {
15537
+ const body = {
15538
+ agent_id: agentId,
15539
+ source_integration: args2.sourceIntegration,
15540
+ source_external_id: args2.sourceExternalId,
15541
+ ...args2.sourceUrl ? { source_url: args2.sourceUrl } : {}
15542
+ };
15543
+ try {
15544
+ let resp = await postOnce(body);
15545
+ if (resp.status === 401) {
15546
+ cachedToken = null;
15547
+ cachedTokenExpiresAt = 0;
15548
+ resp = await postOnce(body);
15549
+ }
15550
+ if (!resp.ok) {
15551
+ const text = await resp.text().catch(() => "");
15552
+ log(
15553
+ `inbound-context: POST failed (${resp.status}) integration=${args2.sourceIntegration}: ${text.slice(0, 200)}`
15554
+ );
15555
+ }
15556
+ } catch (err) {
15557
+ log(`inbound-context: emit threw integration=${args2.sourceIntegration}: ${err.message}`);
15558
+ }
15559
+ }
15560
+ return {
15561
+ recordInbound(args2) {
15562
+ void emit(args2);
15563
+ }
15564
+ };
15565
+ }
15566
+
15567
+ // src/slack-bot-user-id-client.ts
15568
+ var REQUEST_TIMEOUT_MS4 = 1e4;
15500
15569
  function createSlackBotUserIdClient(args) {
15501
15570
  if (!args.agtHost || !args.agtApiKey || !args.agentId) return null;
15502
15571
  const fetchImpl = args.fetchImpl ?? fetch;
@@ -15514,7 +15583,7 @@ function createSlackBotUserIdClient(args) {
15514
15583
  method: "POST",
15515
15584
  headers: { "Content-Type": "application/json" },
15516
15585
  body: JSON.stringify({ host_key: apiKey }),
15517
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
15586
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS4)
15518
15587
  });
15519
15588
  if (!resp.ok) {
15520
15589
  const body = await resp.text().catch(() => "");
@@ -15531,7 +15600,7 @@ function createSlackBotUserIdClient(args) {
15531
15600
  method: "POST",
15532
15601
  headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
15533
15602
  body: JSON.stringify({ agent_id: agentId, bot_user_id: botUserId2 }),
15534
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
15603
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS4)
15535
15604
  });
15536
15605
  }
15537
15606
  async function emitAsync(botUserId2) {
@@ -15563,7 +15632,7 @@ function createSlackBotUserIdClient(args) {
15563
15632
  method: "POST",
15564
15633
  headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
15565
15634
  body: JSON.stringify({ agent_id: agentId, channel_id: "slack", healthy }),
15566
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS3)
15635
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS4)
15567
15636
  });
15568
15637
  };
15569
15638
  try {
@@ -15607,7 +15676,7 @@ function createSlackBotUserIdClient(args) {
15607
15676
  };
15608
15677
  }
15609
15678
 
15610
- // src/slack-spawn-lock.ts
15679
+ // src/mcp-spawn-lock.ts
15611
15680
  import {
15612
15681
  existsSync,
15613
15682
  mkdirSync as mkdirSync2,
@@ -15617,7 +15686,6 @@ import {
15617
15686
  writeFileSync as writeFileSync2
15618
15687
  } from "fs";
15619
15688
  import { join as join2 } from "path";
15620
- var DEFAULT_LOCK_BASENAME = "slack-channel.lock";
15621
15689
  function defaultIsPidAlive(pid) {
15622
15690
  if (!Number.isFinite(pid) || pid <= 0) return false;
15623
15691
  try {
@@ -15629,12 +15697,13 @@ function defaultIsPidAlive(pid) {
15629
15697
  return true;
15630
15698
  }
15631
15699
  }
15632
- function acquireSlackSpawnLock(slackAgentDir, opts = {}) {
15633
- if (!slackAgentDir) return { kind: "no-agent-dir" };
15634
- const isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
15635
- const selfPid = opts.selfPid ?? process.pid;
15636
- const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
15637
- const path = join2(slackAgentDir, DEFAULT_LOCK_BASENAME);
15700
+ function acquireMcpSpawnLock(args) {
15701
+ const { agentDir, basename: basename2, options = {} } = args;
15702
+ if (!agentDir) return { kind: "no-agent-dir" };
15703
+ const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
15704
+ const selfPid = options.selfPid ?? process.pid;
15705
+ const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
15706
+ const path = join2(agentDir, basename2);
15638
15707
  const existing = readLockHolder(path);
15639
15708
  if (existing) {
15640
15709
  if (existing.pid === selfPid) {
@@ -15644,14 +15713,14 @@ function acquireSlackSpawnLock(slackAgentDir, opts = {}) {
15644
15713
  return { kind: "blocked", path, holder: existing };
15645
15714
  }
15646
15715
  }
15647
- mkdirSync2(slackAgentDir, { recursive: true, mode: 448 });
15716
+ mkdirSync2(agentDir, { recursive: true, mode: 448 });
15648
15717
  const tmpPath = `${path}.${selfPid}.tmp`;
15649
15718
  const payload = { pid: selfPid, started_at: now() };
15650
15719
  writeFileSync2(tmpPath, JSON.stringify(payload), { mode: 384 });
15651
15720
  renameSync(tmpPath, path);
15652
15721
  return { kind: "acquired", path };
15653
15722
  }
15654
- function releaseSlackSpawnLock(lockPath, opts = {}) {
15723
+ function releaseMcpSpawnLock(lockPath, opts = {}) {
15655
15724
  if (!lockPath) return;
15656
15725
  const selfPid = opts.selfPid ?? process.pid;
15657
15726
  const existing = readLockHolder(lockPath);
@@ -15721,6 +15790,13 @@ var conversationIngestClient = createConversationIngestClient({
15721
15790
  log: (line) => process.stderr.write(`slack-channel: ${line}
15722
15791
  `)
15723
15792
  });
15793
+ var inboundContextClient = createInboundContextClient({
15794
+ agtHost: AGT_HOST,
15795
+ agtApiKey: AGT_API_KEY,
15796
+ agentId: AGT_AGENT_ID,
15797
+ log: (line) => process.stderr.write(`slack-channel: ${line}
15798
+ `)
15799
+ });
15724
15800
  var SLACK_PEER_CLASSIFIER_CONFIG = {
15725
15801
  peer_agent_mode: parsePeerAgentModeEnv(process.env.SLACK_PEER_AGENT_MODE),
15726
15802
  peer_group_ids: parsePeerGroupIdsEnv(process.env.SLACK_PEER_GROUP_IDS),
@@ -18094,6 +18170,12 @@ async function connectSocketMode() {
18094
18170
  is_bot: isFromBot,
18095
18171
  bot_id: evt.bot_id ?? null
18096
18172
  });
18173
+ if (!isFromBot && threadTs) {
18174
+ inboundContextClient?.recordInbound({
18175
+ sourceIntegration: "slack",
18176
+ sourceExternalId: `${channel}:${threadTs}`
18177
+ });
18178
+ }
18097
18179
  }
18098
18180
  } catch (err) {
18099
18181
  process.stderr.write(`slack-channel: Event error: ${err.message}
@@ -18133,7 +18215,7 @@ function shutdown(reason, exitCode = 0) {
18133
18215
  } catch {
18134
18216
  }
18135
18217
  try {
18136
- releaseSlackSpawnLock(acquiredLockPath);
18218
+ releaseMcpSpawnLock(acquiredLockPath);
18137
18219
  } catch {
18138
18220
  }
18139
18221
  setTimeout(() => process.exit(exitCode), 500).unref();
@@ -18149,7 +18231,10 @@ process.on("unhandledRejection", (reason) => {
18149
18231
  `);
18150
18232
  });
18151
18233
  {
18152
- const lockResult = acquireSlackSpawnLock(SLACK_AGENT_DIR);
18234
+ const lockResult = acquireMcpSpawnLock({
18235
+ agentDir: SLACK_AGENT_DIR,
18236
+ basename: "slack-channel.lock"
18237
+ });
18153
18238
  if (lockResult.kind === "blocked") {
18154
18239
  process.stderr.write(
18155
18240
  `slack-channel: another instance pid=${lockResult.holder.pid} already running (since ${lockResult.holder.started_at}), exiting