@mrrlin-dev/mcp 0.4.10 → 0.4.16

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 (2) hide show
  1. package/dist/bin.cjs +750 -218
  2. package/package.json +2 -2
package/dist/bin.cjs CHANGED
@@ -6246,7 +6246,7 @@ var require_websocket = __commonJS({
6246
6246
  var http2 = require("http");
6247
6247
  var net = require("net");
6248
6248
  var tls = require("tls");
6249
- var { randomBytes: randomBytes4, createHash: createHash5 } = require("crypto");
6249
+ var { randomBytes: randomBytes5, createHash: createHash5 } = require("crypto");
6250
6250
  var { Duplex, Readable } = require("stream");
6251
6251
  var { URL: URL2 } = require("url");
6252
6252
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -6784,7 +6784,7 @@ var require_websocket = __commonJS({
6784
6784
  }
6785
6785
  }
6786
6786
  const defaultPort = isSecure ? 443 : 80;
6787
- const key = randomBytes4(16).toString("base64");
6787
+ const key = randomBytes5(16).toString("base64");
6788
6788
  const request = isSecure ? https.request : http2.request;
6789
6789
  const protocolSet = /* @__PURE__ */ new Set();
6790
6790
  let perMessageDeflate;
@@ -8166,7 +8166,7 @@ var require_graceful_fs = __commonJS({
8166
8166
  function patch(fs11) {
8167
8167
  polyfills(fs11);
8168
8168
  fs11.gracefulify = patch;
8169
- fs11.createReadStream = createReadStream;
8169
+ fs11.createReadStream = createReadStream2;
8170
8170
  fs11.createWriteStream = createWriteStream;
8171
8171
  var fs$readFile = fs11.readFile;
8172
8172
  fs11.readFile = readFile9;
@@ -8376,7 +8376,7 @@ var require_graceful_fs = __commonJS({
8376
8376
  }
8377
8377
  });
8378
8378
  }
8379
- function createReadStream(path22, options) {
8379
+ function createReadStream2(path22, options) {
8380
8380
  return new fs11.ReadStream(path22, options);
8381
8381
  }
8382
8382
  function createWriteStream(path22, options) {
@@ -20644,11 +20644,11 @@ var require_dist = __commonJS({
20644
20644
  });
20645
20645
 
20646
20646
  // src/bin.ts
20647
- var import_node_path30 = __toESM(require("node:path"), 1);
20647
+ var import_node_path31 = __toESM(require("node:path"), 1);
20648
20648
  var import_node_child_process18 = require("node:child_process");
20649
- var import_node_fs17 = require("node:fs");
20649
+ var import_node_fs18 = require("node:fs");
20650
20650
  var import_node_os15 = require("node:os");
20651
- var import_node_path31 = __toESM(require("node:path"), 1);
20651
+ var import_node_path32 = __toESM(require("node:path"), 1);
20652
20652
 
20653
20653
  // src/install-codex.ts
20654
20654
  var import_promises = __toESM(require("node:fs/promises"), 1);
@@ -21471,12 +21471,12 @@ async function verifyProviderReadiness(provider, run3, opts = {}) {
21471
21471
  }
21472
21472
 
21473
21473
  // src/director-bridge.ts
21474
- var import_node_fs16 = __toESM(require("node:fs"), 1);
21474
+ var import_node_fs17 = __toESM(require("node:fs"), 1);
21475
21475
  var import_node_http = __toESM(require("node:http"), 1);
21476
21476
  var import_node_os11 = __toESM(require("node:os"), 1);
21477
- var import_node_path23 = __toESM(require("node:path"), 1);
21477
+ var import_node_path24 = __toESM(require("node:path"), 1);
21478
21478
  var import_node_child_process15 = require("node:child_process");
21479
- var import_node_crypto9 = __toESM(require("node:crypto"), 1);
21479
+ var import_node_crypto10 = __toESM(require("node:crypto"), 1);
21480
21480
 
21481
21481
  // src/turn-replay-buffer.ts
21482
21482
  var DEFAULT_MAX_EVENTS = 2e3;
@@ -36828,6 +36828,8 @@ var openApiOperationIds = {
36828
36828
  getArtifactFile: "getArtifactFile",
36829
36829
  downloadArtifactFile: "downloadArtifactFile",
36830
36830
  listArtifactFiles: "listArtifactFiles",
36831
+ listArtifactFileExcerpts: "listArtifactFileExcerpts",
36832
+ getArtifactFileContent: "getArtifactFileContent",
36831
36833
  getOperatorSettings: "getOperatorSettings",
36832
36834
  updateOperatorSettings: "updateOperatorSettings",
36833
36835
  recordOperatorPresence: "recordOperatorPresence",
@@ -36842,7 +36844,8 @@ var openApiOperationIds = {
36842
36844
  recordSpecConsensusReview: "recordSpecConsensusReview",
36843
36845
  getSpecConsensusReview: "getSpecConsensusReview",
36844
36846
  onboardingKickoffTransition: "onboardingKickoffTransition",
36845
- ingestTelemetry: "ingestTelemetry"
36847
+ ingestTelemetry: "ingestTelemetry",
36848
+ getTelemetry: "getTelemetry"
36846
36849
  };
36847
36850
 
36848
36851
  // ../../packages/schemas/dist/session.js
@@ -37284,6 +37287,20 @@ function totalSize(items) {
37284
37287
  return items.reduce((sum, i) => sum + i.size, 0);
37285
37288
  }
37286
37289
 
37290
+ // ../../packages/schemas/dist/inboxProposal.js
37291
+ function isRunnableProposal(item) {
37292
+ if (item.kind !== "proposal") {
37293
+ return false;
37294
+ }
37295
+ if (item.actionPayload === null || item.actionPayload === void 0) {
37296
+ return false;
37297
+ }
37298
+ if (item.taskId === null || item.taskId === void 0 || item.taskId === "") {
37299
+ return false;
37300
+ }
37301
+ return true;
37302
+ }
37303
+
37287
37304
  // ../../packages/schemas/dist/index.js
37288
37305
  var mrrlinTaskStatusSchema = external_exports.enum([
37289
37306
  "backlog",
@@ -38460,6 +38477,13 @@ var artifactFileCreateResponseSchema = external_exports.object({
38460
38477
  })
38461
38478
  });
38462
38479
  var artifactFileListResponseSchema = external_exports.object({ data: external_exports.array(mrrlinArtifactFilePublicSchema) });
38480
+ var mrrlinArtifactFileExcerptSchema = external_exports.object({
38481
+ excerpt: external_exports.string(),
38482
+ id: external_exports.string().uuid()
38483
+ });
38484
+ var mrrlinArtifactFileExcerptsResponseSchema = external_exports.object({
38485
+ data: external_exports.array(mrrlinArtifactFileExcerptSchema)
38486
+ });
38463
38487
  var artifactFileExpiredResponseSchema = external_exports.object({
38464
38488
  artifact: external_exports.object({
38465
38489
  class: mrrlinArtifactFileClassSchema,
@@ -40175,6 +40199,46 @@ function createMrrlinClient(config2) {
40175
40199
  }
40176
40200
  return response.json();
40177
40201
  }
40202
+ async function requestText(path22, init) {
40203
+ const method = init?.method ?? "GET";
40204
+ const headers = new Headers(init?.headers ?? {});
40205
+ if (token && !headers.has("authorization")) {
40206
+ headers.set("authorization", `Bearer ${token}`);
40207
+ }
40208
+ const response = await customFetch(`${baseUrl}${path22}`, { ...init, headers });
40209
+ if (!response.ok) {
40210
+ const body = await response.text();
40211
+ let code = `HTTP_${response.status}`;
40212
+ let violations = null;
40213
+ let message = `Mrrlin API ${method} ${path22} failed: ${response.status}`;
40214
+ try {
40215
+ const parsed = JSON.parse(body);
40216
+ const violationsParsed = apiViolationsErrorSchema.safeParse(parsed);
40217
+ if (violationsParsed.success) {
40218
+ code = violationsParsed.data.code;
40219
+ violations = violationsParsed.data.violations;
40220
+ message = violationsParsed.data.message;
40221
+ } else {
40222
+ const errorParsed = apiErrorSchema.safeParse(parsed);
40223
+ if (errorParsed.success) {
40224
+ code = errorParsed.data.code;
40225
+ message = errorParsed.data.message;
40226
+ }
40227
+ }
40228
+ } catch {
40229
+ }
40230
+ throw new MrrlinApiError({
40231
+ code,
40232
+ method,
40233
+ path: path22,
40234
+ responseBody: body,
40235
+ status: response.status,
40236
+ violations,
40237
+ message
40238
+ });
40239
+ }
40240
+ return response.text();
40241
+ }
40178
40242
  return {
40179
40243
  async listGithubInstallations() {
40180
40244
  const body = await request(`/github/installations`);
@@ -40894,6 +40958,14 @@ function createMrrlinClient(config2) {
40894
40958
  const body = await request(`${projectPath(projectSlug)}/artifact-files${suffix}`);
40895
40959
  return artifactFileListResponseSchema.parse(body).data;
40896
40960
  },
40961
+ async listArtifactFileExcerpts(projectSlug, taskId) {
40962
+ const params = new URLSearchParams({ taskId });
40963
+ const body = await request(`${projectPath(projectSlug)}/artifact-file-excerpts?${params.toString()}`);
40964
+ return mrrlinArtifactFileExcerptsResponseSchema.parse(body).data;
40965
+ },
40966
+ async getArtifactFileContent(projectSlug, artifactFileId) {
40967
+ return requestText(`${projectPath(projectSlug)}/artifact-files/${encodeURIComponent(artifactFileId)}/content`);
40968
+ },
40897
40969
  async getProjectSchedulerPolicy(projectSlug) {
40898
40970
  const body = await request(`${projectPath(projectSlug)}/scheduler/policy`);
40899
40971
  return projectSchedulerPolicyResponseSchema.parse(body).data;
@@ -41292,16 +41364,16 @@ function resolveBinPath(opts) {
41292
41364
  return null;
41293
41365
  }
41294
41366
  });
41295
- const existsSync4 = opts.existsSync ?? import_node_fs2.existsSync;
41367
+ const existsSync5 = opts.existsSync ?? import_node_fs2.existsSync;
41296
41368
  const resolved = whichSync(BIN_NAME);
41297
- if (resolved && existsSync4(resolved)) {
41369
+ if (resolved && existsSync5(resolved)) {
41298
41370
  return { path: resolved, source: "which" };
41299
41371
  }
41300
41372
  const envPath = (opts.env ?? process.env).PATH ?? "";
41301
41373
  for (const dir of envPath.split(import_node_path5.default.delimiter)) {
41302
41374
  if (!dir) continue;
41303
41375
  const candidate = import_node_path5.default.join(dir, BIN_NAME);
41304
- if (existsSync4(candidate)) {
41376
+ if (existsSync5(candidate)) {
41305
41377
  return { path: candidate, source: "path-walk" };
41306
41378
  }
41307
41379
  }
@@ -41551,7 +41623,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
41551
41623
  }
41552
41624
 
41553
41625
  // src/_generated/version.ts
41554
- var PKG_VERSION = "0.4.10";
41626
+ var PKG_VERSION = "0.4.16";
41555
41627
 
41556
41628
  // src/api-base-url-migrate.ts
41557
41629
  var import_node_fs3 = __toESM(require("node:fs"), 1);
@@ -42114,6 +42186,9 @@ function startSchedulerTickLoop(deps) {
42114
42186
  // src/retention/pruner.ts
42115
42187
  var import_promises4 = require("node:fs/promises");
42116
42188
  var import_node_path9 = __toESM(require("node:path"), 1);
42189
+ function protectedDayFloor(protectedSinceMs) {
42190
+ return new Date(protectedSinceMs).toISOString().slice(0, 10);
42191
+ }
42117
42192
  async function prune(stateDir, bounds) {
42118
42193
  const logsDir = import_node_path9.default.join(stateDir, "logs");
42119
42194
  let entries;
@@ -42122,7 +42197,7 @@ async function prune(stateDir, bounds) {
42122
42197
  } catch (err) {
42123
42198
  if (err?.code === "ENOENT") {
42124
42199
  await emitPruneLog(stateDir, [], {}, 0, null);
42125
- return { daysRemoved: [], reasonByDay: {}, bytesRemoved: 0, oldestDayRemaining: null };
42200
+ return { daysRemoved: [], reasonByDay: {}, bytesRemoved: 0, oldestDayRemaining: null, budgetOvershootBytes: 0 };
42126
42201
  }
42127
42202
  throw err;
42128
42203
  }
@@ -42166,12 +42241,15 @@ async function prune(stateDir, bounds) {
42166
42241
  }
42167
42242
  return true;
42168
42243
  }
42244
+ const protectedFromDay = bounds.protectedSinceMs === void 0 ? null : protectedDayFloor(bounds.protectedSinceMs);
42245
+ const isProtected = (day) => protectedFromDay !== null && day >= protectedFromDay;
42169
42246
  const budgetBytes = bounds.budgetMb * 1024 * 1024;
42170
42247
  let totalSize2 = files.reduce((sum, f) => sum + f.size, 0);
42171
42248
  let i = 0;
42172
42249
  while (totalSize2 > budgetBytes && i < files.length) {
42173
42250
  const candidate = files[i];
42174
42251
  if (!candidate) break;
42252
+ if (isProtected(candidate.day)) break;
42175
42253
  if (!await isDaySafeToDelete(candidate.day)) {
42176
42254
  i += 1;
42177
42255
  continue;
@@ -42189,16 +42267,18 @@ async function prune(stateDir, bounds) {
42189
42267
  for (const info of files) {
42190
42268
  const fileDate = /* @__PURE__ */ new Date(`${info.day}T00:00:00Z`);
42191
42269
  const ageInDays = (now - fileDate.getTime()) / msPerDay;
42192
- if (ageInDays > bounds.ageDays && await isDaySafeToDelete(info.day)) {
42270
+ if (ageInDays > bounds.ageDays && !isProtected(info.day) && await isDaySafeToDelete(info.day)) {
42193
42271
  await (0, import_promises4.unlink)(info.fullPath);
42194
42272
  daysRemoved.push(info.day);
42195
42273
  reasonByDay[info.day] = "age";
42196
42274
  bytesRemoved += info.size;
42275
+ totalSize2 -= info.size;
42197
42276
  continue;
42198
42277
  }
42199
42278
  stillRemaining.push(info);
42200
42279
  }
42201
42280
  const oldestDayRemaining = stillRemaining.length > 0 ? stillRemaining[0]?.day ?? null : null;
42281
+ const budgetOvershootBytes = Math.max(0, totalSize2 - budgetBytes);
42202
42282
  await emitPruneLog(stateDir, daysRemoved, reasonByDay, bytesRemoved, oldestDayRemaining);
42203
42283
  if (daysRemoved.length > 0) {
42204
42284
  const sessionsPath = import_node_path9.default.join(logsDir, ".sessions.json");
@@ -42220,7 +42300,7 @@ async function prune(stateDir, bounds) {
42220
42300
  await (0, import_promises4.rename)(tmpPath, sessionsPath);
42221
42301
  }
42222
42302
  }
42223
- return { daysRemoved, reasonByDay, bytesRemoved, oldestDayRemaining };
42303
+ return { daysRemoved, reasonByDay, bytesRemoved, oldestDayRemaining, budgetOvershootBytes };
42224
42304
  }
42225
42305
  async function emitPruneLog(stateDir, daysRemoved, reasonByDay, bytesRemoved, oldestDayRemaining) {
42226
42306
  const logsDir = import_node_path9.default.join(stateDir, "logs");
@@ -42232,6 +42312,7 @@ async function emitPruneLog(stateDir, daysRemoved, reasonByDay, bytesRemoved, ol
42232
42312
  }
42233
42313
 
42234
42314
  // src/telemetry/turn-aggregator.ts
42315
+ var import_node_crypto3 = require("node:crypto");
42235
42316
  var import_promises5 = require("node:fs/promises");
42236
42317
  var import_node_path10 = require("node:path");
42237
42318
  var BRIDGE_FILE_RE = /^bridge-(\d{4}-\d{2}-\d{2})\.jsonl$/;
@@ -42242,6 +42323,25 @@ var PLUMBING_COUNTER_KEY = {
42242
42323
  buffer_stats: "bufferStats",
42243
42324
  rate_limit_status: "rateLimitStatusRecords"
42244
42325
  };
42326
+ function resolveOwnSlug(records) {
42327
+ for (const rec of records) {
42328
+ const slug = rec.projection?.projectSlug;
42329
+ if (slug) return slug;
42330
+ }
42331
+ return void 0;
42332
+ }
42333
+ function inheritSlug(seeds, index) {
42334
+ let preceding;
42335
+ let following;
42336
+ for (const seed of seeds) {
42337
+ if (seed.index < index) {
42338
+ if (!preceding || seed.index > preceding.index) preceding = seed;
42339
+ } else if (seed.index > index) {
42340
+ if (!following || seed.index < following.index) following = seed;
42341
+ }
42342
+ }
42343
+ return (preceding ?? following)?.slug;
42344
+ }
42245
42345
  async function foldClosedDays(stateDir, today) {
42246
42346
  const logsDir = (0, import_node_path10.join)(stateDir, "logs");
42247
42347
  let entries;
@@ -42257,6 +42357,7 @@ async function foldClosedDays(stateDir, today) {
42257
42357
  if (day === today) continue;
42258
42358
  const raw = await (0, import_promises5.readFile)((0, import_node_path10.join)(logsDir, entry), "utf8");
42259
42359
  const groups = /* @__PURE__ */ new Map();
42360
+ let groupCounter = 0;
42260
42361
  for (const line of raw.split("\n")) {
42261
42362
  const trimmed = line.trim();
42262
42363
  if (!trimmed) continue;
@@ -42271,13 +42372,14 @@ async function foldClosedDays(stateDir, today) {
42271
42372
  const key = `${sessionId === null ? 0 : 1}:${sessionId}:${turnId === null ? 0 : 1}:${turnId}`;
42272
42373
  let group = groups.get(key);
42273
42374
  if (!group) {
42274
- group = { sessionId, turnId, records: [], projectionFailures: 0 };
42375
+ group = { sessionId, turnId, records: [], projectionFailures: 0, firstSeenIndex: groupCounter++ };
42275
42376
  groups.set(key, group);
42276
42377
  }
42277
42378
  group.records.push(rec);
42278
42379
  if (rec.projectionFailed === true) group.projectionFailures += 1;
42279
42380
  }
42280
42381
  const rowsByProject = /* @__PURE__ */ new Map();
42382
+ const pending = [];
42281
42383
  for (const group of groups.values()) {
42282
42384
  if (group.sessionId === null) continue;
42283
42385
  const records = [...group.records].sort((a, b) => {
@@ -42288,15 +42390,7 @@ async function foldClosedDays(stateDir, today) {
42288
42390
  if (bSeq === void 0) return -1;
42289
42391
  return aSeq - bSeq;
42290
42392
  });
42291
- let projectSlug;
42292
- for (const rec of records) {
42293
- const slug = rec.projection?.projectSlug;
42294
- if (slug) {
42295
- projectSlug = slug;
42296
- break;
42297
- }
42298
- }
42299
- projectSlug ??= "_unattributed";
42393
+ const ownSlug = resolveOwnSlug(records);
42300
42394
  const toolResults = records.filter((r) => r.projection?.kind === "tool_result");
42301
42395
  const toolSequence = [];
42302
42396
  for (const rec of records) {
@@ -42353,18 +42447,37 @@ async function foldClosedDays(stateDir, today) {
42353
42447
  plumbing,
42354
42448
  projectionFailures: group.projectionFailures
42355
42449
  };
42356
- const rows = rowsByProject.get(projectSlug) ?? [];
42357
- rows.push(row);
42358
- rowsByProject.set(projectSlug, rows);
42450
+ pending.push({ sessionId: group.sessionId, firstSeenIndex: group.firstSeenIndex, ownSlug, row });
42451
+ }
42452
+ const seedsBySession = /* @__PURE__ */ new Map();
42453
+ for (const item of pending) {
42454
+ if (item.ownSlug === void 0) continue;
42455
+ const seeds = seedsBySession.get(item.sessionId) ?? [];
42456
+ seeds.push({ index: item.firstSeenIndex, slug: item.ownSlug });
42457
+ seedsBySession.set(item.sessionId, seeds);
42458
+ }
42459
+ for (const item of pending) {
42460
+ const slug = item.ownSlug ?? inheritSlug(seedsBySession.get(item.sessionId) ?? [], item.firstSeenIndex) ?? // Neither the turn nor any turn of its session carries project evidence. Unchanged from
42461
+ // before this change: the partition stays `_unattributed`, and daily-uploader.ts is the one
42462
+ // that maps that partition name to MRRLIN_TELEMETRY_DEFAULT_PROJECT at upload time.
42463
+ "_unattributed";
42464
+ const rows = rowsByProject.get(slug) ?? [];
42465
+ rows.push(item.row);
42466
+ rowsByProject.set(slug, rows);
42359
42467
  }
42360
42468
  if (rowsByProject.size === 0) continue;
42361
42469
  const telemetryDir = (0, import_node_path10.join)(stateDir, "telemetry");
42362
42470
  await (0, import_promises5.mkdir)(telemetryDir, { recursive: true });
42363
42471
  for (const [slug, rows] of rowsByProject) {
42364
42472
  const finalPath = (0, import_node_path10.join)(telemetryDir, `rows-${slug}-${day}.jsonl`);
42365
- const tmpPath = `${finalPath}.tmp`;
42366
- await (0, import_promises5.writeFile)(tmpPath, rows.map((r) => JSON.stringify(r)).join("\n") + "\n", "utf8");
42367
- await (0, import_promises5.rename)(tmpPath, finalPath);
42473
+ const tmpPath = `${finalPath}.tmp-${process.pid}-${(0, import_node_crypto3.randomBytes)(6).toString("hex")}`;
42474
+ try {
42475
+ await (0, import_promises5.writeFile)(tmpPath, rows.map((r) => JSON.stringify(r)).join("\n") + "\n", "utf8");
42476
+ await (0, import_promises5.rename)(tmpPath, finalPath);
42477
+ } finally {
42478
+ await (0, import_promises5.unlink)(tmpPath).catch(() => {
42479
+ });
42480
+ }
42368
42481
  }
42369
42482
  }
42370
42483
  }
@@ -42374,7 +42487,7 @@ var import_promises6 = require("node:fs/promises");
42374
42487
  var import_node_path12 = require("node:path");
42375
42488
 
42376
42489
  // src/consensus/wiring.ts
42377
- var import_node_crypto3 = __toESM(require("node:crypto"), 1);
42490
+ var import_node_crypto4 = __toESM(require("node:crypto"), 1);
42378
42491
  var import_node_fs5 = __toESM(require("node:fs"), 1);
42379
42492
  var import_node_path11 = __toESM(require("node:path"), 1);
42380
42493
  var import_node_url2 = require("node:url");
@@ -43901,7 +44014,7 @@ You revise a deliverable to address the accepted issues so it satisfies the acce
43901
44014
  try {
43902
44015
  const diagnostics = Array.isArray(r["diagnostics"]) ? r["diagnostics"] : void 0;
43903
44016
  await client.appendExecutionArtifact(projectSlug, runId, {
43904
- id: `EA-${import_node_crypto3.default.randomUUID()}`,
44017
+ id: `EA-${import_node_crypto4.default.randomUUID()}`,
43905
44018
  kind: "verification",
43906
44019
  payload: {
43907
44020
  source: "deliverable-gate",
@@ -44029,50 +44142,102 @@ async function uploadPendingDays(stateDir, client, bridgeHealth, gateCtx) {
44029
44142
  const fullUnprocessed = pending.map((p) => ({ projectSlug: p.projectSlug, day: p.day }));
44030
44143
  pending.sort((a, b) => a.day.localeCompare(b.day));
44031
44144
  for (const cur of pending) {
44032
- let raw;
44145
+ const failurePath = (0, import_node_path12.join)(telemetryDir, `.failed-${cur.projectSlug}-${cur.day}.json`);
44146
+ let failureState = null;
44033
44147
  try {
44034
- raw = await (0, import_promises6.readFile)(cur.rowsPath, "utf8");
44035
- } catch (err) {
44036
- console.error(`uploadPendingDays: failed to read ${cur.rowsPath}`, err);
44037
- continue;
44038
- }
44039
- const rows = [];
44040
- for (const line of raw.split("\n")) {
44041
- if (!line.trim()) continue;
44042
- try {
44043
- rows.push({ ...JSON.parse(line), gateMarker: "gated" });
44044
- } catch {
44045
- }
44148
+ const data = await (0, import_promises6.readFile)(failurePath, "utf8");
44149
+ failureState = JSON.parse(data);
44150
+ } catch {
44151
+ failureState = null;
44046
44152
  }
44047
- const batch = {
44048
- day: cur.day,
44049
- bridgeVersion: PKG_VERSION,
44050
- knownCrashCount,
44051
- unprocessedPartitions: fullUnprocessed.filter(
44052
- (p) => !(p.projectSlug === cur.projectSlug && p.day === cur.day)
44053
- ),
44054
- rows
44055
- };
44056
- const gateResult = await gate(batch, [], gateCtx);
44057
- const gateMarker = gateResult.gated ? "gated" : "projection-only";
44058
- for (const row of gateResult.artifact.rows) row.gateMarker = gateMarker;
44059
- const targetProject = cur.projectSlug === "_unattributed" ? process.env.MRRLIN_TELEMETRY_DEFAULT_PROJECT : cur.projectSlug;
44060
- if (!targetProject) {
44061
- console.error(`uploadPendingDays: no target project resolved for _unattributed/${cur.day}`);
44153
+ const now = Date.now();
44154
+ if (failureState && failureState.nextAttemptAt > now) {
44062
44155
  continue;
44063
44156
  }
44157
+ let success2 = false;
44064
44158
  try {
44159
+ let raw;
44160
+ try {
44161
+ raw = await (0, import_promises6.readFile)(cur.rowsPath, "utf8");
44162
+ } catch (err) {
44163
+ console.error(`uploadPendingDays: failed to read ${cur.rowsPath}`, err);
44164
+ throw err;
44165
+ }
44166
+ const rows = [];
44167
+ for (const line of raw.split("\n")) {
44168
+ if (!line.trim()) continue;
44169
+ try {
44170
+ rows.push({ ...JSON.parse(line), gateMarker: "gated" });
44171
+ } catch {
44172
+ }
44173
+ }
44174
+ const batch = {
44175
+ day: cur.day,
44176
+ bridgeVersion: PKG_VERSION,
44177
+ knownCrashCount,
44178
+ unprocessedPartitions: fullUnprocessed.filter(
44179
+ (p) => !(p.projectSlug === cur.projectSlug && p.day === cur.day)
44180
+ ),
44181
+ rows
44182
+ };
44183
+ const gateResult = await gate(batch, [], gateCtx);
44184
+ const gateMarker = gateResult.gated ? "gated" : "projection-only";
44185
+ for (const row of gateResult.artifact.rows) row.gateMarker = gateMarker;
44186
+ const targetProject = cur.projectSlug === "_unattributed" ? process.env.MRRLIN_TELEMETRY_DEFAULT_PROJECT : cur.projectSlug;
44187
+ if (!targetProject) {
44188
+ console.error(`uploadPendingDays: no target project resolved for _unattributed/${cur.day}`);
44189
+ continue;
44190
+ }
44065
44191
  await client.postTelemetry(targetProject, gateResult.artifact);
44066
- } catch (err) {
44067
- console.error(`uploadPendingDays: postTelemetry failed for ${cur.projectSlug}/${cur.day}`, err);
44068
- continue;
44192
+ success2 = true;
44193
+ } catch {
44194
+ success2 = false;
44069
44195
  }
44070
- const tmpMarker = `${cur.markerPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
44071
- try {
44072
- await (0, import_promises6.writeFile)(tmpMarker, "");
44073
- await (0, import_promises6.rename)(tmpMarker, cur.markerPath);
44074
- } catch (err) {
44075
- console.error(`uploadPendingDays: failed to write marker for ${cur.projectSlug}/${cur.day}`, err);
44196
+ if (success2) {
44197
+ const tmpMarker = `${cur.markerPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
44198
+ try {
44199
+ await (0, import_promises6.writeFile)(tmpMarker, "");
44200
+ await (0, import_promises6.rename)(tmpMarker, cur.markerPath);
44201
+ } catch (err) {
44202
+ console.error(`uploadPendingDays: failed to write marker for ${cur.projectSlug}/${cur.day}`, err);
44203
+ }
44204
+ if (failureState) {
44205
+ try {
44206
+ await (0, import_promises6.unlink)(failurePath);
44207
+ console.log(`uploadPendingDays: partition ${cur.projectSlug}/${cur.day} recovered after ${failureState.failureCount + 1} consecutive failures`);
44208
+ } catch (err) {
44209
+ console.error(`uploadPendingDays: failed to remove failure state for ${cur.projectSlug}/${cur.day}`, err);
44210
+ }
44211
+ }
44212
+ } else {
44213
+ let newFailureCount = 1;
44214
+ if (failureState) {
44215
+ newFailureCount = failureState.failureCount + 1;
44216
+ }
44217
+ const baseDelayMs = 36e5;
44218
+ const maxDelayMs = 864e5;
44219
+ let delayMs2 = 0;
44220
+ if (newFailureCount >= 2) {
44221
+ delayMs2 = baseDelayMs * 2 ** (newFailureCount - 2);
44222
+ if (delayMs2 > maxDelayMs) {
44223
+ delayMs2 = maxDelayMs;
44224
+ }
44225
+ }
44226
+ const nextAttemptAt = now + delayMs2;
44227
+ const newFailureState = {
44228
+ failureCount: newFailureCount,
44229
+ nextAttemptAt
44230
+ };
44231
+ const tmpFailurePath = `${failurePath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
44232
+ try {
44233
+ await (0, import_promises6.writeFile)(tmpFailurePath, JSON.stringify(newFailureState));
44234
+ await (0, import_promises6.rename)(tmpFailurePath, failurePath);
44235
+ } catch (writeErr) {
44236
+ console.error(`uploadPendingDays: failed to write failure state for ${cur.projectSlug}/${cur.day}`, writeErr);
44237
+ }
44238
+ if (!failureState) {
44239
+ console.error(`uploadPendingDays: partition ${cur.projectSlug}/${cur.day} entering backoff after first failure`);
44240
+ }
44076
44241
  }
44077
44242
  }
44078
44243
  }
@@ -44407,7 +44572,7 @@ function setSelectedProvider(value) {
44407
44572
 
44408
44573
  // src/execution-consumer.ts
44409
44574
  var import_node_child_process12 = require("node:child_process");
44410
- var import_node_crypto5 = __toESM(require("node:crypto"), 1);
44575
+ var import_node_crypto6 = __toESM(require("node:crypto"), 1);
44411
44576
  var import_node_path15 = __toESM(require("node:path"), 1);
44412
44577
  var import_node_util = require("node:util");
44413
44578
 
@@ -44476,7 +44641,7 @@ function mapDeliverableGateResult(result) {
44476
44641
  }
44477
44642
 
44478
44643
  // src/async-tools/draft-spec.ts
44479
- var import_node_crypto4 = require("node:crypto");
44644
+ var import_node_crypto5 = require("node:crypto");
44480
44645
  var DRAFT_SPEC_TOOL_NAME = "draft_task_spec";
44481
44646
  var SPECS_FOLDER = "Specs";
44482
44647
  var DRAFT_SPEC_TLDR_MAX_CHARS = 300;
@@ -44959,7 +45124,7 @@ function onComplete(outcome, input) {
44959
45124
  };
44960
45125
  }
44961
45126
  const summary = outcome.error;
44962
- const hash3 = (0, import_node_crypto4.createHash)("sha256").update(summary).digest("hex").slice(0, 10);
45127
+ const hash3 = (0, import_node_crypto5.createHash)("sha256").update(summary).digest("hex").slice(0, 10);
44963
45128
  return {
44964
45129
  kind: "info",
44965
45130
  id: `II-sched-draft-failed-${input.taskId}-${hash3}`,
@@ -46279,7 +46444,7 @@ function isBridgeGeneratedArtifact(artifact) {
46279
46444
  return typeof payload === "object" && payload !== null && "event" in payload;
46280
46445
  }
46281
46446
  function newArtifactId() {
46282
- return `EA-${import_node_crypto5.default.randomUUID()}`;
46447
+ return `EA-${import_node_crypto6.default.randomUUID()}`;
46283
46448
  }
46284
46449
  var BLAST_RADIUS_FLOOR_PATTERNS = [/^supabase\/migrations\//, /^packages\/db\/src\/schema\.ts$/];
46285
46450
  function isBlastRadiusFloorPath(path22) {
@@ -46521,7 +46686,7 @@ async function driveRun(deps, projectSlug, runId) {
46521
46686
  if (!claimed) return;
46522
46687
  if (!claimed.leaseId) {
46523
46688
  await deps.client.createInboxItem(projectSlug, {
46524
- id: `II-${import_node_crypto5.default.randomUUID()}`,
46689
+ id: `II-${import_node_crypto6.default.randomUUID()}`,
46525
46690
  kind: "handoff",
46526
46691
  taskId: claimed.taskId,
46527
46692
  body: `Execution run ${runId} was claimed but returned no leaseId. This indicates DB/API corruption and requires operator attention.`
@@ -47492,7 +47657,7 @@ async function reconcileTaskAwaitingMerge(deps, projectSlug, task, runId, reason
47492
47657
  }
47493
47658
  try {
47494
47659
  await deps.client.createInboxItem(projectSlug, {
47495
- id: `II-${import_node_crypto5.default.randomUUID()}`,
47660
+ id: `II-${import_node_crypto6.default.randomUUID()}`,
47496
47661
  kind: "handoff",
47497
47662
  taskId: task.id,
47498
47663
  body: `Task ${task.id} (${task.title}) still needs operator follow-up: ${reason}. Mrrlin could not move the task back to Handoff needed automatically.`
@@ -47527,7 +47692,7 @@ async function notifyOperatorHandoff(deps, projectSlug, task, runId, reason) {
47527
47692
  }
47528
47693
  try {
47529
47694
  await deps.client.createInboxItem(projectSlug, {
47530
- id: `II-${import_node_crypto5.default.randomUUID()}`,
47695
+ id: `II-${import_node_crypto6.default.randomUUID()}`,
47531
47696
  kind: "handoff",
47532
47697
  taskId: task.id,
47533
47698
  body: `Execution run ${runId} for task ${task.id} (${task.title}) needs operator attention: ${reason}`
@@ -47665,7 +47830,7 @@ async function deployGateHandoff(deps, projectSlug, task, runId, reason, leaseId
47665
47830
  }).catch(() => {
47666
47831
  });
47667
47832
  await deps.client.createInboxItem(projectSlug, {
47668
- id: `II-${import_node_crypto5.default.randomUUID()}`,
47833
+ id: `II-${import_node_crypto6.default.randomUUID()}`,
47669
47834
  kind: "handoff",
47670
47835
  taskId: task.id,
47671
47836
  body: `Deploy gate for run ${runId} (task ${task.id} \u2014 ${task.title}) needs operator attention: ${reason}`
@@ -47684,7 +47849,7 @@ function maxLeaseReclaims() {
47684
47849
  }
47685
47850
 
47686
47851
  // src/async-job-consumer.ts
47687
- var import_node_crypto7 = __toESM(require("node:crypto"), 1);
47852
+ var import_node_crypto8 = __toESM(require("node:crypto"), 1);
47688
47853
 
47689
47854
  // src/async-tools/registry.ts
47690
47855
  var registry2 = /* @__PURE__ */ new Map();
@@ -47723,7 +47888,7 @@ function redact(input) {
47723
47888
  }
47724
47889
 
47725
47890
  // src/telemetry/projector.ts
47726
- var import_node_crypto6 = require("node:crypto");
47891
+ var import_node_crypto7 = require("node:crypto");
47727
47892
  function isRecord2(x) {
47728
47893
  return typeof x === "object" && x !== null;
47729
47894
  }
@@ -47764,7 +47929,7 @@ function buildProjection(payload, type, ms, telemetry) {
47764
47929
  if (typeof exitCode === "number") proj.exitCode = exitCode;
47765
47930
  const status = tool.status;
47766
47931
  if (typeof status === "string" && status.trim() !== "") {
47767
- proj.statusHash = (0, import_node_crypto6.createHash)("sha256").update(status.trim().toLowerCase()).digest("hex").slice(0, 8);
47932
+ proj.statusHash = (0, import_node_crypto7.createHash)("sha256").update(status.trim().toLowerCase()).digest("hex").slice(0, 8);
47768
47933
  }
47769
47934
  return proj;
47770
47935
  }
@@ -47878,7 +48043,7 @@ function resolveBridgeLogEnabled(env = process.env) {
47878
48043
  const raw = (env.MRRLIN_DIRECTOR_BRIDGE_LOG ?? "").trim().toLowerCase();
47879
48044
  return !(raw === "0" || raw === "false");
47880
48045
  }
47881
- function resolvePromptsEnabled(env) {
48046
+ function resolvePromptsEnabled(env = process.env) {
47882
48047
  const raw = (env.MRRLIN_DIRECTOR_BRIDGE_LOG_PROMPTS ?? "").trim().toLowerCase();
47883
48048
  return !(raw === "0" || raw === "false");
47884
48049
  }
@@ -48079,7 +48244,7 @@ async function driveAsyncJob(deps, projectSlug, jobId) {
48079
48244
  const delivery = descriptor2.onComplete(outcome, input);
48080
48245
  if (!delivery.suppressInbox) {
48081
48246
  await deps.client.createInboxItem(projectSlug, {
48082
- id: delivery.id ?? `II-${import_node_crypto7.default.randomUUID()}`,
48247
+ id: delivery.id ?? `II-${import_node_crypto8.default.randomUUID()}`,
48083
48248
  kind: delivery.kind,
48084
48249
  body: delivery.body,
48085
48250
  ...delivery.taskId ? { taskId: delivery.taskId } : {},
@@ -48097,13 +48262,13 @@ async function deriveRequestedHash(client, toolName, input) {
48097
48262
  if (input.requestedHash) return input.requestedHash;
48098
48263
  if (toolName === "run_spec_consensus" && input.pageId) {
48099
48264
  const page = await client.getWikiPage(input.projectSlug, input.pageId);
48100
- return page.contentHash ?? import_node_crypto7.default.createHash("sha256").update(page.markdownBody).digest("hex");
48265
+ return page.contentHash ?? import_node_crypto8.default.createHash("sha256").update(page.markdownBody).digest("hex");
48101
48266
  }
48102
48267
  if (toolName === "draft_task_spec" && input.taskId) {
48103
48268
  const task = await client.getTask(input.projectSlug, input.taskId);
48104
- return task.updatedAt ?? import_node_crypto7.default.createHash("sha256").update(JSON.stringify(task)).digest("hex");
48269
+ return task.updatedAt ?? import_node_crypto8.default.createHash("sha256").update(JSON.stringify(task)).digest("hex");
48105
48270
  }
48106
- return import_node_crypto7.default.createHash("sha256").update(JSON.stringify(input)).digest("hex");
48271
+ return import_node_crypto8.default.createHash("sha256").update(JSON.stringify(input)).digest("hex");
48107
48272
  }
48108
48273
  async function runAsyncBackstopSweep(deps, drive = (slug, jobId) => driveAsyncJob(deps, slug, jobId)) {
48109
48274
  const projects = await deps.client.listProjects();
@@ -48584,7 +48749,7 @@ function createToolBusyClock(opts) {
48584
48749
  }
48585
48750
 
48586
48751
  // src/worker-token.ts
48587
- var import_node_crypto8 = require("node:crypto");
48752
+ var import_node_crypto9 = require("node:crypto");
48588
48753
  var import_node_fs11 = require("node:fs");
48589
48754
  var import_node_os8 = __toESM(require("node:os"), 1);
48590
48755
  var import_node_path19 = __toESM(require("node:path"), 1);
@@ -48615,7 +48780,7 @@ function readOrCreateWorkerToken() {
48615
48780
  if (existing) return existing;
48616
48781
  const dir = workerDir();
48617
48782
  (0, import_node_fs11.mkdirSync)(dir, { recursive: true, mode: 448 });
48618
- const token = (0, import_node_crypto8.randomBytes)(32).toString("hex");
48783
+ const token = (0, import_node_crypto9.randomBytes)(32).toString("hex");
48619
48784
  (0, import_node_fs11.writeFileSync)(workerTokenPath(), `${token}
48620
48785
  `, { encoding: "utf8", mode: 384 });
48621
48786
  return token;
@@ -48683,7 +48848,7 @@ function wrapConstructor(hashCons) {
48683
48848
  hashC.create = () => hashCons();
48684
48849
  return hashC;
48685
48850
  }
48686
- function randomBytes2(bytesLength = 32) {
48851
+ function randomBytes3(bytesLength = 32) {
48687
48852
  if (crypto5 && typeof crypto5.getRandomValues === "function") {
48688
48853
  return crypto5.getRandomValues(new Uint8Array(bytesLength));
48689
48854
  }
@@ -48722,10 +48887,10 @@ function encodeTranscript(channelId, ephemeralPhonePub, ephemeralAgentPub) {
48722
48887
 
48723
48888
  // ../../packages/director-e2e/dist/random.js
48724
48889
  function generateSecret() {
48725
- return randomBytes2(32);
48890
+ return randomBytes3(32);
48726
48891
  }
48727
48892
  function generateChannelId() {
48728
- return toBase64Url(randomBytes2(16));
48893
+ return toBase64Url(randomBytes3(16));
48729
48894
  }
48730
48895
 
48731
48896
  // ../../packages/director-e2e/dist/qr.js
@@ -49894,7 +50059,7 @@ function validateOpts(curve) {
49894
50059
  }
49895
50060
  function twistedEdwards(curveDef) {
49896
50061
  const CURVE = validateOpts(curveDef);
49897
- const { Fp: Fp2, n: CURVE_ORDER, prehash, hash: cHash, randomBytes: randomBytes4, nByteLength, h: cofactor } = CURVE;
50062
+ const { Fp: Fp2, n: CURVE_ORDER, prehash, hash: cHash, randomBytes: randomBytes5, nByteLength, h: cofactor } = CURVE;
49898
50063
  const MASK = _2n3 << BigInt(nByteLength * 8) - _1n4;
49899
50064
  const modP = Fp2.create;
49900
50065
  const uvRatio2 = CURVE.uvRatio || ((u, v) => {
@@ -50233,7 +50398,7 @@ function twistedEdwards(curveDef) {
50233
50398
  const utils = {
50234
50399
  getExtendedPublicKey,
50235
50400
  // ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
50236
- randomPrivateKey: () => randomBytes4(Fp2.BYTES),
50401
+ randomPrivateKey: () => randomBytes5(Fp2.BYTES),
50237
50402
  /**
50238
50403
  * We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
50239
50404
  * values. This slows down first getPublicKey() by milliseconds (see Speed section),
@@ -50448,7 +50613,7 @@ var ed25519Defaults = {
50448
50613
  Gx: BigInt("15112221349535400772501151409588531511454012693041857206046113283949847762202"),
50449
50614
  Gy: BigInt("46316835694926478169428394003475163141307993866256225615783033603165251855960"),
50450
50615
  hash: sha512,
50451
- randomBytes: randomBytes2,
50616
+ randomBytes: randomBytes3,
50452
50617
  adjustScalarBytes,
50453
50618
  // dom2
50454
50619
  // Ratio of u to v. Allows us to combine inversion and square root. Uses algo from RFC8032 5.1.3.
@@ -50483,7 +50648,7 @@ var x25519 = /* @__PURE__ */ (() => montgomery({
50483
50648
  return mod(pow2(pow_p_5_8, BigInt(3), P) * b2, P);
50484
50649
  },
50485
50650
  adjustScalarBytes,
50486
- randomBytes: randomBytes2
50651
+ randomBytes: randomBytes3
50487
50652
  }))();
50488
50653
  function edwardsToMontgomeryPub(edwardsPub) {
50489
50654
  const { y } = ed25519.ExtendedPoint.fromHex(edwardsPub);
@@ -52292,7 +52457,8 @@ function boundInboxItems(items) {
52292
52457
  kind: it.kind,
52293
52458
  status: it.status,
52294
52459
  body,
52295
- ...it.taskId ? { taskId: it.taskId } : {}
52460
+ ...it.taskId ? { taskId: it.taskId } : {},
52461
+ ...it.kind === "proposal" ? { runnable: isRunnableProposal(it) } : {}
52296
52462
  };
52297
52463
  const size = enc2.encode(JSON.stringify(projected)).length + 1;
52298
52464
  if (bytes3 + size > MAX_INBOX_PAYLOAD_BYTES && out.length > 0) break;
@@ -52780,19 +52946,179 @@ function extractVerificationSection(markdown) {
52780
52946
  return body.length > 0 ? body : null;
52781
52947
  }
52782
52948
 
52949
+ // src/director-reseed.ts
52950
+ var import_node_fs15 = require("node:fs");
52951
+ var import_node_readline = require("node:readline");
52952
+ var import_node_path22 = require("node:path");
52953
+ function estimateTokens(s) {
52954
+ return Buffer.byteLength(s, "utf8");
52955
+ }
52956
+ function dayFilesInWindow(now, windowMs) {
52957
+ const end = new Date(now.getTime());
52958
+ const start = new Date(now.getTime() - windowMs);
52959
+ const startDay = new Date(Date.UTC(
52960
+ start.getUTCFullYear(),
52961
+ start.getUTCMonth(),
52962
+ start.getUTCDate(),
52963
+ 0,
52964
+ 0,
52965
+ 0,
52966
+ 0
52967
+ ));
52968
+ const files = [];
52969
+ for (let cur = new Date(startDay); cur.getTime() <= end.getTime(); cur.setUTCDate(cur.getUTCDate() + 1)) {
52970
+ const name = `bridge-${cur.getUTCFullYear().toString().padStart(4, "0")}-${(cur.getUTCMonth() + 1).toString().padStart(2, "0")}-${cur.getUTCDate().toString().padStart(2, "0")}.jsonl`;
52971
+ files.push(name);
52972
+ }
52973
+ return files;
52974
+ }
52975
+ function isRecord3(value) {
52976
+ return typeof value === "object" && value !== null;
52977
+ }
52978
+ function extractItems(lines2, sessionIds) {
52979
+ const sessionSet = new Set(sessionIds);
52980
+ const items = [];
52981
+ let skipped2 = 0;
52982
+ for (const raw of lines2) {
52983
+ if (raw.trim() === "") {
52984
+ continue;
52985
+ }
52986
+ let parsed;
52987
+ try {
52988
+ parsed = JSON.parse(raw);
52989
+ } catch {
52990
+ skipped2++;
52991
+ continue;
52992
+ }
52993
+ if (!isRecord3(parsed)) {
52994
+ skipped2++;
52995
+ continue;
52996
+ }
52997
+ const obj = parsed;
52998
+ if (!sessionSet.has(obj.sessionId)) continue;
52999
+ const ts = obj.ts;
53000
+ const type = obj.type;
53001
+ const payload = obj.payload;
53002
+ if (type === "turn" && isRecord3(payload) && typeof payload.message === "string") {
53003
+ items.push({ ts, kind: "user", text: payload.message });
53004
+ } else if (type === "event" && isRecord3(payload) && isRecord3(payload.event) && payload.event.type === "assistant" && typeof payload.event.content === "string") {
53005
+ items.push({ ts, kind: "assistant", text: payload.event.content });
53006
+ } else if (type === "event" && isRecord3(payload) && isRecord3(payload.event) && payload.event.type === "tool_use" && isRecord3(payload.event.tool) && typeof payload.event.tool.name === "string") {
53007
+ items.push({ ts, kind: "tool", text: payload.event.tool.name });
53008
+ }
53009
+ }
53010
+ items.sort((a, b) => a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0);
53011
+ return { items, skipped: skipped2 };
53012
+ }
53013
+ function renderReseedBlock(items, opts) {
53014
+ const buildText = (kept2, droppedCount) => {
53015
+ const header = [
53016
+ "[bridge:reconstructed-history] The conversation below is a RECONSTRUCTION assembled",
53017
+ "from this bridge's own logs. It is NOT your memory: you did not run these tools in",
53018
+ "this thread, and nothing here is a live handle.",
53019
+ "- Every external session it mentions is GONE: browser tabs, logins, shell sessions,",
53020
+ " working directories, temporary files.",
53021
+ "- Tool names appear WITHOUT their arguments and WITHOUT their results, and may come",
53022
+ " from a different provider's tool surface than the one you have now. They are",
53023
+ " history, not a menu.",
53024
+ "- Before acting on anything described here, re-verify it against current state."
53025
+ ].join("\n");
53026
+ const firstKeptTs = kept2[0]?.ts;
53027
+ const truncationNotice = droppedCount > 0 && firstKeptTs !== void 0 ? `- TRUNCATED: the oldest ${droppedCount} items were dropped; this reconstruction starts at ${firstKeptTs}.` : "";
53028
+ const roleMap = {
53029
+ user: "OPERATOR",
53030
+ assistant: "DIRECTOR",
53031
+ tool: "TOOL"
53032
+ };
53033
+ const itemsText = kept2.map((it) => `[${it.ts}] ${roleMap[it.kind]}: ${it.text}`).join("\n");
53034
+ const body = [
53035
+ truncationNotice,
53036
+ "--- reconstructed conversation begins ---",
53037
+ itemsText,
53038
+ "--- reconstructed conversation ends ---",
53039
+ "The operator's actual request for THIS turn is the LAST block in this message, below",
53040
+ "any project-context or bridge-notice blocks that follow this one."
53041
+ ].filter((s) => s !== "").join("\n");
53042
+ return `${header}
53043
+ ${body}`;
53044
+ };
53045
+ if (estimateTokens(buildText(items, 0)) <= opts.maxTokens) {
53046
+ return {
53047
+ text: buildText(items, 0),
53048
+ droppedCount: 0,
53049
+ itemCount: items.length
53050
+ };
53051
+ }
53052
+ const kept = [...items];
53053
+ let dropped = 0;
53054
+ const dropOne = () => {
53055
+ const toolIdx = kept.findIndex((it) => it.kind === "tool");
53056
+ kept.splice(toolIdx >= 0 ? toolIdx : 0, 1);
53057
+ };
53058
+ while (kept.length > 0) {
53059
+ dropOne();
53060
+ dropped++;
53061
+ if (kept.length === 0) return null;
53062
+ const candidateText = buildText(kept, dropped);
53063
+ if (estimateTokens(candidateText) <= opts.maxTokens) {
53064
+ const convCount = kept.filter(
53065
+ (it) => it.kind === "user" || it.kind === "assistant"
53066
+ ).length;
53067
+ if (convCount < opts.minConversationalItems) {
53068
+ return null;
53069
+ }
53070
+ return {
53071
+ text: candidateText,
53072
+ droppedCount: dropped,
53073
+ itemCount: kept.length
53074
+ };
53075
+ }
53076
+ }
53077
+ return null;
53078
+ }
53079
+ var NO_HISTORY_BLOCK = "[bridge:no-history] No prior conversation is available for this chat \u2014 it could not be\nreconstructed. Do NOT infer what was discussed before, and do NOT assume any earlier\ntask is still in progress. If the operator's request below refers to prior context,\nsay plainly that the history is unavailable and ask them to restate what they need.";
53080
+ async function readSessionRows(logsDir, files, sessionIds) {
53081
+ const lines2 = [];
53082
+ let missingFiles = 0;
53083
+ for (const f of files) {
53084
+ const filePath = (0, import_node_path22.join)(logsDir, f);
53085
+ try {
53086
+ const stream = (0, import_node_fs15.createReadStream)(filePath, { encoding: "utf8" });
53087
+ const rl = (0, import_node_readline.createInterface)({ input: stream, crlfDelay: Infinity });
53088
+ try {
53089
+ for await (const line of rl) {
53090
+ if (line.trim() === "") continue;
53091
+ if (sessionIds && sessionIds.length > 0 && !sessionIds.some((id) => line.includes(id))) continue;
53092
+ lines2.push(line);
53093
+ }
53094
+ } finally {
53095
+ rl.close();
53096
+ stream.destroy();
53097
+ }
53098
+ } catch (e) {
53099
+ if (isRecord3(e) && e.code === "ENOENT") {
53100
+ missingFiles++;
53101
+ continue;
53102
+ }
53103
+ throw e;
53104
+ }
53105
+ }
53106
+ return { lines: lines2, missingFiles };
53107
+ }
53108
+
52783
53109
  // src/external-agents-scrub.ts
52784
- var import_node_fs15 = __toESM(require("node:fs"), 1);
53110
+ var import_node_fs16 = __toESM(require("node:fs"), 1);
52785
53111
  var import_node_os10 = __toESM(require("node:os"), 1);
52786
- var import_node_path22 = __toESM(require("node:path"), 1);
53112
+ var import_node_path23 = __toESM(require("node:path"), 1);
52787
53113
  var REDACTION = "[REDACTED]";
52788
53114
  var SHORT_VALUE_THRESHOLD = 8;
52789
- var KEYS_ENV_PATH = () => import_node_path22.default.join(import_node_os10.default.homedir(), ".local", "state", "external-agents", "keys.env");
53115
+ var KEYS_ENV_PATH = () => import_node_path23.default.join(import_node_os10.default.homedir(), ".local", "state", "external-agents", "keys.env");
52790
53116
  var keysEnvCache = null;
52791
53117
  function readKeysEnv() {
52792
53118
  const filePath = KEYS_ENV_PATH();
52793
53119
  let mtimeMs = 0;
52794
53120
  try {
52795
- mtimeMs = import_node_fs15.default.statSync(filePath).mtimeMs;
53121
+ mtimeMs = import_node_fs16.default.statSync(filePath).mtimeMs;
52796
53122
  } catch {
52797
53123
  mtimeMs = 0;
52798
53124
  }
@@ -52801,7 +53127,7 @@ function readKeysEnv() {
52801
53127
  }
52802
53128
  const map2 = /* @__PURE__ */ new Map();
52803
53129
  try {
52804
- const content = import_node_fs15.default.readFileSync(filePath, "utf8");
53130
+ const content = import_node_fs16.default.readFileSync(filePath, "utf8");
52805
53131
  for (const rawLine of content.split(/\r?\n/)) {
52806
53132
  const line = rawLine.trim();
52807
53133
  if (line === "" || line.startsWith("#")) continue;
@@ -53219,7 +53545,7 @@ function addBridgeModel(target, model) {
53219
53545
  }
53220
53546
  function readTextIfExists(filePath) {
53221
53547
  try {
53222
- return import_node_fs16.default.readFileSync(filePath, "utf8");
53548
+ return import_node_fs17.default.readFileSync(filePath, "utf8");
53223
53549
  } catch {
53224
53550
  return null;
53225
53551
  }
@@ -53230,9 +53556,9 @@ function readKnownGptSlugsFromTomlFile(filePath) {
53230
53556
  return Array.from(raw.matchAll(/^\s*model\s*=\s*"([^"]+)"/gm)).map((match) => match[1]?.trim() ?? "").filter(isSelectableModelSlug);
53231
53557
  }
53232
53558
  function readCodexFallbackModels() {
53233
- const codexHome = process.env.CODEX_HOME?.trim() || import_node_path23.default.join(import_node_os11.default.homedir(), ".codex");
53559
+ const codexHome = process.env.CODEX_HOME?.trim() || import_node_path24.default.join(import_node_os11.default.homedir(), ".codex");
53234
53560
  const models = /* @__PURE__ */ new Map();
53235
- const cachePath = import_node_path23.default.join(codexHome, "models_cache.json");
53561
+ const cachePath = import_node_path24.default.join(codexHome, "models_cache.json");
53236
53562
  const cacheRaw = readTextIfExists(cachePath);
53237
53563
  if (cacheRaw) {
53238
53564
  try {
@@ -53282,7 +53608,7 @@ function readCodexFallbackModels() {
53282
53608
  );
53283
53609
  }
53284
53610
  }
53285
- for (const slug of readKnownGptSlugsFromTomlFile(import_node_path23.default.join(codexHome, "config.toml"))) {
53611
+ for (const slug of readKnownGptSlugsFromTomlFile(import_node_path24.default.join(codexHome, "config.toml"))) {
53286
53612
  addBridgeModel(
53287
53613
  models,
53288
53614
  mapBridgeModel({
@@ -53296,11 +53622,11 @@ function readCodexFallbackModels() {
53296
53622
  })
53297
53623
  );
53298
53624
  }
53299
- const automationsDir = import_node_path23.default.join(codexHome, "automations");
53625
+ const automationsDir = import_node_path24.default.join(codexHome, "automations");
53300
53626
  try {
53301
- for (const entry of import_node_fs16.default.readdirSync(automationsDir, { withFileTypes: true })) {
53627
+ for (const entry of import_node_fs17.default.readdirSync(automationsDir, { withFileTypes: true })) {
53302
53628
  if (!entry.isDirectory()) continue;
53303
- for (const slug of readKnownGptSlugsFromTomlFile(import_node_path23.default.join(automationsDir, entry.name, "automation.toml"))) {
53629
+ for (const slug of readKnownGptSlugsFromTomlFile(import_node_path24.default.join(automationsDir, entry.name, "automation.toml"))) {
53304
53630
  addBridgeModel(
53305
53631
  models,
53306
53632
  mapBridgeModel({
@@ -53319,10 +53645,16 @@ function readCodexFallbackModels() {
53319
53645
  }
53320
53646
  return [...models.values()];
53321
53647
  }
53648
+ function selfUpdateInboxItemId(offeredVersion) {
53649
+ if (!offeredVersion) return `II-MCPUPD-${(0, import_node_crypto10.randomUUID)()}`;
53650
+ if (/^[A-Za-z0-9._-]+$/.test(offeredVersion)) return `II-MCPUPD-${offeredVersion}`;
53651
+ const hash3 = import_node_crypto10.default.createHash("sha256").update(offeredVersion).digest("hex").slice(0, 10);
53652
+ return `II-MCPUPD-${offeredVersion.replace(/[^A-Za-z0-9._-]/g, "-")}-${hash3}`;
53653
+ }
53322
53654
  function neutralCheckoutCwd(stateDir) {
53323
- const dir = import_node_path23.default.join(stateDir, "no-checkout");
53655
+ const dir = import_node_path24.default.join(stateDir, "no-checkout");
53324
53656
  try {
53325
- import_node_fs16.default.mkdirSync(dir, { recursive: true });
53657
+ import_node_fs17.default.mkdirSync(dir, { recursive: true });
53326
53658
  } catch {
53327
53659
  }
53328
53660
  return dir;
@@ -53752,7 +54084,7 @@ async function runOrphanWorktreeSweep(client, opts) {
53752
54084
  const cutoff = Date.now() - worktreeRetentionMs();
53753
54085
  const isReapable = (worktreePath) => {
53754
54086
  try {
53755
- return import_node_fs16.default.statSync(worktreePath).mtimeMs < cutoff;
54087
+ return import_node_fs17.default.statSync(worktreePath).mtimeMs < cutoff;
53756
54088
  } catch {
53757
54089
  return false;
53758
54090
  }
@@ -53790,8 +54122,8 @@ async function runOrphanWorktreeSweep(client, opts) {
53790
54122
  }
53791
54123
  }
53792
54124
  function readStateDir() {
53793
- if (process.env.CODEX_HOME) return import_node_path23.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge");
53794
- return import_node_path23.default.join(import_node_os11.default.homedir(), ".mrrlin", "director-bridge");
54125
+ if (process.env.CODEX_HOME) return import_node_path24.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge");
54126
+ return import_node_path24.default.join(import_node_os11.default.homedir(), ".mrrlin", "director-bridge");
53795
54127
  }
53796
54128
  function readAllowedOrigins() {
53797
54129
  const raw = (process.env.MRRLIN_DIRECTOR_BRIDGE_ALLOWED_ORIGINS ?? "").trim();
@@ -53866,7 +54198,7 @@ function createBridgeRequestListener(opts) {
53866
54198
  res.end(JSON.stringify({ ok: false, error: "Origin not allowed." }));
53867
54199
  return;
53868
54200
  }
53869
- const nonce = import_node_crypto9.default.randomBytes(16).toString("base64url");
54201
+ const nonce = import_node_crypto10.default.randomBytes(16).toString("base64url");
53870
54202
  res.writeHead(200, { "content-type": "application/json" });
53871
54203
  res.end(JSON.stringify({ ok: true, wsUrl: `ws://${host}:${port}/ws`, token, nonce }));
53872
54204
  return;
@@ -54001,7 +54333,7 @@ function describeZeroOutputLimit(signal) {
54001
54333
  }
54002
54334
  function createBridgeMessageHandler(deps) {
54003
54335
  const tombstones = deps.tombstones ?? /* @__PURE__ */ new Map();
54004
- const pendingTurnSessions = /* @__PURE__ */ new Set();
54336
+ const pendingTurnSessions = deps.pendingTurnSessions ?? /* @__PURE__ */ new Set();
54005
54337
  return (socket, _request, send, logCtx) => {
54006
54338
  const boundSessions = /* @__PURE__ */ new Set();
54007
54339
  socket.on("close", () => {
@@ -54165,7 +54497,7 @@ function createBridgeMessageHandler(deps) {
54165
54497
  }
54166
54498
  }
54167
54499
  socket.on("message", async (data) => {
54168
- const spanId = import_node_crypto9.default.randomUUID();
54500
+ const spanId = import_node_crypto10.default.randomUUID();
54169
54501
  if (logCtx) {
54170
54502
  logCtx.span.id = spanId;
54171
54503
  logCtx.span.at = Date.now();
@@ -54675,6 +55007,7 @@ function createBridgeMessageHandler(deps) {
54675
55007
  const desiredModel = normalizeModel(msg.model);
54676
55008
  const desiredEffort = normalizeReasoningEffort(msg.reasoningEffort);
54677
55009
  const resolvedProviderId = turnSeat.id;
55010
+ let startedFreshThread = false;
54678
55011
  try {
54679
55012
  const startParams = deps.buildStartParams(msg, desiredModel);
54680
55013
  if (!thread) {
@@ -54694,6 +55027,7 @@ function createBridgeMessageHandler(deps) {
54694
55027
  thread = newThread;
54695
55028
  deps.threads.set(directorSessionId, thread);
54696
55029
  deps.persistThreads();
55030
+ startedFreshThread = true;
54697
55031
  } else {
54698
55032
  const existingThread = thread;
54699
55033
  const providerMismatch = existingThread.provider !== null && existingThread.provider !== resolvedProviderId;
@@ -54712,16 +55046,7 @@ function createBridgeMessageHandler(deps) {
54712
55046
  }
54713
55047
  existingThread.threadId = result.threadId;
54714
55048
  existingThread.provider = resolvedProviderId;
54715
- if (!resumed) {
54716
- sendForSpan({
54717
- type: "event",
54718
- directorSessionId,
54719
- event: {
54720
- type: "error",
54721
- content: "Previous Director thread expired; started a fresh local thread for this chat."
54722
- }
54723
- });
54724
- }
55049
+ if (!resumed) startedFreshThread = true;
54725
55050
  existingThread.model = desiredModel;
54726
55051
  existingThread.reasoningEffort = desiredEffort;
54727
55052
  }
@@ -54750,7 +55075,7 @@ function createBridgeMessageHandler(deps) {
54750
55075
  thread.inFlight = true;
54751
55076
  pendingTurnSessions.delete(directorSessionId);
54752
55077
  thread.replay = createReplayBuffer({
54753
- turnId: (0, import_node_crypto9.randomUUID)(),
55078
+ turnId: (0, import_node_crypto10.randomUUID)(),
54754
55079
  spanId,
54755
55080
  ...deps.retentionMs !== void 0 ? { retentionMs: deps.retentionMs } : {},
54756
55081
  ...deps.ackLingerMs !== void 0 ? { ackLingerMs: deps.ackLingerMs } : {}
@@ -54764,6 +55089,16 @@ function createBridgeMessageHandler(deps) {
54764
55089
  directorSessionId,
54765
55090
  turnId: thread.replay.turnId
54766
55091
  });
55092
+ if (startedFreshThread) {
55093
+ sendForSpan({
55094
+ type: "event",
55095
+ directorSessionId,
55096
+ event: {
55097
+ type: "notice",
55098
+ content: "This chat's previous Director thread is gone; continuing on a fresh thread. Earlier context is reconstructed from the bridge log where possible."
55099
+ }
55100
+ });
55101
+ }
54767
55102
  const contextFocusTaskId = msg.context?.focusTaskId?.trim();
54768
55103
  const contextProjectSlug = msg.context?.projectSlug?.trim();
54769
55104
  let answeredQuestions;
@@ -54936,9 +55271,22 @@ function createBridgeMessageHandler(deps) {
54936
55271
  if (updateOffer !== null && updateOfferProjectSlug && deps.fileUpdateInboxItem) {
54937
55272
  void deps.fileUpdateInboxItem(updateOfferProjectSlug);
54938
55273
  }
54939
- const composedPrefix = updateOffer ? contextPrefix ? `${updateOffer}
54940
-
54941
- ${contextPrefix}` : updateOffer : contextPrefix;
55274
+ let reseedPrefix = null;
55275
+ if (startedFreshThread && deps.buildReseedPrefix) {
55276
+ const otherBytes = Buffer.byteLength(contextPrefix ?? "", "utf8") + Buffer.byteLength(updateOffer ?? "", "utf8") + Buffer.byteLength(effectiveText, "utf8");
55277
+ try {
55278
+ reseedPrefix = await deps.buildReseedPrefix({
55279
+ directorSessionId,
55280
+ // Client-supplied, so the elements are validated and not just the shape:
55281
+ // this array selects which sessions' log rows get replayed into a prompt.
55282
+ priorSessionIds: Array.isArray(msg.priorSessionIds) ? msg.priorSessionIds.filter((id) => typeof id === "string" && id.length > 0) : [],
55283
+ otherBytes
55284
+ });
55285
+ } catch {
55286
+ reseedPrefix = null;
55287
+ }
55288
+ }
55289
+ const composedPrefix = [reseedPrefix, contextPrefix, updateOffer].filter((part) => typeof part === "string" && part.length > 0).join("\n\n");
54942
55290
  const composed = composedPrefix ? `${composedPrefix}
54943
55291
 
54944
55292
  ${effectiveText}` : effectiveText;
@@ -55039,6 +55387,7 @@ function sweepIdleThreads(threads, opts) {
55039
55387
  const reaped = [];
55040
55388
  for (const [directorSessionId, thread] of threads.entries()) {
55041
55389
  if (thread.inFlight) continue;
55390
+ if (opts.isPending?.(directorSessionId)) continue;
55042
55391
  if (thread.replay && !shouldGc(thread.replay, opts.now, suspendedNow)) continue;
55043
55392
  if (thread.supersededReplay && !shouldGc(thread.supersededReplay, opts.now, suspendedNow)) continue;
55044
55393
  if (!shouldReapIdleThread(thread, opts.now, suspendedNow, opts.idleThreadTtlMs)) continue;
@@ -55047,6 +55396,25 @@ function sweepIdleThreads(threads, opts) {
55047
55396
  }
55048
55397
  return reaped;
55049
55398
  }
55399
+ function evictOverflowThreads(threads, opts) {
55400
+ if (threads.size <= opts.maxThreads) return [];
55401
+ const suspendedNow = opts.suspendedMs();
55402
+ const evictable = [...threads.entries()].filter(([directorSessionId, thread]) => {
55403
+ if (thread.inFlight) return false;
55404
+ if (opts.isPending?.(directorSessionId)) return false;
55405
+ if (thread.replay && !shouldGc(thread.replay, opts.now, suspendedNow)) return false;
55406
+ if (thread.supersededReplay && !shouldGc(thread.supersededReplay, opts.now, suspendedNow)) return false;
55407
+ return true;
55408
+ }).sort((a, b) => a[1].lastUsedAt - b[1].lastUsedAt);
55409
+ const overflow = threads.size - opts.maxThreads;
55410
+ const evicted = [];
55411
+ for (const [directorSessionId] of evictable) {
55412
+ if (evicted.length >= overflow) break;
55413
+ threads.delete(directorSessionId);
55414
+ evicted.push(directorSessionId);
55415
+ }
55416
+ return evicted;
55417
+ }
55050
55418
  function sweepReplayBuffers(threads, tombstones, opts) {
55051
55419
  const suspendedNow = opts.suspendedMs();
55052
55420
  let retainedBuffers = 0;
@@ -55088,8 +55456,25 @@ function parseConfig() {
55088
55456
  const token = readOrCreateBridgeToken(stateDir);
55089
55457
  const codexCwd = process.env.MRRLIN_DIRECTOR_BRIDGE_CODEX_CWD?.trim() || void 0;
55090
55458
  const codexExecutable = process.env.MRRLIN_DIRECTOR_BRIDGE_CODEX_BIN?.trim() || void 0;
55091
- const idleThreadTtlMs = Math.max(6e4, readEnvInt("MRRLIN_DIRECTOR_BRIDGE_IDLE_TTL_SECONDS", 1800) * 1e3);
55092
- return { host, port, stateDir, allowedOrigins, token, codexCwd, codexExecutable, idleThreadTtlMs };
55459
+ const idleThreadTtlMs = Math.max(6e4, readEnvInt("MRRLIN_DIRECTOR_BRIDGE_IDLE_TTL_SECONDS", 172800) * 1e3);
55460
+ const maxThreads = Math.max(1, readEnvInt("MRRLIN_DIRECTOR_BRIDGE_MAX_THREADS", 128));
55461
+ const reseedMaxTokens = Math.max(1e3, readEnvInt("MRRLIN_DIRECTOR_BRIDGE_RESEED_MAX_TOKENS", 48e3));
55462
+ const turnMaxTokens = Math.max(reseedMaxTokens, readEnvInt("MRRLIN_DIRECTOR_BRIDGE_TURN_MAX_TOKENS", 64e3));
55463
+ const reseedMinItems = Math.max(0, readEnvInt("MRRLIN_DIRECTOR_BRIDGE_RESEED_MIN_ITEMS", 4));
55464
+ return {
55465
+ host,
55466
+ port,
55467
+ stateDir,
55468
+ allowedOrigins,
55469
+ token,
55470
+ codexCwd,
55471
+ codexExecutable,
55472
+ idleThreadTtlMs,
55473
+ maxThreads,
55474
+ reseedMaxTokens,
55475
+ turnMaxTokens,
55476
+ reseedMinItems
55477
+ };
55093
55478
  }
55094
55479
  function scanForOtherBridges() {
55095
55480
  try {
@@ -55102,11 +55487,15 @@ function scanForOtherBridges() {
55102
55487
  return [];
55103
55488
  }
55104
55489
  }
55490
+ function checkTelemetryDefaultProject(env, warn) {
55491
+ if (env.MRRLIN_TELEMETRY_DEFAULT_PROJECT?.trim()) return;
55492
+ warn(
55493
+ "[mrrlin-mcp director-bridge] MRRLIN_TELEMETRY_DEFAULT_PROJECT is not set \u2014 telemetry for turns with no project-scoped tool call will not be uploaded. Set it in the pm2 ecosystem config to enable unattributed telemetry upload."
55494
+ );
55495
+ }
55105
55496
  async function startDirectorBridge() {
55106
55497
  const config2 = parseConfig();
55107
- if (!process.env.MRRLIN_TELEMETRY_DEFAULT_PROJECT?.trim()) {
55108
- throw new Error("MRRLIN_TELEMETRY_DEFAULT_PROJECT must be set (non-empty) at bridge startup.");
55109
- }
55498
+ checkTelemetryDefaultProject(process.env, (m) => console.error(m));
55110
55499
  const turnLock = createTurnLock();
55111
55500
  let lastTurnEndedAt = Date.now();
55112
55501
  const wrappedTurnLock = {
@@ -55243,8 +55632,8 @@ async function startDirectorBridge() {
55243
55632
  codexCwd: config2.codexCwd,
55244
55633
  codexExecutable: config2.codexExecutable,
55245
55634
  checkoutRegistry,
55246
- worktreeRoot: import_node_path23.default.join(config2.stateDir, "worktrees"),
55247
- locksDir: import_node_path23.default.join(config2.stateDir, "locks"),
55635
+ worktreeRoot: import_node_path24.default.join(config2.stateDir, "worktrees"),
55636
+ locksDir: import_node_path24.default.join(config2.stateDir, "locks"),
55248
55637
  // Wire the executor server for code-execution runs. Each run gets its own provider process
55249
55638
  // (app-server for codex, a resident CLI child for claude) at the run worktree, running
55250
55639
  // UNRESTRICTED from the real operator config (owner no-sandbox decision — auth + mrrlin MCP
@@ -55288,13 +55677,13 @@ async function startDirectorBridge() {
55288
55677
  // egressDomains is retained in the seam signature (verification-turn.ts still computes the
55289
55678
  // env host) but no longer constrains anything — the executor is unrestricted.
55290
55679
  startSandbox: async (_egressDomains, providerId) => {
55291
- const cwd = import_node_fs16.default.mkdtempSync(import_node_path23.default.join(import_node_os11.default.tmpdir(), "mrrlin-verify-"));
55680
+ const cwd = import_node_fs17.default.mkdtempSync(import_node_path24.default.join(import_node_os11.default.tmpdir(), "mrrlin-verify-"));
55292
55681
  let executor;
55293
55682
  try {
55294
55683
  executor = await startExecutorServer({ worktree: cwd, provider: providerId });
55295
55684
  } catch (error51) {
55296
55685
  try {
55297
- import_node_fs16.default.rmSync(cwd, { recursive: true, force: true });
55686
+ import_node_fs17.default.rmSync(cwd, { recursive: true, force: true });
55298
55687
  } catch {
55299
55688
  }
55300
55689
  throw error51;
@@ -55305,7 +55694,7 @@ async function startDirectorBridge() {
55305
55694
  dispose: () => {
55306
55695
  executor.dispose();
55307
55696
  try {
55308
- import_node_fs16.default.rmSync(cwd, { recursive: true, force: true });
55697
+ import_node_fs17.default.rmSync(cwd, { recursive: true, force: true });
55309
55698
  } catch {
55310
55699
  }
55311
55700
  }
@@ -55333,7 +55722,7 @@ async function startDirectorBridge() {
55333
55722
  };
55334
55723
  const sessionSinks = createSessionSinkRegistry();
55335
55724
  const tombstones = /* @__PURE__ */ new Map();
55336
- const bridgeInstanceId = (0, import_node_crypto9.randomUUID)();
55725
+ const bridgeInstanceId = (0, import_node_crypto10.randomUUID)();
55337
55726
  const asyncConsumerDeps = {
55338
55727
  client,
55339
55728
  run: wrappedTurnLock.run,
@@ -55450,18 +55839,94 @@ async function startDirectorBridge() {
55450
55839
  );
55451
55840
  }
55452
55841
  };
55842
+ const pendingTurnSessions = /* @__PURE__ */ new Set();
55453
55843
  const cleanupTimer = setInterval(() => {
55844
+ const now = Date.now();
55845
+ const isPending = (directorSessionId) => pendingTurnSessions.has(directorSessionId);
55454
55846
  const reaped = sweepIdleThreads(threads, {
55455
- now: Date.now(),
55847
+ now,
55848
+ suspendedMs: () => suspensionDetector.suspendedMs(),
55849
+ idleThreadTtlMs: config2.idleThreadTtlMs,
55850
+ isPending
55851
+ });
55852
+ const evicted = evictOverflowThreads(threads, {
55853
+ now,
55456
55854
  suspendedMs: () => suspensionDetector.suspendedMs(),
55457
- idleThreadTtlMs: config2.idleThreadTtlMs
55855
+ maxThreads: config2.maxThreads,
55856
+ isPending
55458
55857
  });
55459
- if (reaped.length > 0) persistThreads();
55858
+ for (const sessionId of reaped) {
55859
+ bridgeLogger.logOut({ spanId: "gc", sessionId, type: "<thread-evicted>", payload: { sessionId, reason: "age" } });
55860
+ }
55861
+ for (const sessionId of evicted) {
55862
+ bridgeLogger.logOut({
55863
+ spanId: "gc",
55864
+ sessionId,
55865
+ type: "<thread-evicted>",
55866
+ payload: { sessionId, reason: "count", mapSize: threads.size, maxThreads: config2.maxThreads }
55867
+ });
55868
+ }
55869
+ if (reaped.length > 0 || evicted.length > 0) persistThreads();
55460
55870
  }, 6e4);
55461
55871
  cleanupTimer.unref?.();
55462
55872
  let remoteRelay = null;
55463
55873
  const relayUrl = process.env.MRRLIN_RELAY_URL?.trim();
55464
55874
  const relayConfigured = Boolean(agentSecret && relayUrl);
55875
+ const buildReseedPrefix = async (args) => {
55876
+ const refuse = (reason) => {
55877
+ bridgeLogger.logOut({
55878
+ spanId: "reseed",
55879
+ sessionId: args.directorSessionId,
55880
+ type: "<reseed>",
55881
+ payload: { sessionId: args.directorSessionId, refused: reason }
55882
+ });
55883
+ return NO_HISTORY_BLOCK;
55884
+ };
55885
+ if (!resolveBridgeLogEnabled()) return refuse("log-disabled");
55886
+ if (!resolvePromptsEnabled()) return refuse("prompts-not-logged");
55887
+ const retentionWindowMs = readEnvInt("MRRLIN_LOG_RETENTION_AGE_DAYS", 30) * 864e5;
55888
+ const reseedWindowMs = Math.min(config2.idleThreadTtlMs, retentionWindowMs);
55889
+ if (reseedWindowMs < config2.idleThreadTtlMs) {
55890
+ bridgeLogger.logOut({
55891
+ spanId: "reseed",
55892
+ sessionId: args.directorSessionId,
55893
+ type: "<reseed-window-clamped>",
55894
+ payload: {
55895
+ idleThreadTtlMs: config2.idleThreadTtlMs,
55896
+ retentionWindowMs,
55897
+ effectiveWindowMs: reseedWindowMs
55898
+ }
55899
+ });
55900
+ }
55901
+ const files = dayFilesInWindow(/* @__PURE__ */ new Date(), reseedWindowMs);
55902
+ const sessionIds = [args.directorSessionId, ...args.priorSessionIds];
55903
+ const { lines: lines2, missingFiles } = await readSessionRows(import_node_path24.default.join(config2.stateDir, "logs"), files, sessionIds);
55904
+ const { items, skipped: skipped2 } = extractItems(lines2, sessionIds);
55905
+ if (items.length === 0) return refuse("no-rows");
55906
+ const budget = Math.max(0, Math.min(config2.reseedMaxTokens, config2.turnMaxTokens - args.otherBytes));
55907
+ const block = renderReseedBlock(items, {
55908
+ maxTokens: budget,
55909
+ minConversationalItems: config2.reseedMinItems
55910
+ });
55911
+ if (!block) return refuse("below-minimum-after-truncation");
55912
+ bridgeLogger.logOut({
55913
+ spanId: "reseed",
55914
+ sessionId: args.directorSessionId,
55915
+ type: "<reseed>",
55916
+ payload: {
55917
+ sessionId: args.directorSessionId,
55918
+ priorSessionIds: args.priorSessionIds.length,
55919
+ dayFiles: files.length,
55920
+ missingFiles,
55921
+ parseSkips: skipped2,
55922
+ itemCount: block.itemCount,
55923
+ droppedCount: block.droppedCount,
55924
+ estimatedTokens: Buffer.byteLength(block.text, "utf8"),
55925
+ budget
55926
+ }
55927
+ });
55928
+ return block.text;
55929
+ };
55465
55930
  const sharedHandlerDeps = {
55466
55931
  acquireCodex: (providerId) => agentKeepalive.acquire(providerId),
55467
55932
  releaseCodex: (providerId) => agentKeepalive.release(providerId),
@@ -55490,7 +55955,7 @@ async function startDirectorBridge() {
55490
55955
  fileUpdateInboxItem: async (projectSlug) => {
55491
55956
  const body = selfUpdateHandle.updateInboxBody();
55492
55957
  if (!body) return;
55493
- const id = `II-MCPUPD-${(0, import_node_crypto9.randomUUID)()}`;
55958
+ const id = selfUpdateInboxItemId(selfUpdateHandle.getLatestVersion());
55494
55959
  try {
55495
55960
  const row = await client.createInboxItem(projectSlug, { id, kind: "info", body });
55496
55961
  process.stderr.write(
@@ -55532,13 +55997,21 @@ async function startDirectorBridge() {
55532
55997
  const bridgeLogger = createBridgeLogger(config2.stateDir, process.env);
55533
55998
  let bridgeBinShaShort = "unknown00000";
55534
55999
  try {
55535
- bridgeBinShaShort = import_node_crypto9.default.createHash("sha256").update(import_node_fs16.default.readFileSync(process.argv[1] ?? "")).digest("hex").slice(0, 12);
56000
+ bridgeBinShaShort = import_node_crypto10.default.createHash("sha256").update(import_node_fs17.default.readFileSync(process.argv[1] ?? "")).digest("hex").slice(0, 12);
55536
56001
  } catch {
55537
56002
  }
55538
56003
  attachBridgeWss(server, {
55539
56004
  token: config2.token,
55540
56005
  allowedOrigins: config2.allowedOrigins,
55541
- onReady: createBridgeMessageHandler({ ...sharedHandlerDeps, threads, persistThreads, bridgeInstanceId, tombstones }),
56006
+ onReady: createBridgeMessageHandler({
56007
+ ...sharedHandlerDeps,
56008
+ threads,
56009
+ persistThreads,
56010
+ bridgeInstanceId,
56011
+ tombstones,
56012
+ pendingTurnSessions,
56013
+ buildReseedPrefix
56014
+ }),
55542
56015
  logger: bridgeLogger,
55543
56016
  onPaired: () => health.setPaired(true),
55544
56017
  bridgeInstanceId,
@@ -55659,8 +56132,8 @@ async function startDirectorBridge() {
55659
56132
  );
55660
56133
  void runOrphanWorktreeSweep(client, {
55661
56134
  checkoutRegistry,
55662
- worktreeRoot: import_node_path23.default.join(config2.stateDir, "worktrees"),
55663
- locksDir: import_node_path23.default.join(config2.stateDir, "locks")
56135
+ worktreeRoot: import_node_path24.default.join(config2.stateDir, "worktrees"),
56136
+ locksDir: import_node_path24.default.join(config2.stateDir, "locks")
55664
56137
  });
55665
56138
  {
55666
56139
  const { retainedBuffers, retainedBytes, tombstonesRemoved } = sweepReplayBuffers(threads, tombstones, {
@@ -55710,10 +56183,24 @@ async function startDirectorBridge() {
55710
56183
  codexExecutable: config2.codexExecutable,
55711
56184
  timeoutMs: readEnvInt("MRRLIN_TELEMETRY_GATE_TIMEOUT_SECONDS", 60) * 1e3
55712
56185
  });
55713
- await prune(config2.stateDir, {
56186
+ const pruneResult = await prune(config2.stateDir, {
55714
56187
  ageDays: readEnvInt("MRRLIN_LOG_RETENTION_AGE_DAYS", 30),
55715
- budgetMb: readEnvInt("MRRLIN_LOG_RETENTION_BUDGET_MB", 1024)
56188
+ budgetMb: readEnvInt("MRRLIN_LOG_RETENTION_BUDGET_MB", 1024),
56189
+ // The reconstruction window's day-files are off limits. Derived from the thread
56190
+ // TTL so the two numbers cannot drift apart.
56191
+ protectedSinceMs: Date.now() - config2.idleThreadTtlMs
55716
56192
  });
56193
+ if (pruneResult.budgetOvershootBytes > 0) {
56194
+ bridgeLogger.logOut({
56195
+ spanId: "gc",
56196
+ sessionId: null,
56197
+ type: "<log-budget-overshoot>",
56198
+ payload: {
56199
+ overshootBytes: pruneResult.budgetOvershootBytes,
56200
+ reason: "protected re-seed window not pruned"
56201
+ }
56202
+ });
56203
+ }
55717
56204
  } catch (error51) {
55718
56205
  bridgeLogger.logOut({
55719
56206
  spanId: "gc",
@@ -55943,7 +56430,7 @@ function timingSafeTokenEqual(provided, expected) {
55943
56430
  const a = Buffer.from(provided);
55944
56431
  const b = Buffer.from(expected);
55945
56432
  if (a.length !== b.length) return false;
55946
- return import_node_crypto9.default.timingSafeEqual(a, b);
56433
+ return import_node_crypto10.default.timingSafeEqual(a, b);
55947
56434
  }
55948
56435
  function readDispatchBody(req) {
55949
56436
  return new Promise((resolve, reject) => {
@@ -55965,15 +56452,15 @@ function readDispatchBody(req) {
55965
56452
  function readOrCreateBridgeToken(stateDir) {
55966
56453
  const explicit = (process.env.MRRLIN_DIRECTOR_BRIDGE_TOKEN ?? "").trim();
55967
56454
  if (explicit) return explicit;
55968
- const tokenPath = import_node_path23.default.join(stateDir, "token.txt");
56455
+ const tokenPath = import_node_path24.default.join(stateDir, "token.txt");
55969
56456
  try {
55970
- const existing = import_node_fs16.default.readFileSync(tokenPath, "utf8").trim();
56457
+ const existing = import_node_fs17.default.readFileSync(tokenPath, "utf8").trim();
55971
56458
  if (existing) return existing;
55972
56459
  } catch {
55973
56460
  }
55974
- import_node_fs16.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
55975
- const token = import_node_crypto9.default.randomBytes(32).toString("base64url");
55976
- import_node_fs16.default.writeFileSync(tokenPath, `${token}
56461
+ import_node_fs17.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
56462
+ const token = import_node_crypto10.default.randomBytes(32).toString("base64url");
56463
+ import_node_fs17.default.writeFileSync(tokenPath, `${token}
55977
56464
  `, { mode: 384 });
55978
56465
  return token;
55979
56466
  }
@@ -59357,17 +59844,17 @@ var StdioServerTransport = class {
59357
59844
  };
59358
59845
 
59359
59846
  // src/tools.ts
59360
- var import_node_crypto12 = require("node:crypto");
59847
+ var import_node_crypto13 = require("node:crypto");
59361
59848
  var import_promises10 = require("node:fs/promises");
59362
- var import_node_path26 = __toESM(require("node:path"), 1);
59849
+ var import_node_path27 = __toESM(require("node:path"), 1);
59363
59850
  var import_node_os12 = __toESM(require("node:os"), 1);
59364
59851
 
59365
59852
  // ../../packages/wiki/dist/index.js
59366
59853
  var import_promises8 = __toESM(require("node:fs/promises"), 1);
59367
- var import_node_path24 = __toESM(require("node:path"), 1);
59854
+ var import_node_path25 = __toESM(require("node:path"), 1);
59368
59855
  var INCLUDED_DOC_FOLDERS = /* @__PURE__ */ new Set(["_meta", "explanation", "how-to", "reference", "sdd", "tutorials"]);
59369
59856
  function toPosixPath(input) {
59370
- return input.split(import_node_path24.default.sep).join("/");
59857
+ return input.split(import_node_path25.default.sep).join("/");
59371
59858
  }
59372
59859
  function slugifyWikiSegment(input) {
59373
59860
  return input.trim().toLowerCase().replace(/`/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
@@ -59381,12 +59868,12 @@ async function pathExists3(input) {
59381
59868
  }
59382
59869
  }
59383
59870
  async function findWorkspaceRoot(start = process.cwd()) {
59384
- let current2 = import_node_path24.default.resolve(start);
59871
+ let current2 = import_node_path25.default.resolve(start);
59385
59872
  while (true) {
59386
- if (await pathExists3(import_node_path24.default.join(current2, "pnpm-workspace.yaml")) && await pathExists3(import_node_path24.default.join(current2, "docs"))) {
59873
+ if (await pathExists3(import_node_path25.default.join(current2, "pnpm-workspace.yaml")) && await pathExists3(import_node_path25.default.join(current2, "docs"))) {
59387
59874
  return current2;
59388
59875
  }
59389
- const parent = import_node_path24.default.dirname(current2);
59876
+ const parent = import_node_path25.default.dirname(current2);
59390
59877
  if (parent === current2) {
59391
59878
  throw new Error(`Unable to find Mrrlin workspace root from ${start}`);
59392
59879
  }
@@ -59395,28 +59882,28 @@ async function findWorkspaceRoot(start = process.cwd()) {
59395
59882
  }
59396
59883
  async function resolveDocsRoot(input) {
59397
59884
  if (input) {
59398
- return import_node_path24.default.resolve(input);
59885
+ return import_node_path25.default.resolve(input);
59399
59886
  }
59400
59887
  if (process.env.MRRLIN_DOCS_ROOT) {
59401
- return import_node_path24.default.resolve(process.env.MRRLIN_DOCS_ROOT);
59888
+ return import_node_path25.default.resolve(process.env.MRRLIN_DOCS_ROOT);
59402
59889
  }
59403
- return import_node_path24.default.join(await findWorkspaceRoot(), "docs");
59890
+ return import_node_path25.default.join(await findWorkspaceRoot(), "docs");
59404
59891
  }
59405
59892
  async function resolveIndexPath(input) {
59406
59893
  if (input) {
59407
- return import_node_path24.default.resolve(input);
59894
+ return import_node_path25.default.resolve(input);
59408
59895
  }
59409
59896
  if (process.env.MRRLIN_WIKI_INDEX_PATH) {
59410
- return import_node_path24.default.resolve(process.env.MRRLIN_WIKI_INDEX_PATH);
59897
+ return import_node_path25.default.resolve(process.env.MRRLIN_WIKI_INDEX_PATH);
59411
59898
  }
59412
59899
  const workspaceRoot = await findWorkspaceRoot();
59413
- return import_node_path24.default.join(workspaceRoot, "apps", "web", "public", "wiki-index.json");
59900
+ return import_node_path25.default.join(workspaceRoot, "apps", "web", "public", "wiki-index.json");
59414
59901
  }
59415
59902
  async function walkMarkdownFiles(root, dir = root) {
59416
59903
  const entries = await import_promises8.default.readdir(dir, { withFileTypes: true });
59417
59904
  const files = [];
59418
59905
  for (const entry of entries) {
59419
- const absolute = import_node_path24.default.join(dir, entry.name);
59906
+ const absolute = import_node_path25.default.join(dir, entry.name);
59420
59907
  if (entry.isDirectory()) {
59421
59908
  files.push(...await walkMarkdownFiles(root, absolute));
59422
59909
  continue;
@@ -59435,7 +59922,7 @@ function parseTitle(markdown, sourcePath) {
59435
59922
  return title;
59436
59923
  }
59437
59924
  function documentFromFile(docsRoot, absolutePath, markdown) {
59438
- const relativePath = toPosixPath(import_node_path24.default.relative(docsRoot, absolutePath));
59925
+ const relativePath = toPosixPath(import_node_path25.default.relative(docsRoot, absolutePath));
59439
59926
  const [section, fileName] = relativePath.split("/");
59440
59927
  if (!section || !fileName || !INCLUDED_DOC_FOLDERS.has(section)) {
59441
59928
  return null;
@@ -60047,7 +60534,7 @@ async function resolveActiveRunHead(input, deps) {
60047
60534
  }
60048
60535
 
60049
60536
  // src/async-tools/consensus.ts
60050
- var import_node_crypto10 = require("node:crypto");
60537
+ var import_node_crypto11 = require("node:crypto");
60051
60538
 
60052
60539
  // src/consensus/question-similarity.ts
60053
60540
  var SUPPRESSION_SIMILARITY_THRESHOLD = 0.6;
@@ -60323,9 +60810,9 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
60323
60810
  }
60324
60811
  if (taskId) {
60325
60812
  const scopeId = taskId;
60326
- const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto10.createHash)("sha256").update(toFile.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
60813
+ const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto11.createHash)("sha256").update(toFile.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
60327
60814
  for (const clarification of toFile) {
60328
- const id = `II-${scopeId}-gate-${(0, import_node_crypto10.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
60815
+ const id = `II-${scopeId}-gate-${(0, import_node_crypto11.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
60329
60816
  try {
60330
60817
  const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
60331
60818
  await ctx.client.createInboxItem(input.projectSlug, {
@@ -60343,9 +60830,9 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
60343
60830
  filedQuestionCount = toFile.length;
60344
60831
  } else if (planId) {
60345
60832
  const scopeId = planId;
60346
- const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto10.createHash)("sha256").update(toFile.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
60833
+ const groupId = toFile.length > 1 ? `II-${scopeId}-gate-group-${(0, import_node_crypto11.createHash)("sha256").update(toFile.map((c) => c.rawQuestion).sort().join("\n")).digest("hex").slice(0, 10)}` : void 0;
60347
60834
  for (const clarification of toFile) {
60348
- const id = `II-${scopeId}-gate-${(0, import_node_crypto10.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
60835
+ const id = `II-${scopeId}-gate-${(0, import_node_crypto11.createHash)("sha256").update(clarification.rawQuestion).digest("hex").slice(0, 10)}`;
60349
60836
  try {
60350
60837
  const questionOptions = buildGateQuestionOptions(clarification.rawQuestion, clarification.category);
60351
60838
  await ctx.client.createInboxItem(input.projectSlug, {
@@ -60369,7 +60856,7 @@ async function handleConsensusBlocked(result, ctx, input, resolvedTaskId) {
60369
60856
  taskId: page.taskId ?? null
60370
60857
  });
60371
60858
  const reviewHash = domainScopedReviewHash2(page.contentHash, domain2);
60372
- const baseBlockItemId = `II-${taskId}-gateblock-${(0, import_node_crypto10.createHash)("sha256").update(reviewHash).digest("hex").slice(0, 10)}`;
60859
+ const baseBlockItemId = `II-${taskId}-gateblock-${(0, import_node_crypto11.createHash)("sha256").update(reviewHash).digest("hex").slice(0, 10)}`;
60373
60860
  const reviewAgainAvailable = !taskInboxItems.some(
60374
60861
  (item) => item.id === baseBlockItemId && item.status !== "pending" && item.answer === REVIEW_AGAIN_ANSWER
60375
60862
  );
@@ -60941,17 +61428,17 @@ var intentCheckDescriptor = createIntentCheckDescriptor();
60941
61428
 
60942
61429
  // src/evidence/assembler.ts
60943
61430
  var import_promises9 = require("node:fs/promises");
60944
- var import_node_path25 = require("node:path");
61431
+ var import_node_path26 = require("node:path");
60945
61432
  function evidenceDedupKey(sessionId, reportId) {
60946
61433
  return `${sessionId}:${reportId}`;
60947
61434
  }
60948
- function isRecord3(v) {
61435
+ function isRecord4(v) {
60949
61436
  return v !== null && typeof v === "object";
60950
61437
  }
60951
61438
  async function readDayLines(stateDir, day, sessionId) {
60952
61439
  let raw;
60953
61440
  try {
60954
- raw = await (0, import_promises9.readFile)((0, import_node_path25.join)(stateDir, "logs", `bridge-${day}.jsonl`), "utf8");
61441
+ raw = await (0, import_promises9.readFile)((0, import_node_path26.join)(stateDir, "logs", `bridge-${day}.jsonl`), "utf8");
60955
61442
  } catch {
60956
61443
  return [];
60957
61444
  }
@@ -60964,7 +61451,7 @@ async function readDayLines(stateDir, day, sessionId) {
60964
61451
  } catch {
60965
61452
  continue;
60966
61453
  }
60967
- if (!isRecord3(rec) || !isRecord3(rec.payload)) continue;
61454
+ if (!isRecord4(rec) || !isRecord4(rec.payload)) continue;
60968
61455
  if (rec.payload.directorSessionId !== sessionId) continue;
60969
61456
  kept.push(rec);
60970
61457
  }
@@ -61025,7 +61512,7 @@ async function assembleEvidence(stateDir, sessionId, reportId, gateCtx) {
61025
61512
  }
61026
61513
  let windowPruned = false;
61027
61514
  try {
61028
- const raw = await (0, import_promises9.readFile)((0, import_node_path25.join)(stateDir, "logs", ".pruned.jsonl"), "utf8");
61515
+ const raw = await (0, import_promises9.readFile)((0, import_node_path26.join)(stateDir, "logs", ".pruned.jsonl"), "utf8");
61029
61516
  outer: for (const line of raw.split("\n")) {
61030
61517
  if (!line.trim()) continue;
61031
61518
  let rec;
@@ -61034,7 +61521,7 @@ async function assembleEvidence(stateDir, sessionId, reportId, gateCtx) {
61034
61521
  } catch {
61035
61522
  continue;
61036
61523
  }
61037
- if (!isRecord3(rec) || !Array.isArray(rec.daysRemoved)) continue;
61524
+ if (!isRecord4(rec) || !Array.isArray(rec.daysRemoved)) continue;
61038
61525
  for (const removedDay of rec.daysRemoved) {
61039
61526
  if (daysRead.includes(removedDay)) {
61040
61527
  windowPruned = true;
@@ -61091,7 +61578,7 @@ function createReportEvidenceDescriptor() {
61091
61578
  var reportEvidenceDescriptor = createReportEvidenceDescriptor();
61092
61579
 
61093
61580
  // src/async-tools/enqueue.ts
61094
- var import_node_crypto11 = __toESM(require("node:crypto"), 1);
61581
+ var import_node_crypto12 = __toESM(require("node:crypto"), 1);
61095
61582
  async function enqueueAsyncTool(client, descriptor2, input) {
61096
61583
  if (descriptor2.cachedResult) {
61097
61584
  const cached2 = await descriptor2.cachedResult(client, input);
@@ -61104,7 +61591,7 @@ async function enqueueAsyncTool(client, descriptor2, input) {
61104
61591
  if (active) {
61105
61592
  return { status: "already_running", jobId: active.id, message: "Already running; will notify in inbox." };
61106
61593
  }
61107
- const jobId = `AJ-${import_node_crypto11.default.randomUUID()}`;
61594
+ const jobId = `AJ-${import_node_crypto12.default.randomUUID()}`;
61108
61595
  try {
61109
61596
  await client.createAsyncJob(input.projectSlug, {
61110
61597
  id: jobId,
@@ -62684,7 +63171,7 @@ function createMrrlinTools(options) {
62684
63171
  "PRODUCED_URL_RECORD_FAILED",
62685
63172
  "Unable to record produced URL.",
62686
63173
  async (c, { projectSlug, runId, url: url2, label }) => {
62687
- const id = `EA-pu-${(0, import_node_crypto12.randomBytes)(4).toString("hex")}`;
63174
+ const id = `EA-pu-${(0, import_node_crypto13.randomBytes)(4).toString("hex")}`;
62688
63175
  const payload = label ? { url: url2, label } : { url: url2 };
62689
63176
  return await c.appendExecutionArtifact(projectSlug, runId, {
62690
63177
  id,
@@ -62758,7 +63245,7 @@ function createMrrlinTools(options) {
62758
63245
  "ARTIFACT_FILE_UPLOAD_FAILED",
62759
63246
  "Unable to upload artifact file.",
62760
63247
  async (c, { projectSlug, path: filePath, class: artifactClass, taskId, runId, description }) => {
62761
- const extension2 = import_node_path26.default.extname(filePath).toLowerCase();
63248
+ const extension2 = import_node_path27.default.extname(filePath).toLowerCase();
62762
63249
  const contentType = ARTIFACT_EXTENSION_TYPES[extension2];
62763
63250
  if (!contentType) {
62764
63251
  throw new McpToolCodedError(
@@ -62786,7 +63273,7 @@ function createMrrlinTools(options) {
62786
63273
  "Artifact exceeds 5MB; split or summarize instead."
62787
63274
  );
62788
63275
  }
62789
- const filename = import_node_path26.default.basename(filePath);
63276
+ const filename = import_node_path27.default.basename(filePath);
62790
63277
  return uploadArtifactBytes(c, {
62791
63278
  bytes: bytes3,
62792
63279
  class: artifactClass,
@@ -62811,7 +63298,7 @@ function createMrrlinTools(options) {
62811
63298
  `Invalid filename '${filename}'; provide a bare filename with no path separators or '..'.`
62812
63299
  );
62813
63300
  }
62814
- const extension2 = import_node_path26.default.extname(filename).toLowerCase();
63301
+ const extension2 = import_node_path27.default.extname(filename).toLowerCase();
62815
63302
  const contentType = ARTIFACT_EXTENSION_TYPES[extension2];
62816
63303
  if (!contentType) {
62817
63304
  throw new McpToolCodedError(
@@ -62878,7 +63365,7 @@ function createMrrlinTools(options) {
62878
63365
  "LOCAL_CHECKOUT_REGISTER_FAILED",
62879
63366
  "Unable to register local checkout.",
62880
63367
  async (_c, { projectSlug, path: checkoutPath, repo }) => {
62881
- const stateDir = process.env.CODEX_HOME ? import_node_path26.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge") : import_node_path26.default.join(import_node_os12.default.homedir(), ".mrrlin", "director-bridge");
63368
+ const stateDir = process.env.CODEX_HOME ? import_node_path27.default.join(process.env.CODEX_HOME, "mrrlin", "director-bridge") : import_node_path27.default.join(import_node_os12.default.homedir(), ".mrrlin", "director-bridge");
62882
63369
  const registry3 = new CheckoutRegistry(stateDir);
62883
63370
  const result = registerCheckout(registry3, { projectSlug, path: checkoutPath, repo }, import_node_os12.default.hostname());
62884
63371
  if (!result.ok) {
@@ -63118,7 +63605,7 @@ function runSetCredential(args) {
63118
63605
 
63119
63606
  // src/install-service.ts
63120
63607
  var import_node_os13 = __toESM(require("node:os"), 1);
63121
- var import_node_path27 = __toESM(require("node:path"), 1);
63608
+ var import_node_path28 = __toESM(require("node:path"), 1);
63122
63609
  function installService(deps) {
63123
63610
  if (!deps.hasSecret()) {
63124
63611
  deps.log("No agent credential found. Issue one in Settings, then run: mrrlin-mcp set-credential '<secret>'.");
@@ -63140,7 +63627,7 @@ function installService(deps) {
63140
63627
  return { ok: false };
63141
63628
  }
63142
63629
  const resolvedBins = bins.filter((b) => b !== null);
63143
- const pathEnv = Array.from(new Set(resolvedBins.map((b) => import_node_path27.default.dirname(b)))).join(":");
63630
+ const pathEnv = Array.from(new Set(resolvedBins.map((b) => import_node_path28.default.dirname(b)))).join(":");
63144
63631
  const apiBaseUrl = deps.env.MRRLIN_API_BASE_URL?.trim() || void 0;
63145
63632
  const staging = deps.env.MRRLIN_STAGING === "1" || !apiBaseUrl && bridgeUrlImpliesStaging(deps.env.MRRLIN_BRIDGE_URL);
63146
63633
  const text = renderEcosystemConfig({
@@ -63151,7 +63638,7 @@ function installService(deps) {
63151
63638
  staging,
63152
63639
  apiBaseUrl
63153
63640
  });
63154
- const ecoPath = import_node_path27.default.join(home, ".mrrlin", "ecosystem.config.cjs");
63641
+ const ecoPath = import_node_path28.default.join(home, ".mrrlin", "ecosystem.config.cjs");
63155
63642
  deps.writeFile(ecoPath, text);
63156
63643
  const start = deps.runPm2(["startOrReload", ecoPath]);
63157
63644
  if (start.code !== 0) {
@@ -63163,8 +63650,29 @@ function installService(deps) {
63163
63650
  deps.log(`pm2 save failed (exit ${save.code}).`);
63164
63651
  return { ok: false };
63165
63652
  }
63166
- deps.log(`Service installed (${ecoPath}). For reboot persistence run the printed 'pm2 startup' line ONCE, then reboot and verify 'pm2 status' shows mrrlin-bridge online. If pm2 startup does not stick on macOS, see the runbook's launchd-plist fallback.`);
63653
+ deps.log(`Service installed (${ecoPath}).`);
63167
63654
  deps.runPm2(["startup"]);
63655
+ const hook = deps.bootHookStatus?.();
63656
+ switch (hook?.status) {
63657
+ case void 0:
63658
+ break;
63659
+ case "configured":
63660
+ deps.log(`Boot hook registered (${hook.label}); last run clean.`);
63661
+ break;
63662
+ case "registered_failing":
63663
+ deps.log(
63664
+ `Boot hook registered (${hook.label}) but FAILING - last exit code ${hook.lastExitCode}. The bridge will NOT come back after a reboot. See docs/how-to/run-director-bridge.md.`
63665
+ );
63666
+ break;
63667
+ case "not_configured":
63668
+ deps.log(
63669
+ "No boot hook registered - the bridge will NOT restart after a reboot. See docs/how-to/run-director-bridge.md."
63670
+ );
63671
+ break;
63672
+ case "unknown":
63673
+ deps.log("Boot-hook status not checked on this platform.");
63674
+ break;
63675
+ }
63168
63676
  return { ok: true };
63169
63677
  }
63170
63678
  function uninstallService(deps) {
@@ -63183,12 +63691,12 @@ function uninstallService(deps) {
63183
63691
  // src/uninstall-codex.ts
63184
63692
  var import_promises11 = __toESM(require("node:fs/promises"), 1);
63185
63693
  var import_node_os14 = __toESM(require("node:os"), 1);
63186
- var import_node_path28 = __toESM(require("node:path"), 1);
63694
+ var import_node_path29 = __toESM(require("node:path"), 1);
63187
63695
  var toml3 = __toESM(require_toml(), 1);
63188
63696
  function resolveCodexHome3(options) {
63189
63697
  if (options.codexHome) return options.codexHome;
63190
63698
  if (process.env.CODEX_HOME) return process.env.CODEX_HOME;
63191
- return import_node_path28.default.join(options.homeDir ?? import_node_os14.default.homedir(), ".codex");
63699
+ return import_node_path29.default.join(options.homeDir ?? import_node_os14.default.homedir(), ".codex");
63192
63700
  }
63193
63701
  async function pathExists4(target) {
63194
63702
  try {
@@ -63201,7 +63709,7 @@ async function pathExists4(target) {
63201
63709
  var MRRLIN_BLOCK_RE = /(^|\n)\[mcp_servers\.(?:mrrlin(?:-browser)?|external-agents)(?:\]|\.[^\]\n]*\])[\s\S]*?(?=\n\[|$)/g;
63202
63710
  async function uninstallCodex(options = {}) {
63203
63711
  const codexHome = resolveCodexHome3(options);
63204
- const configPath = import_node_path28.default.join(codexHome, "config.toml");
63712
+ const configPath = import_node_path29.default.join(codexHome, "config.toml");
63205
63713
  const removePrompts = async () => uninstallPrompts(codexHome, options.promptNames ?? []);
63206
63714
  if (!await pathExists4(configPath)) {
63207
63715
  return { action: "missing", configPath, prompts: await removePrompts() };
@@ -63229,10 +63737,10 @@ async function uninstallCodex(options = {}) {
63229
63737
  }
63230
63738
  async function uninstallPrompts(codexHome, names) {
63231
63739
  if (names.length === 0) return [];
63232
- const promptsDir = import_node_path28.default.join(codexHome, "prompts");
63740
+ const promptsDir = import_node_path29.default.join(codexHome, "prompts");
63233
63741
  const out = [];
63234
63742
  for (const name of names) {
63235
- const promptPath = import_node_path28.default.join(promptsDir, `${name}.md`);
63743
+ const promptPath = import_node_path29.default.join(promptsDir, `${name}.md`);
63236
63744
  try {
63237
63745
  await import_promises11.default.unlink(promptPath);
63238
63746
  out.push({ name, action: "removed", promptPath });
@@ -63250,7 +63758,7 @@ async function uninstallPrompts(codexHome, names) {
63250
63758
 
63251
63759
  // src/extract/command.ts
63252
63760
  var import_promises12 = require("node:fs/promises");
63253
- var import_node_path29 = require("node:path");
63761
+ var import_node_path30 = require("node:path");
63254
63762
  function parseArgs(args) {
63255
63763
  const map2 = /* @__PURE__ */ new Map();
63256
63764
  for (let i = 0; i < args.length; i++) {
@@ -63266,7 +63774,7 @@ function parseArgs(args) {
63266
63774
  }
63267
63775
  return map2;
63268
63776
  }
63269
- function isRecord4(v) {
63777
+ function isRecord5(v) {
63270
63778
  return v !== null && typeof v === "object";
63271
63779
  }
63272
63780
  async function runExtract(args, deps) {
@@ -63287,7 +63795,7 @@ Missing required flag(s): ${missing.join(
63287
63795
  const days = await findSessionDays(deps.stateDir, sessionId);
63288
63796
  const matchingLines = [];
63289
63797
  for (const day of days) {
63290
- const logPath = (0, import_node_path29.join)(deps.stateDir, "logs", `bridge-${day}.jsonl`);
63798
+ const logPath = (0, import_node_path30.join)(deps.stateDir, "logs", `bridge-${day}.jsonl`);
63291
63799
  let raw;
63292
63800
  try {
63293
63801
  raw = await (0, import_promises12.readFile)(logPath, "utf8");
@@ -63303,8 +63811,8 @@ Missing required flag(s): ${missing.join(
63303
63811
  } catch {
63304
63812
  continue;
63305
63813
  }
63306
- const payload = isRecord4(parsed) ? parsed.payload : void 0;
63307
- if (isRecord4(payload) && payload.directorSessionId === sessionId) {
63814
+ const payload = isRecord5(parsed) ? parsed.payload : void 0;
63815
+ if (isRecord5(payload) && payload.directorSessionId === sessionId) {
63308
63816
  matchingLines.push(line);
63309
63817
  }
63310
63818
  }
@@ -63383,6 +63891,29 @@ function whichBin(bin) {
63383
63891
  const out = (r.stdout ?? "").trim();
63384
63892
  return r.status === 0 && out ? out.split("\n")[0] ?? null : null;
63385
63893
  }
63894
+ function bootHookStatus() {
63895
+ if (process.platform !== "darwin") return { status: "unknown" };
63896
+ const uid = process.getuid?.();
63897
+ if (uid === void 0 || uid === 0) return { status: "unknown" };
63898
+ const home = process.env.HOME ?? (0, import_node_os15.homedir)();
63899
+ const agents = import_node_path32.default.join(home, "Library", "LaunchAgents");
63900
+ const candidates = [
63901
+ { label: "com.mrrlin.director-bridge", plist: import_node_path32.default.join(agents, "com.mrrlin.director-bridge.plist") },
63902
+ { label: "com.PM2", plist: import_node_path32.default.join(agents, `pm2.${(0, import_node_os15.userInfo)().username}.plist`) }
63903
+ ];
63904
+ for (const candidate of candidates) {
63905
+ const printed = (0, import_node_child_process18.spawnSync)("launchctl", ["print", `gui/${uid}/${candidate.label}`], { encoding: "utf8" });
63906
+ if (printed.status !== 0) continue;
63907
+ if (!(0, import_node_fs18.existsSync)(candidate.plist)) continue;
63908
+ const out = printed.stdout ?? "";
63909
+ const runs = /^\s*runs\s*=\s*(\d+)/m.exec(out);
63910
+ const exitCode = /^\s*last exit code\s*=\s*(-?\d+)/m.exec(out);
63911
+ if (!exitCode || runs?.[1] === "0") return { status: "configured", label: candidate.label };
63912
+ const code = Number(exitCode[1]);
63913
+ return code === 0 ? { status: "configured", label: candidate.label } : { status: "registered_failing", label: candidate.label, lastExitCode: code };
63914
+ }
63915
+ return { status: "not_configured" };
63916
+ }
63386
63917
  var HELP_TEXT = `mrrlin-mcp - Mrrlin Phase 1 MCP server CLI
63387
63918
 
63388
63919
  Usage:
@@ -63629,8 +64160,8 @@ async function main() {
63629
64160
  }
63630
64161
  }
63631
64162
  try {
63632
- const codexHome = process.env.CODEX_HOME ?? import_node_path31.default.join(process.env.HOME ?? (0, import_node_os15.homedir)(), ".codex");
63633
- (0, import_node_fs17.rmSync)(import_node_path31.default.join(codexHome, "prompts", "report-issue.md"), { force: true });
64163
+ const codexHome = process.env.CODEX_HOME ?? import_node_path32.default.join(process.env.HOME ?? (0, import_node_os15.homedir)(), ".codex");
64164
+ (0, import_node_fs18.rmSync)(import_node_path32.default.join(codexHome, "prompts", "report-issue.md"), { force: true });
63634
64165
  } catch {
63635
64166
  }
63636
64167
  const claudeResult = await installClaude({ binPath, force, serveEnv });
@@ -63691,12 +64222,13 @@ async function main() {
63691
64222
  env: process.env,
63692
64223
  cwd: process.cwd(),
63693
64224
  writeFile: (p, c) => {
63694
- (0, import_node_fs17.mkdirSync)(import_node_path31.default.dirname(p), { recursive: true, mode: 448 });
63695
- (0, import_node_fs17.writeFileSync)(p, c, { mode: 384 });
64225
+ (0, import_node_fs18.mkdirSync)(import_node_path32.default.dirname(p), { recursive: true, mode: 448 });
64226
+ (0, import_node_fs18.writeFileSync)(p, c, { mode: 384 });
63696
64227
  },
63697
64228
  runPm2: pm2Runner,
63698
64229
  which: whichBin,
63699
64230
  hasSecret: () => Boolean(readAgentSecret()),
64231
+ bootHookStatus,
63700
64232
  log: (m) => process.stderr.write(`[mrrlin-mcp install-service] ${m}
63701
64233
  `)
63702
64234
  }).ok;
@@ -63709,14 +64241,14 @@ async function main() {
63709
64241
  runPm2: (a) => ({ code: pm2Runner(a).code }),
63710
64242
  removeFile: (p) => {
63711
64243
  try {
63712
- (0, import_node_fs17.rmSync)(p, { force: true });
64244
+ (0, import_node_fs18.rmSync)(p, { force: true });
63713
64245
  } catch {
63714
64246
  }
63715
64247
  },
63716
64248
  purgeSecret: purge,
63717
64249
  secretPath: agentCredentialPath(),
63718
64250
  tokenPath: operatorTokenPath(),
63719
- ecoPath: import_node_path31.default.join(process.env.HOME ?? (0, import_node_os15.homedir)(), ".mrrlin", "ecosystem.config.cjs"),
64251
+ ecoPath: import_node_path32.default.join(process.env.HOME ?? (0, import_node_os15.homedir)(), ".mrrlin", "ecosystem.config.cjs"),
63720
64252
  log: (m) => process.stderr.write(`[mrrlin-mcp uninstall-service] ${m}
63721
64253
  `)
63722
64254
  });
@@ -63730,14 +64262,14 @@ async function main() {
63730
64262
  runPm2: (a) => ({ code: pm2Runner(a).code }),
63731
64263
  removeFile: (p) => {
63732
64264
  try {
63733
- (0, import_node_fs17.rmSync)(p, { force: true });
64265
+ (0, import_node_fs18.rmSync)(p, { force: true });
63734
64266
  } catch {
63735
64267
  }
63736
64268
  },
63737
64269
  purgeSecret: true,
63738
64270
  secretPath: agentCredentialPath(),
63739
64271
  tokenPath: operatorTokenPath(),
63740
- ecoPath: import_node_path31.default.join(home, ".mrrlin", "ecosystem.config.cjs"),
64272
+ ecoPath: import_node_path32.default.join(home, ".mrrlin", "ecosystem.config.cjs"),
63741
64273
  log
63742
64274
  });
63743
64275
  let codexOk = true;
@@ -63757,7 +64289,7 @@ async function main() {
63757
64289
  log(`codex config NOT modified: ${error51 instanceof Error ? error51.message : String(error51)}`);
63758
64290
  }
63759
64291
  try {
63760
- (0, import_node_fs17.rmSync)(import_node_path31.default.join(home, ".mrrlin"), { recursive: true, force: true });
64292
+ (0, import_node_fs18.rmSync)(import_node_path32.default.join(home, ".mrrlin"), { recursive: true, force: true });
63761
64293
  } catch {
63762
64294
  }
63763
64295
  log("removed ~/.mrrlin");
@@ -63798,7 +64330,7 @@ ${HELP_TEXT}`);
63798
64330
  }
63799
64331
  }
63800
64332
  function resolveSelfBinPath() {
63801
- return import_node_path30.default.resolve(process.argv[1] ?? process.execPath);
64333
+ return import_node_path31.default.resolve(process.argv[1] ?? process.execPath);
63802
64334
  }
63803
64335
  main().catch((error51) => {
63804
64336
  process.stderr.write(`mrrlin-mcp fatal error: ${error51 instanceof Error ? error51.message : String(error51)}