jules-orchestrator-kit 0.75.0 → 0.75.2

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.
@@ -254,6 +254,11 @@ export function secretScanVariants(addedLines) {
254
254
  // Collapse method concatenations like .concat("...") or .join("")
255
255
  dejoined = dejoined.replace(/\.concat\(\s*["'`]/g, "").replace(/\.join\(\s*["'`]{2}\s*\)/g, "");
256
256
 
257
+ // Collapse array elements like ["A", "B"]
258
+ dejoined = dejoined.replace(/["'`]\s*,\s*["'`]/g, "");
259
+ // Collapse split variables across newlines (e.g. const a = "AKIA"; const b = "...")
260
+ dejoined = dejoined.replace(/["'`]\s*[,;+]*\s*[\r\n]+\s*(?:(?:export\s+)?(?:const|let|var)\s+[A-Za-z0-9_]+\s*=\s*)?["'`]/g, "");
261
+
257
262
  // Collapse whitespace/newlines between adjacent base64 characters (including line-wrapped PEM/base64, template literals, and quoted string chunks)
258
263
  const base64Dejoined = stripped
259
264
  .replace(/([A-Za-z0-9+/=_-])\s*[\r\n]+\s*(?=[A-Za-z0-9+/=_-])/g, "$1")
@@ -266,9 +271,16 @@ export function secretScanVariants(addedLines) {
266
271
  // source-level join AND spelled with homoglyphs still surfaces.
267
272
  const confusable = normalizeSecretText(stripped);
268
273
  const confusableDejoined = normalizeSecretText(dejoined);
274
+ const uniDecoded = dejoined
275
+ .replace(/\\u([0-9a-fA-F]{4})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)))
276
+ .replace(/\\x([0-9a-fA-F]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
277
+
278
+ const stringLiteralsCollapsed = (stripped.match(/(["'`])(?:(?!\1)[^\n])*\1/g) || [])
279
+ .map(s => s.slice(1, -1))
280
+ .join('');
269
281
 
270
282
  return {
271
- all: [...new Set([addedLines, stripped, dejoined, base64Dejoined, hexDecoded, pctDecoded, confusable, confusableDejoined])],
283
+ all: [...new Set([addedLines, stripped, dejoined, base64Dejoined, hexDecoded, pctDecoded, confusable, confusableDejoined, uniDecoded, stringLiteralsCollapsed])],
272
284
  normalized: dejoined,
273
285
  base64Normalized: base64Dejoined,
274
286
  };
package/src/security.mjs CHANGED
@@ -167,6 +167,10 @@ function splitDiffByFile(diffText) {
167
167
  current.lines.push({ text: line.slice(1), no: lineNo });
168
168
  if (lineNo !== null) lineNo++;
169
169
  } else if (lineNo !== null && !line.startsWith("-") && !line.startsWith("\\")) {
170
+ if (line.startsWith(" ")) {
171
+ if (!current) select(null);
172
+ current.lines.push({ text: line.slice(1), no: lineNo });
173
+ }
170
174
  lineNo++; // A context line advances the post-image just as an added one does.
171
175
  }
172
176
  }
package/src/state.mjs CHANGED
@@ -233,24 +233,36 @@ export function withVfsMutex(mutexDir, fn, opts = {}) {
233
233
  const maxRetries = opts.maxRetries || 200;
234
234
  const retryDelayMs = opts.retryDelayMs || 10;
235
235
 
236
+ const staleMs = opts.staleMs || 2000;
237
+
236
238
  for (let i = 0; i < maxRetries; i++) {
239
+ let acquired = false;
237
240
  try {
238
241
  mkdirSync(mutexDir);
239
- try {
240
- return fn();
241
- } finally {
242
- try {
243
- rmdirSync(mutexDir);
244
- } catch (_) {}
245
- }
242
+ acquired = true;
246
243
  } catch (err) {
247
244
  if (err.code === "EEXIST") {
245
+ try {
246
+ if (Date.now() - statSync(mutexDir).mtimeMs > staleMs) {
247
+ rmdirSync(mutexDir);
248
+ }
249
+ } catch (_) {}
248
250
  const deadline = Date.now() + retryDelayMs;
249
251
  while (Date.now() < deadline) {}
250
252
  continue;
251
253
  }
252
254
  throw err;
253
255
  }
256
+
257
+ if (acquired) {
258
+ try {
259
+ return fn();
260
+ } finally {
261
+ try {
262
+ rmdirSync(mutexDir);
263
+ } catch (_) {}
264
+ }
265
+ }
254
266
  }
255
267
  throw new MutexTimeoutError(`Failed to acquire VFS mutex lock at ${mutexDir} after ${maxRetries} retries`);
256
268
  }
@@ -634,9 +646,10 @@ export function acquireLock(agentName, taskId, files = [], rootOrOpts = resolveR
634
646
  const root = typeof rootOrOpts === "string" ? rootOrOpts : (rootOrOpts?.root || resolveRoot());
635
647
  const branch = (typeof rootOrOpts === "object" && rootOrOpts?.branch) || opts?.branch || process.env.JULES_BRANCH || process.env.BRANCH_NAME || "";
636
648
  const lockDir = getLockDir(root);
637
- const lockFile = join(lockDir, `${taskId}.json`);
649
+ return withVfsMutex(join(lockDir, ".acquire.mutex"), () => {
650
+ const lockFile = join(lockDir, `${taskId}.json`);
638
651
 
639
- if (existsSync(lockFile)) {
652
+ if (existsSync(lockFile)) {
640
653
  try {
641
654
  const existing = JSON.parse(readFileSync(lockFile, "utf-8"));
642
655
  if (isLockLive(existing)) {
@@ -740,6 +753,7 @@ export function acquireLock(agentName, taskId, files = [], rootOrOpts = resolveR
740
753
  try { closeSync(fd); } catch (_) {}
741
754
  }
742
755
  }
756
+ });
743
757
  }
744
758
 
745
759
  export function releaseLock(taskId, rootOrOpts = resolveRoot()) {
@@ -1,5 +1,5 @@
1
1
  import { writeFileSync, mkdirSync, existsSync } from "node:fs";
2
- import { resolve, join } from "node:path";
2
+ import { resolve } from "node:path";
3
3
  import { untrackedOnboardingArtifacts } from "./git.mjs";
4
4
  import { loadConfig } from "./config.mjs";
5
5
  import { gate } from "./engine.mjs";
@@ -220,33 +220,22 @@ export function planTaskCreate(root = process.cwd(), inputObj = {}) {
220
220
  const rawId = inputObj.id || `TASK-${Date.now().toString(36).toUpperCase()}`;
221
221
  const taskId = String(rawId).replace(/[^a-zA-Z0-9_-]/g, "_");
222
222
 
223
- const hasAstro = existsSync(join(root, "astro.config.mjs")) || existsSync(join(root, "astro.config.ts"));
224
- const hasWrangler =
225
- existsSync(join(root, "wrangler.jsonc")) ||
226
- existsSync(join(root, "wrangler.json")) ||
227
- existsSync(join(root, "wrangler.toml"));
228
-
223
+ const configInvariants = Array.isArray(config.invariants) ? config.invariants : [];
229
224
  const autoInvariants = [
230
225
  "Preserve existing signatures, surrounding logic, and coding style.",
231
226
  "Do not modify files outside scope.allow.",
227
+ ...configInvariants,
232
228
  ];
233
- if (hasAstro) {
234
- autoInvariants.push("Zero-JS runtime-princip: skicka noll onödig klient-JS i src/ och använd plana <img> framför Astro <Image>.");
235
- }
236
- if (hasWrangler) {
237
- autoInvariants.push("Edge runtime constraints: inga otillåtna Node-imports (t.ex. node:fs, node:path, sharp) i workerd runtime-kod.");
238
- }
239
229
  const invariants = Array.isArray(inputObj.invariants) && inputObj.invariants.length > 0
240
230
  ? inputObj.invariants
241
231
  : autoInvariants;
242
232
 
243
- const autoMcpDirectives = [];
244
- if (hasAstro) autoMcpDirectives.push("astro-docs");
245
- if (hasWrangler) autoMcpDirectives.push("cloudflare-docs");
246
- if (existsSync(join(root, "emdash.config.ts"))) autoMcpDirectives.push("emdash-docs");
247
- if (autoMcpDirectives.length > 0) autoMcpDirectives.push("context7");
248
-
249
- const mcpDirectives = inputObj.mcpDirectives || inputObj.mcp_directives || (autoMcpDirectives.length > 0 ? autoMcpDirectives : undefined);
233
+ const configDirectives = Array.isArray(config.mcp_directives)
234
+ ? config.mcp_directives
235
+ : Array.isArray(config.mcpDirectives)
236
+ ? config.mcpDirectives
237
+ : [];
238
+ const mcpDirectives = inputObj.mcpDirectives || inputObj.mcp_directives || (configDirectives.length > 0 ? configDirectives : undefined);
250
239
  const mcpDirectiveLine = mcpDirectives && mcpDirectives.length > 0
251
240
  ? `MCP DIRECTIVE: Mandating pre-execution documentation lookup via [${mcpDirectives.join(" | ")}] before modifying code.\n\n`
252
241
  : "";