@integrity-labs/agt-cli 0.28.737 → 0.28.739

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.
Files changed (24) hide show
  1. package/dist/bin/agt.js +5 -5
  2. package/dist/{chunk-MV3QBGO4.js → chunk-JDNULCLF.js} +2 -2
  3. package/dist/{chunk-WB2E236K.js → chunk-MAXKAS4Q.js} +13 -1
  4. package/dist/chunk-MAXKAS4Q.js.map +1 -0
  5. package/dist/{chunk-7LRKRRC5.js → chunk-ZKWY6MNV.js} +4 -4
  6. package/dist/{claude-pair-runtime-57FICYWW.js → claude-pair-runtime-JUGH4TOW.js} +2 -2
  7. package/dist/lib/manager-worker.js +132 -19
  8. package/dist/lib/manager-worker.js.map +1 -1
  9. package/dist/mcp/direct-chat-channel.js +12 -0
  10. package/dist/mcp/index.js +12 -0
  11. package/dist/mcp/origami.js +12 -0
  12. package/dist/mcp/slack-channel.js +12 -0
  13. package/dist/mcp/telegram-channel.js +12 -0
  14. package/dist/{persistent-session-NE6DCBFD.js → persistent-session-E7TUUVAI.js} +3 -3
  15. package/dist/{responsiveness-probe-VN4OPV56.js → responsiveness-probe-PXXWLOML.js} +3 -3
  16. package/dist/{session-auth-dead-TI6JWW4G.js → session-auth-dead-HZZJK37X.js} +2 -2
  17. package/package.json +1 -1
  18. package/dist/chunk-WB2E236K.js.map +0 -1
  19. /package/dist/{chunk-MV3QBGO4.js.map → chunk-JDNULCLF.js.map} +0 -0
  20. /package/dist/{chunk-7LRKRRC5.js.map → chunk-ZKWY6MNV.js.map} +0 -0
  21. /package/dist/{claude-pair-runtime-57FICYWW.js.map → claude-pair-runtime-JUGH4TOW.js.map} +0 -0
  22. /package/dist/{persistent-session-NE6DCBFD.js.map → persistent-session-E7TUUVAI.js.map} +0 -0
  23. /package/dist/{responsiveness-probe-VN4OPV56.js.map → responsiveness-probe-PXXWLOML.js.map} +0 -0
  24. /package/dist/{session-auth-dead-TI6JWW4G.js.map → session-auth-dead-HZZJK37X.js.map} +0 -0
@@ -13,7 +13,7 @@ import {
13
13
  parseEnvFileEntries,
14
14
  parseEnvIntegrations,
15
15
  shellQuote
16
- } from "./chunk-MV3QBGO4.js";
16
+ } from "./chunk-JDNULCLF.js";
17
17
  import {
18
18
  BIND_FAILURE_QUARANTINE_THRESHOLD,
19
19
  INTEGRATIONS_SECTION_END,
@@ -62,7 +62,7 @@ import {
62
62
  sessionTranscriptDir,
63
63
  worseConnectivityOutcome,
64
64
  wrapScheduledTaskPrompt
65
- } from "./chunk-WB2E236K.js";
65
+ } from "./chunk-MAXKAS4Q.js";
66
66
  import {
67
67
  parsePsRows
68
68
  } from "./chunk-XWVM4KPK.js";
@@ -6427,7 +6427,7 @@ function exchangeFailureKind(err) {
6427
6427
  }
6428
6428
 
6429
6429
  // src/lib/api-client.ts
6430
- var agtCliVersion = true ? "0.28.737" : "dev";
6430
+ var agtCliVersion = true ? "0.28.739" : "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-7LRKRRC5.js.map
10476
+ //# sourceMappingURL=chunk-ZKWY6MNV.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-NE6DCBFD.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-E7TUUVAI.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-57FICYWW.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-JUGH4TOW.js.map
@@ -57,7 +57,7 @@ import {
57
57
  safeWriteJsonAtomic,
58
58
  setConfigHash,
59
59
  tripClass
60
- } from "../chunk-7LRKRRC5.js";
60
+ } from "../chunk-ZKWY6MNV.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-MV3QBGO4.js";
121
+ } from "../chunk-JDNULCLF.js";
122
122
  import {
123
123
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
124
124
  AnchorSessionClient,
@@ -203,7 +203,7 @@ import {
203
203
  subagentActivityAgeSeconds,
204
204
  sumTranscriptUsageInWindow,
205
205
  transcriptActivityAgeSeconds
206
- } from "../chunk-WB2E236K.js";
206
+ } from "../chunk-MAXKAS4Q.js";
207
207
  import {
208
208
  reapOrphanChannelMcps
209
209
  } from "../chunk-XWVM4KPK.js";
