@mrrlin-dev/mcp 0.4.108 → 0.4.110

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 +118 -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,24 @@ 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
+ Reflect.deleteProperty(env, key);
44192
+ } else {
44193
+ env[key] = value;
44194
+ }
44195
+ }
44196
+ return env;
44197
+ }
44198
+ function envOption(env) {
44199
+ return env === void 0 ? {} : { env };
44200
+ }
44201
+
44173
44202
  // ../../packages/agent-client/dist/providers/claude/process.js
44174
44203
  var import_node_child_process3 = require("node:child_process");
44175
44204
  var import_node_crypto = require("node:crypto");
@@ -44550,7 +44579,7 @@ var ClaudeAgentHandle = class {
44550
44579
  appendSystemPrompt: params.baseInstructions,
44551
44580
  wrapCommand: this.wrapCommand,
44552
44581
  brokerSocket: this.brokerSocket,
44553
- ...this.childEnv === void 0 ? {} : { env: this.childEnv }
44582
+ ...envOption(threadProcessEnv(this.childEnv, params.env))
44554
44583
  });
44555
44584
  this.processes.set(child.sessionId, child);
44556
44585
  this.mappers.set(child.sessionId, new ClaudeBridgeEventMapper());
@@ -44572,7 +44601,7 @@ var ClaudeAgentHandle = class {
44572
44601
  appendSystemPrompt: params.baseInstructions,
44573
44602
  wrapCommand: this.wrapCommand,
44574
44603
  brokerSocket: this.brokerSocket,
44575
- ...this.childEnv === void 0 ? {} : { env: this.childEnv }
44604
+ ...envOption(threadProcessEnv(this.childEnv, params.env))
44576
44605
  });
44577
44606
  this.processes.set(threadId, child);
44578
44607
  this.mappers.set(threadId, new ClaudeBridgeEventMapper());
@@ -45086,7 +45115,7 @@ var AgyAgentHandle = class {
45086
45115
  wrapCommand: this.wrapCommand,
45087
45116
  brokerSocket: this.brokerSocket,
45088
45117
  disabledTools: this.disabledTools,
45089
- ...this.childEnv === void 0 ? {} : { env: this.childEnv }
45118
+ ...envOption(threadProcessEnv(this.childEnv, params.env))
45090
45119
  });
45091
45120
  this.processes.set(child.sessionId, child);
45092
45121
  this.mappers.set(child.sessionId, new AgyBridgeEventMapper());
@@ -45110,7 +45139,7 @@ var AgyAgentHandle = class {
45110
45139
  wrapCommand: this.wrapCommand,
45111
45140
  brokerSocket: this.brokerSocket,
45112
45141
  disabledTools: this.disabledTools,
45113
- ...this.childEnv === void 0 ? {} : { env: this.childEnv }
45142
+ ...envOption(threadProcessEnv(this.childEnv, params.env))
45114
45143
  });
45115
45144
  this.processes.set(threadId, child);
45116
45145
  this.mappers.set(threadId, new AgyBridgeEventMapper());
