@integrity-labs/agt-cli 0.28.412 → 0.28.414

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-5ES25OE6.js";
43
+ } from "../chunk-UI5WQACL.js";
44
44
  import {
45
45
  AnchorSessionClient,
46
46
  CHANNEL_REGISTRY,
@@ -4829,7 +4829,7 @@ import { execFileSync, execSync } from "child_process";
4829
4829
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4830
4830
  import chalk18 from "chalk";
4831
4831
  import ora16 from "ora";
4832
- var cliVersion = true ? "0.28.412" : "dev";
4832
+ var cliVersion = true ? "0.28.414" : "dev";
4833
4833
  async function fetchLatestVersion() {
4834
4834
  const host2 = getHost();
4835
4835
  if (!host2) return null;
@@ -6001,7 +6001,7 @@ function handleError(err) {
6001
6001
  }
6002
6002
 
6003
6003
  // src/bin/agt.ts
6004
- var cliVersion2 = true ? "0.28.412" : "dev";
6004
+ var cliVersion2 = true ? "0.28.414" : "dev";
6005
6005
  var program = new Command();
6006
6006
  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");
6007
6007
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -6247,7 +6247,7 @@ function requireHost() {
6247
6247
  }
6248
6248
 
6249
6249
  // src/lib/api-client.ts
6250
- var agtCliVersion = true ? "0.28.412" : "dev";
6250
+ var agtCliVersion = true ? "0.28.414" : "dev";
6251
6251
  var lastConfigHash = null;
6252
6252
  function setConfigHash(hash) {
6253
6253
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -8751,4 +8751,4 @@ export {
8751
8751
  managerInstallSystemUnitCommand,
8752
8752
  managerUninstallSystemUnitCommand
8753
8753
  };
8754
- //# sourceMappingURL=chunk-5ES25OE6.js.map
8754
+ //# sourceMappingURL=chunk-UI5WQACL.js.map
@@ -45,7 +45,7 @@ import {
45
45
  resolveEffectivePinRaw,
46
46
  safeWriteJsonAtomic,
47
47
  setConfigHash
48
- } from "../chunk-5ES25OE6.js";
48
+ } from "../chunk-UI5WQACL.js";
49
49
  import {
50
50
  getProjectDir as getProjectDir2,
51
51
  getReadyTasks,
@@ -154,7 +154,7 @@ import {
154
154
  } from "../chunk-XWVM4KPK.js";
155
155
 
156
156
  // src/lib/manager-worker.ts
157
- import { createHash as createHash14 } from "crypto";
157
+ import { createHash as createHash15 } from "crypto";
158
158
  import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, mkdirSync as mkdirSync10, existsSync as existsSync11, rmSync as rmSync5, readdirSync as readdirSync6, statSync as statSync4, copyFileSync } from "fs";
159
159
  import { execFileSync as syncExecFile } from "child_process";
160
160
  import { join as join22, dirname as dirname7, delimiter as pathDelimiter } from "path";
@@ -1348,6 +1348,62 @@ function restartTargetsOpencodeServe(framework) {
1348
1348
  return framework === "opencode";
1349
1349
  }
1350
1350
 
1351
+ // src/lib/agent-runtime-teardown.ts
1352
+ function claudeTmuxSession(codeName) {
1353
+ return `agt-${codeName}`;
1354
+ }
1355
+ function stopAgentRuntime(codeName, reason, log2, deps) {
1356
+ const step = (label, run) => {
1357
+ try {
1358
+ run();
1359
+ } catch (err) {
1360
+ log2(`[teardown] ${label} failed for '${codeName}' (${reason}): ${err.message}`);
1361
+ }
1362
+ };
1363
+ step("channel-ingest", () => deps.stopManagerHeldChannelIngest(codeName, log2));
1364
+ step("opencode-serve", () => deps.stopOpencodeServe(codeName, log2));
1365
+ step("claude-session", () => deps.stopClaudePersistentSession(codeName));
1366
+ step("claude-pane", () => {
1367
+ if (deps.killTmuxSession(claudeTmuxSession(codeName))) {
1368
+ log2(`[teardown] killed claude pane '${claudeTmuxSession(codeName)}' for '${codeName}' (${reason})`);
1369
+ }
1370
+ });
1371
+ }
1372
+
1373
+ // src/lib/opencode-credential-fingerprint.ts
1374
+ import { createHash as createHash4 } from "crypto";
1375
+ var FINGERPRINT_LENGTH = 16;
1376
+ var ABSENT = "none";
1377
+ function fingerprintOf(secret) {
1378
+ if (!secret) return ABSENT;
1379
+ return createHash4("sha256").update(secret, "utf8").digest("hex").slice(0, FINGERPRINT_LENGTH);
1380
+ }
1381
+ function opencodeCredentialFingerprint(inputs) {
1382
+ const openRouter = inputs.openRouterToken ? inputs.openRouterFingerprint || fingerprintOf(inputs.openRouterToken) : ABSENT;
1383
+ return `or:${openRouter}|an:${fingerprintOf(inputs.anthropicKey)}`;
1384
+ }
1385
+ function slots(fingerprint3) {
1386
+ const out = {};
1387
+ for (const part of fingerprint3.split("|")) {
1388
+ const idx = part.indexOf(":");
1389
+ if (idx > 0) out[part.slice(0, idx)] = part.slice(idx + 1);
1390
+ }
1391
+ return out;
1392
+ }
1393
+ function shouldRestartForCredentialChange(recorded, current) {
1394
+ if (recorded === current) return false;
1395
+ const before = slots(recorded);
1396
+ const after = slots(current);
1397
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])) {
1398
+ const was = before[key] ?? ABSENT;
1399
+ const now = after[key] ?? ABSENT;
1400
+ if (was === now) continue;
1401
+ if (now === ABSENT) continue;
1402
+ return true;
1403
+ }
1404
+ return false;
1405
+ }
1406
+
1351
1407
  // src/lib/connectivity-probe-runner.ts
