@mrrlin-dev/mcp 0.4.8 → 0.4.10

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 +198 -5
  2. package/package.json +3 -3
package/dist/bin.cjs CHANGED
@@ -37682,7 +37682,16 @@ var mrrlinProjectSummarySchema = external_exports.object({
37682
37682
  // model-tier-planning-and-arbiter D9: per-project judge switch. NULL/true means the judge runs
37683
37683
  // as today; false means intent-check.ts skips it entirely. Output-optional for the same reason
37684
37684
  // defaultDomain is: existing project fixtures/upsert callers need no churn.
37685
- intentJudgeEnabled: external_exports.boolean().optional()
37685
+ intentJudgeEnabled: external_exports.boolean().optional(),
37686
+ // agent-seat-surfaces S8/task 3.1a: raw counts only (K UNCLEAR / M judge verdicts, rolling
37687
+ // trailing 30 days, per project) — the suppress-below-20 and rate-vs-counts rendering choice
37688
+ // is a display concern (task 3.2), not this schema's. Nullable+optional: composed by getProject
37689
+ // from a separate async read (packages/db/src/index.ts's getIntentJudgeFireRate), never present
37690
+ // on the upsert/rename write paths that share this schema via mrrlinProjectUpsertSchema.
37691
+ intentJudgeFireRate: external_exports.object({
37692
+ unclearCount: external_exports.number().int().nonnegative(),
37693
+ judgeVerdictCount: external_exports.number().int().nonnegative()
37694
+ }).nullable().optional()
37686
37695
  });
37687
37696
  var mrrlinProjectUpsertSchema = mrrlinProjectSummarySchema.omit({
37688
37697
  onboardingKickoffState: true,
@@ -40326,7 +40335,12 @@ function createMrrlinClient(config2) {
40326
40335
  });
40327
40336
  return taskDependencyResponseSchema.parse(body).data;
40328
40337
  },
40329
- async getProject(projectSlug) {
40338
+ async getProject(projectSlug, opts) {
40339
+ if (opts?.includeFireRate) {
40340
+ const params = new URLSearchParams({ includeFireRate: "true" });
40341
+ const body2 = await request(`${projectPath(projectSlug)}?${params.toString()}`);
40342
+ return projectSummaryResponseSchema.parse(body2).data;
40343
+ }
40330
40344
  const body = await request(projectPath(projectSlug));
40331
40345
  return projectSummaryResponseSchema.parse(body).data;
40332
40346
  },
@@ -41537,7 +41551,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
41537
41551
  }
41538
41552
 
41539
41553
  // src/_generated/version.ts
41540
- var PKG_VERSION = "0.4.8";
41554
+ var PKG_VERSION = "0.4.10";
41541
41555
 
41542
41556
  // src/api-base-url-migrate.ts
41543
41557
  var import_node_fs3 = __toESM(require("node:fs"), 1);
@@ -42200,7 +42214,7 @@ async function prune(stateDir, bounds) {
42200
42214
  }
42201
42215
  }