@@ -57016,6 +57045,9 @@ var READ_TOOL_NAMES = /* @__PURE__ */ new Set([
57016
57045
  "search_knowledge",
57017
57046
  "search_wiki_pages",
57018
57047
  "create_task",
57048
+ // propose_task only records a pending intent proposal; nothing happens until the operator
57049
+ // confirms it in the web UI, so it is as in-product as create_task.
57050
+ "propose_task",
57019
57051
  "update_task",
57020
57052
  "create_wiki_page",
57021
57053
  "update_wiki_page",
@@ -57781,6 +57813,7 @@ var mcpToolNames = {
57781
57813
  deleteTaskDependency: "delete_task_dependency",
57782
57814
  createInboxItem: "create_inbox_item",
57783
57815
  createTask: "create_task",
57816
+ proposeTask: "propose_task",
57784
57817
  decideInboxItem: "decide_inbox_item",
57785
57818
  getExecutionRun: "get_execution_run",
57786
57819
  getProjectSchedulerPolicy: "get_project_scheduler_policy",
@@ -58403,6 +58436,22 @@ var listTasksInputSchema = projectScopedSchema.merge(mrrlinTaskListFiltersSchema
58403
58436
  var getTaskInputSchema = taskScopedSchema;
58404
58437
  var createTaskInputSchema = proposalProvenanceSchema.merge(projectScopedSchema).merge(mrrlinTaskCreateWithoutIdSchema);
58405
58438
  var updateTaskInputSchema = taskScopedSchema.merge(mrrlinTaskUpdateSchema);
58439
+ var proposeTaskInputSchema = projectScopedSchema.extend({
58440
+ 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."),
58441
+ title: external_exports.string().min(1).max(500),
58442
+ notes: external_exports.string().max(2e4).optional(),
58443
+ type: mrrlinTaskTypeSchema.optional(),
58444
+ category: external_exports.string().min(1).optional(),
58445
+ subcategory: external_exports.string().min(1).optional(),
58446
+ impact: mrrlinTaskImpactSchema.optional(),
58447
+ autonomyLevel: mrrlinAutonomyLevelSchema.optional(),
58448
+ autonomyReason: external_exports.string().min(1).max(240).optional(),
58449
+ autoDeploy: mrrlinAutoDeploySchema.optional(),
58450
+ domains: mrrlinTaskDomainsSchema.optional(),
58451
+ tags: external_exports.array(external_exports.string().min(1)).optional(),
58452
+ planId: mrrlinPlanIdSchema.optional(),
58453
+ specWikiPageId: mrrlinWikiPageIdSchema.optional()
58454
+ });
58406
58455
  var resetDirectorThreadInputSchema = taskScopedSchema;
58407
58456
  var archiveTaskInputSchema = taskScopedSchema;
58408
58457
  var unarchiveTaskInputSchema = taskScopedSchema;
@@ -58540,6 +58589,7 @@ var mcpToolInputSchemas = {
58540
58589
  [mcpToolNames.createWikiPage]: createWikiPageInputSchema,
58541
58590
  [mcpToolNames.deleteTaskDependency]: deleteTaskDependencyInputSchema,
58542
58591
  [mcpToolNames.createTask]: createTaskInputSchema,
58592
+ [mcpToolNames.proposeTask]: proposeTaskInputSchema,
58543
58593
  [mcpToolNames.getExecutionRun]: getExecutionRunInputSchema,
58544
58594
  [mcpToolNames.getProjectSchedulerPolicy]: getProjectSchedulerPolicyInputSchema,
58545
58595
  [mcpToolNames.getSchedulerPendingTasks]: getSchedulerPendingTasksInputSchema,
@@ -58604,6 +58654,7 @@ var mcpToolOperationIds = {
58604
58654
  [mcpToolNames.createWikiPage]: openApiOperationIds.createWikiPage,
58605
58655
  [mcpToolNames.deleteTaskDependency]: openApiOperationIds.deleteTaskDependency,
58606
58656
  [mcpToolNames.createTask]: openApiOperationIds.createTask,
58657
+ [mcpToolNames.proposeTask]: openApiOperationIds.createIntentProposal,
58607
58658
  [mcpToolNames.getExecutionRun]: openApiOperationIds.getExecutionRun,
58608
58659
  [mcpToolNames.getProjectSchedulerPolicy]: openApiOperationIds.getProjectSchedulerPolicy,
58609
58660
  [mcpToolNames.getSchedulerPendingTasks]: openApiOperationIds.getSchedulerPendingTasks,
@@ -58658,7 +58709,8 @@ var mcpToolDescriptions = {
58658
58709
  [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
58710
  [mcpToolNames.createWikiPage]: "Create a markdown Wiki page.",
58660
58711
  [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.",
58712
+ [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.",
58713
+ [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
58714
  [mcpToolNames.getProject]: "Fetch a single project summary by slug.",
58663
58715
  [mcpToolNames.getProjectSnapshot]: "Fetch the full project snapshot: GitHub binding, tasks, plans, and wiki pages.",
58664
58716
  [mcpToolNames.getTask]: "Fetch a single task by project slug and task id.",
@@ -58903,6 +58955,7 @@ async function getAgentProviderForPanel(client, opts = {}) {
58903
58955
  function createMrrlinTools(options) {
58904
58956
  const client = createMrrlinClient(withAuth(options));
58905
58957
  async function resolveBoundSlug() {
58958
+ if (process.env.MRRLIN_PROJECT_BINDING === "none") return void 0;
58906
58959
  if (options.activeProjectSlug !== void 0) return options.activeProjectSlug;
58907
58960
  const envSlug = process.env.MRRLIN_PROJECT_SLUG?.trim();
58908
58961
  if (envSlug) return envSlug;
@@ -59102,10 +59155,35 @@ function createMrrlinTools(options) {
59102
59155
  `Task materialization digest mismatch for proposal ${proposalId} v${proposalVersion}.`
59103
59156
  );
59104
59157
  }
59158
+ if (confirmation.materializedTaskId) {
59159
+ return await c.getTask(projectSlug, confirmation.materializedTaskId);
59160
+ }
59105
59161
  const status = input.status ?? "todo";
59106
59162
  return await c.createTask(projectSlug, { ...input, status });
59107
59163
  }
59108
59164
  ),
59165
+ [mcpToolNames.proposeTask]: makeTool(
59166
+ client,
59167
+ mcpToolNames.proposeTask,
59168
+ "TASK_PROPOSE_FAILED",
59169
+ "Unable to propose task.",
59170
+ async (c, { projectSlug, kind, ...intent }) => {
59171
+ await checkTenantBoundary(projectSlug, "task proposal");
59172
+ const proposal = await c.createIntentProposal(projectSlug, {
59173
+ resolvedIntent: { kind: kind ?? "task", ...intent }
59174
+ });
59175
+ return {
59176
+ proposalId: proposal.proposalId,
59177
+ proposalVersion: proposal.version,
59178
+ proposalDigest: proposal.digest,
59179
+ state: proposal.state,
59180
+ kind: kind ?? "task",
59181
+ title: intent.title,
59182
+ awaitingOperatorConfirmation: true,
59183
+ 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."
59184
+ };
59185
+ }
59186
+ ),
59109
59187
  [mcpToolNames.getProject]: makeTool(
59110
59188
  client,
59111
59189
  mcpToolNames.getProject,
@@ -61664,7 +61742,7 @@ Do NOT derail the current task - if the operator has an in-flight question, answ
61664
61742
  }
61665
61743
 
61666
61744
  // src/_generated/version.ts
61667
- var PKG_VERSION = "0.4.108";
61745
+ var PKG_VERSION = "0.4.110";
61668
61746
 
61669
61747
  // src/receipt-signing.ts
61670
61748
  var import_node_crypto13 = __toESM(require("node:crypto"), 1);
@@ -66359,7 +66437,7 @@ ${renderTaskStateSection(taskState)}`;
66359
66437
  turnCwd = worktreePath;
66360
66438
  useExecutor = true;
66361
66439
  if (deps.acquireExecutorAgent) {
66362
- const executorAgent = await deps.acquireExecutorAgent(worktreePath, resolvedProviderId);
66440
+ const executorAgent = await deps.acquireExecutorAgent(worktreePath, resolvedProviderId, projectSlug);
66363
66441
  codex = executorAgent.handle;
66364
66442
  disposeCodex = executorAgent.dispose;
66365
66443
  } else {
@@ -74276,6 +74354,13 @@ function neutralCheckoutCwd(stateDir) {
74276
74354
  }
74277
74355
  return dir;
74278
74356
  }
74357
+ function directorThreadEnv(projectSlug) {
74358
+ const slug = projectSlug ?? "";
74359
+ if (!slug || slug.length > 200 || /[\s\p{Cc}]/u.test(slug)) {
74360
+ return { MRRLIN_PROJECT_SLUG: null, MRRLIN_PROJECT_BINDING: "none" };
74361
+ }
74362
+ return { MRRLIN_PROJECT_SLUG: slug, MRRLIN_PROJECT_BINDING: null };
74363
+ }
74279
74364
  function buildThreadStartParams(msg, config2, desiredModel) {
74280
74365
  const baseInstructions = [
74281
74366
  "You are the Mrrlin Director assistant.",
@@ -74313,10 +74398,10 @@ function buildThreadStartParams(msg, config2, desiredModel) {
74313
74398
  "- Every read or write of Mrrlin data \u2014 tasks, plans, wiki pages, inbox items, execution runs,",
74314
74399
  " projects \u2014 must go through the corresponding typed MCP tool. Never use `commandExecution`,",
74315
74400
  " `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.",
74401
+ "- New work is created through `propose_task`, one call per task: it accepts `title`, `notes`,",
74402
+ " `category`, `subcategory`, `impact`, `autonomyLevel`, `planId`, and more in one call. The",
74403
+ " operator confirms the proposal in the chat, and that confirmation creates the task \u2014 you do",
74404
+ " not call `create_task` for it. Do not propose then patch.",
74320
74405
  "- `commandExecution` is for the operator's own machine and repositories \u2014 running builds, tests,",
74321
74406
  " `git`, inspecting local files \u2014 and for public third-party web checks. It is not a transport",
74322
74407
  " for this product's API.",
@@ -74399,11 +74484,12 @@ function buildThreadStartParams(msg, config2, desiredModel) {
74399
74484
  "NOT add words of your own.",
74400
74485
  "",
74401
74486
  "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.",
74487
+ "you MUST propose the tasks in the same turn: call `propose_task` once per task (`kind: 'plan'` for",
74488
+ "a plan) so the operator gets a confirmation card for each one immediately. Never leave the",
74489
+ "operator with nothing to confirm after agreeing on scope. Never claim tasks are created, queued,",
74490
+ "or ready before the operator confirmed them: say they are waiting for the operator's confirmation",
74491
+ "in the chat. Proposals bind to the operator's latest message, so propose in the same turn as the",
74492
+ "request \u2014 a later operator message makes an unconfirmed proposal stale.",
74407
74493
  "When referring to created tasks or where to view them, use natural, human-friendly language with a markdown link",
74408
74494
  "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
74495
  "Never output raw technical URL fragments like '\u043D\u0430 /tasks' or 'on /tasks', and never use developer database",
@@ -74690,7 +74776,10 @@ function buildThreadStartParams(msg, config2, desiredModel) {
74690
74776
  model: desiredModel,
74691
74777
  baseInstructions,
74692
74778
  developerInstructions: null,
74693
- ephemeral: null
74779
+ ephemeral: null,
74780
+ // Claude and Agy spawn a process per thread and every Director turn starts or resumes one,
74781
+ // so the turn's own project always reaches the child, including after a project switch.
74782
+ env: directorThreadEnv(msg.context?.projectSlug)
74694
74783
  };
74695
74784
  }
74696
74785
  function readEnvInt(name, fallback) {
@@ -76778,9 +76867,12 @@ async function startDirectorBridge() {
76778
76867
  // Task 1.11: providerId is the caller's own pinned/minted seat (execution-consumer.ts's
76779
76868
  // resolvedProviderId), not resolved ambiently here — this run-scoped spawn has no business
76780
76869
  // re-reading a setting that could have moved since the run's own seat was decided.
76781
- acquireExecutorAgent: async (worktreePath, providerId) => {
76870
+ acquireExecutorAgent: async (worktreePath, providerId, projectSlug) => {
76782
76871
  const executor = await startExecutorServer({
76783
76872
  worktree: worktreePath,
76873
+ // The run's provider process carries exactly its own project (or none), so its Mrrlin MCP
76874
+ // server is bound to it and never to whatever the bridge's environment names.
76875
+ env: threadProcessEnv(process.env, directorThreadEnv(projectSlug)),
76784
76876
  disabledGitHooksDir: prepareDisabledGitHooksDir(import_node_path40.default.join(config2.stateDir, "locks"), import_node_path40.default.basename(worktreePath)),
76785
76877
  provider: providerId
76786
76878
  });
@@ -77711,7 +77803,7 @@ function renderContextPrefix(context) {
77711
77803
  }
77712
77804
  if (context?.activeProposal) {
77713
77805
  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.`
77806
+ `[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
77807
  );
77716
77808
  }
77717
77809
  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.110",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mrrlin-mcp": "dist/bin.cjs"