@@ -5597,6 +5597,58 @@ function createTickBoardFetcher(fetchBoard) {
5597
5597
  };
5598
5598
  }
5599
5599
 
5600
+ // src/lib/kanban-board-token.ts
5601
+ var BoardTokenCache = class {
5602
+ entries = /* @__PURE__ */ new Map();
5603
+ /** The token to send as `known_token`, or undefined when we hold no board. */
5604
+ tokenFor(agentId) {
5605
+ return this.entries.get(agentId)?.token;
5606
+ }
5607
+ /**
5608
+ * Resolve a response into rows.
5609
+ *
5610
+ * Returns `null` when the server said `unchanged` but we hold nothing for
5611
+ * this agent — which should be impossible (we only send a token we have) but
5612
+ * is reachable across a teardown/re-add race, and the caller must re-fetch
5613
+ * rather than silently serve an empty board. An empty board is not a harmless
5614
+ * default here: section 12 writes whatever it is handed into `notifyBoardCache`
5615
+ * as this tick's truth, so seeding "nothing is done" over a board that has
5616
+ * done cards makes the NEXT tick report every one of them as newly done — a
5617
+ * burst of false completion notifications to real users.
5618
+ */
5619
+ resolve(agentId, res) {
5620
+ if (res.unchanged === true) {
5621
+ const held = this.entries.get(agentId);
5622
+ if (!held) return null;
5623
+ if (typeof res.board_token === "string" && res.board_token.length > 0) {
5624
+ this.entries.set(agentId, { token: res.board_token, rows: held.rows });
5625
+ }
5626
+ return held.rows;
5627
+ }
5628
+ const rows = res.items ?? [];
5629
+ if (typeof res.board_token === "string" && res.board_token.length > 0) {
5630
+ this.entries.set(agentId, { token: res.board_token, rows });
5631
+ } else {
5632
+ this.entries.delete(agentId);
5633
+ }
5634
+ return rows;
5635
+ }
5636
+ /**
5637
+ * Drop an agent's board. Teardown must call this: a board left behind is
5638
+ * served to the agent if it is later RE-ADDED, which is exactly when its work
5639
+ * is most likely to have moved on. Same argument as ENG-9585's
5640
+ * `lastRefreshPayload` purge, and the same reason it is registered in
5641
+ * `clearAgentCaches` rather than deleted ad hoc at each call site.
5642
+ */
5643
+ forget(agentId) {
5644
+ this.entries.delete(agentId);
5645
+ }
5646
+ /** Test/diagnostic only. */
5647
+ size() {
5648
+ return this.entries.size;
5649
+ }
5650
+ };
5651
+
5600
5652
  // src/lib/refresh-manifest-flags.ts
5601
5653
  var REFRESH_MANIFEST_FLAGS = {
5602
5654
  /** Knowledge as slug + hash instead of inline bodies. */
@@ -5655,6 +5707,25 @@ function createFetchGate(ttlMs) {
5655
5707
  };
5656
5708
  }
5657
5709
 
5710
+ // src/lib/conditional-fetch.ts
5711
+ function stillHeld(quoted, live) {
5712
+ if (!quoted || !live) return void 0;
5713
+ return live.hash === quoted.hash ? live : void 0;
5714
+ }
5715
+ function resolveConditionalResponse(wire, hasCachedBody) {
5716
+ if (wire.unchanged === true) {
5717
+ return hasCachedBody ? { use: "cached" } : { use: "no-body" };
5718
+ }
5719
+ const token = wire.payload_hash;
5720
+ return {
5721
+ use: "wire",
5722
+ // An empty string is rejected as well as a non-string: it would round-trip
5723
+ // as a token the server can never match, so it is indistinguishable from
5724
+ // absent and should be stored as absent rather than as a hash of "".
5725
+ token: typeof token === "string" && token.length > 0 ? token : null
5726
+ };
5727
+ }
5728
+
5658
5729
  // src/lib/channel-hash-cache.ts
5659
5730
  import { existsSync as existsSync8, readFileSync as readFileSync19, writeFileSync as writeFileSync10 } from "fs";
5660
5731
  import { join as join25 } from "path";