42202
42216
  if (fileExists) {
42203
- for (const day of daysRemoved) delete sessionsObj[day];
42217
+ for (const day of daysRemoved) Reflect.deleteProperty(sessionsObj, day);
42204
42218
  const tmpPath = `${sessionsPath}.tmp`;
42205
42219
  await (0, import_promises4.writeFile)(tmpPath, JSON.stringify(sessionsObj));
42206
42220
  await (0, import_promises4.rename)(tmpPath, sessionsPath);
@@ -48183,6 +48197,76 @@ async function runDeliverableGate(_target, deps) {
48183
48197
  return { outcome: result.outcome, rounds: result.rounds };
48184
48198
  }
48185
48199
 
48200
+ // src/chat-compaction.ts
48201
+ var DEFAULT_COMPACTION_SEED_BUDGET_BYTES = 8e3;
48202
+ var DEFAULT_COMPACTION_TIMEOUT_MS = 3e4;
48203
+ function normalizeWhitespace(str) {
48204
+ return str.replace(/\s+/g, " ").trim();
48205
+ }
48206
+ function renderMessage(msg) {
48207
+ return `${msg.role}: ${msg.content}`;
48208
+ }
48209
+ async function compactChat(input, deps) {
48210
+ if (input.history.length === 0) {
48211
+ return { kind: "no-seed", note: "No chat history to compact." };
48212
+ }
48213
+ const runExec = deps?.runAgentExec ?? runAgentExec;
48214
+ const operatorMessages = input.history.filter((msg) => msg.role === "operator").map((msg) => msg.content);
48215
+ const transcript = input.history.map(renderMessage).join("\n");
48216
+ const prompt = [
48217
+ "Summarise the following chat transcript, preserving every operator message exactly as written.",
48218
+ "",
48219
+ transcript,
48220
+ "",
48221
+ "Summary:"
48222
+ ].join("\n");
48223
+ const exec = await runExec(
48224
+ {
48225
+ prompt,
48226
+ developerInstructions: "Produce a concise summary that includes all operator messages verbatim.",
48227
+ cwd: input.cwd,
48228
+ timeoutMs: input.timeoutMs,
48229
+ sandbox: "read-only",
48230
+ codexExecutable: input.codexExecutable,
48231
+ model: input.weakModel
48232
+ },
48233
+ void 0,
48234
+ input.provider
48235
+ );
48236
+ if (exec.ok) {
48237
+ const summaryNorm = normalizeWhitespace(exec.text);
48238
+ const allPresent = operatorMessages.every((op) => {
48239
+ const opNorm = normalizeWhitespace(op);
48240
+ return summaryNorm.includes(opNorm);
48241
+ });
48242
+ const withinBudget = Buffer.byteLength(exec.text, "utf8") <= input.budgetBytes;
48243
+ if (allPresent && withinBudget) {
48244
+ return { kind: "summary", seed: exec.text };
48245
+ }
48246
+ }
48247
+ const kept = [];
48248
+ let usedBytes = 0;
48249
+ for (let i = input.history.length - 1; i >= 0; i--) {
48250
+ const msg = input.history[i];
48251
+ if (!msg) continue;
48252
+ const rendered = renderMessage(msg);
48253
+ const msgBytes = Buffer.byteLength(rendered, "utf8") + 1;
48254
+ if (usedBytes + msgBytes > input.budgetBytes) {
48255
+ break;
48256
+ }
48257
+ kept.push(msg);
48258
+ usedBytes += msgBytes;
48259
+ }
48260
+ if (kept.length === 0) {
48261
+ return {
48262
+ kind: "no-seed",
48263
+ note: "No chat messages fit within the provided byte budget."
48264
+ };
48265
+ }
48266
+ const seed = kept.reverse().map(renderMessage).join("\n");
48267
+ return { kind: "raw-tail", seed };
48268
+ }
48269
+
48186
48270
  // src/session-sink-registry.ts
48187
48271
  var DEFAULT_MAX_SESSION_SINKS = 500;
48188
48272
  function createSessionSinkRegistry(maxEntries = DEFAULT_MAX_SESSION_SINKS) {
@@ -48255,6 +48339,16 @@ function createAgentKeepalive(opts) {
48255
48339
  }, opts.idleMs(providerId));
48256
48340
  slot.idleTimer.unref?.();
48257
48341
  },
