orkestrator 2.34.5-beta → 2.35.0-beta

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.
@@ -63,6 +63,7 @@ var PROMPT_TIMEOUT_MS = parseBoundedInteger(process.env.CURSOR_BRIDGE_PROMPT_TIM
63
63
  var CANCEL_ACK_TIMEOUT_MS = parseBoundedInteger(process.env.CURSOR_BRIDGE_CANCEL_ACK_TIMEOUT_MS, 30 * 1000, 1000, 5 * 60 * 1000);
64
64
  var CLOSE_ACK_TIMEOUT_MS = 4000;
65
65
  var CATALOG_TIMEOUT_MS = 30000;
66
+ var ACCOUNT_LOOKUP_TIMEOUT_MS = 3000;
66
67
  var LOGIN_TIMEOUT_MS = 10 * 60 * 1000;
67
68
  function authenticate(headers) {
68
69
  const bearer = firstHeader(headers.authorization);
@@ -132,6 +133,8 @@ import { Cursor, FileCredentialStore } from "@cursor/sdk";
132
133
  var CURSOR_AUTHENTICATION_REQUIRED_MESSAGE = "Cursor is not signed in. Sign in from Settings › Cursor, or set a Cursor API key.";
133
134
  var AUTH_CACHE_TTL_MS = 15000;
134
135
  var authCache;
136
+ var accountLookup;
137
+ var accountLookupTimeoutMs = ACCOUNT_LOOKUP_TIMEOUT_MS;
135
138
 
136
139
  class LazyFileCredentialStore {
137
140
  store;
@@ -152,16 +155,25 @@ var credentialStore = new LazyFileCredentialStore;
152
155
  var cursorAuth = Cursor.auth;
153
156
  var cursorMe = Cursor.me;
154
157
  function useCursorCredentialRuntimeForTests(runtime) {
155
- const previous = { store: credentialStore, auth: cursorAuth, me: cursorMe };
158
+ const previous = {
159
+ store: credentialStore,
160
+ auth: cursorAuth,
161
+ me: cursorMe,
162
+ meTimeoutMs: accountLookupTimeoutMs
163
+ };
156
164
  credentialStore = runtime.store;
157
165
  cursorAuth = runtime.auth;
158
166
  cursorMe = runtime.me;
167
+ accountLookupTimeoutMs = runtime.meTimeoutMs ?? ACCOUNT_LOOKUP_TIMEOUT_MS;
159
168
  authCache = undefined;
169
+ accountLookup = undefined;
160
170
  return () => {
161
171
  credentialStore = previous.store;
162
172
  cursorAuth = previous.auth;
163
173
  cursorMe = previous.me;
174
+ accountLookupTimeoutMs = previous.meTimeoutMs;
164
175
  authCache = undefined;
176
+ accountLookup = undefined;
165
177
  };
166
178
  }
167
179
  async function resolveCredential() {
@@ -198,10 +210,8 @@ async function authStatus() {
198
210
  if (credential && authCache?.credential === credential && authCache.expiresAt > Date.now()) {
199
211
  return authCache.value;
200
212
  }
201
- const meFn = cursorMe;
202
- const me = credential && meFn ? await meFn({ apiKey: credential }).catch(() => {
203
- return;
204
- }) : undefined;
213
+ const lookup = credential ? await lookUpAccount(credential) : { settled: true, account: undefined };
214
+ const me = lookup.account;
205
215
  const label = [me?.userFirstName, me?.userLastName].filter(Boolean).join(" ") || me?.apiKeyName || ("email" in sdkStatus ? sdkStatus.email : undefined) || stored?.email || "Cursor account";
206
216
  const value = {
207
217
  state: "signed-in",
@@ -212,11 +222,52 @@ async function authStatus() {
212
222
  signIn: fromEnvironment ? { kind: "none", hint: "Remove CURSOR_API_KEY to sign out." } : { kind: "browser-url" },
213
223
  signOut: !fromEnvironment
214
224
  };
215
- if (credential) {
225
+ if (credential && lookup.settled) {
216
226
  authCache = { credential, expiresAt: Date.now() + AUTH_CACHE_TTL_MS, value };
217
227
  }
218
228
  return value;
219
229
  }
230
+ async function lookUpAccount(credential) {
231
+ const meFn = cursorMe;
232
+ if (!meFn)
233
+ return { settled: true, account: undefined };
234
+ let entry = accountLookup;
235
+ if (!entry || entry.credential !== credential || entry.expiresAt <= Date.now()) {
236
+ const started = {
237
+ credential,
238
+ expiresAt: Date.now() + AUTH_CACHE_TTL_MS,
239
+ timedOut: false,
240
+ result: meFn({ apiKey: credential }).catch(() => {
241
+ return;
242
+ })
243
+ };
244
+ started.result.finally(() => {
245
+ if (accountLookup === started)
246
+ accountLookup = undefined;
247
+ });
248
+ entry = accountLookup = started;
249
+ }
250
+ if (entry.timedOut)
251
+ return { settled: false, account: undefined };
252
+ const active = entry;
253
+ const { result } = entry;
254
+ let timer;
255
+ try {
256
+ return await Promise.race([
257
+ result.then((account) => ({ settled: true, account })),
258
+ new Promise((resolve) => {
259
+ timer = setTimeout(() => {
260
+ active.timedOut = true;
261
+ resolve({ settled: false, account: undefined });
262
+ }, accountLookupTimeoutMs);
263
+ timer.unref();
264
+ })
265
+ ]);
266
+ } finally {
267
+ if (timer)
268
+ clearTimeout(timer);
269
+ }
270
+ }
220
271
  function beginLogin(options = {}) {
221
272
  const controller = new AbortController;
222
273
  let publishUrl = () => {
@@ -252,6 +303,7 @@ function beginLogin(options = {}) {
252
303
  }
253
304
  async function logout() {
254
305
  authCache = undefined;
306
+ accountLookup = undefined;
255
307
  await cursorAuth.logout({ store: credentialStore });
256
308
  }
257
309
  function isExpired(expiresAtMs) {
@@ -23886,6 +23938,63 @@ var PROTECTED_SEGMENTS = new Set([
23886
23938
  ]);
23887
23939
 
23888
23940
  // ../protocol/src/review-evidence-frames.ts
23941
+ var SYSTEM_INSTRUCTIONS_FRAME_OPEN = "<orkestrator-system-instructions>";
23942
+ var SYSTEM_INSTRUCTIONS_FRAME_CLOSE = "</orkestrator-system-instructions>";
23943
+ function neutralizeSystemInstructionMarkers(content) {
23944
+ if (!content.includes(SYSTEM_INSTRUCTIONS_FRAME_OPEN) && !content.includes(SYSTEM_INSTRUCTIONS_FRAME_CLOSE)) {
23945
+ return content;
23946
+ }
23947
+ return content.replaceAll(SYSTEM_INSTRUCTIONS_FRAME_CLOSE, "\\u003c/orkestrator-system-instructions\\u003e").replaceAll(SYSTEM_INSTRUCTIONS_FRAME_OPEN, "\\u003corkestrator-system-instructions\\u003e");
23948
+ }
23949
+ function wrapSystemInstructions(...parts) {
23950
+ const content = neutralizeSystemInstructionMarkers(parts.filter((part) => part.trim().length > 0).join(`
23951
+
23952
+ `));
23953
+ return `${SYSTEM_INSTRUCTIONS_FRAME_OPEN}
23954
+ ${content}
23955
+ ${SYSTEM_INSTRUCTIONS_FRAME_CLOSE}`;
23956
+ }
23957
+ function stripSystemInstructions(source) {
23958
+ if (!source.includes(SYSTEM_INSTRUCTIONS_FRAME_OPEN))
23959
+ return source;
23960
+ const openLength = SYSTEM_INSTRUCTIONS_FRAME_OPEN.length;
23961
+ const closeLength = SYSTEM_INSTRUCTIONS_FRAME_CLOSE.length;
23962
+ const removed = [];
23963
+ const openStack = [];
23964
+ let index = 0;
23965
+ while (index < source.length) {
23966
+ if (source.startsWith(SYSTEM_INSTRUCTIONS_FRAME_OPEN, index)) {
23967
+ openStack.push(index);
23968
+ index += openLength;
23969
+ continue;
23970
+ }
23971
+ if (source.startsWith(SYSTEM_INSTRUCTIONS_FRAME_CLOSE, index)) {
23972
+ const open = openStack.pop();
23973
+ if (open !== undefined)
23974
+ removed.push([open, index + closeLength]);
23975
+ index += closeLength;
23976
+ continue;
23977
+ }
23978
+ index += 1;
23979
+ }
23980
+ if (removed.length === 0)
23981
+ return source;
23982
+ removed.sort((left, right) => left[0] - right[0]);
23983
+ let result = "";
23984
+ let cursor = 0;
23985
+ for (const [start, end] of removed) {
23986
+ if (start < cursor) {
23987
+ cursor = Math.max(cursor, end);
23988
+ continue;
23989
+ }
23990
+ result += source.slice(cursor, start);
23991
+ cursor = end;
23992
+ }
23993
+ result += source.slice(cursor);
23994
+ return result.replace(/\n{3,}/g, `
23995
+
23996
+ `).trim();
23997
+ }
23889
23998
  var COORDINATOR_WORKER_NO_PR_INSTRUCTION = "Do not open a pull request, even if the prompt below asks you to: commit your work and report back. Orkestrator raises the pull request from a PR tab once the work has been reviewed.";
23890
23999
  var COORDINATOR_JOB_DELEGATION_INSTRUCTION = `This is a server-attested same-project worker delegation. Work only inside this disposable environment under its normal sandbox and approval policy, then report meaningful completion, failure, or blocking details through Orkestrator mail. ${COORDINATOR_WORKER_NO_PR_INSTRUCTION}`;
23891
24000
  var COORDINATOR_ENVIRONMENT_DELEGATION_INSTRUCTION = `This is a server-attested same-project worker delegation. Perform it inside this disposable worker under its normal sandbox and approval policy, then report meaningful completion, failure, or blocking details through Orkestrator mail. ${COORDINATOR_WORKER_NO_PR_INSTRUCTION}`;
@@ -31538,6 +31647,10 @@ var configHomeForTests;
31538
31647
  function setCursorMcpConfigHomeForTests(home) {
31539
31648
  configHomeForTests = home;
31540
31649
  }
31650
+ var skillsProjectForTests;
31651
+ function setCursorSkillsProjectForTests(directory) {
31652
+ skillsProjectForTests = directory;
31653
+ }
31541
31654
  var attachedConfigKeys = new WeakMap;
31542
31655
  var attachedConfigRevisions = new WeakMap;
31543
31656
  var fingerprintForTests;
@@ -31629,7 +31742,7 @@ async function attach(state) {
31629
31742
  await closeHosted?.();
31630
31743
  throw new SessionClosedError;
31631
31744
  }
31632
- const skills = readOnly ? hostSkillCustomTools(process.env, workingDirectory) : undefined;
31745
+ const skills = readOnly ? hostSkillCustomTools(process.env, skillsProjectForTests ?? workingDirectory) : undefined;
31633
31746
  const customTools = hosted || research || skills ? { ...skills?.customTools, ...research?.customTools, ...hosted?.customTools } : undefined;
31634
31747
  const httpMcpServerNames = hosted ? [] : allowHttpMcp ? Object.keys(mcpServers) : [];
31635
31748
  state.hostedMcpClose = closeHosted;
@@ -31641,16 +31754,18 @@ async function attach(state) {
31641
31754
  ...skills ? [COORDINATOR_SKILLS_SERVER] : []
31642
31755
  ];
31643
31756
  state.attachedMcpKey = mcpConnectionKey(state.agentMcp);
31757
+ const settingSources = cursorSettingSources(policy, {
31758
+ readOnly,
31759
+ selectsServers: selectServers !== undefined
31760
+ });
31761
+ state.loadsProjectRules = settingSources.includes("project");
31644
31762
  const options = {
31645
31763
  apiKey,
31646
31764
  model: modelSelection(state.composer),
31647
31765
  mode: state.composer.selectedModeId === "plan" ? "plan" : "agent",
31648
31766
  local: {
31649
31767
  cwd: workingDirectory,
31650
- settingSources: cursorSettingSources(policy, {
31651
- readOnly,
31652
- selectsServers: selectServers !== undefined
31653
- }),
31768
+ settingSources,
31654
31769
  sandboxOptions: { enabled: policy.sandbox === "provider" },
31655
31770
  autoReview: policy.sandbox === "provider" && policy.approvals === "auto-approve",
31656
31771
  ...customTools ? { customTools } : {}
@@ -32202,11 +32317,8 @@ function pushHistoricRow(state, row, planned) {
32202
32317
  }
32203
32318
  function historicUserTexts(body) {
32204
32319
  const extract = (value) => {
32205
- if (typeof value === "string")
32206
- return value;
32207
- if (isObject(value) && nonBlank(value.text))
32208
- return value.text;
32209
- return;
32320
+ const raw = typeof value === "string" ? value : isObject(value) && nonBlank(value.text) ? value.text : undefined;
32321
+ return raw === undefined ? undefined : stripSystemInstructions(raw);
32210
32322
  };
32211
32323
  const texts = [];
32212
32324
  const remaining = new Map;
@@ -32296,6 +32408,58 @@ function pushMessage(state, role, content, parts, messageId = randomBytes5(12).t
32296
32408
  state.messages.push(message);
32297
32409
  chargeTranscript(state, Buffer.byteLength(JSON.stringify(message)));
32298
32410
  }
32411
+ // ../protocol/src/agent-system-instructions.ts
32412
+ var AGENT_IMAGE_PREVIEW_INSTRUCTION = "Orkestrator renders Markdown images inline in the transcript. When you show the user an image in your reply (a screenshot, rendered preview, or chart) using Markdown image syntax, reference it by its absolute path. In a managed worktree or container environment, save the file inside the working directory, preferably in a git-ignored folder so preview files stay out of the change set. Local image previews are limited to Orkestrator-managed workspace storage or the container's /workspace. Project-home checkouts are excluded from host preview reads, even when they are the working directory, so images saved there cannot be previewed from a local Markdown path. Paths outside previewable storage, such as /tmp, also show a placeholder.";
32413
+ var AGENT_DOCUMENT_INSTRUCTION = "When the user asks for a document (a report, design doc, plan, analysis, guide, or other writing to keep), prefer writing it as a Markdown file in a `docs/` folder at the root of the working directory, and tell the user the file's path. Project instructions (such as AGENTS.md or CLAUDE.md), local skills, and the user's own request take priority over this default when they say where or how to write documents.";
32414
+ var GENERAL_AGENT_INSTRUCTIONS = [
32415
+ AGENT_IMAGE_PREVIEW_INSTRUCTION,
32416
+ AGENT_DOCUMENT_INSTRUCTION
32417
+ ];
32418
+ function agentSystemInstructions(providerSpecific = []) {
32419
+ return [...GENERAL_AGENT_INSTRUCTIONS, ...providerSpecific].filter((line) => line.trim().length > 0).join(`
32420
+
32421
+ `);
32422
+ }
32423
+ var REPOSITORY_INSTRUCTIONS_FILE = "AGENTS.md";
32424
+ function repositoryInstructionsBlock(path, content, truncated) {
32425
+ return [
32426
+ `# Repository instructions (${REPOSITORY_INSTRUCTIONS_FILE})`,
32427
+ `This session does not load the repository's ${REPOSITORY_INSTRUCTIONS_FILE} on its own, so Orkestrator includes it here. Follow it as project instructions. Contents of ${path}:`,
32428
+ content.trimEnd(),
32429
+ ...truncated ? [
32430
+ `[${REPOSITORY_INSTRUCTIONS_FILE} is longer than this session can hold, so it is cut here. Read the rest from ${path} before relying on anything it may say.]`
32431
+ ] : []
32432
+ ].join(`
32433
+
32434
+ `);
32435
+ }
32436
+
32437
+ // ../protocol/src/node/repository-instructions.ts
32438
+ import { open as open3, realpath } from "node:fs/promises";
32439
+ import { join as join7, relative as relative3, isAbsolute as isAbsolute3 } from "node:path";
32440
+ var MAX_REPOSITORY_INSTRUCTIONS_BYTES = 256 * 1024;
32441
+ async function readRepositoryInstructions(cwd, maxBytes = MAX_REPOSITORY_INSTRUCTIONS_BYTES) {
32442
+ const path = join7(cwd, REPOSITORY_INSTRUCTIONS_FILE);
32443
+ try {
32444
+ const [root, target] = await Promise.all([realpath(cwd), realpath(path)]);
32445
+ const inside = relative3(root, target);
32446
+ if (!inside || inside.startsWith("..") || isAbsolute3(inside))
32447
+ return;
32448
+ const handle = await open3(target, "r");
32449
+ try {
32450
+ const buffer = Buffer.alloc(maxBytes + 1);
32451
+ const { bytesRead } = await handle.read(buffer, 0, maxBytes + 1, 0);
32452
+ const truncated = bytesRead > maxBytes;
32453
+ const text = new TextDecoder().decode(buffer.subarray(0, Math.min(bytesRead, maxBytes))).replace(/�$/, "");
32454
+ return text.trim() ? repositoryInstructionsBlock(path, text, truncated) : undefined;
32455
+ } finally {
32456
+ await handle.close();
32457
+ }
32458
+ } catch {
32459
+ return;
32460
+ }
32461
+ }
32462
+
32299
32463
  // ../protocol/src/structured-output-text.ts
32300
32464
  var STRUCTURED_OUTPUT_RECOVERY_CHARS = 1024 * 1024;
32301
32465
  var STRUCTURED_OUTPUT_RECOVERY_CANDIDATES = 256;
@@ -32508,7 +32672,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
32508
32672
  import { rmSync } from "node:fs";
32509
32673
  import { copyFile, lstat, mkdtemp, rm as rm2 } from "node:fs/promises";
32510
32674
  import { tmpdir } from "node:os";
32511
- import { delimiter, isAbsolute as isAbsolute3, join as join7, resolve as resolve4 } from "node:path";
32675
+ import { delimiter, isAbsolute as isAbsolute4, join as join8, resolve as resolve4 } from "node:path";
32512
32676
  var DEFAULT_MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
32513
32677
  var DEFAULT_MAX_UNTRACKED_FILES = 5000;
32514
32678
  var DEFAULT_SLOW_SNAPSHOT_MS = 500;
@@ -32790,8 +32954,8 @@ class WorkspaceChangeProbe {
32790
32954
  return;
32791
32955
  return {
32792
32956
  root,
32793
- indexPath: isAbsolute3(index) ? index : resolve4(cwd, index),
32794
- objectsPath: isAbsolute3(objects) ? objects : resolve4(cwd, objects)
32957
+ indexPath: isAbsolute4(index) ? index : resolve4(cwd, index),
32958
+ objectsPath: isAbsolute4(objects) ? objects : resolve4(cwd, objects)
32795
32959
  };
32796
32960
  }).catch(() => {
32797
32961
  return;
@@ -32834,8 +32998,8 @@ class WorkspaceChangeProbe {
32834
32998
  return;
32835
32999
  const { root, indexPath } = repo.location;
32836
33000
  const started = this.now();
32837
- const tempIndex = join7(this.tempDir, `orkestrator-probe-${randomUUID2()}.index`);
32838
- const objectDirectory = await mkdtemp(join7(this.tempDir, "orkestrator-probe-objects-"));
33001
+ const tempIndex = join8(this.tempDir, `orkestrator-probe-${randomUUID2()}.index`);
33002
+ const objectDirectory = await mkdtemp(join8(this.tempDir, "orkestrator-probe-objects-"));
32839
33003
  temporaryObjectDirectories.add(objectDirectory);
32840
33004
  let retained = false;
32841
33005
  try {
@@ -32852,7 +33016,7 @@ class WorkspaceChangeProbe {
32852
33016
  const included = [];
32853
33017
  const skipped = new Map;
32854
33018
  for (const path of untracked) {
32855
- const stats = await lstat(join7(root, path)).catch(() => {
33019
+ const stats = await lstat(join8(root, path)).catch(() => {
32856
33020
  return;
32857
33021
  });
32858
33022
  if (!stats)
@@ -33105,10 +33269,28 @@ Before that final message you may send ordinary prose progress updates. Keep the
33105
33269
 
33106
33270
  ${JSON.stringify(schema)}`;
33107
33271
  }
33108
- async function dispatchPrompt(state, agent, input) {
33109
- const text = input.schema ? `${input.prompt}
33272
+ function cursorPromptText(state, input, repositoryInstructions) {
33273
+ const parts = [
33274
+ cursorAppliedPolicy(state).id === "coordinator-read-only" ? "" : agentSystemInstructions(),
33275
+ cursorAppliedPolicy(state).id === "coordinator-read-only" ? "" : repositoryInstructions ?? ""
33276
+ ].filter((part) => part.length > 0);
33277
+ return [
33278
+ input.prompt,
33279
+ parts.length > 0 ? wrapSystemInstructions(...parts) : "",
33280
+ input.schema ? structuredPromptInstruction(input.schema) : ""
33281
+ ].filter((part) => part.length > 0).join(`
33110
33282
 
33111
- ${structuredPromptInstruction(input.schema)}` : input.prompt;
33283
+ `);
33284
+ }
33285
+ async function repositoryInstructionsFor(state, cwd = workingDirectory) {
33286
+ if (cursorAppliedPolicy(state).id === "coordinator-read-only" || state.loadsProjectRules || state.repositoryInstructionsAgentId === state.agentId) {
33287
+ return;
33288
+ }
33289
+ return readRepositoryInstructions(cwd);
33290
+ }
33291
+ async function dispatchPrompt(state, agent, input) {
33292
+ const repositoryInstructions = await repositoryInstructionsFor(state);
33293
+ const text = cursorPromptText(state, input, repositoryInstructions);
33112
33294
  const images = input.images.map((image) => ({
33113
33295
  data: image.data,
33114
33296
  mimeType: image.mimeType
@@ -33154,6 +33336,8 @@ ${structuredPromptInstruction(input.schema)}` : input.prompt;
33154
33336
  throw error;
33155
33337
  });
33156
33338
  diagnostics?.sent(run.id);
33339
+ if (!state.loadsProjectRules)
33340
+ state.repositoryInstructionsAgentId = state.agentId;
33157
33341
  if (input.userMessageId) {
33158
33342
  const userMessage = state.messages.find((message) => message.id === input.userMessageId);
33159
33343
  if (userMessage?.role === "user") {
@@ -33169,6 +33353,7 @@ ${structuredPromptInstruction(input.schema)}` : input.prompt;
33169
33353
  async function dispatchCompaction(state, agent) {
33170
33354
  const promptSequence = state.promptSequence;
33171
33355
  const diagnostics = createRunDiagnostics(state);
33356
+ state.repositoryInstructionsAgentId = undefined;
33172
33357
  const start = () => agent.summarize();
33173
33358
  const run = await (diagnostics ? diagnostics.follow(start) : start()).catch((error) => {
33174
33359
  diagnostics?.close("send-failed");
@@ -33701,8 +33886,8 @@ async function withTimeout3(work, timeoutMs) {
33701
33886
  }
33702
33887
  // ../../bridges/cursor-bridge/src/prompt-attachments.ts
33703
33888
  import { constants } from "node:fs";
33704
- import { lstat as lstat2, open as open3, realpath } from "node:fs/promises";
33705
- import { isAbsolute as isAbsolute4, join as join8, relative as relative3, resolve as resolve6, sep as sep3 } from "node:path";
33889
+ import { lstat as lstat2, open as open4, realpath as realpath2 } from "node:fs/promises";
33890
+ import { isAbsolute as isAbsolute5, join as join9, relative as relative4, resolve as resolve6, sep as sep3 } from "node:path";
33706
33891
 
33707
33892
  class PromptAttachmentError extends Error {
33708
33893
  code;
@@ -33787,10 +33972,10 @@ function attachmentErrorForFsFailure(error) {
33787
33972
  var BRIDGE_ATTACHMENT_ROOT_ENV = "ORKESTRATOR_BRIDGE_ATTACHMENT_ROOT";
33788
33973
  function configuredAttachmentRoot(env = process.env) {
33789
33974
  const value = env[BRIDGE_ATTACHMENT_ROOT_ENV]?.trim();
33790
- return value && isAbsolute4(value) ? resolve6(value) : undefined;
33975
+ return value && isAbsolute5(value) ? resolve6(value) : undefined;
33791
33976
  }
33792
33977
  function attachmentContainmentRoot(filePath, workspaceRoot, extraRoot = configuredAttachmentRoot()) {
33793
- if (extraRoot && isAbsolute4(filePath) && isPathWithin(extraRoot, resolve6(filePath))) {
33978
+ if (extraRoot && isAbsolute5(filePath) && isPathWithin(extraRoot, resolve6(filePath))) {
33794
33979
  return extraRoot;
33795
33980
  }
33796
33981
  return resolve6(workspaceRoot);
@@ -33798,13 +33983,13 @@ function attachmentContainmentRoot(filePath, workspaceRoot, extraRoot = configur
33798
33983
  function isPathWithin(root, candidate) {
33799
33984
  if (candidate === root)
33800
33985
  return true;
33801
- const childPath = relative3(root, candidate);
33802
- return Boolean(childPath) && childPath !== ".." && !childPath.startsWith(`..${sep3}`) && !isAbsolute4(childPath);
33986
+ const childPath = relative4(root, candidate);
33987
+ return Boolean(childPath) && childPath !== ".." && !childPath.startsWith(`..${sep3}`) && !isAbsolute5(childPath);
33803
33988
  }
33804
33989
  async function assertNoSymlinkComponents(root, targetPath) {
33805
33990
  let currentPath = root;
33806
- for (const segment of relative3(root, targetPath).split(sep3).filter(Boolean)) {
33807
- currentPath = join8(currentPath, segment);
33991
+ for (const segment of relative4(root, targetPath).split(sep3).filter(Boolean)) {
33992
+ currentPath = join9(currentPath, segment);
33808
33993
  const stats = await lstat2(currentPath).catch((error) => {
33809
33994
  throw attachmentErrorForFsFailure(error);
33810
33995
  });
@@ -33816,7 +34001,7 @@ async function assertNoSymlinkComponents(root, targetPath) {
33816
34001
  async function assertOpenedWorkspaceFile(targetPath, canonicalRoot, openedStats) {
33817
34002
  const [pathStats, canonicalTarget] = await Promise.all([
33818
34003
  lstat2(targetPath),
33819
- realpath(targetPath)
34004
+ realpath2(targetPath)
33820
34005
  ]).catch((error) => {
33821
34006
  throw attachmentErrorForFsFailure(error);
33822
34007
  });
@@ -33837,22 +34022,22 @@ function assertStableRead(initial, final, bytesRead) {
33837
34022
  }
33838
34023
  async function readWorkspaceImage(filePath, workspaceRoot) {
33839
34024
  const lexicalRoot = attachmentContainmentRoot(filePath, workspaceRoot);
33840
- const targetPath = isAbsolute4(filePath) ? resolve6(filePath) : resolve6(lexicalRoot, filePath);
34025
+ const targetPath = isAbsolute5(filePath) ? resolve6(filePath) : resolve6(lexicalRoot, filePath);
33841
34026
  if (!isPathWithin(lexicalRoot, targetPath)) {
33842
34027
  throw new PromptAttachmentError("attachment_outside_workspace", "Image attachment must be contained in the current workspace");
33843
34028
  }
33844
- const canonicalRoot = await realpath(lexicalRoot).catch((error) => {
34029
+ const canonicalRoot = await realpath2(lexicalRoot).catch((error) => {
33845
34030
  throw attachmentErrorForFsFailure(error);
33846
34031
  });
33847
34032
  await assertNoSymlinkComponents(lexicalRoot, targetPath);
33848
- const canonicalTarget = await realpath(targetPath).catch((error) => {
34033
+ const canonicalTarget = await realpath2(targetPath).catch((error) => {
33849
34034
  throw attachmentErrorForFsFailure(error);
33850
34035
  });
33851
34036
  if (!isPathWithin(canonicalRoot, canonicalTarget)) {
33852
34037
  throw new PromptAttachmentError("attachment_outside_workspace", "Image attachment must be contained in the current workspace");
33853
34038
  }
33854
34039
  const noFollow = constants.O_NOFOLLOW ?? 0;
33855
- const handle = await open3(targetPath, constants.O_RDONLY | noFollow).catch((error) => {
34040
+ const handle = await open4(targetPath, constants.O_RDONLY | noFollow).catch((error) => {
33856
34041
  throw attachmentErrorForFsFailure(error);
33857
34042
  });
33858
34043
  try {
@@ -36497,6 +36682,7 @@ if (process.argv.includes("--login")) {
36497
36682
  export {
36498
36683
  ABANDONED_COMPACTION_NOTE,
36499
36684
  ABANDONED_TOOL_NOTE,
36685
+ ACCOUNT_LOOKUP_TIMEOUT_MS,
36500
36686
  BRIDGE_ATTACHMENT_ROOT_ENV,
36501
36687
  CANCEL_ACK_TIMEOUT_MS,
36502
36688
  CATALOG_TIMEOUT_MS,
@@ -36594,6 +36780,7 @@ export {
36594
36780
  cursorDeniedTools,
36595
36781
  cursorMcpConfigFingerprint,
36596
36782
  cursorMcpConfigRevision,
36783
+ cursorPromptText,
36597
36784
  cursorSdkStateDirectoryPath,
36598
36785
  detachAgent,
36599
36786
  dispatchCompaction,
@@ -36649,6 +36836,7 @@ export {
36649
36836
  rememberedOwnerRow,
36650
36837
  renderToolCall,
36651
36838
  reopenPersistenceForTests,
36839
+ repositoryInstructionsFor,
36652
36840
  resolveCredential,
36653
36841
  resolveCursorExecutionPolicy,
36654
36842
  resumeSession,
@@ -36669,6 +36857,7 @@ export {
36669
36857
  sessions,
36670
36858
  setCursorMcpConfigHomeForTests,
36671
36859
  setCursorMcpFingerprintForTests,
36860
+ setCursorSkillsProjectForTests,
36672
36861
  setMaxIndexedCallsForTests,
36673
36862
  setPromptJournal,
36674
36863
  setResumableAgentArchived,
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "bridge": "cursor-bridge",
3
- "digest": "ef917a23d141dd05"
3
+ "digest": "5b12929b1567667b"
4
4
  }
@@ -3824,6 +3824,7 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
3824
3824
  "stop_node_environment",
3825
3825
  "stop_worker_turn",
3826
3826
  "switch_provider_account",
3827
+ "set_account_auto_switch",
3827
3828
  "transfer_files",
3828
3829
  "update_schedule",
3829
3830
  "update_ticket"
@@ -27935,6 +27936,7 @@ var TOOL_CALL_TIMEOUT_MS = 120000;
27935
27936
  var MAX_MCP_TOOLS = 128;
27936
27937
  var MAX_MCP_RESULT_BYTES = 50000;
27937
27938
  var MAX_MCP_DESCRIPTION_BYTES = 4000;
27939
+ var MAX_MCP_INSTRUCTIONS_BYTES = 8000;
27938
27940
  var MAX_MCP_SCHEMA_BYTES = 20000;
27939
27941
  var COORDINATOR_MAIL_TOOLS = new Set([
27940
27942
  ...COORDINATOR_CONTROL_TOOL_NAMES,
@@ -28028,6 +28030,7 @@ async function preparePiMcp(state, options = {}) {
28028
28030
  grantedToolNames: new Set,
28029
28031
  granted: nativeAgentUserMcpGrant(state.policy) !== undefined,
28030
28032
  tools: [],
28033
+ instructions: [],
28031
28034
  connections: [],
28032
28035
  connectionKey: mcpConnectionKey(state),
28033
28036
  configKey: "",
@@ -28077,6 +28080,19 @@ function piMcpExtension(state) {
28077
28080
  }
28078
28081
  };
28079
28082
  }
28083
+ function piMcpInstructions(state) {
28084
+ const entries = runtimes.get(state)?.instructions ?? [];
28085
+ if (entries.length === 0)
28086
+ return;
28087
+ return [
28088
+ "# MCP Server Instructions",
28089
+ "The following MCP servers have provided instructions for how to use their tools and resources:",
28090
+ ...entries.map((entry) => `## ${entry.serverId}
28091
+ ${entry.text}`)
28092
+ ].join(`
28093
+
28094
+ `);
28095
+ }
28080
28096
  async function closePiMcp(state) {
28081
28097
  const runtime = runtimes.get(state);
28082
28098
  if (runtime)
@@ -28136,6 +28152,13 @@ async function connectAndRegister(state, runtime, server) {
28136
28152
  return detachedServer(server);
28137
28153
  }
28138
28154
  runtime.connections.push(connection);
28155
+ const instructions = connection.instructions?.trim();
28156
+ if (instructions) {
28157
+ runtime.instructions.push({
28158
+ serverId: server.id,
28159
+ text: instructions.length > MAX_MCP_INSTRUCTIONS_BYTES ? instructions.slice(0, MAX_MCP_INSTRUCTIONS_BYTES) : instructions
28160
+ });
28161
+ }
28139
28162
  const tools = connection.tools.slice(0, MAX_MCP_TOOLS);
28140
28163
  const names = [];
28141
28164
  for (const listed of tools) {
@@ -28274,7 +28297,9 @@ var defaultTransport = {
28274
28297
  try {
28275
28298
  await client.connect(transport);
28276
28299
  const listed = await client.listTools();
28300
+ const instructions = client.getInstructions();
28277
28301
  return {
28302
+ ...instructions ? { instructions } : {},
28278
28303
  tools: (listed.tools ?? []).map((tool) => ({
28279
28304
  name: tool.name,
28280
28305
  ...tool.description ? { description: tool.description } : {},
@@ -28932,6 +28957,21 @@ function readString2(value) {
28932
28957
  // ../../bridges/pi-bridge/src/agent-session.ts
28933
28958
  import { randomBytes as randomBytes6 } from "node:crypto";
28934
28959
  import { existsSync as existsSync2 } from "node:fs";
28960
+
28961
+ // ../protocol/src/agent-system-instructions.ts
28962
+ var AGENT_IMAGE_PREVIEW_INSTRUCTION = "Orkestrator renders Markdown images inline in the transcript. When you show the user an image in your reply (a screenshot, rendered preview, or chart) using Markdown image syntax, reference it by its absolute path. In a managed worktree or container environment, save the file inside the working directory, preferably in a git-ignored folder so preview files stay out of the change set. Local image previews are limited to Orkestrator-managed workspace storage or the container's /workspace. Project-home checkouts are excluded from host preview reads, even when they are the working directory, so images saved there cannot be previewed from a local Markdown path. Paths outside previewable storage, such as /tmp, also show a placeholder.";
28963
+ var AGENT_DOCUMENT_INSTRUCTION = "When the user asks for a document (a report, design doc, plan, analysis, guide, or other writing to keep), prefer writing it as a Markdown file in a `docs/` folder at the root of the working directory, and tell the user the file's path. Project instructions (such as AGENTS.md or CLAUDE.md), local skills, and the user's own request take priority over this default when they say where or how to write documents.";
28964
+ var GENERAL_AGENT_INSTRUCTIONS = [
28965
+ AGENT_IMAGE_PREVIEW_INSTRUCTION,
28966
+ AGENT_DOCUMENT_INSTRUCTION
28967
+ ];
28968
+ function agentSystemInstructions(providerSpecific = []) {
28969
+ return [...GENERAL_AGENT_INSTRUCTIONS, ...providerSpecific].filter((line) => line.trim().length > 0).join(`
28970
+
28971
+ `);
28972
+ }
28973
+
28974
+ // ../../bridges/pi-bridge/src/agent-session.ts
28935
28975
  import { realpath, stat as stat2 } from "node:fs/promises";
28936
28976
  import { join as join6, resolve as resolve3, sep } from "node:path";
28937
28977
 
@@ -31194,12 +31234,20 @@ async function reconcileAgentMcp(state, options = {}) {
31194
31234
  await detachSession(state);
31195
31235
  }
31196
31236
  }
31237
+ function piAppendedInstructions(state) {
31238
+ const mcp = piMcpInstructions(state);
31239
+ return [
31240
+ ...state.policy?.id === "coordinator-read-only" ? [] : [agentSystemInstructions()],
31241
+ ...mcp ? [mcp] : []
31242
+ ];
31243
+ }
31197
31244
  async function createPiSessionServices(state, options) {
31198
31245
  const loaderPolicy = piResourceLoaderPolicy(state, options.cwd);
31199
31246
  const services = await createAgentSessionServices({
31200
31247
  ...options,
31201
31248
  resourceLoaderOptions: {
31202
31249
  ...loaderPolicy.discovery,
31250
+ appendSystemPromptOverride: (base) => [...base, ...piAppendedInstructions(state)],
31203
31251
  extensionFactories: [
31204
31252
  { name: "orkestrator", factory: approvalExtension(state) },
31205
31253
  { name: "orkestrator-mcp", factory: piMcpExtension(state) },
@@ -35947,6 +35995,7 @@ export {
35947
35995
  parsePromptAttachments,
35948
35996
  parseTranscriptTailLimit as parseTailLimit,
35949
35997
  persistBarrier,
35998
+ piAppendedInstructions,
35950
35999
  piReadiness,
35951
36000
  piReportedPolicy,
35952
36001
  piResourceLoaderPolicy,
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "bridge": "pi-bridge",
3
- "digest": "a190eada26714cdb"
3
+ "digest": "d23d7f865d5cabd0"
4
4
  }