@integrity-labs/agt-cli 0.28.733 → 0.28.735

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.
@@ -13,7 +13,7 @@ import {
13
13
  parseEnvFileEntries,
14
14
  parseEnvIntegrations,
15
15
  shellQuote
16
- } from "./chunk-7KT2QNXF.js";
16
+ } from "./chunk-5CF4DRXA.js";
17
17
  import {
18
18
  BIND_FAILURE_QUARANTINE_THRESHOLD,
19
19
  INTEGRATIONS_SECTION_END,
@@ -6427,7 +6427,7 @@ function exchangeFailureKind(err) {
6427
6427
  }
6428
6428
 
6429
6429
  // src/lib/api-client.ts
6430
- var agtCliVersion = true ? "0.28.733" : "dev";
6430
+ var agtCliVersion = true ? "0.28.735" : "dev";
6431
6431
  var lastConfigHash = null;
6432
6432
  function setConfigHash(hash) {
6433
6433
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -10473,4 +10473,4 @@ export {
10473
10473
  managerInstallSystemUnitCommand,
10474
10474
  managerUninstallSystemUnitCommand
10475
10475
  };
10476
- //# sourceMappingURL=chunk-LT7UGH4O.js.map
10476
+ //# sourceMappingURL=chunk-AZZ7MLBE.js.map
@@ -100,7 +100,7 @@ async function spawnPairSession(session) {
100
100
  return { ok: true };
101
101
  } catch {
102
102
  }
103
- const { resolveClaudeBinary } = await import("./persistent-session-H7CP2TUX.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-4KGDH66U.js");
104
104
  const claudeBin = resolveClaudeBinary();
105
105
  const pairEnv = {
106
106
  ...process.env,
@@ -373,4 +373,4 @@ export {
373
373
  startClaudePair,
374
374
  submitClaudePairCode
375
375
  };
376
- //# sourceMappingURL=claude-pair-runtime-K5OUT4QN.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-AWLXOAV6.js.map
@@ -57,7 +57,7 @@ import {
57
57
  safeWriteJsonAtomic,
58
58
  setConfigHash,
59
59
  tripClass
60
- } from "../chunk-LT7UGH4O.js";
60
+ } from "../chunk-AZZ7MLBE.js";
61
61
  import {
62
62
  getProjectDir as getProjectDir2,
63
63
  getReadyTasks,
@@ -118,7 +118,7 @@ import {
118
118
  takeZombieDetection,
119
119
  toOpencodeModel,
120
120
  writeEgressAllowlist
121
- } from "../chunk-7KT2QNXF.js";
121
+ } from "../chunk-5CF4DRXA.js";
122
122
  import {
123
123
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
124
124
  AnchorSessionClient,
@@ -5584,6 +5584,48 @@ var REFRESH_MANIFEST_FLAGS = {
5584
5584
  integration_skills_manifest: true
5585
5585
  };
5586
5586
 
5587
+ // src/lib/fetch-cadence.ts
5588
+ function createFetchGate(ttlMs) {
5589
+ const lastFetchedAt = /* @__PURE__ */ new Map();
5590
+ const forceCount = /* @__PURE__ */ new Map();
5591
+ const consumedForceCount = /* @__PURE__ */ new Map();
5592
+ let generation = 0;
5593
+ const fetchedAtGeneration = /* @__PURE__ */ new Map();
5594
+ const pendingForces = (key) => (forceCount.get(key) ?? 0) > (consumedForceCount.get(key) ?? 0);
5595
+ return {
5596
+ shouldFetch(key, now = Date.now()) {
5597
+ if (pendingForces(key)) return true;
5598
+ if ((fetchedAtGeneration.get(key) ?? -1) < generation) return true;
5599
+ const last = lastFetchedAt.get(key);
5600
+ if (last === void 0) return true;
5601
+ return now - last >= ttlMs;
5602
+ },
5603
+ beginFetch(key) {
5604
+ return { forceCount: forceCount.get(key) ?? 0, generation };
5605
+ },
5606
+ markFetched(key, token, now = Date.now()) {
5607
+ lastFetchedAt.set(key, now);
5608
+ fetchedAtGeneration.set(key, token.generation);
5609
+ consumedForceCount.set(key, token.forceCount);
5610
+ },
5611
+ forceNext(key) {
5612
+ forceCount.set(key, (forceCount.get(key) ?? 0) + 1);
5613
+ },
5614
+ forceAll() {
5615
+ generation += 1;
5616
+ },
5617
+ forget(key) {
5618
+ lastFetchedAt.delete(key);
5619
+ fetchedAtGeneration.delete(key);
5620
+ forceCount.delete(key);
5621
+ consumedForceCount.delete(key);
5622
+ },
5623
+ size() {
5624
+ return lastFetchedAt.size;
5625
+ }
5626
+ };
5627
+ }
5628
+
5587
5629
  // src/lib/channel-hash-cache.ts
5588
5630
  import { existsSync as existsSync8, readFileSync as readFileSync19, writeFileSync as writeFileSync10 } from "fs";
5589
5631
  import { join as join25 } from "path";
@@ -9218,6 +9260,9 @@ function decideInboundAccess(input) {
9218
9260
  if (input.orgBoundary && !input.orgBoundary.forward) {
9219
9261
  return { kind: "drop", reason: "org_boundary" };
9220
9262
  }
9263
+ if (input.chatType && !input.chatType.forward) {
9264
+ return { kind: "drop", reason: "chat_type" };
9265
+ }
9221
9266
  if (input.senderPolicy && !input.senderPolicy.forward) {
9222
9267
  const reason = input.senderPolicy.reason;
9223
9268
  const declineWorthy = input.sameOrg && reason !== "internal_only";
@@ -9993,6 +10038,11 @@ function parsePeerAgentModeEnv2(raw) {
9993
10038
  return "off";
9994
10039
  }
9995
10040
 
10041
+ // ../../packages/core/dist/channels/governance/telegram-chat-type.js
10042
+ function isTelegramPrivateChat(chatType) {
10043
+ return chatType === "private";
10044
+ }
10045
+
9996
10046
  // ../../packages/core/dist/channels/governance/telegram-sender-gate.js
9997
10047
  function evaluateTelegramInboundGate(evt, deps) {
9998
10048
  const isBot = Boolean(evt.from?.is_bot);
@@ -10029,6 +10079,10 @@ function evaluateTelegramInboundGate(evt, deps) {
10029
10079
  // TELEGRAM_ALLOWED_CHATS is Telegram's org-boundary axis. An unlisted chat is
10030
10080
  // external ⇒ silent drop.
10031
10081
  orgBoundary: deps.allowedChats.size > 0 && !deps.allowedChats.has(chatId) ? { forward: false } : { forward: true },
10082
+ // CS-1628: the chat-kind axis. Only ever restricts - when the policy is off
10083
+ // this is `{ forward: true }`, which the composer treats identically to the
10084
+ // field being absent.
10085
+ chatType: deps.privateChatsOnly && !isTelegramPrivateChat(evt.chat.type) ? { forward: false } : { forward: true },
10032
10086
  // No sender-policy mode system and no command dispatch on the injection gate.
10033
10087
  sameOrg: false
10034
10088
  });
@@ -12347,7 +12401,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
12347
12401
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
12348
12402
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
12349
12403
  async function channelInboundActivityAgeSecondsFor(codeName) {
12350
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-MPPDJSOL.js");
12404
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-HNUYLIFJ.js");
12351
12405
  const newest = newestPendingInboundActivityMtimeMs(dirname10(paneLogPath(codeName)));
12352
12406
  if (newest === null) return null;
12353
12407
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -12960,6 +13014,9 @@ function resolveAgentFramework(codeName) {
12960
13014
  function clearAgentCaches(agentId, codeName) {
12961
13015
  const channelCacheMutated = clearAgentState(agentId, codeName);
12962
13016
  agentChannelTokens.delete(codeName);
13017
+ lastRefreshPayload.delete(agentId);
13018
+ refreshFetchGate.forget(agentId);
13019
+ memoriesFetchGate.forget(agentId);
12963
13020
  forgetRealtimeRebindState(agentId);
12964
13021
  forgetChannelSyncState(agentId);
12965
13022
  sharedBusyBuckets.reset(codeName);
@@ -12995,7 +13052,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
12995
13052
  var lastVersionCheckAt = 0;
12996
13053
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
12997
13054
  var lastResponsivenessProbeAt = 0;
12998
- var agtCliVersion = true ? "0.28.733" : "dev";
13055
+ var agtCliVersion = true ? "0.28.735" : "dev";
12999
13056
  function resolveBrewPath(execFileSync3) {
13000
13057
  try {
13001
13058
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -14505,6 +14562,16 @@ ${SKILLS_INDEX_END}`;
14505
14562
  );
14506
14563
  }
14507
14564
  }
14565
+ var DESIRED_STATE_FETCH_TTL_MS = 6e4;
14566
+ var refreshFetchGate = createFetchGate(DESIRED_STATE_FETCH_TTL_MS);
14567
+ var lastRefreshPayload = /* @__PURE__ */ new Map();
14568
+ var memoriesFetchGate = createFetchGate(DESIRED_STATE_FETCH_TTL_MS);
14569
+ var pollCycleFetches = 0;
14570
+ var pollCycleFetchSkips = 0;
14571
+ function forceRefreshFetch() {
14572
+ refreshFetchGate.forceAll();
14573
+ memoriesFetchGate.forceAll();
14574
+ }
14508
14575
  var consecutivePollFailures = 0;
14509
14576
  function reorderRestartedFirst(agents, restartedIds) {
14510
14577
  if (restartedIds.size === 0) return agents;
@@ -14522,7 +14589,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
14522
14589
  if (codeNames.length === 0) return;
14523
14590
  void (async () => {
14524
14591
  try {
14525
- const { collectDiagnostics } = await import("../persistent-session-H7CP2TUX.js");
14592
+ const { collectDiagnostics } = await import("../persistent-session-4KGDH66U.js");
14526
14593
  await api.post("/host/heartbeat", {
14527
14594
  host_id: hostId,
14528
14595
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -14663,7 +14730,7 @@ async function pollCycleInner() {
14663
14730
  }
14664
14731
  try {
14665
14732
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
14666
- const { collectDiagnostics } = await import("../persistent-session-H7CP2TUX.js");
14733
+ const { collectDiagnostics } = await import("../persistent-session-4KGDH66U.js");
14667
14734
  const diagCodeNames = [...agentState.persistentSessionAgents];
14668
14735
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
14669
14736
  let tailscaleHostname;
@@ -14832,7 +14899,7 @@ async function pollCycleInner() {
14832
14899
  collectPanelessActivityProbes,
14833
14900
  getResponsivenessIntervalMs,
14834
14901
  occupancyQualificationClassifications
14835
- } = await import("../responsiveness-probe-MPPDJSOL.js");
14902
+ } = await import("../responsiveness-probe-HNUYLIFJ.js");
14836
14903
  const probeIntervalMs = getResponsivenessIntervalMs();
14837
14904
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
14838
14905
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -14942,7 +15009,7 @@ async function pollCycleInner() {
14942
15009
  collectResponsivenessProbes,
14943
15010
  livePendingInboundOldestAgeSeconds,
14944
15011
  parkPendingInbound
14945
- } = await import("../responsiveness-probe-MPPDJSOL.js");
15012
+ } = await import("../responsiveness-probe-HNUYLIFJ.js");
14946
15013
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
14947
15014
  const wedgeNow = /* @__PURE__ */ new Date();
14948
15015
  const liveAgents = agentState.persistentSessionAgents;
@@ -15258,6 +15325,9 @@ async function pollCycleInner() {
15258
15325
  const processOrder = fastRespawn ? reorderRestartedFirst(agents, new Set(restartAcks.keys())) : agents;
15259
15326
  const managedToolkits = createManagedToolkitReader(agents.map((a) => a.agent_id));
15260
15327
  managedPruneLedger = createManagedPruneLedger();
15328
+ const cycleStartedAt = Date.now();
15329
+ pollCycleFetches = 0;
15330
+ pollCycleFetchSkips = 0;
15261
15331
  for (const agent of processOrder) {
15262
15332
  if (restartInFlight.has(agent.agent_id)) {
15263
15333
  const existing = state8.agents.find((a) => a.agentId === agent.agent_id);
@@ -15296,6 +15366,9 @@ async function pollCycleInner() {
15296
15366
  promptBindProbeAttempts.set(agent.code_name, PROMPT_BIND_PROBE_ATTEMPTS);
15297
15367
  }
15298
15368
  }
15369
+ log(
15370
+ `[poll-cycle] agents=${processOrder.length} refresh_fetched=${pollCycleFetches} refresh_skipped=${pollCycleFetchSkips} duration_ms=${Date.now() - cycleStartedAt}`
15371
+ );
15299
15372
  void maybeReportActivityCache({ api, log });
15300
15373
  const crossAgentPrune = managedPruneLedger.summarize();
15301
15374
  if (crossAgentPrune) log(crossAgentPrune);
@@ -15814,6 +15887,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
15814
15887
  if (previousStatus && previousStatus !== agent.status) {
15815
15888
  log(`Agent '${agent.code_name}' status changed: ${previousStatus} \u2192 ${agent.status}`);
15816
15889
  agentState.knownVersions.delete(agent.agent_id);
15890
+ refreshFetchGate.forceNext(agent.agent_id);
15817
15891
  if (previousStatus === "paused" && agent.status === "active" && restartBreaker.isTripped(agent.code_name)) {
15818
15892
  restartBreaker.clear(agent.code_name);
15819
15893
  reportedTrips.delete(agent.code_name);
@@ -15853,85 +15927,95 @@ async function processAgent(agent, agentStates, managedToolkits) {
15853
15927
  }
15854
15928
  agentState.knownStatuses.set(agent.agent_id, agent.status);
15855
15929
  let refreshData;
15856
- try {
15857
- refreshData = await api.post("/host/refresh", {
15858
- agent_id: agent.agent_id,
15859
- // ENG-9585: ask for hashes instead of bodies on all three lanes. The
15860
- // rationale for each flag, and for their staying three flags rather than
15861
- // one, now lives with the constant — so that a new `/host/refresh` caller
15862
- // inherits it instead of having to rediscover it. The manifest is
15863
- // normalised back into `refreshData.knowledge` immediately below.
15864
- ...REFRESH_MANIFEST_FLAGS
15865
- });
15866
- if (refreshData.knowledge_manifest) {
15867
- const resolved = await resolveKnowledgeFromManifest({
15868
- manifest: refreshData.knowledge_manifest,
15869
- cacheDir: join40(config.configDir, "_knowledge"),
15870
- delivery: refreshData.agent?.knowledge_delivery ?? "both",
15871
- fetchContents: async (hashes) => {
15872
- const res = await api.post(
15873
- "/host/knowledge/contents",
15874
- { agent_id: agent.agent_id, hashes }
15875
- );
15876
- return res.contents ?? [];
15877
- },
15878
- log
15879
- });
15880
- refreshData.knowledge = resolved;
15881
- }
15882
- if (refreshData.global_skills_manifest || refreshData.shared_skills_manifest) {
15883
- const resolvedSkills = await resolveSkillsFromManifest({
15884
- globals: refreshData.global_skills_manifest ?? [],
15885
- shared: refreshData.shared_skills_manifest ?? [],
15886
- cacheDir: join40(config.configDir, "_skills"),
15887
- fetchContents: async (hashes) => {
15888
- const res = await api.post(
15889
- "/host/skills/contents",
15890
- { agent_id: agent.agent_id, hashes }
15891
- );
15892
- return res.contents ?? [];
15893
- },
15894
- log
15895
- });
15896
- if (resolvedSkills) {
15897
- refreshData.global_skills = resolvedSkills.globals;
15898
- refreshData.shared_skills = resolvedSkills.shared;
15899
- }
15900
- }
15901
- if (refreshData.integration_skills_manifest) {
15902
- const resolvedIntegrationSkills = await resolveIntegrationSkillsFromManifest({
15903
- manifest: refreshData.integration_skills_manifest,
15904
- cacheDir: join40(config.configDir, "_skills"),
15905
- fetchContents: async (hashes) => {
15906
- const res = await api.post(
15907
- "/host/skills/contents",
15908
- { agent_id: agent.agent_id, hashes }
15909
- );
15910
- return res.contents ?? [];
15911
- },
15912
- log
15930
+ const cachedRefresh = lastRefreshPayload.get(agent.agent_id);
15931
+ const refreshToken = refreshFetchGate.beginFetch(agent.agent_id);
15932
+ if (cachedRefresh !== void 0 && !refreshFetchGate.shouldFetch(agent.agent_id)) {
15933
+ refreshData = cachedRefresh;
15934
+ pollCycleFetchSkips += 1;
15935
+ } else {
15936
+ try {
15937
+ refreshData = await api.post("/host/refresh", {
15938
+ agent_id: agent.agent_id,
15939
+ // ENG-9585: ask for hashes instead of bodies on all three lanes. The
15940
+ // rationale for each flag, and for their staying three flags rather than
15941
+ // one, now lives with the constant — so that a new `/host/refresh` caller
15942
+ // inherits it instead of having to rediscover it. The manifest is
15943
+ // normalised back into `refreshData.knowledge` immediately below.
15944
+ ...REFRESH_MANIFEST_FLAGS
15913
15945
  });
15914
- if (resolvedIntegrationSkills) {
15915
- refreshData.integration_skills = resolvedIntegrationSkills;
15946
+ if (refreshData.knowledge_manifest) {
15947
+ const resolved = await resolveKnowledgeFromManifest({
15948
+ manifest: refreshData.knowledge_manifest,
15949
+ cacheDir: join40(config.configDir, "_knowledge"),
15950
+ delivery: refreshData.agent?.knowledge_delivery ?? "both",
15951
+ fetchContents: async (hashes) => {
15952
+ const res = await api.post(
15953
+ "/host/knowledge/contents",
15954
+ { agent_id: agent.agent_id, hashes }
15955
+ );
15956
+ return res.contents ?? [];
15957
+ },
15958
+ log
15959
+ });
15960
+ refreshData.knowledge = resolved;
15961
+ }
15962
+ if (refreshData.global_skills_manifest || refreshData.shared_skills_manifest) {
15963
+ const resolvedSkills = await resolveSkillsFromManifest({
15964
+ globals: refreshData.global_skills_manifest ?? [],
15965
+ shared: refreshData.shared_skills_manifest ?? [],
15966
+ cacheDir: join40(config.configDir, "_skills"),
15967
+ fetchContents: async (hashes) => {
15968
+ const res = await api.post(
15969
+ "/host/skills/contents",
15970
+ { agent_id: agent.agent_id, hashes }
15971
+ );
15972
+ return res.contents ?? [];
15973
+ },
15974
+ log
15975
+ });
15976
+ if (resolvedSkills) {
15977
+ refreshData.global_skills = resolvedSkills.globals;
15978
+ refreshData.shared_skills = resolvedSkills.shared;
15979
+ }
15916
15980
  }
15981
+ if (refreshData.integration_skills_manifest) {
15982
+ const resolvedIntegrationSkills = await resolveIntegrationSkillsFromManifest({
15983
+ manifest: refreshData.integration_skills_manifest,
15984
+ cacheDir: join40(config.configDir, "_skills"),
15985
+ fetchContents: async (hashes) => {
15986
+ const res = await api.post(
15987
+ "/host/skills/contents",
15988
+ { agent_id: agent.agent_id, hashes }
15989
+ );
15990
+ return res.contents ?? [];
15991
+ },
15992
+ log
15993
+ });
15994
+ if (resolvedIntegrationSkills) {
15995
+ refreshData.integration_skills = resolvedIntegrationSkills;
15996
+ }
15997
+ }
15998
+ } catch (err) {
15999
+ log(`Refresh failed for '${agent.code_name}': ${err.message}`);
16000
+ const existing = state8.agents.find((a) => a.agentId === agent.agent_id);
16001
+ agentStates.push(existing ?? {
16002
+ agentId: agent.agent_id,
16003
+ codeName: agent.code_name,
16004
+ status: agent.status,
16005
+ charterVersion: "",
16006
+ toolsVersion: "",
16007
+ secretsHash: null,
16008
+ lastRefreshAt: null,
16009
+ lastProvisionAt: null,
16010
+ lastDriftCheckAt: null,
16011
+ lastSecretsProvisionAt: null,
16012
+ acpSessions: []
16013
+ });
16014
+ return;
15917
16015
  }
15918
- } catch (err) {
15919
- log(`Refresh failed for '${agent.code_name}': ${err.message}`);
15920
- const existing = state8.agents.find((a) => a.agentId === agent.agent_id);
15921
- agentStates.push(existing ?? {
15922
- agentId: agent.agent_id,
15923
- codeName: agent.code_name,
15924
- status: agent.status,
15925
- charterVersion: "",
15926
- toolsVersion: "",
15927
- secretsHash: null,
15928
- lastRefreshAt: null,
15929
- lastProvisionAt: null,
15930
- lastDriftCheckAt: null,
15931
- lastSecretsProvisionAt: null,
15932
- acpSessions: []
15933
- });
15934
- return;
16016
+ lastRefreshPayload.set(agent.agent_id, refreshData);
16017
+ refreshFetchGate.markFetched(agent.agent_id, refreshToken);
16018
+ pollCycleFetches += 1;
15935
16019
  }
15936
16020
  if (!getAlertSlackWebhook() && refreshData.team?.settings) {
15937
16021
  const webhook = refreshData.team.settings["alert_slack_webhook"];
@@ -16801,8 +16885,8 @@ async function processAgent(agent, agentStates, managedToolkits) {
16801
16885
  for (const integration of integrations) {
16802
16886
  if (integration.auth_type !== "oauth2") continue;
16803
16887
  const expiresAt = integration.credentials?.token_expires_at;
16804
- const refreshToken = integration.credentials?.refresh_token;
16805
- if (!expiresAt || !refreshToken) continue;
16888
+ const refreshToken2 = integration.credentials?.refresh_token;
16889
+ if (!expiresAt || !refreshToken2) continue;
16806
16890
  const msRemaining = new Date(expiresAt).getTime() - Date.now();
16807
16891
  if (msRemaining > 10 * 60 * 1e3) continue;
16808
16892
  try {
@@ -17448,7 +17532,14 @@ async function processAgent(agent, agentStates, managedToolkits) {
17448
17532
  }
17449
17533
  const fetchTickBoard = createTickBoardFetcher(async () => {
17450
17534
  const boardData = await api.post("/host/my-kanban", {
17451
- agent_id: agent.agent_id
17535
+ agent_id: agent.agent_id,
17536
+ // ENG-9585: this is the ONLY caller that polls (every ~10s, per agent),
17537
+ // and it reads exactly `BoardItem`. `description` and `notes` are 57.8%
17538
+ // of the payload and are read here by nothing — but the `kanban_list` MCP
17539
+ // tool and `agt kanban` DO read them, so the route cannot simply drop
17540
+ // them. Asking for the lean shape is this caller saying what it needs;
17541
+ // every other caller keeps the full payload by default.
17542
+ detail: "board"
17452
17543
  });
17453
17544
  return (boardData.items ?? []).map(sanitizeBoardItem);
17454
17545
  });
@@ -18933,6 +19024,7 @@ function ensureRealtimeDriftStarted(agentStates) {
18933
19024
  const agent = agentStates.find((a) => a.agentId === doc.agent_id);
18934
19025
  if (agent) {
18935
19026
  agentState.knownVersions.delete(doc.agent_id);
19027
+ refreshFetchGate.forceNext(doc.agent_id);
18936
19028
  log(`[realtime] Drift invalidated for '${agent.codeName}' \u2014 will re-provision next cycle`);
18937
19029
  }
18938
19030
  },
@@ -19009,7 +19101,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
19009
19101
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
19010
19102
  void (async () => {
19011
19103
  try {
19012
- const { collectDiagnostics } = await import("../persistent-session-H7CP2TUX.js");
19104
+ const { collectDiagnostics } = await import("../persistent-session-4KGDH66U.js");
19013
19105
  await api.post("/host/heartbeat", {
19014
19106
  host_id: hostId,
19015
19107
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -19055,7 +19147,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
19055
19147
  }
19056
19148
  try {
19057
19149
  const hostId = await getHostId();
19058
- const { collectDiagnostics } = await import("../persistent-session-H7CP2TUX.js");
19150
+ const { collectDiagnostics } = await import("../persistent-session-4KGDH66U.js");
19059
19151
  await api.post("/host/heartbeat", {
19060
19152
  host_id: hostId,
19061
19153
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -19096,6 +19188,8 @@ function ensureRealtimeAssignStarted(agentStates) {
19096
19188
  );
19097
19189
  },
19098
19190
  onAssign: (payload) => {
19191
+ refreshFetchGate.forceNext(payload.agent_id);
19192
+ memoriesFetchGate.forceNext(payload.agent_id);
19099
19193
  log(`[realtime] Agent ${payload.agent_id} assigned \u2014 will pick up next cycle`);
19100
19194
  markAgentForFreshMemorySync(payload.agent_id);
19101
19195
  },
@@ -19311,6 +19405,7 @@ function triggerEarlyPoll(reason) {
19311
19405
  pollTimer = null;
19312
19406
  }
19313
19407
  log(`[realtime] Triggering early poll: ${reason}`);
19408
+ forceRefreshFetch();
19314
19409
  pollTimer = setTimeout(() => {
19315
19410
  void pollCycleAndReschedule(pollCycle, {
19316
19411
  onError: (err) => log(`[poll-loop] early-poll cycle rejected: ${err?.message ?? String(err)}`),
@@ -19687,7 +19782,7 @@ async function processClaudePairSessions(agents) {
19687
19782
  killPairSession,
19688
19783
  pairTmuxSession,
19689
19784
  finalizeClaudePairOnboarding
19690
- } = await import("../claude-pair-runtime-K5OUT4QN.js");
19785
+ } = await import("../claude-pair-runtime-AWLXOAV6.js");
19691
19786
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
19692
19787
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
19693
19788
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -20191,10 +20286,15 @@ async function downloadMemories(agent, memoryDir, log2, { force, configDir }) {
20191
20286
  const localListHash = createHash19("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
20192
20287
  const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
20193
20288
  const prevDownload = lastDownloadHash.get(agent.agent_id);
20289
+ const memoriesToken = memoriesFetchGate.beginFetch(agent.agent_id);
20290
+ if (!force && !memoriesFetchGate.shouldFetch(agent.agent_id)) {
20291
+ return { status: "deferred", tombstones: null, truncated: false };
20292
+ }
20194
20293
  try {
20195
20294
  const dbMemories = await api.post("/host/memories", {
20196
20295
  agent_id: agent.agent_id
20197
20296
  });
20297
+ memoriesFetchGate.markFetched(agent.agent_id, memoriesToken);
20198
20298
  const tombstones = Array.isArray(dbMemories.tombstones) ? dbMemories.tombstones : null;
20199
20299
  const responseHash = createHash19("sha256").update(JSON.stringify({ m: dbMemories.memories ?? [], t: tombstones })).digest("hex").slice(0, 16);
20200
20300
  if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {