@mrrlin-dev/mcp 0.4.8 → 0.4.9

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 +183 -5
  2. package/package.json +5 -5
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.9";
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) {
@@ -52812,6 +52896,7 @@ function scrubDeep(value, scrubSet) {
52812
52896
  // src/external-agents-handlers.ts
52813
52897
  var BUDGET_STATE_MS = 1e4;
52814
52898
  var BUDGET_PROBE_MS = 6e4;
52899
+ var BUDGET_VERIFY_READ_ONLY_MS = 6e4;
52815
52900
  var BUDGET_AUDIT_MS = 18e4;
52816
52901
  var BUDGET_TOGGLE_MS = 1e4;
52817
52902
  var BUDGET_ADD_MODEL_MS = 1e4;
@@ -52903,6 +52988,21 @@ async function handleAgentsProbe(deps, request) {
52903
52988
  }
52904
52989
  return finalizeReply({ ok: true, data: parsed.value }, scrubSet);
52905
52990
  }
52991
+ async function handleAgentsVerifyReadOnly(deps, request) {
52992
+ const scrubSet = defaultScrubSet(deps);
52993
+ if (deps.lastKnownState === null || !deps.lastKnownState.agentIds.includes(request.id)) {
52994
+ return finalizeReply(invalidInput("id", "unknown agent id"), scrubSet);
52995
+ }
52996
+ const result = await deps.run(["verify-read-only", request.id], { budgetMs: BUDGET_VERIFY_READ_ONLY_MS });
52997
+ if (result.code !== 0) {
52998
+ return finalizeReply({ ok: false, error: classifyCliFailure(result) }, scrubSet);
52999
+ }
53000
+ const parsed = tryParseJson(result.stdout);
53001
+ if (!parsed.ok) {
53002
+ return finalizeReply({ ok: false, error: { code: "cli_broken", detail: result.stderr || void 0 } }, scrubSet);
53003
+ }
53004
+ return finalizeReply({ ok: true, data: parsed.value }, scrubSet);
53005
+ }
52906
53006
  async function handleAgentsAudit(deps, request) {
52907
53007
  const scrubSet = defaultScrubSet(deps);
52908
53008
  if (deps.lastKnownState === null) {
@@ -53057,6 +53157,13 @@ var DEFAULT_DIRECTOR_BRIDGE_ALLOWED_ORIGINS = [
53057
53157
  // Production web (unchanged).
53058
53158
  "https://mrrlin-web.vercel.app"
53059
53159
  ];
53160
+ var COMPACT_REQUEST_HISTORY_MAX_ITEMS = 500;
53161
+ function isValidChatHistoryMessage(value) {
53162
+ if (!value || typeof value !== "object") return false;
53163
+ const role = value.role;
53164
+ const content = value.content;
53165
+ return (role === "operator" || role === "assistant" || role === "system") && typeof content === "string";
53166
+ }
53060
53167
  function normalizeReasoningEffort(value) {
53061
53168
  if (typeof value !== "string") return null;
53062
53169
  const trimmed = value.trim();
@@ -54151,6 +54258,50 @@ function createBridgeMessageHandler(deps) {
54151
54258
  sendForSpan({ type: "checkout-register", ok: true, data: result.data });
54152
54259
  return;
54153
54260
  }
54261
+ if (msg.type === "compact-request") {
54262
+ const directorSessionId2 = typeof msg.directorSessionId === "string" ? msg.directorSessionId : "";
54263
+ const lineageId = typeof msg.lineageId === "string" ? msg.lineageId : "";
54264
+ if (!directorSessionId2 || !lineageId) {
54265
+ sendForSpan({
54266
+ type: "error",
54267
+ directorSessionId: directorSessionId2 || void 0,
54268
+ error: "compact-request requires directorSessionId and lineageId."
54269
+ });
54270
+ return;
54271
+ }
54272
+ if (!Array.isArray(msg.history) || !msg.history.every(isValidChatHistoryMessage)) {
54273
+ sendForSpan({
54274
+ type: "error",
54275
+ directorSessionId: directorSessionId2,
54276
+ error: "compact-request.history must be an array of {role, content} messages."
54277
+ });
54278
+ return;
54279
+ }
54280
+ const history = msg.history.slice(-COMPACT_REQUEST_HISTORY_MAX_ITEMS);
54281
+ if (!deps.runCompaction) {
54282
+ sendForSpan({
54283
+ type: "error",
54284
+ directorSessionId: directorSessionId2,
54285
+ error: "compaction is not configured on this bridge."
54286
+ });
54287
+ return;
54288
+ }
54289
+ const compactionSeat = await mintResolvedSeat(resolveProvider(process.env, getSelectedProvider()));
54290
+ const result = await deps.runCompaction({
54291
+ history,
54292
+ provider: compactionSeat.id,
54293
+ weakModel: compactionSeat.models.weak
54294
+ });
54295
+ const seed = result.kind === "no-seed" ? result.note : result.seed;
54296
+ sendForSpan({
54297
+ type: "compact-directive",
54298
+ directorSessionId: directorSessionId2,
54299
+ lineageId,
54300
+ resolvedSeat: compactionSeat.id,
54301
+ seed
54302
+ });
54303
+ return;
54304
+ }
54154
54305
  if (msg.type === "agents-state") {
54155
54306
  if (!deps.runExternalAgentsCli) {
54156
54307
  sendForSpan({ type: "agents-state", ok: false, error: { code: "bridge_offline", detail: "external-agents is not configured on this bridge" } });
@@ -54185,6 +54336,21 @@ function createBridgeMessageHandler(deps) {
54185
54336
  sendForSpan({ type: "agents-probe", ...reply });
54186
54337
  return;
54187
54338
  }
54339
+ if (msg.type === "agents-verify-read-only") {
54340
+ if (!deps.runExternalAgentsCli) {
54341
+ sendForSpan({ type: "agents-verify-read-only", ok: false, error: { code: "bridge_offline", detail: "external-agents is not configured on this bridge" } });
54342
+ return;
54343
+ }
54344
+ const handlerDeps = {
54345
+ run: deps.runExternalAgentsCli,
54346
+ registryEnvNames: externalAgentsRegistryEnvNames,
54347
+ lastKnownState: externalAgentsLastKnownState
54348
+ };
54349
+ const req = msg;
54350
+ const reply = await withPoolStateReread(handlerDeps, await handleAgentsVerifyReadOnly(handlerDeps, { id: req.id }));
54351
+ sendForSpan({ type: "agents-verify-read-only", ...reply });
54352
+ return;
54353
+ }
54188
54354
  if (msg.type === "agents-audit") {
54189
54355
  if (!deps.runExternalAgentsCli) {
54190
54356
  sendForSpan({ type: "agents-audit", ok: false, error: { code: "bridge_offline", detail: "external-agents is not configured on this bridge" } });
@@ -55334,7 +55500,19 @@ async function startDirectorBridge() {
55334
55500
  listAnsweredQuestions: (projectSlug, taskId) => client.listInboxItems(projectSlug, { status: "acknowledged", kind: "question", taskId }),
55335
55501
  // External-agents control plane: the real spawn runner, with the EXTERNAL_AGENTS_CLI operator
55336
55502
  // 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"] })
55503
+ runExternalAgentsCli: (args, opts) => runExternalAgentsCli(args, { ...opts, envCliPath: process.env["EXTERNAL_AGENTS_CLI"] }),
55504
+ // Compaction runner for `compact-request` (agent-seat-surfaces task 2.5). Closes over `config`
55505
+ // (in scope here, unlike inside the handler) for cwd/executable, and the module-level budget +
55506
+ // timeout constants. The handler supplies only the resolved seat's provider + weak model.
55507
+ runCompaction: (args) => compactChat({
55508
+ history: args.history,
55509
+ provider: args.provider,
55510
+ weakModel: args.weakModel,
55511
+ budgetBytes: DEFAULT_COMPACTION_SEED_BUDGET_BYTES,
55512
+ cwd: config2.codexCwd ?? process.cwd(),
55513
+ timeoutMs: DEFAULT_COMPACTION_TIMEOUT_MS,
55514
+ codexExecutable: config2.codexExecutable
55515
+ })
55338
55516
  };
55339
55517
  const bridgeLogger = createBridgeLogger(config2.stateDir, process.env);
55340
55518
  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.9",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mrrlin-mcp": "dist/bin.cjs"
@@ -21,12 +21,12 @@
21
21
  "@types/ws": "^8.18.1",
22
22
  "esbuild": "^0.24.0",
23
23
  "tsx": "^4.22.3",
24
- "@mrrlin/client": "0.0.0",
25
- "@mrrlin/agent-client": "0.0.0",
26
24
  "@mrrlin/director-e2e": "0.0.0",
25
+ "@mrrlin/agent-client": "0.0.0",
26
+ "@mrrlin/client": "0.0.0",
27
27
  "@mrrlin/schemas": "0.0.0",
28
- "@mrrlin/tsconfig": "0.0.0",
29
- "@mrrlin/wiki": "0.0.0"
28
+ "@mrrlin/wiki": "0.0.0",
29
+ "@mrrlin/tsconfig": "0.0.0"
30
30
  },
31
31
  "dependencies": {
32
32
  "@iarna/toml": "^2.2.5",