@mrrlin-dev/mcp 0.4.108 → 0.4.109

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 +117 -26
  2. package/package.json +1 -1
package/dist/bin.cjs CHANGED
@@ -37432,6 +37432,7 @@ var openApiOperationIds = {
37432
37432
  listOperatorTranscriptMessages: "listOperatorTranscriptMessages",
37433
37433
  createIntentProposal: "createIntentProposal",
37434
37434
  getIntentProposal: "getIntentProposal",
37435
+ listIntentProposals: "listIntentProposals",
37435
37436
  recordIntentClarification: "recordIntentClarification",
37436
37437
  answerIntentClarification: "answerIntentClarification",
37437
37438
  confirmIntentProposal: "confirmIntentProposal",
@@ -38171,13 +38172,13 @@ var mrrlinIntentProposalSchema = external_exports.object({
38171
38172
  updatedAt: external_exports.string()
38172
38173
  });
38173
38174
  var mrrlinIntentProposalCreateSchema = external_exports.object({
38174
- proposalId: external_exports.string().uuid(),
38175
- version: external_exports.number().int().min(1),
38175
+ proposalId: external_exports.string().uuid().optional(),
38176
+ version: external_exports.number().int().min(1).optional(),
38176
38177
  resolvedIntent: external_exports.record(external_exports.string(), external_exports.unknown()),
38177
- digest: external_exports.string().min(1),
38178
+ digest: external_exports.string().min(1).optional(),
38178
38179
  transcriptRevision: external_exports.string().optional(),
38179
- expectedTranscriptRevisionNumber: external_exports.number().int().nonnegative(),
38180
- messageIds: external_exports.array(external_exports.string().uuid())
38180
+ expectedTranscriptRevisionNumber: external_exports.number().int().nonnegative().optional(),
38181
+ messageIds: external_exports.array(external_exports.string().uuid()).min(1).optional()
38181
38182
  }).strict();
38182
38183
  var intentProposalResponseSchema = external_exports.object({
38183
38184
  data: mrrlinIntentProposalSchema
@@ -38221,7 +38222,8 @@ var mrrlinIntentConfirmRequestSchema = external_exports.object({
38221
38222
  actor: external_exports.string().min(1).optional(),
38222
38223
  materializeParams: external_exports.object({
38223
38224
  task: external_exports.object({
38224
- id: external_exports.string().min(1),
38225
+ // Omitted ⇒ the API allocates the next per-project GT-N id before confirming.
38226
+ id: external_exports.string().min(1).optional(),
38225
38227
  title: external_exports.string().min(1).optional(),
38226
38228
  category: external_exports.string().optional(),
38227
38229
  subcategory: external_exports.string().optional(),
@@ -38268,11 +38270,16 @@ var mrrlinIntentConfirmationSchema = external_exports.object({
38268
38270
  digest: external_exports.string().min(1),
38269
38271
  transcriptRevision: external_exports.string(),
38270
38272
  transcriptRevisionNumber: external_exports.number().int().nonnegative().optional(),
38273
+ // The task the confirmation materialized, when it materialized one.
38274
+ materializedTaskId: external_exports.string().nullable().optional(),
38271
38275
  createdAt: external_exports.string()
38272
38276
  });
38273
38277
  var intentConfirmationMaybeResponseSchema = external_exports.object({
38274
38278
  data: mrrlinIntentConfirmationSchema.nullable()
38275
38279
  });
38280
+ var intentProposalListResponseSchema = external_exports.object({
38281
+ data: external_exports.array(mrrlinIntentProposalSchema)
38282
+ });
38276
38283
 
38277
38284
  // ../../packages/schemas/dist/appsumo.js
38278
38285
  var APPSUMO_CODE_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
@@ -41647,6 +41654,10 @@ function createMrrlinClient(config2) {
41647
41654
  });
41648
41655
  return intentProposalResponseSchema.parse(body).data;
41649
41656
  },
41657
+ async listIntentProposals(projectSlug) {
41658
+ const body = await request(`${projectPath(projectSlug)}/intent/proposals`);
41659
+ return intentProposalListResponseSchema.parse(body).data;
41660
+ },
41650
41661
  async getIntentProposal(projectSlug, proposalId, version2) {
41651
41662
  try {
41652
41663
  const body = await request(`${projectPath(projectSlug)}/intent/proposals/${encodeURIComponent(proposalId)}/${encodeURIComponent(String(version2))}`);
@@ -41685,7 +41696,7 @@ function createMrrlinClient(config2) {
41685
41696
  if (error51 instanceof MrrlinApiError && (error51.status === 409 || error51.status === 422)) {
41686
41697
  try {
41687
41698
  const parsed = JSON.parse(error51.responseBody);
41688
- const maybeResult = mrrlinIntentConfirmResponseSchema.safeParse(parsed);
41699
+ const maybeResult = mrrlinIntentConfirmResponseSchema.safeParse(parsed?.data ?? parsed);
41689
41700
  if (maybeResult.success)
41690
41701
  return maybeResult.data;
41691
41702
  } catch {
@@ -44170,6 +44181,23 @@ var CodexAgentHandle = class {
44170
44181
  }
44171
44182
  };
44172
44183
 
44184
+ // ../../packages/agent-client/dist/providers/thread-env.js
44185
+ function threadProcessEnv(childEnv, additions) {
44186
+ if (!additions || Object.keys(additions).length === 0)
44187
+ return childEnv;
44188
+ const env = { ...childEnv ?? process.env };
44189
+ for (const [key, value] of Object.entries(additions)) {
44190
+ if (value === null)
44191
+ delete env[key];
44192
+ else
44193
+ env[key] = value;
44194
+ }
44195
+ return env;
44196
+ }
44197
+ function envOption(env) {
44198
+ return env === void 0 ? {} : { env };
44199
+ }
44200
+
44173
44201
  // ../../packages/agent-client/dist/providers/claude/process.js
44174
44202
  var import_node_child_process3 = require("node:child_process");
44175
44203
  var import_node_crypto = require("node:crypto");
@@ -44550,7 +44578,7 @@ var ClaudeAgentHandle = class {
44550
44578
  appendSystemPrompt: params.baseInstructions,
44551
44579
  wrapCommand: this.wrapCommand,
44552
44580
  brokerSocket: this.brokerSocket,
44553
- ...this.childEnv === void 0 ? {} : { env: this.childEnv }
44581
+ ...envOption(threadProcessEnv(this.childEnv, params.env))
44554
44582
  });
44555
44583
  this.processes.set(child.sessionId, child);
44556
44584
  this.mappers.set(child.sessionId, new ClaudeBridgeEventMapper());
@@ -44572,7 +44600,7 @@ var ClaudeAgentHandle = class {
44572
44600
  appendSystemPrompt: params.baseInstructions,
44573
44601
  wrapCommand: this.wrapCommand,
44574
44602
  brokerSocket: this.brokerSocket,
44575
- ...this.childEnv === void 0 ? {} : { env: this.childEnv }
44603
+ ...envOption(threadProcessEnv(this.childEnv, params.env))
44576
44604
  });
44577
44605
  this.processes.set(threadId, child);
44578
44606
  this.mappers.set(threadId, new ClaudeBridgeEventMapper());
@@ -45086,7 +45114,7 @@ var AgyAgentHandle = class {
45086
45114
  wrapCommand: this.wrapCommand,
45087
45115
  brokerSocket: this.brokerSocket,
45088
45116
  disabledTools: this.disabledTools,
45089
- ...this.childEnv === void 0 ? {} : { env: this.childEnv }
45117
+ ...envOption(threadProcessEnv(this.childEnv, params.env))
45090
45118
  });
45091
45119
  this.processes.set(child.sessionId, child);
45092
45120
  this.mappers.set(child.sessionId, new AgyBridgeEventMapper());
@@ -45110,7 +45138,7 @@ var AgyAgentHandle = class {
45110
45138
  wrapCommand: this.wrapCommand,
45111
45139
  brokerSocket: this.brokerSocket,
45112
45140
  disabledTools: this.disabledTools,
45113
- ...this.childEnv === void 0 ? {} : { env: this.childEnv }
45141
+ ...envOption(threadProcessEnv(this.childEnv, params.env))
45114
45142
  });
45115
45143
  this.processes.set(threadId, child);
45116
45144
  this.mappers.set(threadId, new AgyBridgeEventMapper());
@@ -57016,6 +57044,9 @@ var READ_TOOL_NAMES = /* @__PURE__ */ new Set([
57016
57044
  "search_knowledge",
57017
57045
  "search_wiki_pages",
57018
57046
  "create_task",
57047
+ // propose_task only records a pending intent proposal; nothing happens until the operator
57048
+ // confirms it in the web UI, so it is as in-product as create_task.
57049
+ "propose_task",
57019
57050
  "update_task",
57020
57051
  "create_wiki_page",
57021
57052
  "update_wiki_page",
@@ -57781,6 +57812,7 @@ var mcpToolNames = {
57781
57812
  deleteTaskDependency: "delete_task_dependency",
57782
57813
  createInboxItem: "create_inbox_item",
57783
57814
  createTask: "create_task",
57815
+ proposeTask: "propose_task",
57784
57816
  decideInboxItem: "decide_inbox_item",
57785
57817
  getExecutionRun: "get_execution_run",
57786
57818
  getProjectSchedulerPolicy: "get_project_scheduler_policy",
@@ -58403,6 +58435,22 @@ var listTasksInputSchema = projectScopedSchema.merge(mrrlinTaskListFiltersSchema
58403
58435
  var getTaskInputSchema = taskScopedSchema;
58404
58436
  var createTaskInputSchema = proposalProvenanceSchema.merge(projectScopedSchema).merge(mrrlinTaskCreateWithoutIdSchema);
58405
58437
  var updateTaskInputSchema = taskScopedSchema.merge(mrrlinTaskUpdateSchema);
58438
+ var proposeTaskInputSchema = projectScopedSchema.extend({
58439
+ kind: external_exports.enum(["task", "plan"]).optional().describe("What the operator's confirmation creates. 'task' (default): the task itself. 'plan': nothing yet \u2014 call create_plan with the confirmed proposalId/proposalVersion/proposalDigest."),
58440
+ title: external_exports.string().min(1).max(500),
58441
+ notes: external_exports.string().max(2e4).optional(),
58442
+ type: mrrlinTaskTypeSchema.optional(),
58443
+ category: external_exports.string().min(1).optional(),
58444
+ subcategory: external_exports.string().min(1).optional(),
58445
+ impact: mrrlinTaskImpactSchema.optional(),
58446
+ autonomyLevel: mrrlinAutonomyLevelSchema.optional(),
58447
+ autonomyReason: external_exports.string().min(1).max(240).optional(),
58448
+ autoDeploy: mrrlinAutoDeploySchema.optional(),
58449
+ domains: mrrlinTaskDomainsSchema.optional(),
58450
+ tags: external_exports.array(external_exports.string().min(1)).optional(),
58451
+ planId: mrrlinPlanIdSchema.optional(),
58452
+ specWikiPageId: mrrlinWikiPageIdSchema.optional()
58453
+ });
58406
58454
  var resetDirectorThreadInputSchema = taskScopedSchema;
58407
58455
  var archiveTaskInputSchema = taskScopedSchema;
58408
58456
  var unarchiveTaskInputSchema = taskScopedSchema;
@@ -58540,6 +58588,7 @@ var mcpToolInputSchemas = {
58540
58588
  [mcpToolNames.createWikiPage]: createWikiPageInputSchema,
58541
58589
  [mcpToolNames.deleteTaskDependency]: deleteTaskDependencyInputSchema,
58542
58590
  [mcpToolNames.createTask]: createTaskInputSchema,
58591
+ [mcpToolNames.proposeTask]: proposeTaskInputSchema,
58543
58592
  [mcpToolNames.getExecutionRun]: getExecutionRunInputSchema,
58544
58593
  [mcpToolNames.getProjectSchedulerPolicy]: getProjectSchedulerPolicyInputSchema,
58545
58594
  [mcpToolNames.getSchedulerPendingTasks]: getSchedulerPendingTasksInputSchema,
@@ -58604,6 +58653,7 @@ var mcpToolOperationIds = {
58604
58653
  [mcpToolNames.createWikiPage]: openApiOperationIds.createWikiPage,
58605
58654
  [mcpToolNames.deleteTaskDependency]: openApiOperationIds.deleteTaskDependency,
58606
58655
  [mcpToolNames.createTask]: openApiOperationIds.createTask,
58656
+ [mcpToolNames.proposeTask]: openApiOperationIds.createIntentProposal,
58607
58657
  [mcpToolNames.getExecutionRun]: openApiOperationIds.getExecutionRun,
58608
58658
  [mcpToolNames.getProjectSchedulerPolicy]: openApiOperationIds.getProjectSchedulerPolicy,
58609
58659
  [mcpToolNames.getSchedulerPendingTasks]: openApiOperationIds.getSchedulerPendingTasks,
@@ -58658,7 +58708,8 @@ var mcpToolDescriptions = {
58658
58708
  [mcpToolNames.appendTaskEvent]: "Append an operator note to a task's activity timeline. Also use it as the task-linked receipt path for material immediate work that has a parent task; summarize scope, outcome/no-result, evidence or artifacts, blockers, and next action in the note body.",
58659
58709
  [mcpToolNames.createWikiPage]: "Create a markdown Wiki page.",
58660
58710
  [mcpToolNames.deleteTaskDependency]: "Delete a blocked-by relationship.",
58661
- [mcpToolNames.createTask]: "Create a new task in a project. Defaults to status='todo' to authorize immediate autopilot work; pass status='backlog' only if explicitly parking the task for later. 'backlog' and 'todo' are the only creatable statuses. Pass parentTaskId when spawning a follow-up task to continue work from an existing task \u2014 its operator_seed_request is inherited automatically if you don't supply your own. Domain is required UNLESS parentTaskId is supplied (a follow-up inherits its parent's domain automatically). Domain selects which reviewer roster judges the task's spec. Valid values: code, seo, marketing, content. When creating tasks for work that produces a published web page, author the work as a [code_change] task and a [page] task (with domain: 'content') linked by blocked_by. Set autoDeploy according to the publish policy: 'prod' if only meaningful once live (robots.txt, sitemap), 'dev' to stop at preview (landing page, layout), or 'off' if the repo is not the operator's own.",
58711
+ [mcpToolNames.proposeTask]: "Propose a task (or plan) for the operator to confirm. This is how work gets created: a proposal does nothing on its own; the operator sees it in the Director chat and confirms it, and that confirmation creates the task. It binds to the operator's latest message in this chat, so call it right after the operator asked for the work, in the same turn. Call it once per task. Then tell the operator, in plain words, that the task is waiting for their confirmation in the chat \u2014 never say it was created. A new operator message makes an unconfirmed proposal stale; propose again if the operator still wants it. Fails with NO_OPERATOR_TRANSCRIPT when the operator has not said anything this proposal could quote.",
58712
+ [mcpToolNames.createTask]: "Only for a proposal the operator already confirmed: pass its proposalId, proposalVersion and proposalDigest. A confirmed task proposal has already created its task, and this returns that task; never call it to create new work \u2014 use propose_task. Create a new task in a project. Defaults to status='todo' to authorize immediate autopilot work; pass status='backlog' only if explicitly parking the task for later. 'backlog' and 'todo' are the only creatable statuses. Pass parentTaskId when spawning a follow-up task to continue work from an existing task \u2014 its operator_seed_request is inherited automatically if you don't supply your own. Domain is required UNLESS parentTaskId is supplied (a follow-up inherits its parent's domain automatically). Domain selects which reviewer roster judges the task's spec. Valid values: code, seo, marketing, content. When creating tasks for work that produces a published web page, author the work as a [code_change] task and a [page] task (with domain: 'content') linked by blocked_by. Set autoDeploy according to the publish policy: 'prod' if only meaningful once live (robots.txt, sitemap), 'dev' to stop at preview (landing page, layout), or 'off' if the repo is not the operator's own.",
58662
58713
  [mcpToolNames.getProject]: "Fetch a single project summary by slug.",
58663
58714
  [mcpToolNames.getProjectSnapshot]: "Fetch the full project snapshot: GitHub binding, tasks, plans, and wiki pages.",
58664
58715
  [mcpToolNames.getTask]: "Fetch a single task by project slug and task id.",
@@ -58903,6 +58954,7 @@ async function getAgentProviderForPanel(client, opts = {}) {
58903
58954
  function createMrrlinTools(options) {
58904
58955
  const client = createMrrlinClient(withAuth(options));
58905
58956
  async function resolveBoundSlug() {
58957
+ if (process.env.MRRLIN_PROJECT_BINDING === "none") return void 0;
58906
58958
  if (options.activeProjectSlug !== void 0) return options.activeProjectSlug;
58907
58959
  const envSlug = process.env.MRRLIN_PROJECT_SLUG?.trim();
58908
58960
  if (envSlug) return envSlug;
@@ -59102,10 +59154,35 @@ function createMrrlinTools(options) {
59102
59154
  `Task materialization digest mismatch for proposal ${proposalId} v${proposalVersion}.`
59103
59155
  );
59104
59156
  }
59157
+ if (confirmation.materializedTaskId) {
59158
+ return await c.getTask(projectSlug, confirmation.materializedTaskId);
59159
+ }
59105
59160
  const status = input.status ?? "todo";
59106
59161
  return await c.createTask(projectSlug, { ...input, status });
59107
59162
  }
59108
59163
  ),
59164
+ [mcpToolNames.proposeTask]: makeTool(
59165
+ client,
59166
+ mcpToolNames.proposeTask,
59167
+ "TASK_PROPOSE_FAILED",
59168
+ "Unable to propose task.",
59169
+ async (c, { projectSlug, kind, ...intent }) => {
59170
+ await checkTenantBoundary(projectSlug, "task proposal");
59171
+ const proposal = await c.createIntentProposal(projectSlug, {
59172
+ resolvedIntent: { kind: kind ?? "task", ...intent }
59173
+ });
59174
+ return {
59175
+ proposalId: proposal.proposalId,
59176
+ proposalVersion: proposal.version,
59177
+ proposalDigest: proposal.digest,
59178
+ state: proposal.state,
59179
+ kind: kind ?? "task",
59180
+ title: intent.title,
59181
+ awaitingOperatorConfirmation: true,
59182
+ next: (kind ?? "task") === "task" ? "Tell the operator the task is waiting for their confirmation in the chat. Their confirmation creates it; do not call create_task." : "Tell the operator the plan is waiting for their confirmation in the chat. After they confirm, call create_plan with proposalId, proposalVersion and proposalDigest."
59183
+ };
59184
+ }
59185
+ ),
59109
59186
  [mcpToolNames.getProject]: makeTool(
59110
59187
  client,
59111
59188
  mcpToolNames.getProject,
@@ -61664,7 +61741,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
61664
61741
  }
61665
61742
 
61666
61743
  // src/_generated/version.ts
61667
- var PKG_VERSION = "0.4.108";
61744
+ var PKG_VERSION = "0.4.109";
61668
61745
 
61669
61746
  // src/receipt-signing.ts
61670
61747
  var import_node_crypto13 = __toESM(require("node:crypto"), 1);
@@ -66359,7 +66436,7 @@ ${renderTaskStateSection(taskState)}`;
66359
66436
  turnCwd = worktreePath;
66360
66437
  useExecutor = true;
66361
66438
  if (deps.acquireExecutorAgent) {
66362
- const executorAgent = await deps.acquireExecutorAgent(worktreePath, resolvedProviderId);
66439
+ const executorAgent = await deps.acquireExecutorAgent(worktreePath, resolvedProviderId, projectSlug);
66363
66440
  codex = executorAgent.handle;
66364
66441
  disposeCodex = executorAgent.dispose;
66365
66442
  } else {
@@ -74276,6 +74353,13 @@ function neutralCheckoutCwd(stateDir) {
74276
74353
  }
74277
74354
  return dir;
74278
74355
  }
74356
+ function directorThreadEnv(projectSlug) {
74357
+ const slug = projectSlug ?? "";
74358
+ if (!slug || slug.length > 200 || /[\s\p{Cc}]/u.test(slug)) {
74359
+ return { MRRLIN_PROJECT_SLUG: null, MRRLIN_PROJECT_BINDING: "none" };
74360
+ }
74361
+ return { MRRLIN_PROJECT_SLUG: slug, MRRLIN_PROJECT_BINDING: null };
74362
+ }
74279
74363
  function buildThreadStartParams(msg, config2, desiredModel) {
74280
74364
  const baseInstructions = [
74281
74365
  "You are the Mrrlin Director assistant.",
@@ -74313,10 +74397,10 @@ function buildThreadStartParams(msg, config2, desiredModel) {
74313
74397
  "- Every read or write of Mrrlin data \u2014 tasks, plans, wiki pages, inbox items, execution runs,",
74314
74398
  " projects \u2014 must go through the corresponding typed MCP tool. Never use `commandExecution`,",
74315
74399
  " `curl`, `python`, or any direct HTTP call to the Mrrlin API.",
74316
- "- A single `create_task` call creates a task completely; it accepts `title`, `notes`, `category`,",
74317
- " `subcategory`, `impact`, `autonomyLevel`, `planId`, `parentTaskId`, and more in one call.",
74318
- " Do not create then patch, and do not re-read the task afterwards unless the operator asked",
74319
- " for confirmation.",
74400
+ "- New work is created through `propose_task`, one call per task: it accepts `title`, `notes`,",
74401
+ " `category`, `subcategory`, `impact`, `autonomyLevel`, `planId`, and more in one call. The",
74402
+ " operator confirms the proposal in the chat, and that confirmation creates the task \u2014 you do",
74403
+ " not call `create_task` for it. Do not propose then patch.",
74320
74404
  "- `commandExecution` is for the operator's own machine and repositories \u2014 running builds, tests,",
74321
74405
  " `git`, inspecting local files \u2014 and for public third-party web checks. It is not a transport",
74322
74406
  " for this product's API.",
@@ -74399,11 +74483,12 @@ function buildThreadStartParams(msg, config2, desiredModel) {
74399
74483
  "NOT add words of your own.",
74400
74484
  "",
74401
74485
  "Once the operator replies and clarifies or confirms the scope (or if scope was already clear),",
74402
- "you MUST create the tasks: create the plan (if needed) and call `create_task` in the same turn so",
74403
- "actionable task cards appear on the Tasks board immediately. Never leave the board empty with 0",
74404
- "tasks after agreeing on scope. Never claim tasks are created, queued, or ready unless `create_task`",
74405
- "was actually invoked and returned the task ID. Set `status: 'todo'` on newly created actionable",
74406
- "tasks unless explicitly asked for backlog, so they appear in active Kanban columns immediately.",
74486
+ "you MUST propose the tasks in the same turn: call `propose_task` once per task (`kind: 'plan'` for",
74487
+ "a plan) so the operator gets a confirmation card for each one immediately. Never leave the",
74488
+ "operator with nothing to confirm after agreeing on scope. Never claim tasks are created, queued,",
74489
+ "or ready before the operator confirmed them: say they are waiting for the operator's confirmation",
74490
+ "in the chat. Proposals bind to the operator's latest message, so propose in the same turn as the",
74491
+ "request \u2014 a later operator message makes an unconfirmed proposal stale.",
74407
74492
  "When referring to created tasks or where to view them, use natural, human-friendly language with a markdown link",
74408
74493
  "to the Tasks page (e.g. 'on the [Tasks page](/tasks)', '[Tasks board](/tasks)', or in Ukrainian '\u043D\u0430 [\u0441\u0442\u043E\u0440\u0456\u043D\u0446\u0456 \u0437\u0430\u0434\u0430\u0447](/tasks)').",
74409
74494
  "Never output raw technical URL fragments like '\u043D\u0430 /tasks' or 'on /tasks', and never use developer database",
@@ -74690,7 +74775,10 @@ function buildThreadStartParams(msg, config2, desiredModel) {
74690
74775
  model: desiredModel,
74691
74776
  baseInstructions,
74692
74777
  developerInstructions: null,
74693
- ephemeral: null
74778
+ ephemeral: null,
74779
+ // Claude and Agy spawn a process per thread and every Director turn starts or resumes one,
74780
+ // so the turn's own project always reaches the child, including after a project switch.
74781
+ env: directorThreadEnv(msg.context?.projectSlug)
74694
74782
  };
74695
74783
  }
74696
74784
  function readEnvInt(name, fallback) {
@@ -76778,9 +76866,12 @@ async function startDirectorBridge() {
76778
76866
  // Task 1.11: providerId is the caller's own pinned/minted seat (execution-consumer.ts's
76779
76867
  // resolvedProviderId), not resolved ambiently here — this run-scoped spawn has no business
76780
76868
  // re-reading a setting that could have moved since the run's own seat was decided.
76781
- acquireExecutorAgent: async (worktreePath, providerId) => {
76869
+ acquireExecutorAgent: async (worktreePath, providerId, projectSlug) => {
76782
76870
  const executor = await startExecutorServer({
76783
76871
  worktree: worktreePath,
76872
+ // The run's provider process carries exactly its own project (or none), so its Mrrlin MCP
76873
+ // server is bound to it and never to whatever the bridge's environment names.
76874
+ env: threadProcessEnv(process.env, directorThreadEnv(projectSlug)),
76784
76875
  disabledGitHooksDir: prepareDisabledGitHooksDir(import_node_path40.default.join(config2.stateDir, "locks"), import_node_path40.default.basename(worktreePath)),
76785
76876
  provider: providerId
76786
76877
  });
@@ -77711,7 +77802,7 @@ function renderContextPrefix(context) {
77711
77802
  }
77712
77803
  if (context?.activeProposal) {
77713
77804
  parts.push(
77714
- `[ACTIVE PROPOSAL] Proposal ID: ${context.activeProposal.id} (version ${context.activeProposal.version}). Work materialization requires explicit operator confirmation of this proposal. You MUST provide proposalId, proposalVersion, and proposalDigest when creating tasks or plans. Direct agent materialization without confirmation will fail closed.`
77805
+ `[ACTIVE PROPOSAL] Proposal ID: ${context.activeProposal.id} (version ${context.activeProposal.version}). Work is created only by the operator confirming a proposal in the chat; propose new work with propose_task. create_plan, and create_task for an already-confirmed proposal, need its proposalId, proposalVersion, and proposalDigest.`
77715
77806
  );
77716
77807
  }
77717
77808
  if (context?.clarifications?.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrrlin-dev/mcp",
3
- "version": "0.4.108",
3
+ "version": "0.4.109",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mrrlin-mcp": "dist/bin.cjs"