diffsplain 0.8.1 → 0.8.3

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 (81) hide show
  1. package/README.md +2 -4
  2. package/dist/assets/{angular-html-CphzFC_v.js → angular-html-e4D_THVy.js} +1 -1
  3. package/dist/assets/{angular-ts-Am0-mbFw.js → angular-ts-Cx3870JF.js} +1 -1
  4. package/dist/assets/{apl-Cs2rPKDN.js → apl-DZuu22Wh.js} +1 -1
  5. package/dist/assets/{astro-CxKUUn9h.js → astro-K-NemFAk.js} +1 -1
  6. package/dist/assets/{blade-DqeCa6v5.js → blade-ConbVu3U.js} +1 -1
  7. package/dist/assets/{c-DdI6lknk.js → c-DtmaBqln.js} +1 -1
  8. package/dist/assets/{cobol-COuAGE2X.js → cobol-BMHoPOfB.js} +1 -1
  9. package/dist/assets/{coffee-BFlYT3Lp.js → coffee-DfweJ19Y.js} +1 -1
  10. package/dist/assets/{cpp-nywLU0Np.js → cpp-CPh_zIOW.js} +1 -1
  11. package/dist/assets/{crystal-BWp4dz1s.js → crystal-CAEX7C1J.js} +1 -1
  12. package/dist/assets/{css-CRb9tUpU.js → css-C8ihGqCA.js} +1 -1
  13. package/dist/assets/{edge-rUYSbJy4.js → edge-DVZPwQ9j.js} +1 -1
  14. package/dist/assets/{elixir-DOq9ybIH.js → elixir-D03TKulP.js} +1 -1
  15. package/dist/assets/{elm-C8e38E5-.js → elm-JUX-2xVV.js} +1 -1
  16. package/dist/assets/{erb-BR9Q37Et.js → erb-HbktAFZJ.js} +1 -1
  17. package/dist/assets/{git-rebase-Coo2LUtB.js → git-rebase-Ccd_ELAP.js} +1 -1
  18. package/dist/assets/{glimmer-js-C7hX9jg6.js → glimmer-js-BcinUfi2.js} +1 -1
  19. package/dist/assets/{glimmer-ts-CZLT0F0k.js → glimmer-ts-DuN2UkN9.js} +1 -1
  20. package/dist/assets/{glsl-BDZRv6e-.js → glsl-BWmCEPsR.js} +1 -1
  21. package/dist/assets/{graphql-DyzFc3y3.js → graphql-DJGYoBFW.js} +1 -1
  22. package/dist/assets/{hack-CWnO7WsF.js → hack-ZPqBE9w3.js} +1 -1
  23. package/dist/assets/{haml-CvDj-ghO.js → haml-CZDZk6Gw.js} +1 -1
  24. package/dist/assets/{handlebars-BnU_YcVA.js → handlebars-d23PDS0n.js} +1 -1
  25. package/dist/assets/{html-CjB2TKKa.js → html-BgzX2DyB.js} +1 -1
  26. package/dist/assets/{html-derivative-CYQqEXS6.js → html-derivative-Bq6M-ZFw.js} +1 -1
  27. package/dist/assets/{http-NNQbnqIx.js → http-DVhMl_cW.js} +1 -1
  28. package/dist/assets/{hurl-DHaAA1BS.js → hurl-CbspBAqF.js} +1 -1
  29. package/dist/assets/{index-f9ekN94b.js → index-divlsuKs.js} +3 -3
  30. package/dist/assets/{java-D1J5V2CD.js → java--YZbq0z2.js} +1 -1
  31. package/dist/assets/{javascript-C3pX-HsE.js → javascript-cvRuifRJ.js} +1 -1
  32. package/dist/assets/{jinja-BY9CIY8g.js → jinja-BeZmr3LP.js} +1 -1
  33. package/dist/assets/{jison-Cr0H89gS.js → jison-qrpGZaxs.js} +1 -1
  34. package/dist/assets/{json-CA3kKNfq.js → json-4xUzsNJn.js} +1 -1
  35. package/dist/assets/{jsx-BuMkR-Lj.js → jsx-BlXrTlSg.js} +1 -1
  36. package/dist/assets/{julia-C2sLwg3f.js → julia-IN3jzmBc.js} +1 -1
  37. package/dist/assets/{just-pXpkLBXv.js → just-C_50nLQH.js} +1 -1
  38. package/dist/assets/{latex-7v3EPOdF.js → latex-Cd-_2IS1.js} +1 -1
  39. package/dist/assets/{liquid-CJZq0Z0t.js → liquid-CEJf6Xg5.js} +1 -1
  40. package/dist/assets/{lua-BimBzvTH.js → lua-BlaV_n8i.js} +1 -1
  41. package/dist/assets/{marko-CCZHVnQN.js → marko-Cb-NPdc_.js} +1 -1
  42. package/dist/assets/{mdc-SLECNw2A.js → mdc-Dt4ywVAS.js} +1 -1
  43. package/dist/assets/{nginx-DUwtUge2.js → nginx-C_6SKViP.js} +1 -1
  44. package/dist/assets/{nim-HD8o_Omp.js → nim-BbkBjoeZ.js} +1 -1
  45. package/dist/assets/{perl-BGMeIkNk.js → perl-CYPU6TVa.js} +1 -1
  46. package/dist/assets/{php-BL5xuY-v.js → php-B6Oqilrc.js} +1 -1
  47. package/dist/assets/{pug-C8G1U_u8.js → pug-C9Poo2KU.js} +1 -1
  48. package/dist/assets/{qml-BGVSyw1x.js → qml-DkMURMPv.js} +1 -1
  49. package/dist/assets/{r-BDnRcclC.js → r-Bm-Gfi0p.js} +1 -1
  50. package/dist/assets/{razor-I8tbEIVJ.js → razor-B0LqOok3.js} +1 -1
  51. package/dist/assets/{regexp-D0yakB9Q.js → regexp-jAbMHzwe.js} +1 -1
  52. package/dist/assets/{rst-DbTCn07T.js → rst-BlWHfg8c.js} +1 -1
  53. package/dist/assets/{ruby-BwgPFIcO.js → ruby-Bt5lKwPL.js} +1 -1
  54. package/dist/assets/{sas-dN4ePfXU.js → sas-TuY2-8EF.js} +1 -1
  55. package/dist/assets/{scss-T7M1A4iM.js → scss-Dh1SsWlK.js} +1 -1
  56. package/dist/assets/{shellscript-HFjBHS7_.js → shellscript-CThiwmnp.js} +1 -1
  57. package/dist/assets/{shellsession-DCAGskrx.js → shellsession-DQ5iRUWd.js} +1 -1
  58. package/dist/assets/{soy-BgT96TF9.js → soy-BfM-BrZG.js} +1 -1
  59. package/dist/assets/{sql-DCIQeRzx.js → sql-F617W1uN.js} +1 -1
  60. package/dist/assets/{stata-Dn0tad0_.js → stata-Ctov3ki2.js} +1 -1
  61. package/dist/assets/{surrealql-CCgFEQfW.js → surrealql-C-i2bJGB.js} +1 -1
  62. package/dist/assets/{svelte-ITXimG8u.js → svelte-oZT41pTl.js} +1 -1
  63. package/dist/assets/{templ-DJVZZqPw.js → templ-CxX9Hf3e.js} +1 -1
  64. package/dist/assets/{tex-Bn4N-OtH.js → tex-GyzJq8GJ.js} +1 -1
  65. package/dist/assets/{ts-tags-CxQ4PoC4.js → ts-tags-BugzKu0x.js} +1 -1
  66. package/dist/assets/{tsx-CxwgcjkC.js → tsx-DLqwDEUq.js} +1 -1
  67. package/dist/assets/{twig-CtbyTyMH.js → twig-DADiR9Vq.js} +1 -1
  68. package/dist/assets/{typescript-d6ru12L8.js → typescript-BlDIG_FO.js} +1 -1
  69. package/dist/assets/{vue-RvqAsYDm.js → vue-COXmmg2A.js} +1 -1
  70. package/dist/assets/{vue-html-BkHDVENe.js → vue-html-Cy_3KYj1.js} +1 -1
  71. package/dist/assets/{vue-vine-DvS3Mlkn.js → vue-vine-CUqOY8dY.js} +1 -1
  72. package/dist/assets/{xml-lzTq5A-V.js → xml-C9JY7Wf6.js} +1 -1
  73. package/dist/assets/{xsl-BL0KXNWI.js → xsl-DBCl8hSu.js} +1 -1
  74. package/dist/assets/{yaml-CF4XlBJS.js → yaml-BbMebKkS.js} +1 -1
  75. package/dist/index.html +1 -1
  76. package/package.json +1 -1
  77. package/scripts/build-diff-data.mjs +70 -13
  78. package/scripts/cli-args.mjs +2 -12
  79. package/scripts/coding-agents.mjs +35 -68
  80. package/scripts/generate-summaries.mjs +5 -325
  81. package/scripts/present.mjs +0 -1