48342
+ refreshNow: (providerId) => {
48343
+ const slot = getSlot(providerId);
48344
+ if (slot.holdCount > 0) return false;
48345
+ cancelTimer(slot);
48346
+ if (slot.managerHeld) {
48347
+ slot.managerHeld = false;
48348
+ opts.release(providerId);
48349
+ }
48350
+ return true;
48351
+ },
48258
48352
  dropNow: () => {
48259
48353
  for (const [providerId, slot] of slots) {
48260
48354
  cancelTimer(slot);
@@ -52812,6 +52906,7 @@ function scrubDeep(value, scrubSet) {
52812
52906
  // src/external-agents-handlers.ts
52813
52907
  var BUDGET_STATE_MS = 1e4;
52814
52908
  var BUDGET_PROBE_MS = 6e4;
52909
+ var BUDGET_VERIFY_READ_ONLY_MS = 6e4;
52815
52910
  var BUDGET_AUDIT_MS = 18e4;
52816
52911
  var BUDGET_TOGGLE_MS = 1e4;
52817
52912
  var BUDGET_ADD_MODEL_MS = 1e4;
@@ -52903,6 +52998,21 @@ async function handleAgentsProbe(deps, request) {
52903
52998
  }
52904
52999
  return finalizeReply({ ok: true, data: parsed.value }, scrubSet);
52905
53000
  }
53001
+ async function handleAgentsVerifyReadOnly(deps, request) {
53002
+ const scrubSet = defaultScrubSet(deps);
53003
+ if (deps.lastKnownState === null || !deps.lastKnownState.agentIds.includes(request.id)) {
53004
+ return finalizeReply(invalidInput("id", "unknown agent id"), scrubSet);
53005
+ }
53006
+ const result = await deps.run(["verify-read-only", request.id], { budgetMs: BUDGET_VERIFY_READ_ONLY_MS });
53007
+ if (result.code !== 0) {
53008
+ return finalizeReply({ ok: false, error: classifyCliFailure(result) }, scrubSet);
53009
+ }
53010
+ const parsed = tryParseJson(result.stdout);
53011
+ if (!parsed.ok) {
53012
+ return finalizeReply({ ok: false, error: { code: "cli_broken", detail: result.stderr || void 0 } }, scrubSet);
53013
+ }
53014
+ return finalizeReply({ ok: true, data: parsed.value }, scrubSet);
53015
+ }
52906
53016
  async function handleAgentsAudit(deps, request) {
52907
53017
  const scrubSet = defaultScrubSet(deps);
52908
53018
  if (deps.lastKnownState === null) {
@@ -53057,6 +53167,13 @@ var DEFAULT_DIRECTOR_BRIDGE_ALLOWED_ORIGINS = [
53057
53167
  // Production web (unchanged).
53058
53168
  "https://mrrlin-web.vercel.app"
53059
53169
  ];
53170
+ var COMPACT_REQUEST_HISTORY_MAX_ITEMS = 500;
53171
+ function isValidChatHistoryMessage(value) {
53172
+ if (!value || typeof value !== "object") return false;
53173
+ const role = value.role;
53174
+ const content = value.content;
53175
+ return (role === "operator" || role === "assistant" || role === "system") && typeof content === "string";
53176
+ }
53060
53177
  function normalizeReasoningEffort(value) {
53061
53178
  if (typeof value !== "string") return null;
53062
53179
  const trimmed = value.trim();
@@ -54151,6 +54268,50 @@ function createBridgeMessageHandler(deps) {
54151
54268
  sendForSpan({ type: "checkout-register", ok: true, data: result.data });
54152
54269
  return;
54153
54270
  }
54271
+ if (msg.type === "compact-request") {
54272
+ const directorSessionId2 = typeof msg.directorSessionId === "string" ? msg.directorSessionId : "";
54273
+ const lineageId = typeof msg.lineageId === "string" ? msg.lineageId : "";
54274
+ if (!directorSessionId2 || !lineageId) {
54275
+ sendForSpan({
54276
+ type: "error",
54277
+ directorSessionId: directorSessionId2 || void 0,
54278
+ error: "compact-request requires directorSessionId and lineageId."
54279
+ });
54280
+ return;
54281
+ }
54282
+ if (!Array.isArray(msg.history) || !msg.history.every(isValidChatHistoryMessage)) {
54283
+ sendForSpan({
54284
+ type: "error",
54285
+ directorSessionId: directorSessionId2,
54286
+ error: "compact-request.history must be an array of {role, content} messages."
54287
+ });
54288
+ return;
54289
+ }
54290
+ const history = msg.history.slice(-COMPACT_REQUEST_HISTORY_MAX_ITEMS);
54291
+ if (!deps.runCompaction) {
54292
+ sendForSpan({
54293
+ type: "error",
54294
+ directorSessionId: directorSessionId2,
54295
+ error: "compaction is not configured on this bridge."
54296
+ });
54297
+ return;
54298
+ }
54299
+ const compactionSeat = await mintResolvedSeat(resolveProvider(process.env, getSelectedProvider()));
54300
+ const result = await deps.runCompaction({
54301
+ history,
54302
+ provider: compactionSeat.id,
54303
+ weakModel: compactionSeat.models.weak
54304
+ });
54305
+ const seed = result.kind === "no-seed" ? result.note : result.seed;
54306
+ sendForSpan({
54307
+ type: "compact-directive",
54308
+ directorSessionId: directorSessionId2,
54309
+ lineageId,
54310
+ resolvedSeat: compactionSeat.id,
54311
+ seed
54312
+ });
54313
+ return;
54314
+ }
54154
54315
  if (msg.type === "agents-state") {
54155
54316
  if (!deps.runExternalAgentsCli) {
54156
54317
  sendForSpan({ type: "agents-state", ok: false, error: { code: "bridge_offline", detail: "external-agents is not configured on this bridge" } });
@@ -54185,6 +54346,21 @@ function createBridgeMessageHandler(deps) {
54185
54346
  sendForSpan({ type: "agents-probe", ...reply });
54186
54347
  return;
54187
54348
  }
54349
+ if (msg.type === "agents-verify-read-only") {
54350
+ if (!deps.runExternalAgentsCli) {
54351
+ sendForSpan({ type: "agents-verify-read-only", ok: false, error: { code: "bridge_offline", detail: "external-agents is not configured on this bridge" } });
54352
+ return;
54353
+ }
54354
+ const handlerDeps = {
54355
+ run: deps.runExternalAgentsCli,
54356
+ registryEnvNames: externalAgentsRegistryEnvNames,
54357
+ lastKnownState: externalAgentsLastKnownState
54358
+ };
54359
+ const req = msg;
54360
+ const reply = await withPoolStateReread(handlerDeps, await handleAgentsVerifyReadOnly(handlerDeps, { id: req.id }));
54361
+ sendForSpan({ type: "agents-verify-read-only", ...reply });
54362
+ return;
54363
+ }
54188
54364
  if (msg.type === "agents-audit") {
54189
54365
  if (!deps.runExternalAgentsCli) {
54190
54366
  sendForSpan({ type: "agents-audit", ok: false, error: { code: "bridge_offline", detail: "external-agents is not configured on this bridge" } });
@@ -54337,6 +54513,10 @@ function createBridgeMessageHandler(deps) {
54337
54513
  sendForSpan({ type: "bootstrap", models, rateLimits, ...deps.bridgeInstanceId ? { bridgeInstanceId: deps.bridgeInstanceId } : {}, ...bootstrapError ? { error: bootstrapError } : {} });
54338
54514
  return;
54339
54515
  }
54516
+ if (msg.type === "refresh-codex") {
54517
+ sendForSpan({ type: "codex-refreshed", status: deps.refreshCodex?.() === false ? "busy" : "ok" });
54518
+ return;
54519
+ }
54340
54520
  if (msg.type === "resume") {
54341
54521
  handleResumeRequest(msg, sendForSpan);
54342
54522
  return;
@@ -55285,6 +55465,7 @@ async function startDirectorBridge() {
55285
55465
  const sharedHandlerDeps = {
55286
55466
  acquireCodex: (providerId) => agentKeepalive.acquire(providerId),
55287
55467
  releaseCodex: (providerId) => agentKeepalive.release(providerId),
55468
+ refreshCodex: () => agentKeepalive.refreshNow(resolveProvider(process.env, getSelectedProvider()).id),
55288
55469
  run: wrappedTurnLock.run,
55289
55470
  suspendedMs: () => suspensionDetector.suspendedMs(),
55290
55471
  buildStartParams: (msg, desiredModel) => buildThreadStartParams(msg, config2, desiredModel),
@@ -55334,7 +55515,19 @@ async function startDirectorBridge() {
55334
55515
  listAnsweredQuestions: (projectSlug, taskId) => client.listInboxItems(projectSlug, { status: "acknowledged", kind: "question", taskId }),
55335
55516
  // External-agents control plane: the real spawn runner, with the EXTERNAL_AGENTS_CLI operator
55336
55517
  // escape hatch bound here (the handlers stay spawn-free and this is the only production seam).
55337
- runExternalAgentsCli: (args, opts) => runExternalAgentsCli(args, { ...opts, envCliPath: process.env["EXTERNAL_AGENTS_CLI"] })
55518
+ runExternalAgentsCli: (args, opts) => runExternalAgentsCli(args, { ...opts, envCliPath: process.env["EXTERNAL_AGENTS_CLI"] }),
55519
+ // Compaction runner for `compact-request` (agent-seat-surfaces task 2.5). Closes over `config`
55520
+ // (in scope here, unlike inside the handler) for cwd/executable, and the module-level budget +
55521
+ // timeout constants. The handler supplies only the resolved seat's provider + weak model.
55522
+ runCompaction: (args) => compactChat({
55523
+ history: args.history,
55524
+ provider: args.provider,
55525
+ weakModel: args.weakModel,
55526
+ budgetBytes: DEFAULT_COMPACTION_SEED_BUDGET_BYTES,
55527
+ cwd: config2.codexCwd ?? process.cwd(),
55528
+ timeoutMs: DEFAULT_COMPACTION_TIMEOUT_MS,
55529
+ codexExecutable: config2.codexExecutable
55530
+ })
55338
55531
  };
55339
55532
  const bridgeLogger = createBridgeLogger(config2.stateDir, process.env);
55340
55533
  let bridgeBinShaShort = "unknown00000";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrrlin-dev/mcp",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mrrlin-mcp": "dist/bin.cjs"
@@ -23,10 +23,10 @@
23
23
  "tsx": "^4.22.3",
24
24
  "@mrrlin/client": "0.0.0",
25
25
  "@mrrlin/agent-client": "0.0.0",
26
- "@mrrlin/director-e2e": "0.0.0",
27
26
  "@mrrlin/schemas": "0.0.0",
28
27
  "@mrrlin/tsconfig": "0.0.0",
29
- "@mrrlin/wiki": "0.0.0"
28
+ "@mrrlin/wiki": "0.0.0",
29
+ "@mrrlin/director-e2e": "0.0.0"
30
30
  },
31
31
  "dependencies": {
32
32
  "@iarna/toml": "^2.2.5",