@integrity-labs/agt-cli 0.27.128 → 0.27.130

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.
@@ -17,7 +17,7 @@ import {
17
17
  provisionStopHook,
18
18
  requireHost,
19
19
  safeWriteJsonAtomic
20
- } from "../chunk-TXZLQK3S.js";
20
+ } from "../chunk-H4QCCP2M.js";
21
21
  import {
22
22
  getProjectDir as getProjectDir2,
23
23
  getReadyTasks,
@@ -56,7 +56,7 @@ import {
56
56
  stopPersistentSession,
57
57
  takeWatchdogGiveUpCount,
58
58
  takeZombieDetection
59
- } from "../chunk-QON5CU3L.js";
59
+ } from "../chunk-JQOOOLKR.js";
60
60
  import {
61
61
  KANBAN_CHECK_COMMAND,
62
62
  SUPPRESS_SENTINEL,
@@ -83,7 +83,7 @@ import {
83
83
  resolveDmTarget,
84
84
  worseConnectivityOutcome,
85
85
  wrapScheduledTaskPrompt
86
- } from "../chunk-MH7HA6QV.js";
86
+ } from "../chunk-XCV5NU45.js";
87
87
  import {
88
88
  parsePsRows,
89
89
  reapOrphanChannelMcps
@@ -502,14 +502,14 @@ function reapMissingMcpSessions(args) {
502
502
  if (!missingRaw.includes(key)) liveKeys.add(key);
503
503
  }
504
504
  for (const key of liveKeys) {
505
- const state6 = presenceReaperState.get(stateKey(codeName, key));
506
- if (state6) {
507
- state6.attempts = 0;
508
- state6.lastSeenLiveAt = now();
509
- state6.lastAttemptedSessionStartedAt = null;
510
- state6.gaveUpLogged = false;
511
- state6.firstMissingAt = null;
512
- state6.quarantineLogged = false;
505
+ const state7 = presenceReaperState.get(stateKey(codeName, key));
506
+ if (state7) {
507
+ state7.attempts = 0;
508
+ state7.lastSeenLiveAt = now();
509
+ state7.lastAttemptedSessionStartedAt = null;
510
+ state7.gaveUpLogged = false;
511
+ state7.firstMissingAt = null;
512
+ state7.quarantineLogged = false;
513
513
  }
514
514
  }
515
515
  if (missing.length === 0) {
@@ -527,7 +527,7 @@ function reapMissingMcpSessions(args) {
527
527
  const wouldQuarantine = [];
528
528
  for (const key of missing) {
529
529
  const sk = stateKey(codeName, key);
530
- const state6 = presenceReaperState.get(sk) ?? {
530
+ const state7 = presenceReaperState.get(sk) ?? {
531
531
  attempts: 0,
532
532
  lastSeenLiveAt: null,
533
533
  lastAttemptedSessionStartedAt: null,
@@ -535,19 +535,19 @@ function reapMissingMcpSessions(args) {
535
535
  firstMissingAt: null,
536
536
  quarantineLogged: false
537
537
  };
538
- if (state6.firstMissingAt === null) state6.firstMissingAt = nowMs;
539
- if (state6.lastAttemptedSessionStartedAt !== sessionStartedAt) {
540
- state6.attempts += 1;
541
- state6.lastAttemptedSessionStartedAt = sessionStartedAt;
538
+ if (state7.firstMissingAt === null) state7.firstMissingAt = nowMs;
539
+ if (state7.lastAttemptedSessionStartedAt !== sessionStartedAt) {
540
+ state7.attempts += 1;
541
+ state7.lastAttemptedSessionStartedAt = sessionStartedAt;
542
542
  }
543
- presenceReaperState.set(sk, state6);
544
- if (state6.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
543
+ presenceReaperState.set(sk, state7);
544
+ if (state7.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
545
545
  givenUp.push(key);
546
- if (!state6.gaveUpLogged) {
546
+ if (!state7.gaveUpLogged) {
547
547
  log2(
548
548
  `[mcp-presence-reaper] giving up on '${codeName}:${key}' after ${MAX_PRESENCE_RESTART_ATTEMPTS} consecutive failed restarts \u2014 declared but never recovers (likely orphaned config; see ENG-5279)`
549
549
  );
550
- state6.gaveUpLogged = true;
550
+ state7.gaveUpLogged = true;
551
551
  if (onGiveUp) {
552
552
  try {
553
553
  onGiveUp(codeName, key);
@@ -558,10 +558,10 @@ function reapMissingMcpSessions(args) {
558
558
  }
559
559
  }
560
560
  }
561
- if (quarantineMode !== "off" && !state6.quarantineLogged && classifyKey(key) === "optional") {
562
- const dwellElapsed = quarantineDwellMs <= 0 || state6.firstMissingAt !== null && nowMs - state6.firstMissingAt >= quarantineDwellMs;
561
+ if (quarantineMode !== "off" && !state7.quarantineLogged && classifyKey(key) === "optional") {
562
+ const dwellElapsed = quarantineDwellMs <= 0 || state7.firstMissingAt !== null && nowMs - state7.firstMissingAt >= quarantineDwellMs;
563
563
  if (dwellElapsed) {
564
- state6.quarantineLogged = true;
564
+ state7.quarantineLogged = true;
565
565
  if (quarantineMode === "enforce") {
566
566
  quarantined.push(key);
567
567
  log2(
@@ -2101,14 +2101,173 @@ function readRecentTurns(dir, nowMs) {
2101
2101
  return turns;
2102
2102
  }
2103
2103
 
2104
+ // src/lib/memory-extractor.ts
2105
+ var MIN_CHECK_INTERVAL_MS4 = 10 * 6e4;
2106
+ var WINDOW_PAD_MS2 = 5 * 6e4;
2107
+ var VALID_MEMORY_TYPES = /* @__PURE__ */ new Set(["user", "feedback", "project", "reference"]);
2108
+ var SECRET_PATTERNS = [
2109
+ // labelled secrets: `api_key=...`, `password: ...`, `bearer ...`
2110
+ /\b[\w-]*(?:secret|password|passwd|token|api[_-]?key|bearer)[\w-]*\s*[=:]\s*\S+/gi,
2111
+ // common credential prefixes (stripe, github, slack, augmented host keys, …)
2112
+ /\b(?:sk|pk|rk|tlk|ghp|gho|ghs|xox[baprs])[-_][A-Za-z0-9]{12,}\b/g,
2113
+ // long hex blobs (hashes / hex-encoded keys)
2114
+ /\b[A-Fa-f0-9]{32,}\b/g,
2115
+ // long base64 blobs (tokens / encoded secrets)
2116
+ /\b[A-Za-z0-9+/]{40,}={0,2}\b/g,
2117
+ // email addresses
2118
+ /\b[\w.+-]+@[\w-]+\.[\w.-]+\b/g
2119
+ ];
2120
+ function scrubSensitive(text) {
2121
+ let out = text;
2122
+ for (const re of SECRET_PATTERNS) out = out.replace(re, "[redacted]");
2123
+ return out;
2124
+ }
2125
+ var state4 = /* @__PURE__ */ new Map();
2126
+ function buildExtractionPrompt(channel, transcript) {
2127
+ return `You are extracting durable, reusable memories from one completed ${channel} conversation between an AI agent and an end-user. A good memory is a stable preference, fact, correction, or recurring work context that will help the agent in FUTURE conversations \u2014 not a one-off task detail or pleasantry.
2128
+
2129
+ Conversation transcript:
2130
+ """
2131
+ ${transcript}
2132
+ """
2133
+
2134
+ Extract 0-5 candidate memories. For each:
2135
+ - name: short descriptive title (max 50 chars)
2136
+ - content: the memory itself, self-contained (max 200 chars). Do NOT include secrets or sensitive personal data.
2137
+ - type: "user" (a person's stable preference), "feedback" (a correction to how the agent should work), "project" (durable work context), or "reference" (a pointer to an external resource)
2138
+ - confidence: 0.0-1.0 \u2014 how sure you are this is worth remembering long-term
2139
+ - rationale: why this should persist (max 100 chars)
2140
+
2141
+ If there is nothing durable worth remembering, return an empty array. Do NOT invent memories to fill the list.
2142
+
2143
+ Respond with ONLY a JSON array, no other text:
2144
+ [{"name":"...","content":"...","type":"...","confidence":0.8,"rationale":"..."}]`;
2145
+ }
2146
+ function parseCandidates(raw) {
2147
+ const match = raw.match(/\[[\s\S]*\]/);
2148
+ if (!match) return null;
2149
+ let arr;
2150
+ try {
2151
+ arr = JSON.parse(match[0]);
2152
+ } catch {
2153
+ return null;
2154
+ }
2155
+ if (!Array.isArray(arr)) return null;
2156
+ const out = [];
2157
+ for (const entry of arr) {
2158
+ if (!entry || typeof entry !== "object") continue;
2159
+ const c = entry;
2160
+ const name = typeof c.name === "string" ? c.name.trim() : "";
2161
+ if (!name) continue;
2162
+ if (typeof c.type !== "string" || !VALID_MEMORY_TYPES.has(c.type)) continue;
2163
+ const confidence = typeof c.confidence === "number" && Number.isFinite(c.confidence) ? Math.max(0, Math.min(1, c.confidence)) : 0;
2164
+ if (confidence === 0) continue;
2165
+ out.push({
2166
+ name: name.slice(0, 80),
2167
+ content: (typeof c.content === "string" ? c.content : "").trim().slice(0, 500),
2168
+ type: c.type,
2169
+ confidence,
2170
+ rationale: (typeof c.rationale === "string" ? c.rationale : "").trim().slice(0, 200)
2171
+ });
2172
+ }
2173
+ return out;
2174
+ }
2175
+ async function maybeExtractMemories(args) {
2176
+ const { api: api2, backend, codeName, agentId, log: log2 } = args;
2177
+ const now = args.now ?? /* @__PURE__ */ new Date();
2178
+ const nowMs = now.getTime();
2179
+ const existing = state4.get(codeName);
2180
+ if (existing && nowMs - existing.lastCheckedAt < MIN_CHECK_INTERVAL_MS4) {
2181
+ return;
2182
+ }
2183
+ state4.set(codeName, { lastCheckedAt: nowMs });
2184
+ let pending;
2185
+ try {
2186
+ const resp = await api2.get(
2187
+ `/host/memories/pending-extraction?agent_id=${encodeURIComponent(agentId)}`
2188
+ );
2189
+ pending = resp?.conversations ?? [];
2190
+ } catch (err) {
2191
+ log2(`[memory-extract] ${codeName}: pending fetch failed: ${err.message}`);
2192
+ return;
2193
+ }
2194
+ if (pending.length === 0) return;
2195
+ const dir = args.transcriptDir ?? sessionTranscriptDir(getProjectDir(codeName));
2196
+ const allTurns = readRecentTurns(dir, nowMs);
2197
+ if (allTurns.length === 0) {
2198
+ for (const conv of pending) {
2199
+ await reportSkip2(api2, agentId, conv.conversation_id, log2, codeName);
2200
+ }
2201
+ return;
2202
+ }
2203
+ for (const conv of pending) {
2204
+ const tokens = channelRefTokens(conv.channel_ref);
2205
+ const windowStart = Date.parse(conv.started_at) - WINDOW_PAD_MS2;
2206
+ const windowEnd = Date.parse(conv.last_message_at) + WINDOW_PAD_MS2;
2207
+ const turns = reconstructConversation(allTurns, tokens, windowStart, windowEnd);
2208
+ if (turns.length === 0) {
2209
+ await reportSkip2(api2, agentId, conv.conversation_id, log2, codeName);
2210
+ continue;
2211
+ }
2212
+ const transcript = renderTranscript(turns);
2213
+ if (!transcript.trim()) {
2214
+ await reportSkip2(api2, agentId, conv.conversation_id, log2, codeName);
2215
+ continue;
2216
+ }
2217
+ let candidates;
2218
+ try {
2219
+ const out = await backend.run(buildExtractionPrompt(conv.channel, transcript));
2220
+ candidates = parseCandidates(out);
2221
+ } catch (err) {
2222
+ log2(`[memory-extract] ${codeName}: extraction failed: ${err.message}`);
2223
+ continue;
2224
+ }
2225
+ if (candidates === null) {
2226
+ log2(`[memory-extract] ${codeName}: unparseable output for ${conv.conversation_id.slice(0, 8)} \u2014 will retry`);
2227
+ continue;
2228
+ }
2229
+ const scrubbed = candidates.map((c) => ({
2230
+ ...c,
2231
+ content: scrubSensitive(c.content),
2232
+ rationale: scrubSensitive(c.rationale)
2233
+ }));
2234
+ try {
2235
+ const res = await api2.post(
2236
+ "/host/memories/candidates",
2237
+ {
2238
+ agent_id: agentId,
2239
+ conversation_id: conv.conversation_id,
2240
+ candidates: scrubbed
2241
+ }
2242
+ );
2243
+ log2(
2244
+ `[memory-extract] ${codeName}: ${conv.conversation_id.slice(0, 8)} \u2192 ${candidates.length} candidate(s), ${res?.promoted ?? 0} promoted`
2245
+ );
2246
+ } catch (err) {
2247
+ log2(`[memory-extract] ${codeName}: report failed: ${err.message}`);
2248
+ }
2249
+ }
2250
+ }
2251
+ async function reportSkip2(api2, agentId, conversationId, log2, codeName) {
2252
+ try {
2253
+ await api2.post("/host/memories/candidates", {
2254
+ agent_id: agentId,
2255
+ conversation_id: conversationId,
2256
+ skipped: true
2257
+ });
2258
+ } catch (err) {
2259
+ log2(`[memory-extract] ${codeName}: skip report failed: ${err.message}`);
2260
+ }
2261
+ }
2262
+
2104
2263
  // src/lib/activity-cache-monitor.ts
2105
2264
  import { existsSync as existsSync2, readFileSync as readFileSync6 } from "fs";
2106
2265
  import { homedir } from "os";
2107
2266
  import { join as join4 } from "path";
2108
- var MIN_CHECK_INTERVAL_MS4 = 6e4;
2267
+ var MIN_CHECK_INTERVAL_MS5 = 6e4;
2109
2268
  var STATS_CACHE_PATH = join4(homedir(), ".claude", "stats-cache.json");
2110
2269
  var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
2111
- var state4 = { lastObservedDate: null, lastCheckedAt: 0 };
2270
+ var state5 = { lastObservedDate: null, lastCheckedAt: 0 };
2112
2271
  function selectNewDailyRows(raw, lastObservedDate) {
2113
2272
  let parsed;
2114
2273
  try {
@@ -2147,8 +2306,8 @@ async function maybeReportActivityCache(args) {
2147
2306
  const { api: api2, log: log2 } = args;
2148
2307
  const now = args.now ?? /* @__PURE__ */ new Date();
2149
2308
  const nowMs = now.getTime();
2150
- if (nowMs - state4.lastCheckedAt < MIN_CHECK_INTERVAL_MS4) return;
2151
- state4.lastCheckedAt = nowMs;
2309
+ if (nowMs - state5.lastCheckedAt < MIN_CHECK_INTERVAL_MS5) return;
2310
+ state5.lastCheckedAt = nowMs;
2152
2311
  if (!existsSync2(STATS_CACHE_PATH)) {
2153
2312
  return;
2154
2313
  }
@@ -2159,12 +2318,12 @@ async function maybeReportActivityCache(args) {
2159
2318
  log2(`[activity-cache] readFileSync failed: ${err.message}`);
2160
2319
  return;
2161
2320
  }
2162
- const rows = selectNewDailyRows(raw, state4.lastObservedDate);
2321
+ const rows = selectNewDailyRows(raw, state5.lastObservedDate);
2163
2322
  if (rows.length === 0) return;
2164
2323
  for (const row of rows) {
2165
2324
  try {
2166
2325
  await api2.post("/host/activity-observations", row);
2167
- state4.lastObservedDate = row.date;
2326
+ state5.lastObservedDate = row.date;
2168
2327
  } catch (err) {
2169
2328
  log2(
2170
2329
  `[activity-cache] POST /host/activity-observations failed for date=${row.date}: ${err.message}`
@@ -3278,10 +3437,10 @@ function recordWedgeForCards(states, inProgressCardIds, nowMs, config2) {
3278
3437
  function pruneCardStates(states, liveInProgressCardIds, nowMs, config2) {
3279
3438
  const live = liveInProgressCardIds instanceof Set ? liveInProgressCardIds : new Set(liveInProgressCardIds);
3280
3439
  const next = /* @__PURE__ */ new Map();
3281
- for (const [id, state6] of states) {
3440
+ for (const [id, state7] of states) {
3282
3441
  if (!live.has(id)) continue;
3283
- if (nowMs - state6.lastWedgeAtMs > config2.cooldownMs) continue;
3284
- next.set(id, state6);
3442
+ if (nowMs - state7.lastWedgeAtMs > config2.cooldownMs) continue;
3443
+ next.set(id, state7);
3285
3444
  }
3286
3445
  return next;
3287
3446
  }
@@ -3971,8 +4130,8 @@ var KNOWN_SAFE_TAIL_SIGNATURES = /* @__PURE__ */ new Set(["session_id_in_use"]);
3971
4130
  function shouldSkipRevokedCleanup(previousKnownStatus) {
3972
4131
  return previousKnownStatus === "revoked";
3973
4132
  }
3974
- function hasRevokedResiduals(state6) {
3975
- return state6.gatewayRunning || state6.portAllocated || state6.provisionDirExists;
4133
+ function hasRevokedResiduals(state7) {
4134
+ return state7.gatewayRunning || state7.portAllocated || state7.provisionDirExists;
3976
4135
  }
3977
4136
  var pendingSessionRestarts = /* @__PURE__ */ new Map();
3978
4137
  var pendingRestartVerifications = /* @__PURE__ */ new Map();
@@ -4077,12 +4236,12 @@ function maybeAutoResume(agent) {
4077
4236
  autoResumeMarkers.set(codeName, { trippedAt, autoResumedAt: Date.now() });
4078
4237
  restartBreaker.clear(codeName);
4079
4238
  reportedTrips.delete(codeName);
4080
- state5 = {
4081
- ...state5,
4239
+ state6 = {
4240
+ ...state6,
4082
4241
  circuitBreakerTrips: restartBreaker.serialize(),
4083
4242
  circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
4084
4243
  };
4085
- send({ type: "state-update", state: state5 });
4244
+ send({ type: "state-update", state: state6 });
4086
4245
  log(`[auto-resume] agent=${codeName} resumed \u2014 re-trip within backoff window will stay paused (ENG-6088)`);
4087
4246
  } else {
4088
4247
  autoResumeStandDowns.add(`${codeName}:${trippedAt}`);
@@ -4238,9 +4397,17 @@ function readMcpHttpServerConfig(projectDir, serverKey) {
4238
4397
  }
4239
4398
  async function runAgentConnectivityProbes(agent, integrations, projectDir) {
4240
4399
  if (integrations.length === 0) return;
4400
+ const probeEnv = { ...process.env };
4401
+ try {
4402
+ const envIntPath = join8(projectDir, ".env.integrations");
4403
+ if (existsSync5(envIntPath)) {
4404
+ Object.assign(probeEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
4405
+ }
4406
+ } catch {
4407
+ }
4241
4408
  const probeDeps = {
4242
4409
  fetchImpl: fetch,
4243
- runCli: (binary, args) => runCliProbe(binary, args),
4410
+ runCli: (binary, args) => runCliProbe(binary, args, { env: probeEnv }),
4244
4411
  mcpProbe: async (target) => {
4245
4412
  const cfg = readMcpHttpServerConfig(projectDir, target.serverKey);
4246
4413
  if (!cfg) {
@@ -4452,7 +4619,7 @@ function __setAgentChannelTokensForTest(codeName, tokens) {
4452
4619
  }
4453
4620
  var activeChannels = /* @__PURE__ */ new Map();
4454
4621
  var gatewaysStartedThisCycle = /* @__PURE__ */ new Set();
4455
- var state5 = {
4622
+ var state6 = {
4456
4623
  pid: process.pid,
4457
4624
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
4458
4625
  lastPollAt: null,
@@ -4494,7 +4661,7 @@ var cachedMaintenanceWindow = null;
4494
4661
  var lastVersionCheckAt = 0;
4495
4662
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
4496
4663
  var lastResponsivenessProbeAt = 0;
4497
- var agtCliVersion = true ? "0.27.128" : "dev";
4664
+ var agtCliVersion = true ? "0.27.130" : "dev";
4498
4665
  function resolveBrewPath(execFileSync4) {
4499
4666
  try {
4500
4667
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -5146,6 +5313,10 @@ async function runEvalClaude(prompt, model) {
5146
5313
  });
5147
5314
  return stdout;
5148
5315
  }
5316
+ function memoryExtractionEnabled() {
5317
+ const v = (process.env["AGT_MEMORY_EXTRACTION_ENABLED"] ?? "").trim().toLowerCase();
5318
+ return v === "true" || v === "1" || v === "yes";
5319
+ }
5149
5320
  var conversationEvalBackend = null;
5150
5321
  function resolveConversationEvalBackend() {
5151
5322
  if (conversationEvalBackend) return conversationEvalBackend;
@@ -5472,10 +5643,10 @@ function isGatewayHung(codeName) {
5472
5643
  if (!Array.isArray(jobs)) return false;
5473
5644
  const now = Date.now();
5474
5645
  for (const job of jobs) {
5475
- const state6 = job.state;
5476
- if (!state6) continue;
5477
- const runStartedAt = state6.runStartedAtMs;
5478
- const isRunning = state6.status === "running" || state6.running === true;
5646
+ const state7 = job.state;
5647
+ if (!state7) continue;
5648
+ const runStartedAt = state7.runStartedAtMs;
5649
+ const isRunning = state7.status === "running" || state7.running === true;
5479
5650
  if (isRunning && runStartedAt && now - runStartedAt > GATEWAY_HUNG_TIMEOUT_MS) {
5480
5651
  return true;
5481
5652
  }
@@ -5676,7 +5847,7 @@ async function pollCycle() {
5676
5847
  const now = Date.now();
5677
5848
  if (now - lastVersionCheckAt > VERSION_CHECK_INTERVAL_MS) {
5678
5849
  try {
5679
- const firstAgent = state5.agents[0];
5850
+ const firstAgent = state6.agents[0];
5680
5851
  const versionAdapter = firstAgent ? resolveAgentFramework(firstAgent.codeName) : getFramework("openclaw");
5681
5852
  if (versionAdapter.getVersion) {
5682
5853
  cachedFrameworkVersion = await versionAdapter.getVersion();
@@ -5687,7 +5858,7 @@ async function pollCycle() {
5687
5858
  }
5688
5859
  try {
5689
5860
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
5690
- const { collectDiagnostics } = await import("../persistent-session-PJQZYG2L.js");
5861
+ const { collectDiagnostics } = await import("../persistent-session-NTO2PO2V.js");
5691
5862
  const diagCodeNames = [...agentState.persistentSessionAgents];
5692
5863
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
5693
5864
  let tailscaleHostname;
@@ -5720,7 +5891,7 @@ async function pollCycle() {
5720
5891
  const errId = createHash3("sha256").update(errText).digest("hex").slice(0, 12);
5721
5892
  log(`Claude auth detection failed (error_id=${errId})`);
5722
5893
  }
5723
- const hostHasClaudeCode = state5.agents.some(
5894
+ const hostHasClaudeCode = state6.agents.some(
5724
5895
  (a) => agentFrameworkCache.get(a.codeName) === "claude-code"
5725
5896
  );
5726
5897
  if (hostHasClaudeCode) {
@@ -5774,12 +5945,12 @@ async function pollCycle() {
5774
5945
  const {
5775
5946
  collectResponsivenessProbes,
5776
5947
  getResponsivenessIntervalMs
5777
- } = await import("../responsiveness-probe-MGMZQSP7.js");
5948
+ } = await import("../responsiveness-probe-BG7O5UIG.js");
5778
5949
  const probeIntervalMs = getResponsivenessIntervalMs();
5779
5950
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
5780
5951
  const probeCodeNames = [...agentState.persistentSessionAgents];
5781
5952
  if (probeCodeNames.length > 0) {
5782
- const { takeAcpxExecFailureCount, creditAcpxExecFailureCount } = await import("../persistent-session-PJQZYG2L.js");
5953
+ const { takeAcpxExecFailureCount, creditAcpxExecFailureCount } = await import("../persistent-session-NTO2PO2V.js");
5783
5954
  const drainedGiveUps = /* @__PURE__ */ new Map();
5784
5955
  const drainedAcpxFailures = /* @__PURE__ */ new Map();
5785
5956
  const probes = collectResponsivenessProbes(probeCodeNames).map((p) => {
@@ -5813,7 +5984,7 @@ async function pollCycle() {
5813
5984
  collectResponsivenessProbes,
5814
5985
  livePendingInboundOldestAgeSeconds,
5815
5986
  deadLetterPendingInbound
5816
- } = await import("../responsiveness-probe-MGMZQSP7.js");
5987
+ } = await import("../responsiveness-probe-BG7O5UIG.js");
5817
5988
  const wedgeNow = /* @__PURE__ */ new Date();
5818
5989
  const liveAgents = agentState.persistentSessionAgents;
5819
5990
  for (const tracked of consecutiveWedgeCycles.keys()) {
@@ -5933,7 +6104,7 @@ async function pollCycle() {
5933
6104
  for (const agent of agents) {
5934
6105
  const requested = agent.restart_requested_at ?? null;
5935
6106
  if (!requested) continue;
5936
- const prev = state5.agents.find((a) => a.agentId === agent.agent_id);
6107
+ const prev = state6.agents.find((a) => a.agentId === agent.agent_id);
5937
6108
  const lastProcessed = prev?.lastRestartProcessedAt ?? null;
5938
6109
  if (lastProcessed && Date.parse(lastProcessed) >= Date.parse(requested)) continue;
5939
6110
  log(`[restart] Dashboard requested restart for '${agent.code_name}' at ${requested}`);
@@ -5956,7 +6127,7 @@ async function pollCycle() {
5956
6127
  await processAgent(agent, agentStates);
5957
6128
  } catch (err) {
5958
6129
  log(`Error processing agent '${agent.code_name}': ${err.message}`);
5959
- const existing = state5.agents.find((a) => a.agentId === agent.agent_id);
6130
+ const existing = state6.agents.find((a) => a.agentId === agent.agent_id);
5960
6131
  if (existing) {
5961
6132
  agentStates.push(existing);
5962
6133
  } else {
@@ -5982,12 +6153,12 @@ async function pollCycle() {
5982
6153
  void maybeReportActivityCache({ api, log });
5983
6154
  const restartAckStateChanged = applyRestartAcks({
5984
6155
  agentStates,
5985
- priorAgents: state5.agents,
6156
+ priorAgents: state6.agents,
5986
6157
  restartAcks
5987
6158
  });
5988
6159
  if (restartAckStateChanged) {
5989
6160
  try {
5990
- const ackedState = { ...state5, agents: agentStates };
6161
+ const ackedState = { ...state6, agents: agentStates };
5991
6162
  atomicWriteFileSync(getStateFile(), JSON.stringify(ackedState, null, 2));
5992
6163
  } catch (err) {
5993
6164
  log(`[restart] failed to persist ack immediately: ${err.message}`);
@@ -6005,7 +6176,7 @@ async function pollCycle() {
6005
6176
  } catch {
6006
6177
  }
6007
6178
  const currentIds = new Set(agents.map((a) => a.agent_id));
6008
- for (const prev of state5.agents) {
6179
+ for (const prev of state6.agents) {
6009
6180
  if (!currentIds.has(prev.agentId)) {
6010
6181
  log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
6011
6182
  const adapter = resolveAgentFramework(prev.codeName);
@@ -6146,10 +6317,10 @@ async function pollCycle() {
6146
6317
  }
6147
6318
  } catch {
6148
6319
  }
6149
- state5 = {
6150
- ...state5,
6320
+ state6 = {
6321
+ ...state6,
6151
6322
  lastPollAt: (/* @__PURE__ */ new Date()).toISOString(),
6152
- pollCount: state5.pollCount + 1,
6323
+ pollCount: state6.pollCount + 1,
6153
6324
  agents: agentStates,
6154
6325
  // ENG-5441: serialise trip state on every poll so manager restarts
6155
6326
  // never silently clear a tripped breaker. Cheap — only tripped
@@ -6163,9 +6334,9 @@ async function pollCycle() {
6163
6334
  consecutivePollFailures = 0;
6164
6335
  }
6165
6336
  verifyPendingRestarts(Date.now());
6166
- send({ type: "state-update", state: state5 });
6337
+ send({ type: "state-update", state: state6 });
6167
6338
  } catch (err) {
6168
- state5.errorCount++;
6339
+ state6.errorCount++;
6169
6340
  const message = err.message;
6170
6341
  log(`Poll error: ${message}`);
6171
6342
  send({ type: "error", message });
@@ -6216,6 +6387,15 @@ async function processAgent(agent, agentStates) {
6216
6387
  agentId: agent.agent_id,
6217
6388
  log
6218
6389
  });
6390
+ if (memoryExtractionEnabled()) {
6391
+ void maybeExtractMemories({
6392
+ api,
6393
+ backend: resolveConversationEvalBackend(),
6394
+ codeName: agent.code_name,
6395
+ agentId: agent.agent_id,
6396
+ log
6397
+ });
6398
+ }
6219
6399
  }
6220
6400
  const now = (/* @__PURE__ */ new Date()).toISOString();
6221
6401
  const adapter = resolveAgentFramework(agent.code_name);
@@ -6340,11 +6520,11 @@ async function processAgent(agent, agentStates) {
6340
6520
  const marker = autoResumeMarkers.get(agent.code_name);
6341
6521
  const isSelfResume = marker !== void 0 && Date.now() - marker.autoResumedAt < AUTO_RESUME_SELF_WINDOW_MS;
6342
6522
  if (!isSelfResume && autoResumeMarkers.delete(agent.code_name)) {
6343
- state5 = {
6344
- ...state5,
6523
+ state6 = {
6524
+ ...state6,
6345
6525
  circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
6346
6526
  };
6347
- send({ type: "state-update", state: state5 });
6527
+ send({ type: "state-update", state: state6 });
6348
6528
  log(`[auto-resume] Cleared auto-resume marker for '${agent.code_name}' on operator resume \u2014 credit re-armed (ENG-6088)`);
6349
6529
  }
6350
6530
  }
@@ -6375,7 +6555,7 @@ async function processAgent(agent, agentStates) {
6375
6555
  });
6376
6556
  } catch (err) {
6377
6557
  log(`Refresh failed for '${agent.code_name}': ${err.message}`);
6378
- const existing = state5.agents.find((a) => a.agentId === agent.agent_id);
6558
+ const existing = state6.agents.find((a) => a.agentId === agent.agent_id);
6379
6559
  agentStates.push(existing ?? {
6380
6560
  agentId: agent.agent_id,
6381
6561
  codeName: agent.code_name,
@@ -6439,7 +6619,7 @@ async function processAgent(agent, agentStates) {
6439
6619
  const charterVersion = refreshData.charter.version;
6440
6620
  const toolsVersion = refreshData.tools.version;
6441
6621
  const known = agentState.knownVersions.get(agent.agent_id);
6442
- let lastProvisionAt = state5.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
6622
+ let lastProvisionAt = state6.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
6443
6623
  const quarantinedChannels = channelQuarantineStore().getQuarantinedKeys(agent.code_name);
6444
6624
  const currentChannelIds = setWithout(
6445
6625
  launchableChannelIds(refreshData.channel_configs),
@@ -7003,7 +7183,7 @@ async function processAgent(agent, agentStates) {
7003
7183
  log(`Failed to provision direct-chat channel for '${agent.code_name}': ${err.message}`);
7004
7184
  }
7005
7185
  }
7006
- let lastSecretsProvisionAt = state5.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
7186
+ let lastSecretsProvisionAt = state6.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
7007
7187
  let secretsHash = agentState.knownSecretsHashes.get(agent.agent_id) ?? null;
7008
7188
  try {
7009
7189
  const secretsData = await api.post("/host/secrets", { agent_id: agent.agent_id });
@@ -7996,19 +8176,19 @@ function clearStaleCronRunState(jobsPath) {
7996
8176
  let changed = false;
7997
8177
  const now = Date.now();
7998
8178
  for (const job of jobs) {
7999
- const state6 = job.state;
8000
- if (!state6) continue;
8001
- const runStartedAt = state6.runningAtMs ?? state6.runStartedAtMs;
8002
- const isRunning = state6.running === true || state6.status === "running";
8179
+ const state7 = job.state;
8180
+ if (!state7) continue;
8181
+ const runStartedAt = state7.runningAtMs ?? state7.runStartedAtMs;
8182
+ const isRunning = state7.running === true || state7.status === "running";
8003
8183
  if (isRunning && runStartedAt && now - runStartedAt > STALE_RUN_TIMEOUT_MS) {
8004
- state6.running = false;
8005
- delete state6.status;
8006
- delete state6.runStartedAtMs;
8007
- delete state6.currentRunId;
8184
+ state7.running = false;
8185
+ delete state7.status;
8186
+ delete state7.runStartedAtMs;
8187
+ delete state7.currentRunId;
8008
8188
  changed = true;
8009
8189
  log(`Cleared stale running state for cron job '${job.name}' (stuck for ${Math.round((now - runStartedAt) / 6e4)}min)`);
8010
8190
  } else if (isRunning && !runStartedAt) {
8011
- state6.running = false;
8191
+ state7.running = false;
8012
8192
  changed = true;
8013
8193
  log(`Cleared stale running state for cron job '${job.name}' (no start timestamp)`);
8014
8194
  }
@@ -8095,16 +8275,16 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
8095
8275
  const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
8096
8276
  if (combinedHash !== prevHash) {
8097
8277
  const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
8098
- const state7 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
8099
- claudeSchedulerStates.set(codeName, state7);
8278
+ const state8 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
8279
+ claudeSchedulerStates.set(codeName, state8);
8100
8280
  agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
8101
8281
  log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
8102
8282
  }
8103
8283
  if (!claudeSchedulerStates.has(codeName)) {
8104
8284
  claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
8105
8285
  }
8106
- const state6 = claudeSchedulerStates.get(codeName);
8107
- const ready = getReadyTasks(state6, inFlightClaudeTasks);
8286
+ const state7 = claudeSchedulerStates.get(codeName);
8287
+ const ready = getReadyTasks(state7, inFlightClaudeTasks);
8108
8288
  if (ready.length === 0) return;
8109
8289
  for (const task of ready) {
8110
8290
  if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
@@ -8264,8 +8444,8 @@ async function deliverScheduledCardResult(codeName, agentId, cardId) {
8264
8444
  markScheduledCardDeliveryComplete(cardId);
8265
8445
  return "terminal";
8266
8446
  }
8267
- const state6 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
8268
- const task = state6.tasks[card.source_ref];
8447
+ const state7 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
8448
+ const task = state7.tasks[card.source_ref];
8269
8449
  if (!task) {
8270
8450
  log(`[scheduled-kanban] delivery: no scheduler task for source_ref=${card.source_ref} on '${codeName}' \u2014 skipping`);
8271
8451
  markScheduledCardDeliveryComplete(cardId);
@@ -8828,9 +9008,9 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash3("sha256").
8828
9008
  } else if (!claudeSchedulerStates.has(codeName)) {
8829
9009
  claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
8830
9010
  }
8831
- const state6 = claudeSchedulerStates.get(codeName);
8832
- if (state6) {
8833
- const ready = getReadyTasks(state6, inFlightClaudeTasks);
9011
+ const state7 = claudeSchedulerStates.get(codeName);
9012
+ if (state7) {
9013
+ const ready = getReadyTasks(state7, inFlightClaudeTasks);
8834
9014
  if (ready.length > 0) {
8835
9015
  log(`[persistent-session] ${ready.length} ready task(s) for '${codeName}': ${ready.map((t) => `${t.name}(next=${t.nextFireAt ? new Date(t.nextFireAt).toISOString() : "null"})`).join(", ")}`);
8836
9016
  }
@@ -10310,7 +10490,7 @@ async function processClaudePairSessions(agents) {
10310
10490
  killPairSession,
10311
10491
  pairTmuxSession,
10312
10492
  finalizeClaudePairOnboarding
10313
- } = await import("../claude-pair-runtime-QNOWFDJ7.js");
10493
+ } = await import("../claude-pair-runtime-GWZWHISO.js");
10314
10494
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
10315
10495
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
10316
10496
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -10928,8 +11108,8 @@ function startManager(opts) {
10928
11108
  const raw = readFileSync9(stateFile, "utf-8");
10929
11109
  const parsed = JSON.parse(raw);
10930
11110
  if (Array.isArray(parsed.agents)) {
10931
- state5.agents = parsed.agents;
10932
- log(`[startup] rehydrated ${state5.agents.length} agent state(s) from ${stateFile}`);
11111
+ state6.agents = parsed.agents;
11112
+ log(`[startup] rehydrated ${state6.agents.length} agent state(s) from ${stateFile}`);
10933
11113
  }
10934
11114
  if (parsed.circuitBreakerTrips && typeof parsed.circuitBreakerTrips === "object") {
10935
11115
  restartBreaker.hydrate(parsed.circuitBreakerTrips);