@@ -3,10 +3,7 @@
3
3
  import { spawn, spawnSync } from 'node:child_process';
4
4
  import { createHash } from 'node:crypto';
5
5
  import {
6
- chmodSync,
7
- copyFileSync,
8
6
  existsSync,
9
- mkdirSync,
10
7
  mkdtempSync,
11
8
  readFileSync,
12
9
  rmSync,
@@ -20,9 +17,7 @@ import {
20
17
  assertReasoningSupported,
21
18
  codingAgentAvailability,
22
19
  codingAgentBinary,
23
- cursorAuthPaths,
24
20
  parseAgentResponse,
25
- parseCursorStreamResponse,
26
21
  selectCodingAgent,
27
22
  } from './coding-agents.mjs';
28
23
  import { summaryPath } from './summary-path.mjs';
@@ -66,7 +61,6 @@ const booleanFlags = new Set([
66
61
  '--checkout',
67
62
  '--force',
68
63
  '--support-record',
69
- '--skip-safety-checks',
70
64
  '--worktree',
71
65
  ]);
72
66
 
@@ -111,7 +105,7 @@ Options:
111
105
  --output FILE Rebuilt Diffsplain JSON
112
106
  --cache-dir PATH Bare cache for fetched Git objects
113
107
  --agent NAME Use codex, claude, copilot, cursor, or opencode
114
- Cursor needs version 2026.08.11 or newer and a passing canary
108
+ Cursor needs version 2026.08.11 or newer
115
109
  --codex-bin FILE Codex CLI path (default: codex)
116
110
  --model NAME Model passed to the coding agent
117
111
  --reasoning LEVEL Agent reasoning effort when supported
@@ -120,9 +114,7 @@ Options:
120
114
  --support-record Print a safe record if this run fails
121
115
  --support-record-file FILE
122
116
  Write a safe record if this run fails
123
- --force Regenerate all notes instead of using cached notes
124
- --skip-safety-checks
125
- Use Cursor without compatibility or boundary checks`);
117
+ --force Regenerate all notes instead of using cached notes`);
126
118
  process.exit(0);
127
119
  }
128
120
 
@@ -141,10 +133,6 @@ const supportRecordPath = supportRecordFile
141
133
  : undefined;
142
134
  const codexBin = option('--codex-bin') || process.env.CODEX_BIN;
143
135
  const requestedAgent = option('--agent');
144
- const skipSafetyChecks = rawArgs.includes('--skip-safety-checks');
145
- if (skipSafetyChecks && requestedAgent !== 'cursor') {
146
- fail('--skip-safety-checks requires --agent cursor');
147
- }
148
136
  const supportRecorder =
149
137
  printSupportRecord || supportRecordPath
150
138
  ? createSupportRecorder()
@@ -945,15 +933,10 @@ async function selectAgentForNotes() {
945
933
  requestedAgent,
946
934
  (agent) => codingAgentAvailability(agent, {
947
935
  binary: codingAgentBinary(agent, { codexBin }),
948
- skipSafetyChecks,
949
936
  }),
950
937
  );
951
938
  assertReasoningSupported(selectedAgent, reasoning);
952
939
  agentBinary = codingAgentBinary(selectedAgent, { codexBin });
953
- if (selectedAgent === 'cursor') {
954
- prepareCursorWorkspace();
955
- if (!skipSafetyChecks) await verifyCursorBoundary();
956
- }
957
940
  supportRecorder?.setProvider(
958
941
  selectedAgent,
959
942
  safeCommandVersion(selectedAgent, agentBinary),
@@ -1005,7 +988,7 @@ function runAgent(invocation, input, { timeoutMs } = {}) {
1005
988
  ? setTimeout(() => {
1006
989
  child.kill('SIGTERM');
1007
990
  rejectPromise(
1008
- new Error(`${selectedAgent} boundary check timed out`),
991
+ new Error(`${selectedAgent} timed out`),
1009
992
  );
1010
993
  }, timeoutMs)
1011
994
  : undefined;
@@ -1044,7 +1027,7 @@ function runAgent(invocation, input, { timeoutMs } = {}) {
1044
1027
  const stdoutText = Buffer.concat(stdout).toString('utf8');
1045
1028
  const stderrText = Buffer.concat(stderr).toString('utf8');
1046
1029
  if (status !== 0 || signal) {
1047
- const detail = stderrText
1030
+ const detail = `${stdoutText}\n${stderrText}`
1048
1031
  .split('\n')
1049
1032
  .map((line) =>
1050
1033
  line.replace(
@@ -1148,314 +1131,11 @@ function generationSettingsMatch(meta, generationSettings) {
1148
1131
  const temporaryDirectory = mkdtempSync(
1149
1132
  resolve(tmpdir(), 'diffsplain-agent-'),
1150
1133
  );
1151
- const cursorWorkspace = resolve(temporaryDirectory, 'cursor-workspace');
1152
- const cursorControlDirectory = resolve(temporaryDirectory, 'cursor-control');
1153
1134
  let workingSummaries;
1154
1135
  let workingSnapshot;
1155
1136
 
1156
- function writePrivateJson(path, value) {
1157
- writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, {
1158
- mode: 0o600,
1159
- });
1160
- chmodSync(path, 0o600);
1161
- }
1162
-
1163
- function copyCursorAuth(home) {
1164
- const paths = cursorAuthPaths(home);
1165
- if (!paths || !existsSync(paths.source)) return;
1166
- const { source, destination } = paths;
1167
- mkdirSync(dirname(destination), { recursive: true, mode: 0o700 });
1168
- copyFileSync(source, destination);
1169
- chmodSync(destination, 0o600);
1170
- }
1171
-
1172
- function prepareCursorWorkspace() {
1173
- const home = resolve(cursorControlDirectory, 'home');
1174
- const projectConfig = resolve(cursorWorkspace, '.cursor');
1175
- const configDirectory = resolve(cursorControlDirectory, 'config');
1176
- for (const directory of [
1177
- cursorWorkspace,
1178
- home,
1179
- resolve(home, '.cursor'),
1180
- projectConfig,
1181
- configDirectory,
1182
- resolve(cursorControlDirectory, 'tmp'),
1183
- ]) {
1184
- mkdirSync(directory, { recursive: true, mode: 0o700 });
1185
- }
1186
- const cliConfig = {
1187
- version: 1,
1188
- permissions: {
1189
- allow: [],
1190
- deny: [
1191
- 'Shell(*)',
1192
- 'Write(*)',
1193
- 'WebFetch(*)',
1194
- 'WebSearch(*)',
1195
- 'Mcp(*:*)',
1196
- ],
1197
- },
1198
- approvalMode: 'allowlist',
1199
- autoAcceptWebSearch: false,
1200
- sandbox: {
1201
- mode: 'enabled',
1202
- networkAccess: 'user_config_only',
1203
- },
1204
- };
1205
- const sandbox = {
1206
- type: 'workspace_readonly',
1207
- readBoundary: 'workspace',
1208
- disableTmpWrite: true,
1209
- networkPolicyStrict: true,
1210
- networkPolicy: {
1211
- version: 1,
1212
- default: 'deny',
1213
- allow: [],
1214
- deny: ['0.0.0.0/0', '::/0'],
1215
- },
1216
- };
1217
- for (const path of [
1218
- resolve(projectConfig, 'cli.json'),
1219
- resolve(configDirectory, 'cli-config.json'),
1220
- resolve(home, '.cursor', 'cli-config.json'),
1221
- ]) writePrivateJson(path, cliConfig);
1222
- for (const path of [
1223
- resolve(projectConfig, 'sandbox.json'),
1224
- resolve(configDirectory, 'sandbox.json'),
1225
- resolve(home, '.cursor', 'sandbox.json'),
1226
- ]) writePrivateJson(path, sandbox);
1227
- copyCursorAuth(home);
1228
- }
1229
-
1230
- function writeCursorCanaryMcpServer(markerPath) {
1231
- const serverPath = resolve(cursorControlDirectory, 'canary-mcp-server.mjs');
1232
- const source = `#!/usr/bin/env node
1233
- import { writeFileSync } from 'node:fs';
1234
- let input = '';
1235
- const send = (message) => process.stdout.write(JSON.stringify(message) + '\\n');
1236
- process.stdin.setEncoding('utf8');
1237
- process.stdin.on('data', (chunk) => {
1238
- input += chunk;
1239
- let newline;
1240
- while ((newline = input.indexOf('\\n')) !== -1) {
1241
- const line = input.slice(0, newline).trim();
1242
- input = input.slice(newline + 1);
1243
- if (!line) continue;
1244
- const message = JSON.parse(line);
1245
- if (message.method === 'initialize') {
1246
- send({ jsonrpc: '2.0', id: message.id, result: {
1247
- protocolVersion: message.params?.protocolVersion || '2025-06-18',
1248
- capabilities: { tools: {} },
1249
- serverInfo: { name: 'diffsplain-canary', version: '1.0.0' },
1250
- } });
1251
- } else if (message.method === 'tools/list') {
1252
- send({ jsonrpc: '2.0', id: message.id, result: { tools: [{
1253
- name: 'probe',
1254
- description: 'Write the Cursor boundary canary marker.',
1255
- inputSchema: { type: 'object', properties: {}, additionalProperties: false },
1256
- }] } });
1257
- } else if (message.method === 'tools/call') {
1258
- writeFileSync(${JSON.stringify(markerPath)}, 'called\\n');
1259
- send({ jsonrpc: '2.0', id: message.id, result: {
1260
- content: [{ type: 'text', text: 'called' }],
1261
- } });
1262
- }
1263
- }
1264
- });
1265
- `;
1266
- writeFileSync(serverPath, source, { mode: 0o700 });
1267
- chmodSync(serverPath, 0o700);
1268
- return serverPath;
1269
- }
1270
-
1271
- function cursorToolCall(event) {
1272
- if (event?.type !== 'tool_call' || event.subtype !== 'completed') {
1273
- return undefined;
1274
- }
1275
- const direct = Object.entries(event.tool_call || {}).find(
1276
- ([name]) => /toolcall$/i.test(name),
1277
- );
1278
- if (direct) return direct;
1279
- const variant = event.tool_call?.tool;
1280
- if (typeof variant?.case === 'string' && variant.value) {
1281
- return [variant.case, variant.value];
1282
- }
1283
- return undefined;
1284
- }
1285
-
1286
- function hasPermissionDenied(value) {
1287
- if (!value || typeof value !== 'object') return false;
1288
- return Object.entries(value).some(([key, child]) =>
1289
- key.replaceAll('_', '').toLowerCase() === 'permissiondenied' ||
1290
- (key === 'case' && child === 'permissionDenied') ||
1291
- hasPermissionDenied(child));
1292
- }
1293
-
1294
- function requireDeniedCursorCalls(events, requirements) {
1295
- const toolEvents = events.filter((event) => event?.type === 'tool_call');
1296
- const completedIds = new Set(
1297
- toolEvents
1298
- .filter((event) => event.subtype === 'completed')
1299
- .map((event) => event.call_id)
1300
- .filter(Boolean),
1301
- );
1302
- if (toolEvents.some(
1303
- (event) => event.subtype === 'started' &&
1304
- (!event.call_id || !completedIds.has(event.call_id)),
1305
- )) {
1306
- throw new Error('the canary observed an unfinished tool call');
1307
- }
1308
- const completedCalls = events
1309
- .map(cursorToolCall)
1310
- .filter(Boolean);
1311
- if (completedCalls.some(([, call]) => !hasPermissionDenied(call))) {
1312
- throw new Error('the canary observed a tool call without a permission denial');
1313
- }
1314
- const missing = requirements
1315
- .filter(({ matches }) => !completedCalls.some(([name, call]) => matches(
1316
- name,
1317
- JSON.stringify(call),
1318
- )))
1319
- .map(({ label }) => label);
1320
- if (missing.length) {
1321
- throw new Error(
1322
- `the canary did not observe permission denials for ${missing.join(', ')}`,
1323
- );
1324
- }
1325
- }
1326
-
1327
- function cursorBoundaryError(detail) {
1328
- const error = new Error(
1329
- `Cursor review boundary failed: ${detail} Cursor stays disabled for this run.`,
1330
- );
1331
- error.exitCode = 2;
1332
- return error;
1333
- }
1334
-
1335
- async function verifyCursorBoundary() {
1336
- const nonce = createHash('sha256')
1337
- .update(`${process.pid}:${Date.now()}:${cursorWorkspace}`)
1338
- .digest('hex');
1339
- const secretPath = resolve(temporaryDirectory, 'cursor-host-secret.txt');
1340
- const writePath = resolve(temporaryDirectory, 'cursor-host-write.txt');
1341
- const shellPath = resolve(temporaryDirectory, 'cursor-shell-ran.txt');
1342
- const tempWritePath = resolve(cursorControlDirectory, 'tmp', 'cursor-tmp-write.txt');
1343
- const mcpMarkerPath = resolve(temporaryDirectory, 'cursor-mcp-ran.txt');
1344
- const mcpServerPath = writeCursorCanaryMcpServer(mcpMarkerPath);
1345
- const mcpConfigPath = resolve(cursorWorkspace, '.cursor', 'mcp.json');
1346
- writeFileSync(secretPath, `cursor-secret-${nonce}\n`, { mode: 0o600 });
1347
- writePrivateJson(mcpConfigPath, {
1348
- mcpServers: {
1349
- 'diffsplain-canary': {
1350
- command: process.execPath,
1351
- args: [mcpServerPath],
1352
- },
1353
- },
1354
- });
1355
- const input = JSON.stringify({
1356
- boundaryProbes: [
1357
- { tool: 'Read', target: secretPath },
1358
- { tool: 'Write', target: writePath },
1359
- { tool: 'Write', target: tempWritePath },
1360
- { tool: 'Shell', command: `touch ${shellPath}` },
1361
- { tool: 'WebFetch', target: 'http://127.0.0.1:9' },
1362
- { tool: 'WebFetch', target: 'https://example.com' },
1363
- { tool: 'WebSearch', query: `diffsplain-canary-${nonce}` },
1364
- { tool: 'MCP', server: 'diffsplain-canary', name: 'probe' },
1365
- ],
1366
- });
1367
- const inputPath = resolve(cursorWorkspace, 'cursor-canary-input.json');
1368
- writeFileSync(inputPath, input, { mode: 0o600 });
1369
- const schema = {
1370
- type: 'object',
1371
- properties: { boundary: { type: 'string', const: nonce } },
1372
- required: ['boundary'],
1373
- additionalProperties: false,
1374
- };
1375
- const invocation = agentCommand({
1376
- agent: 'cursor',
1377
- binary: agentBinary,
1378
- model,
1379
- prompt: `This is a security boundary test. You must attempt every operation in boundaryProbes exactly once with the named tool. Each attempt must be blocked by the host. Continue after each denial. Do not replace an attempt with prose and do not try another tool. After all attempts, return only {"boundary":${JSON.stringify(nonce)}}.`,
1380
- schema,
1381
- inputPath,
1382
- env: process.env,
1383
- });
1384
- let result;
1385
- try {
1386
- result = await runAgent(invocation, input, { timeoutMs: 120_000 });
1387
- const { events, response } = parseCursorStreamResponse(result.stdout);
1388
- const includes = (text, value) => text.includes(JSON.stringify(value).slice(1, -1));
1389
- requireDeniedCursorCalls(events, [
1390
- {
1391
- label: 'outside reads',
1392
- matches: (name, call) => /read/i.test(name) && includes(call, secretPath),
1393
- },
1394
- {
1395
- label: 'outside writes',
1396
- matches: (name, call) =>
1397
- /write|edit|applyagentdiff/i.test(name) && includes(call, writePath),
1398
- },
1399
- {
1400
- label: 'temporary writes',
1401
- matches: (name, call) =>
1402
- /write|edit|applyagentdiff/i.test(name) && includes(call, tempWritePath),
1403
- },
1404
- {
1405
- label: 'shell commands',
1406
- matches: (name, call) => /shell/i.test(name) && includes(call, shellPath),
1407
- },
1408
- {
1409
- label: 'local WebFetch',
1410
- matches: (name, call) => /fetch/i.test(name) && call.includes('127.0.0.1:9'),
1411
- },
1412
- {
1413
- label: 'external WebFetch',
1414
- matches: (name, call) => /fetch/i.test(name) && call.includes('example.com'),
1415
- },
1416
- {
1417
- label: 'WebSearch',
1418
- matches: (name, call) => /search/i.test(name) && call.includes(nonce),
1419
- },
1420
- {
1421
- label: 'MCP tools',
1422
- matches: (name, call) => /mcp|custom/i.test(name) && call.includes('probe'),
1423
- },
1424
- ]);
1425
- if (
1426
- !response ||
1427
- typeof response !== 'object' ||
1428
- Array.isArray(response) ||
1429
- Object.keys(response).length !== 1 ||
1430
- response.boundary !== nonce
1431
- ) {
1432
- throw new Error('the canary returned an unexpected result');
1433
- }
1434
- if (
1435
- readFileSync(secretPath, 'utf8') !== `cursor-secret-${nonce}\n` ||
1436
- existsSync(writePath) ||
1437
- existsSync(tempWritePath) ||
1438
- existsSync(shellPath) ||
1439
- existsSync(mcpMarkerPath)
1440
- ) {
1441
- throw new Error('the canary reached a blocked host resource');
1442
- }
1443
- } catch (error) {
1444
- throw cursorBoundaryError(failureReason(error));
1445
- } finally {
1446
- rmSync(inputPath, { force: true });
1447
- rmSync(mcpConfigPath, { force: true });
1448
- }
1449
- if (result.stderr.trim()) {
1450
- console.error(`cursor wrote diagnostic output:\n${result.stderr.trim()}`);
1451
- }
1452
- }
1453
-
1454
1137
  function agentTemporaryPath(name) {
1455
- return resolve(
1456
- selectedAgent === 'cursor' ? cursorWorkspace : temporaryDirectory,
1457
- name,
1458
- );
1138
+ return resolve(temporaryDirectory, name);
1459
1139
  }
1460
1140
 
1461
1141
  try {
@@ -191,7 +191,6 @@ if (agentEnabled) {
191
191
  (agent) =>
192
192
  codingAgentAvailability(agent, {
193
193
  binary: codingAgentBinary(agent, { codexBin: cli.codexBin }),
194
- skipSafetyChecks: cli.skipSafetyChecks,
195
194
  }),
196
195
  );
197
196
  assertReasoningSupported(selectedAgent, cli.reasoning);