1352
1408
  var DEFAULT_INTERVAL_MS = 60 * 60 * 1e3;
1353
1409
  var DEFAULT_MAX_PER_RUN = 25;
@@ -1799,7 +1855,7 @@ function isDirectChatMessageExpired(createdAt, nowMs, maxAgeMs) {
1799
1855
  }
1800
1856
 
1801
1857
  // ../../packages/core/dist/host-config/capture.js
1802
- import { createHash as createHash4 } from "crypto";
1858
+ import { createHash as createHash5 } from "crypto";
1803
1859
  var NON_SECRET_ENV_GATES = [
1804
1860
  "AGT_MEMORY_EXTRACTION_ENABLED",
1805
1861
  "AGT_CHANNEL_REPLAY_ENABLED",
@@ -3417,10 +3473,10 @@ function parseExpiresAt(raw) {
3417
3473
  }
3418
3474
 
3419
3475
  // src/lib/channel-config-hash.ts
3420
- import { createHash as createHash5 } from "crypto";
3476
+ import { createHash as createHash6 } from "crypto";
3421
3477
  var CHANNEL_WRITE_VERSION = 9;
3422
3478
  function computeChannelConfigHash(input) {
3423
- return createHash5("sha256").update(
3479
+ return createHash6("sha256").update(
3424
3480
  canonicalJson({
3425
3481
  writeVersion: CHANNEL_WRITE_VERSION,
3426
3482
  cliVersion: input.cliVersion,
@@ -4491,7 +4547,7 @@ function saveKanbanNudgeState(source, configDir) {
4491
4547
  }
4492
4548
 
4493
4549
  // src/lib/manager/kanban/notify.ts
4494
- import { createHash as createHash6 } from "crypto";
4550
+ import { createHash as createHash7 } from "crypto";
4495
4551
  async function enqueueKanbanNotice(opts) {
4496
4552
  try {
4497
4553
  const res = await api.post(
@@ -4506,7 +4562,7 @@ async function enqueueKanbanNotice(opts) {
4506
4562
  return res.ok === true;
4507
4563
  } catch (err) {
4508
4564
  const errText = err instanceof Error ? err.message : String(err);
4509
- const errId = createHash6("sha256").update(errText).digest("hex").slice(0, 12);
4565
+ const errId = createHash7("sha256").update(errText).digest("hex").slice(0, 12);
4510
4566
  log(`[kanban] notice enqueue failed for agent_id=${opts.agentId} error_id=${errId}`);
4511
4567
  return false;
4512
4568
  }
@@ -4521,7 +4577,7 @@ async function cancelKanbanNotice(agentId) {
4521
4577
  return typeof res.cancelled === "number" ? res.cancelled : 0;
4522
4578
  } catch (err) {
4523
4579
  const errText = err instanceof Error ? err.message : String(err);
4524
- const errId = createHash6("sha256").update(errText).digest("hex").slice(0, 12);
4580
+ const errId = createHash7("sha256").update(errText).digest("hex").slice(0, 12);
4525
4581
  log(`[kanban] notice cancel failed for agent_id=${agentId} error_id=${errId}`);
4526
4582
  return null;
4527
4583
  }
@@ -4927,7 +4983,7 @@ function deriveScheduledTaskNotify(task) {
4927
4983
  }
4928
4984
 
4929
4985
  // src/lib/manager/scheduler/runs.ts
4930
- import { createHash as createHash7 } from "crypto";
4986
+ import { createHash as createHash8 } from "crypto";
4931
4987
  async function startRun(opts) {
4932
4988
  try {
4933
4989
  const res = await api.post(
@@ -4942,7 +4998,7 @@ async function startRun(opts) {
4942
4998
  };
4943
4999
  } catch (err) {
4944
5000
  const errText = err instanceof Error ? err.message : String(err);
4945
- const errId = createHash7("sha256").update(errText).digest("hex").slice(0, 12);
5001
+ const errId = createHash8("sha256").update(errText).digest("hex").slice(0, 12);
4946
5002
  log(`[runs] start failed for agent_id=${opts.agent_id} source_type=${opts.source_type} error_id=${errId}`);
4947
5003
  return { run_id: null, kanban_item_id: null };
4948
5004
  }
@@ -4959,7 +5015,7 @@ async function finishRun(runId, outcome, options = {}) {
4959
5015
  });
4960
5016
  } catch (err) {
4961
5017
  const errText = err instanceof Error ? err.message : String(err);
4962
- const errId = createHash7("sha256").update(errText).digest("hex").slice(0, 12);
5018
+ const errId = createHash8("sha256").update(errText).digest("hex").slice(0, 12);
4963
5019
  log(`[runs] finish failed for run_id=${runId} outcome=${outcome} error_id=${errId}`);
4964
5020
  }
4965
5021
  }
@@ -4976,7 +5032,7 @@ async function fetchPriorScheduledRuns(agentId, taskId) {
4976
5032
  return rows.filter((r) => typeof r.output_text === "string" && r.output_text.length > 0).map((r) => ({ startedAt: r.started_at, output: r.output_text }));
4977
5033
  } catch (err) {
4978
5034
  const errText = err instanceof Error ? err.message : String(err);
4979
- const errId = createHash7("sha256").update(errText).digest("hex").slice(0, 12);
5035
+ const errId = createHash8("sha256").update(errText).digest("hex").slice(0, 12);
4980
5036
  log(`[runs] prior-runs lookup failed for task_id=${taskId} error_id=${errId}`);
4981
5037
  return [];
4982
5038
  }
@@ -5025,13 +5081,13 @@ function closeScheduledRunsForCode(codeName, outcome, reason) {
5025
5081
  }
5026
5082
 
5027
5083
  // src/lib/manager/scheduler/kanban-route.ts
5028
- import { createHash as createHash9 } from "crypto";
5084
+ import { createHash as createHash10 } from "crypto";
5029
5085
  import { writeFileSync as writeFileSync8, renameSync, mkdirSync as mkdirSync6 } from "fs";
5030
5086
  import { homedir as homedir7 } from "os";
5031
5087
  import { join as join17, dirname as dirname5 } from "path";
5032
5088
 
5033
5089
  // src/lib/manager/scheduler/notify.ts
5034
- import { createHash as createHash8 } from "crypto";
5090
+ import { createHash as createHash9 } from "crypto";
5035
5091
  async function enqueueScheduledTaskNotice(opts) {
5036
5092
  try {
5037
5093
  const res = await api.post(
@@ -5047,7 +5103,7 @@ async function enqueueScheduledTaskNotice(opts) {
5047
5103
  return res.ok === true;
5048
5104
  } catch (err) {
5049
5105
  const errText = err instanceof Error ? err.message : String(err);
5050
- const errId = createHash8("sha256").update(errText).digest("hex").slice(0, 12);
5106
+ const errId = createHash9("sha256").update(errText).digest("hex").slice(0, 12);
5051
5107
  log(
5052
5108
  `[scheduled-kanban] notice enqueue failed for agent_id=${opts.agentId} task_id=${opts.taskId} error_id=${errId}`
5053
5109
  );
@@ -5481,24 +5537,24 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
5481
5537
  const assertion = parseDeliverAssertion(rawOutput);
5482
5538
  if (!assertion.deliver) {
5483
5539
  const trimmed = (rawOutput ?? "").trim();
5484
- const outputHash = trimmed.length === 0 ? "empty" : createHash9("sha256").update(trimmed).digest("hex").slice(0, 12);
5540
+ const outputHash = trimmed.length === 0 ? "empty" : createHash10("sha256").update(trimmed).digest("hex").slice(0, 12);
5485
5541
  log(`[claude-scheduler] Suppressed by delivery_policy=conditional for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 ${assertion.vacuous ? "deliver marker had a vacuous reason" : "no deliver marker"}; output_len=${trimmed.length} output_hash=${outputHash}`);
5486
5542
  if (delivery?.mode === "announce" && delivery.to) {
5487
5543
  await reportDeliveryStatus(agentId, delivery.taskId, { status: "skipped", error_code: "SUPPRESSED_BY_POLICY" });
5488
5544
  }
5489
5545
  return { ok: true };
5490
5546
  }
5491
- const reasonHash = createHash9("sha256").update(assertion.reason ?? "").digest("hex").slice(0, 12);
5547
+ const reasonHash = createHash10("sha256").update(assertion.reason ?? "").digest("hex").slice(0, 12);
5492
5548
  log(`[claude-scheduler] Delivering conditional run for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 agent asserted a concrete trigger (reason_len=${(assertion.reason ?? "").length} reason_hash=${reasonHash})`);
5493
5549
  rawOutput = assertion.deliverable;
5494
5550
  }
5495
5551
  const classification = classifyOutput(rawOutput);
5496
5552
  if (classification.action === "suppress") {
5497
5553
  const trimmed = (rawOutput ?? "").trim();
5498
- const outputHash = trimmed.length === 0 ? "empty" : createHash9("sha256").update(trimmed).digest("hex").slice(0, 12);
5554
+ const outputHash = trimmed.length === 0 ? "empty" : createHash10("sha256").update(trimmed).digest("hex").slice(0, 12);
5499
5555
  log(`[claude-scheduler] Suppressing delivery for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 output_len=${trimmed.length} output_hash=${outputHash}`);
5500
5556
  if (classification.suppressedNotes) {
5501
- const notesHash = createHash9("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
5557
+ const notesHash = createHash10("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
5502
5558
  log(`[claude-scheduler] Suppressed notes for '${codeName}' (task=${delivery?.taskId ?? "n/a"}) \u2014 notes_len=${classification.suppressedNotes.length} notes_hash=${notesHash}`);
5503
5559
  }
5504
5560
  if (delivery?.mode === "announce" && delivery.to) {
@@ -5564,7 +5620,7 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
5564
5620
  }
5565
5621
 
5566
5622
  // src/lib/manager/scheduler/execution.ts
5567
- import { createHash as createHash10 } from "crypto";
5623
+ import { createHash as createHash11 } from "crypto";
5568
5624
  import { homedir as homedir8 } from "os";
5569
5625
  import { join as join18 } from "path";
5570
5626
  function claudePidFilePath() {
@@ -5588,10 +5644,10 @@ function unregisterClaudeSpawn(pid) {
5588
5644
  }
5589
5645
  async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData) {
5590
5646
  const codeName = agent.code_name;
5591
- const stableTasksHash = createHash10("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
5592
- const boardHash = boardItems.length > 0 ? createHash10("sha256").update(JSON.stringify(boardItems.map((b) => ({ id: b.id, title: b.title, status: b.status, priority: b.priority, deliverable: b.deliverable })))).digest("hex").slice(0, 16) : "empty";
5647
+ const stableTasksHash = createHash11("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
5648
+ const boardHash = boardItems.length > 0 ? createHash11("sha256").update(JSON.stringify(boardItems.map((b) => ({ id: b.id, title: b.title, status: b.status, priority: b.priority, deliverable: b.deliverable })))).digest("hex").slice(0, 16) : "empty";
5593
5649
  const resolvedModels = resolveModelChain(refreshData);
5594
- const modelsHash = createHash10("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
5650
+ const modelsHash = createHash11("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
5595
5651
  const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
5596
5652
  const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
5597
5653
  if (combinedHash !== prevHash) {
@@ -5629,7 +5685,7 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
5629
5685
  }
5630
5686
 
5631
5687
  // src/lib/opencode-slack-ingest.ts
5632
- import { createHash as createHash11 } from "crypto";
5688
+ import { createHash as createHash12 } from "crypto";
5633
5689
 
5634
5690
  // ../../packages/core/dist/channels/slack-rich-text.js
5635
5691
  var MAX_DEPTH = 12;
@@ -6565,7 +6621,7 @@ function startSlackIngest(config2) {
6565
6621
  // src/lib/opencode-slack-ingest.ts
6566
6622
  var ingests = /* @__PURE__ */ new Map();
6567
6623
  function fingerprint(params) {
6568
- return createHash11("sha256").update(
6624
+ return createHash12("sha256").update(
6569
6625
  JSON.stringify({
6570
6626
  appToken: params.appToken,
6571
6627
  botToken: params.botToken,
@@ -6646,7 +6702,7 @@ function stopAllOpencodeSlackIngests(log2) {
6646
6702
  }
6647
6703
 
6648
6704
  // src/lib/manager/opencode-scheduler.ts
6649
- import { createHash as createHash12 } from "crypto";
6705
+ import { createHash as createHash13 } from "crypto";
6650
6706
  var MAX_OPENCODE_SCHED_CONCURRENCY = 2;
6651
6707
  var opencodeSchedulerStates = /* @__PURE__ */ new Map();
6652
6708
  var inFlightOpencodeTasks = /* @__PURE__ */ new Set();
@@ -6660,7 +6716,7 @@ function shouldDeliver(task, reply) {
6660
6716
  }
6661
6717
  async function syncAndCheckOpencodeScheduler(agent, tasks, refreshData) {
6662
6718
  const codeName = agent.code_name;
6663
- const tasksHash = createHash12("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
6719
+ const tasksHash = createHash13("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
6664
6720
  if (knownOpencodeTaskHashes.get(agent.agent_id) !== tasksHash) {
6665
6721
  const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
6666
6722
  const state8 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
@@ -6738,7 +6794,7 @@ async function fireOpencodeScheduledTask(agent, task) {
6738
6794
  }
6739
6795
 
6740
6796
  // src/lib/opencode-telegram-ingest.ts
6741
- import { createHash as createHash13 } from "crypto";
6797
+ import { createHash as createHash14 } from "crypto";
6742
6798
  import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync15, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
6743
6799
  import { randomUUID } from "crypto";
6744
6800
  import { join as join19 } from "path";
@@ -7274,7 +7330,7 @@ function anySignal(signals) {
7274
7330
  // src/lib/opencode-telegram-ingest.ts
7275
7331
  var ingests2 = /* @__PURE__ */ new Map();
7276
7332
  function fingerprint2(params) {
7277
- return createHash13("sha256").update(
7333
+ return createHash14("sha256").update(
7278
7334
  JSON.stringify({
7279
7335
  botToken: params.botToken,
7280
7336
  allowedChats: [...params.allowedChats].sort(),
@@ -9025,7 +9081,7 @@ var sessionLaunchManagedStructure = /* @__PURE__ */ new Map();
9025
9081
  function projectMcpHash(_codeName, projectDir) {
9026
9082
  try {
9027
9083
  const raw = readFileSync18(join22(projectDir, ".mcp.json"), "utf-8");
9028
- return createHash14("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
9084
+ return createHash15("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
9029
9085
  } catch {
9030
9086
  return null;
9031
9087
  }
@@ -9212,6 +9268,27 @@ function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason, gate
9212
9268
  }
9213
9269
  }
9214
9270
  }
9271
+ var agentRuntimeTeardownDeps = {
9272
+ stopOpencodeServe: (codeName, teardownLog) => stopOpencodeSession(codeName, teardownLog),
9273
+ stopManagerHeldChannelIngest: (codeName, teardownLog) => {
9274
+ stopOpencodeSlackIngest(codeName, teardownLog);
9275
+ stopOpencodeTelegramIngest(codeName, teardownLog);
9276
+ },
9277
+ // No breaker reason: a deprovision is intentional and must not count against
9278
+ // the restart circuit breaker (see stopPersistentSessionAndForgetMcpBaseline).
9279
+ stopClaudePersistentSession: (codeName) => stopPersistentSessionAndForgetMcpBaseline(codeName),
9280
+ killTmuxSession: (sessionName) => {
9281
+ try {
9282
+ syncExecFile("tmux", ["kill-session", "-t", sessionName], { stdio: "ignore" });
9283
+ return true;
9284
+ } catch {
9285
+ return false;
9286
+ }
9287
+ }
9288
+ };
9289
+ function stopAgentRuntime2(codeName, reason) {
9290
+ stopAgentRuntime(codeName, reason, log, agentRuntimeTeardownDeps);
9291
+ }
9215
9292
  function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
9216
9293
  const currentHash = projectMcpHash(codeName, projectDir);
9217
9294
  const action = decideMcpDriftAction(currentHash, runningMcpHashes.get(codeName));
@@ -9350,6 +9427,7 @@ function clearAgentCaches(agentId, codeName) {
9350
9427
  stopOpencodeSlackIngest(codeName, log);
9351
9428
  stopOpencodeTelegramIngest(codeName, log);
9352
9429
  agentFrameworkCache.delete(codeName);
9430
+ opencodeCredentialFingerprintBySession.delete(codeName);
9353
9431
  kanbanBoardCache.delete(codeName);
9354
9432
  clearKanbanNudgeState(codeName);
9355
9433
  notifyBoardCache.delete(codeName);
@@ -9378,7 +9456,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
9378
9456
  var lastVersionCheckAt = 0;
9379
9457
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
9380
9458
  var lastResponsivenessProbeAt = 0;
9381
- var agtCliVersion = true ? "0.28.412" : "dev";
9459
+ var agtCliVersion = true ? "0.28.414" : "dev";
9382
9460
  function resolveBrewPath(execFileSync2) {
9383
9461
  try {
9384
9462
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -10663,7 +10741,7 @@ async function pollCycle() {
10663
10741
  claudeAuth = await detectClaudeAuth();
10664
10742
  } catch (err) {
10665
10743
  const errText = err instanceof Error ? err.message : String(err);
10666
- const errId = createHash14("sha256").update(errText).digest("hex").slice(0, 12);
10744
+ const errId = createHash15("sha256").update(errText).digest("hex").slice(0, 12);
10667
10745
  log(`Claude auth detection failed (error_id=${errId})`);
10668
10746
  }
10669
10747
  const hostHasClaudeCode = state6.agents.some(
@@ -11142,12 +11220,7 @@ async function pollCycle() {
11142
11220
  if (!currentIds.has(prev.agentId)) {
11143
11221
  log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
11144
11222
  const adapter = resolveAgentFramework(prev.codeName);
11145
- stopPersistentSessionAndForgetMcpBaseline(prev.codeName);
11146
- try {
11147
- const { execSync: es } = await import("child_process");
11148
- es(`tmux kill-session -t agt-${prev.codeName} 2>/dev/null`, { stdio: "ignore" });
11149
- } catch {
11150
- }
11223
+ stopAgentRuntime2(prev.codeName, "removed-from-host");
11151
11224
  killAgentChannelProcesses(prev.codeName, { log });
11152
11225
  const agentDir = join22(adapter.getAgentDir(prev.codeName), "provision");
11153
11226
  await cleanupAgentFiles(prev.codeName, agentDir);
@@ -11393,13 +11466,7 @@ async function processAgent(agent, agentStates) {
11393
11466
  } else if (killPausedCodeNames.delete(agent.code_name)) {
11394
11467
  log(`[kill-switch] '${agent.code_name}' kill switch cleared while still ${agent.status} (no resume \u2014 paused for another reason)`);
11395
11468
  }
11396
- stopPersistentSessionAndForgetMcpBaseline(agent.code_name);
11397
- try {
11398
- const { execSync: es } = await import("child_process");
11399
- es(`tmux kill-session -t agt-${agent.code_name} 2>/dev/null`, { stdio: "ignore" });
11400
- log(`Killed tmux session for paused agent '${agent.code_name}'`);
11401
- } catch {
11402
- }
11469
+ stopAgentRuntime2(agent.code_name, agent.status);
11403
11470
  if (agent.status === "paused" && !agent.kill_switch) {
11404
11471
  maybeAutoResume(agent);
11405
11472
  }
@@ -11447,12 +11514,7 @@ async function processAgent(agent, agentStates) {
11447
11514
  );
11448
11515
  }
11449
11516
  log(`Agent '${agent.code_name}' is revoked, cleaning up`);
11450
- stopPersistentSessionAndForgetMcpBaseline(agent.code_name);
11451
- try {
11452
- const { execSync: es } = await import("child_process");
11453
- es(`tmux kill-session -t agt-${agent.code_name} 2>/dev/null`, { stdio: "ignore" });
11454
- } catch {
11455
- }
11517
+ stopAgentRuntime2(agent.code_name, "revoked");
11456
11518
  killAgentChannelProcesses(agent.code_name, { log });
11457
11519
  await cleanupAgentFiles(agent.code_name, agentDir);
11458
11520
  clearAgentCaches(agent.agent_id, agent.code_name);
@@ -12100,7 +12162,7 @@ async function processAgent(agent, agentStates) {
12100
12162
  const behaviourSubset = extractMsTeamsBehaviourSubset(
12101
12163
  msteamsEntry?.config
12102
12164
  );
12103
- const behaviourHash = createHash14("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
12165
+ const behaviourHash = createHash15("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
12104
12166
  const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
12105
12167
  const msteamsBehaviourRestrictive = isMsTeamsBehaviourRestrictive(behaviourSubset);
12106
12168
  const behaviourDecision = decideSenderPolicyRestart({
@@ -12156,7 +12218,7 @@ async function processAgent(agent, agentStates) {
12156
12218
  const slackBehaviourSubset = extractSlackBehaviourSubset(
12157
12219
  slackEntry?.config
12158
12220
  );
12159
- const slackBehaviourHash = createHash14("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
12221
+ const slackBehaviourHash = createHash15("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
12160
12222
  const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
12161
12223
  const slackBehaviourRestrictive = isSlackBehaviourRestrictive(slackBehaviourSubset);
12162
12224
  const slackBehaviourDecision = decideSenderPolicyRestart({
@@ -12589,10 +12651,10 @@ async function processAgent(agent, agentStates) {
12589
12651
  desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
12590
12652
  }
12591
12653
  const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
12592
- const headersHash = createHash14("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
12654
+ const headersHash = createHash15("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
12593
12655
  return `${e.serverId}|${e.url}|${headersHash}`;
12594
12656
  }).join("\n");
12595
- const mcpHash = createHash14("sha256").update(hashBasis).digest("hex").slice(0, 16);
12657
+ const mcpHash = createHash15("sha256").update(hashBasis).digest("hex").slice(0, 16);
12596
12658
  const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
12597
12659
  const structureHash = managedMcpStructureHash(desiredEntries);
12598
12660
  const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
@@ -12607,7 +12669,7 @@ async function processAgent(agent, agentStates) {
12607
12669
  if (mcpHash !== prevMcpHash) {
12608
12670
  for (const e of desiredEntries) {
12609
12671
  frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
12610
- const urlHash = createHash14("sha256").update(e.url).digest("hex").slice(0, 12);
12672
+ const urlHash = createHash15("sha256").update(e.url).digest("hex").slice(0, 12);
12611
12673
  log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
12612
12674
  }
12613
12675
  if (frameworkAdapter.removeMcpServer && frameworkAdapter.readMcpServers) {
@@ -12709,7 +12771,7 @@ async function processAgent(agent, agentStates) {
12709
12771
  if (frameworkAdapter.installSkillFiles) {
12710
12772
  const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
12711
12773
  const installedIntegrationSkills = [];
12712
- const { createHash: createHash15 } = await import("crypto");
12774
+ const { createHash: createHash16 } = await import("crypto");
12713
12775
  const refreshAny = refreshData;
12714
12776
  const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
12715
12777
  const contextBySlug = /* @__PURE__ */ new Map();
@@ -12738,7 +12800,7 @@ async function processAgent(agent, agentStates) {
12738
12800
  )
12739
12801
  }));
12740
12802
  const bundle = buildIntegrationBundle(renderedScopes);
12741
- const contentHash = createHash15("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
12803
+ const contentHash = createHash16("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
12742
12804
  const hashKey = `plugin-skill:${agent.agent_id}:${integrationSkillId}`;
12743
12805
  if (agentState.knownSkillHashes.get(hashKey) === contentHash) continue;
12744
12806
  frameworkAdapter.installSkillFiles(agent.code_name, integrationSkillId, bundle.files);
@@ -12805,7 +12867,7 @@ async function processAgent(agent, agentStates) {
12805
12867
  const plan = planGlobalSkillSync(
12806
12868
  [...globalSkillsPayload ?? [], ...sharedSkillsPayload ?? []],
12807
12869
  prevIds,
12808
- (content) => createHash15("sha256").update(content).digest("hex").slice(0, 12),
12870
+ (content) => createHash16("sha256").update(content).digest("hex").slice(0, 12),
12809
12871
  (skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`),
12810
12872
  { desiredResolved }
12811
12873
  );
@@ -12856,7 +12918,7 @@ async function processAgent(agent, agentStates) {
12856
12918
  const slug = hook.integration_slug ?? hook.plugin_slug;
12857
12919
  if (!slug) continue;
12858
12920
  try {
12859
- const scriptHash = createHash15("sha256").update(hook.script).digest("hex").slice(0, 12);
12921
+ const scriptHash = createHash16("sha256").update(hook.script).digest("hex").slice(0, 12);
12860
12922
  const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
12861
12923
  if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
12862
12924
  const result = await frameworkAdapter.executePluginHook({
@@ -12871,9 +12933,9 @@ async function processAgent(agent, agentStates) {
12871
12933
  } else if (result.timedOut) {
12872
12934
  log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
12873
12935
  } else {
12874
- const stderrHash = createHash15("sha256").update(result.stderr).digest("hex").slice(0, 12);
12936
+ const stderrHash = createHash16("sha256").update(result.stderr).digest("hex").slice(0, 12);
12875
12937
  const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
12876
- const missingCmdHash = missingCmd ? createHash15("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
12938
+ const missingCmdHash = missingCmd ? createHash16("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
12877
12939
  log(
12878
12940
  `Integration hook on_install '${slug}' exited ${result.exitCode} for '${agent.code_name}' ` + (missingCmdHash ? `[missing_command_hash=${missingCmdHash}] ` : "") + `[stderr_hash=${stderrHash} stderr_len=${result.stderr.length}]`
12879
12941
  );
@@ -13472,6 +13534,7 @@ var lastModelApiErrorSig = /* @__PURE__ */ new Map();
13472
13534
  var DAY_ROLLOVER_FORCE_GRACE_MIN = 30;
13473
13535
  var persistentSessionStuckTracker = new PersistentSessionStuckTracker();
13474
13536
  var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
13537
+ var opencodeCredentialFingerprintBySession = /* @__PURE__ */ new Map();
13475
13538
  var egressAllowlistBySession = /* @__PURE__ */ new Map();
13476
13539
  function deriveEgressAllowlist(toolsRaw) {
13477
13540
  return buildEgressAllowlist(
@@ -13481,16 +13544,31 @@ function deriveEgressAllowlist(toolsRaw) {
13481
13544
  var egressAllowlistEqual = (a, b) => a.length === b.length && a.every((d, i) => d === b[i]);
13482
13545
  async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
13483
13546
  const codeName = agent.code_name;
13547
+ const openRouterRaw = refreshData.openrouter ?? null;
13548
+ const anthropicRaw = refreshData.anthropic ?? null;
13549
+ const credentialFingerprint = opencodeCredentialFingerprint({
13550
+ openRouterToken: openRouterRaw?.auth_token,
13551
+ openRouterFingerprint: openRouterRaw?.fingerprint,
13552
+ anthropicKey: anthropicRaw?.api_key
13553
+ });
13484
13554
  if (isOpencodeSessionHealthy(codeName)) {
13485
- await ensureOpencodeSlackIngest({ codeName, agentId: agent.agent_id, log });
13486
- await ensureOpencodeTelegramIngest({ codeName, agentId: agent.agent_id, log });
13487
- return { decision: "healthy", spawnAttempted: false, sessionHealthyAfter: true };
13555
+ const recorded = opencodeCredentialFingerprintBySession.get(codeName);
13556
+ if (recorded && shouldRestartForCredentialChange(recorded, credentialFingerprint)) {
13557
+ log(
13558
+ `[opencode-session] Provider credential rotated for '${codeName}' (${recorded} \u2192 ${credentialFingerprint}) \u2014 restarting serve`
13559
+ );
13560
+ stopOpencodeSession(codeName, log);
13561
+ opencodeCredentialFingerprintBySession.delete(codeName);
13562
+ } else {
13563
+ await ensureOpencodeSlackIngest({ codeName, agentId: agent.agent_id, log });
13564
+ await ensureOpencodeTelegramIngest({ codeName, agentId: agent.agent_id, log });
13565
+ if (!recorded) opencodeCredentialFingerprintBySession.set(codeName, credentialFingerprint);
13566
+ return { decision: "healthy", spawnAttempted: false, sessionHealthyAfter: true };
13567
+ }
13488
13568
  }
13489
13569
  stopOpencodeSlackIngest(codeName, log);
13490
13570
  stopOpencodeTelegramIngest(codeName, log);
13491
13571
  const opencodeProjectDir = join22(getFramework("opencode").getAgentDir(codeName), "provision");
13492
- const openRouterRaw = refreshData.openrouter ?? null;
13493
- const anthropicRaw = refreshData.anthropic ?? null;
13494
13572
  const serveEnv = {
13495
13573
  AGT_HOST: requireHost(),
13496
13574
  AGT_API_KEY: getApiKey() ?? void 0,
@@ -13522,6 +13600,7 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
13522
13600
  detail: tail ? `opencode spawn failed: ${tail.slice(0, 200)}` : "opencode spawn failed"
13523
13601
  };
13524
13602
  }
13603
+ opencodeCredentialFingerprintBySession.set(codeName, credentialFingerprint);
13525
13604
  return {
13526
13605
  decision: "spawn",
13527
13606
  spawnAttempted,
@@ -13691,7 +13770,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
13691
13770
  const ctx = getLastFailureContext(codeName);
13692
13771
  const recovery = prepareForRespawn(codeName);
13693
13772
  const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
13694
- ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash14("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
13773
+ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash15("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
13695
13774
  const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
13696
13775
  const recoverySummary = recovery ? `; recovery=${recovery}` : "";
13697
13776
  log(
@@ -13705,7 +13784,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash14("sha256")
13705
13784
  );
13706
13785
  getHostId().then((hostId) => {
13707
13786
  if (!hostId) return;
13708
- const paneTailHash = zombie.paneTail ? `sha256:${createHash14("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
13787
+ const paneTailHash = zombie.paneTail ? `sha256:${createHash15("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
13709
13788
  return api.post("/host/events", {
13710
13789
  host_id: hostId,
13711
13790
  agent_code_name: codeName,
@@ -13854,7 +13933,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash14("sha256")
13854
13933
  if (!claudeAuthTupleBySession.has(codeName)) {
13855
13934
  claudeAuthTupleBySession.set(codeName, currentAuthTuple);
13856
13935
  }
13857
- const stableTasksHash = createHash14("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
13936
+ const stableTasksHash = createHash15("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
13858
13937
  const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
13859
13938
  if (stableTasksHash !== prevHash) {
13860
13939
  const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
@@ -14855,7 +14934,7 @@ async function syncMemories(agent, configDir, log2) {
14855
14934
  if (!file.endsWith(".md")) continue;
14856
14935
  try {
14857
14936
  const raw = readFileSync18(join22(memoryDir, file), "utf-8");
14858
- const fileHash = createHash14("sha256").update(raw).digest("hex").slice(0, 16);
14937
+ const fileHash = createHash15("sha256").update(raw).digest("hex").slice(0, 16);
14859
14938
  currentHashes.set(file, fileHash);
14860
14939
  if (prevHashes.get(file) === fileHash) continue;
14861
14940
  const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
@@ -14893,14 +14972,14 @@ async function syncMemories(agent, configDir, log2) {
14893
14972
  }
14894
14973
  async function downloadMemories(agent, memoryDir, log2, { force }) {
14895
14974
  const localFiles = existsSync11(memoryDir) ? readdirSync6(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
14896
- const localListHash = createHash14("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
14975
+ const localListHash = createHash15("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
14897
14976
  const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
14898
14977
  const prevDownload = lastDownloadHash.get(agent.agent_id);
14899
14978
  try {
14900
14979
  const dbMemories = await api.post("/host/memories", {
14901
14980
  agent_id: agent.agent_id
14902
14981
  });
14903
- const responseHash = createHash14("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
14982
+ const responseHash = createHash15("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
14904
14983
  if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
14905
14984
  return true;
14906
14985
  }
@@ -14939,7 +15018,7 @@ ${mem.content}
14939
15018
  }
14940
15019
  if (written > 0 || overwritten > 0) {
14941
15020
  const updatedFiles = readdirSync6(memoryDir).filter((f) => f.endsWith(".md")).sort();
14942
- lastLocalFileHash.set(agent.agent_id, createHash14("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
15021
+ lastLocalFileHash.set(agent.agent_id, createHash15("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
14943
15022
  log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
14944
15023
  }
14945
15024
  }
@@ -15364,7 +15443,7 @@ function deployMcpAssets() {
15364
15443
  const fileHash = (p) => {
15365
15444
  try {
15366
15445
  if (!existsSync11(p)) return null;
15367
- return createHash14("sha256").update(readFileSync18(p)).digest("hex");
15446
+ return createHash15("sha256").update(readFileSync18(p)).digest("hex");
15368
15447
  } catch {
15369
15448
  return null;
15370
15449
  }