@@ -12430,7 +12501,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
12430
12501
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
12431
12502
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
12432
12503
  async function channelInboundActivityAgeSecondsFor(codeName) {
12433
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-VN4OPV56.js");
12504
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-PXXWLOML.js");
12434
12505
  const newest = newestPendingInboundActivityMtimeMs(dirname10(paneLogPath(codeName)));
12435
12506
  if (newest === null) return null;
12436
12507
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -13044,6 +13115,7 @@ function clearAgentCaches(agentId, codeName) {
13044
13115
  const channelCacheMutated = clearAgentState(agentId, codeName);
13045
13116
  agentChannelTokens.delete(codeName);
13046
13117
  lastRefreshPayload.delete(agentId);
13118
+ boardTokenCache.forget(agentId);
13047
13119
  refreshFetchGate.forget(agentId);
13048
13120
  memoriesFetchGate.forget(agentId);
13049
13121
  forgetRealtimeRebindState(agentId);
@@ -13065,6 +13137,7 @@ function clearAgentCaches(agentId, codeName) {
13065
13137
  memoryFileHashes.delete(agentId);
13066
13138
  lastDownloadHash.delete(agentId);
13067
13139
  lastLocalFileHash.delete(agentId);
13140
+ lastMemoriesBody.delete(agentId);
13068
13141
  for (const key of taskDisplayInfo.keys()) {
13069
13142
  if (key.startsWith(`${codeName}:`)) taskDisplayInfo.delete(key);
13070
13143
  }
@@ -13081,7 +13154,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
13081
13154
  var lastVersionCheckAt = 0;
13082
13155
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
13083
13156
  var lastResponsivenessProbeAt = 0;
13084
- var agtCliVersion = true ? "0.28.737" : "dev";
13157
+ var agtCliVersion = true ? "0.28.739" : "dev";
13085
13158
  function resolveBrewPath(execFileSync3) {
13086
13159
  try {
13087
13160
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -14599,6 +14672,8 @@ var lastRefreshPayload = /* @__PURE__ */ new Map();
14599
14672
  var memoriesFetchGate = createFetchGate(DESIRED_STATE_FETCH_TTL_MS);
14600
14673
  var pollCycleFetches = 0;
14601
14674
  var pollCycleFetchSkips = 0;
14675
+ var pollCycleMemoriesUnchanged = 0;
14676
+ var pollCycleMemoriesFull = 0;
14602
14677
  function forceRefreshFetch() {
14603
14678
  refreshFetchGate.forceAll();
14604
14679
  memoriesFetchGate.forceAll();
@@ -14620,7 +14695,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
14620
14695
  if (codeNames.length === 0) return;
14621
14696
  void (async () => {
14622
14697
  try {
14623
- const { collectDiagnostics } = await import("../persistent-session-NE6DCBFD.js");
14698
+ const { collectDiagnostics } = await import("../persistent-session-E7TUUVAI.js");
14624
14699
  await api.post("/host/heartbeat", {
14625
14700
  host_id: hostId,
14626
14701
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -14761,7 +14836,7 @@ async function pollCycleInner() {
14761
14836
  }
14762
14837
  try {
14763
14838
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
14764
- const { collectDiagnostics } = await import("../persistent-session-NE6DCBFD.js");
14839
+ const { collectDiagnostics } = await import("../persistent-session-E7TUUVAI.js");
14765
14840
  const diagCodeNames = [...agentState.persistentSessionAgents];
14766
14841
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
14767
14842
  let tailscaleHostname;
@@ -14930,7 +15005,7 @@ async function pollCycleInner() {
14930
15005
  collectPanelessActivityProbes,
14931
15006
  getResponsivenessIntervalMs,
14932
15007
  occupancyQualificationClassifications
14933
- } = await import("../responsiveness-probe-VN4OPV56.js");
15008
+ } = await import("../responsiveness-probe-PXXWLOML.js");
14934
15009
  const probeIntervalMs = getResponsivenessIntervalMs();
14935
15010
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
14936
15011
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -15040,7 +15115,7 @@ async function pollCycleInner() {
15040
15115
  collectResponsivenessProbes,
15041
15116
  livePendingInboundOldestAgeSeconds,
15042
15117
  parkPendingInbound
15043
- } = await import("../responsiveness-probe-VN4OPV56.js");
15118
+ } = await import("../responsiveness-probe-PXXWLOML.js");
15044
15119
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
15045
15120
  const wedgeNow = /* @__PURE__ */ new Date();
15046
15121
  const liveAgents = agentState.persistentSessionAgents;
@@ -15196,7 +15271,7 @@ async function pollCycleInner() {
15196
15271
  }
15197
15272
  try {
15198
15273
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
15199
- const { probeSessionAuth } = await import("../session-auth-dead-TI6JWW4G.js");
15274
+ const { probeSessionAuth } = await import("../session-auth-dead-HZZJK37X.js");
15200
15275
  const observations = [];
15201
15276
  const pendingCacheCommits = [];
15202
15277
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -15359,6 +15434,8 @@ async function pollCycleInner() {
15359
15434
  const cycleStartedAt = Date.now();
15360
15435
  pollCycleFetches = 0;
15361
15436
  pollCycleFetchSkips = 0;
15437
+ pollCycleMemoriesUnchanged = 0;
15438
+ pollCycleMemoriesFull = 0;
15362
15439
  for (const agent of processOrder) {
15363
15440
  if (restartInFlight.has(agent.agent_id)) {
15364
15441
  const existing = state8.agents.find((a) => a.agentId === agent.agent_id);
@@ -15398,7 +15475,7 @@ async function pollCycleInner() {
15398
15475
  }
15399
15476
  }
15400
15477
  log(
15401
- `[poll-cycle] agents=${processOrder.length} refresh_fetched=${pollCycleFetches} refresh_skipped=${pollCycleFetchSkips} duration_ms=${Date.now() - cycleStartedAt}`
15478
+ `[poll-cycle] agents=${processOrder.length} refresh_fetched=${pollCycleFetches} refresh_skipped=${pollCycleFetchSkips} memories_unchanged=${pollCycleMemoriesUnchanged} memories_full=${pollCycleMemoriesFull} duration_ms=${Date.now() - cycleStartedAt}`
15402
15479
  );
15403
15480
  void maybeReportActivityCache({ api, log });
15404
15481
  const crossAgentPrune = managedPruneLedger.summarize();
@@ -17580,7 +17657,8 @@ async function processAgent(agent, agentStates, managedToolkits) {
17580
17657
  }
17581
17658
  }
17582
17659
  const fetchTickBoard = createTickBoardFetcher(async () => {
17583
- const boardData = await api.post("/host/my-kanban", {
17660
+ const knownToken = boardTokenCache.tokenFor(agent.agent_id);
17661
+ const post = (token) => api.post("/host/my-kanban", {
17584
17662
  agent_id: agent.agent_id,
17585
17663
  // ENG-9585: this is the ONLY caller that polls (every ~10s, per agent),
17586
17664
  // and it reads exactly `BoardItem`. `description` and `notes` are 57.8%
@@ -17588,9 +17666,14 @@ async function processAgent(agent, agentStates, managedToolkits) {
17588
17666
  // tool and `agt kanban` DO read them, so the route cannot simply drop
17589
17667
  // them. Asking for the lean shape is this caller saying what it needs;
17590
17668
  // every other caller keeps the full payload by default.
17591
- detail: "board"
17669
+ detail: "board",
17670
+ ...token ? { known_token: token } : {}
17592
17671
  });
17593
- return (boardData.items ?? []).map(sanitizeBoardItem);
17672
+ let rows = boardTokenCache.resolve(agent.agent_id, await post(knownToken));
17673
+ if (rows === null) {
17674
+ rows = boardTokenCache.resolve(agent.agent_id, await post(void 0)) ?? [];
17675
+ }
17676
+ return rows.map(sanitizeBoardItem);
17594
17677
  });
17595
17678
  let boardItems = [];
17596
17679
  let boardItemsStale = false;
@@ -19150,7 +19233,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
19150
19233
  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}`));
19151
19234
  void (async () => {
19152
19235
  try {
19153
- const { collectDiagnostics } = await import("../persistent-session-NE6DCBFD.js");
19236
+ const { collectDiagnostics } = await import("../persistent-session-E7TUUVAI.js");
19154
19237
  await api.post("/host/heartbeat", {
19155
19238
  host_id: hostId,
19156
19239
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -19196,7 +19279,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
19196
19279
  }
19197
19280
  try {
19198
19281
  const hostId = await getHostId();
19199
- const { collectDiagnostics } = await import("../persistent-session-NE6DCBFD.js");
19282
+ const { collectDiagnostics } = await import("../persistent-session-E7TUUVAI.js");
19200
19283
  await api.post("/host/heartbeat", {
19201
19284
  host_id: hostId,
19202
19285
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -19813,6 +19896,7 @@ async function maybeInjectKanbanCheck(codeName, agentId, actionableCount, signat
19813
19896
  var lastHarvestAt = /* @__PURE__ */ new Map();
19814
19897
  var HARVEST_INTERVAL_MS = 3 * 60 * 1e3;
19815
19898
  var kanbanBoardCache = /* @__PURE__ */ new Map();
19899
+ var boardTokenCache = new BoardTokenCache();
19816
19900
  var notifyBoardCache = /* @__PURE__ */ new Map();
19817
19901
  var notifiedWaitingHumanIds = /* @__PURE__ */ new Map();
19818
19902
  var spawnedPairIds = /* @__PURE__ */ new Set();
@@ -19831,7 +19915,7 @@ async function processClaudePairSessions(agents) {
19831
19915
  killPairSession,
19832
19916
  pairTmuxSession,
19833
19917
  finalizeClaudePairOnboarding
19834
- } = await import("../claude-pair-runtime-57FICYWW.js");
19918
+ } = await import("../claude-pair-runtime-JUGH4TOW.js");
19835
19919
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
19836
19920
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
19837
19921
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -20054,6 +20138,7 @@ function generateArtifacts(agent, refreshData, adapter, renderIntegrationsSectio
20054
20138
  var memoryFileHashes = /* @__PURE__ */ new Map();
20055
20139
  var lastDownloadHash = /* @__PURE__ */ new Map();
20056
20140
  var lastLocalFileHash = /* @__PURE__ */ new Map();
20141
+ var lastMemoriesBody = /* @__PURE__ */ new Map();
20057
20142
  var memoryManifests = /* @__PURE__ */ new Map();
20058
20143
  function memoryRetirementDir(configDir, codeName) {
20059
20144
  return join40(configDir, "_memory-retirement", codeName);
@@ -20122,6 +20207,7 @@ function markAgentForFreshMemorySync(agentId) {
20122
20207
  memoryFileHashes.delete(agentId);
20123
20208
  lastDownloadHash.delete(agentId);
20124
20209
  lastLocalFileHash.delete(agentId);
20210
+ lastMemoriesBody.delete(agentId);
20125
20211
  }
20126
20212
  function parseMemoryFile(raw, fallbackName) {
20127
20213
  const trimmed = raw.trim();
@@ -20340,10 +20426,37 @@ async function downloadMemories(agent, memoryDir, log2, { force, configDir }) {
20340
20426
  return { status: "deferred", tombstones: null, truncated: false };
20341
20427
  }
20342
20428
  try {
20343
- const dbMemories = await api.post("/host/memories", {
20344
- agent_id: agent.agent_id
20429
+ const quoted = lastMemoriesBody.get(agent.agent_id);
20430
+ let wire = await api.post("/host/memories", {
20431
+ agent_id: agent.agent_id,
20432
+ ...quoted ? { known_hash: quoted.hash } : {}
20345
20433
  });
20346
20434
  memoriesFetchGate.markFetched(agent.agent_id, memoriesToken);
20435
+ let held = stillHeld(quoted, lastMemoriesBody.get(agent.agent_id));
20436
+ if (resolveConditionalResponse(wire, held !== void 0).use === "no-body") {
20437
+ log2(
20438
+ `Memory download for '${agent.code_name}': unchanged marker with no cached body (cache purged mid-flight?) \u2014 re-fetching in full`
20439
+ );
20440
+ wire = await api.post("/host/memories", { agent_id: agent.agent_id });
20441
+ held = void 0;
20442
+ }
20443
+ let dbMemories;
20444
+ const decision = resolveConditionalResponse(wire, held !== void 0);
20445
+ if (decision.use === "cached") {
20446
+ dbMemories = held.body;
20447
+ pollCycleMemoriesUnchanged += 1;
20448
+ } else if (decision.use === "no-body") {
20449
+ log2(`Memory download for '${agent.code_name}': server sent 'unchanged' for an untokened request`);
20450
+ return { status: "failed", tombstones: null, truncated: false };
20451
+ } else {
20452
+ dbMemories = wire;
20453
+ if (decision.token) {
20454
+ lastMemoriesBody.set(agent.agent_id, { hash: decision.token, body: dbMemories });
20455
+ } else {
20456
+ lastMemoriesBody.delete(agent.agent_id);
20457
+ }
20458
+ pollCycleMemoriesFull += 1;
20459
+ }
20347
20460
  const tombstones = Array.isArray(dbMemories.tombstones) ? dbMemories.tombstones : null;
20348
20461
  const responseHash = createHash19("sha256").update(JSON.stringify({ m: dbMemories.memories ?? [], t: tombstones })).digest("hex").slice(0, 16);
20349
20462
  if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {