jules-orchestrator-kit 0.8.0 → 0.8.1

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.
package/.env.example CHANGED
@@ -27,6 +27,9 @@ JULES_REPO=owner/repo
27
27
  # Allow modifications to command-defining files (package.json, Cargo.toml, etc.) in PRs (Default: false)
28
28
  # JULES_ALLOW_COMMAND_FILE_CHANGES=false
29
29
 
30
+ # Allow modifications to agent rule files (AGENTS.md, JULES_RULES_TEMPLATE.md, .agent/rules/**) in PRs (Default: false)
31
+ # JULES_ALLOW_AGENT_RULE_CHANGES=false
32
+
30
33
  # --- Execution Scope & Git ---
31
34
  # Base Branch for PR Audits & Merge-Base Calculations (Default: main)
32
35
  BASE_BRANCH=main
package/README.md CHANGED
@@ -167,7 +167,8 @@ All commands are registered in `package.json` and can be run via `npm run <comma
167
167
  | `JULES_REPOLESS` | Set to `true` or `1` to run in repoless/serverless mode |
168
168
  | `JULES_DRY_RUN` | Set to `true` or `1` to simulate dispatching without making API calls |
169
169
  | `JULES_DAILY_BUDGET` | Daily max session budget for autonomous dispatches (Default: `300`) |
170
- | `JULES_ALLOW_COMMAND_FILE_CHANGES` | Set to `true` to allow PR changes to command-defining files like `package.json` (Default: `false`) |
170
+ | `JULES_ALLOW_COMMAND_FILE_CHANGES` | Set to `true` to allow PR changes to command/config files like `package.json`, `tsconfig.json`, `vite.config.ts` (Default: `false`) |
171
+ | `JULES_ALLOW_AGENT_RULE_CHANGES` | Set to `true` to allow PR changes to agent rule files like `AGENTS.md`, `JULES_RULES_TEMPLATE.md` (Default: `false`) |
171
172
  | `BASE_BRANCH` | Base branch for PR Audits & Merge-Base calculations (Default: `main`) |
172
173
  | `GITHUB_HEAD_REF` | PR Head Branch (Used dynamically by CI during OODA repair) |
173
174
  | `JULES_PROJECT_ROOT` | Root directory of the project (Auto-assigned during swarm executions) |
package/bin/init.js CHANGED
@@ -265,6 +265,9 @@ const requiredGitignoreEntries = [
265
265
  ".env",
266
266
  ".agent/history/",
267
267
  ".agent/state/",
268
+ ".agent/jules-queue/.state/",
269
+ ".agent/jules-queue/failed/",
270
+ ".agent/jules-queue/.processing/",
268
271
  ".agent/jules-queue/*.md",
269
272
  "!.agent/jules-queue/README.md"
270
273
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jules-orchestrator-kit",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Orchestration kit for running Google Jules autonomous agents.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -183,20 +183,26 @@ async function runQueue() {
183
183
  logQueueState(file, "FAILED", error);
184
184
 
185
185
  const failureClass = classifyQueueFailure(error);
186
- const NON_RETRYABLE = new Set(["security_violation", "diff_too_large", "budget_exhausted"]);
186
+ const NON_RETRYABLE = new Set(["security_violation", "diff_too_large"]);
187
187
 
188
188
  const taskState = getTaskState(file);
189
- const attempts = (taskState.attempts || 0) + 1;
190
189
 
191
- if (NON_RETRYABLE.has(failureClass) || attempts >= 3) {
192
- const destPath = path.join(failedDir, file);
193
- setTaskState(file, { attempts, status: "FAILED_PERMANENT", failure_class: failureClass, last_error: error.message, updated_at: new Date().toISOString() });
194
- try { await safeMoveAsync(processingPath, destPath); } catch (_) {}
195
- log.error(`Task [${title}] failed (${failureClass}). Moved to failed/. Error: ${error.message}`);
196
- } else {
197
- setTaskState(file, { attempts, status: "REQUEUED", failure_class: failureClass, last_error: error.message, updated_at: new Date().toISOString() });
190
+ if (failureClass === "budget_exhausted") {
191
+ setTaskState(file, { attempts: taskState.attempts || 0, status: "DEFERRED_BUDGET", failure_class: failureClass, last_error: error.message, updated_at: new Date().toISOString() });
198
192
  try { await safeMoveAsync(processingPath, filePath); } catch (_) {}
199
- log.warn(`Task [${title}] failed (${failureClass}). Re-queued (Attempt ${attempts}/3). Error: ${error.message}`);
193
+ log.warn(`Task [${title}] deferred due to daily budget limit. Left in queue for next budget reset.`);
194
+ } else {
195
+ const attempts = (taskState.attempts || 0) + 1;
196
+ if (NON_RETRYABLE.has(failureClass) || attempts >= 3) {
197
+ const destPath = path.join(failedDir, file);
198
+ setTaskState(file, { attempts, status: "FAILED_PERMANENT", failure_class: failureClass, last_error: error.message, updated_at: new Date().toISOString() });
199
+ try { await safeMoveAsync(processingPath, destPath); } catch (_) {}
200
+ log.error(`Task [${title}] failed (${failureClass}). Moved to failed/. Error: ${error.message}`);
201
+ } else {
202
+ setTaskState(file, { attempts, status: "REQUEUED", failure_class: failureClass, last_error: error.message, updated_at: new Date().toISOString() });
203
+ try { await safeMoveAsync(processingPath, filePath); } catch (_) {}
204
+ log.warn(`Task [${title}] failed (${failureClass}). Re-queued (Attempt ${attempts}/3). Error: ${error.message}`);
205
+ }
200
206
  }
201
207
  }
202
208
  })
@@ -208,6 +208,22 @@ export function parseAndCleanStderr(stderrStr) {
208
208
  return lines.slice(-100).join("\n");
209
209
  }
210
210
 
211
+ export function getOodaStateFile(mergeBase = "main") {
212
+ const oodaDir = path.resolve(process.cwd(), ".agent/state/ooda");
213
+ if (!fs.existsSync(oodaDir)) fs.mkdirSync(oodaDir, { recursive: true });
214
+
215
+ const keyInput = [
216
+ process.env.GITHUB_REPOSITORY || "",
217
+ process.env.GITHUB_REF || "",
218
+ process.env.GITHUB_HEAD_REF || "",
219
+ mergeBase,
220
+ process.cwd()
221
+ ].join("\u0000");
222
+
223
+ const key = crypto.createHash("sha256").update(keyInput).digest("hex").slice(0, 16);
224
+ return path.join(oodaDir, `${key}.json`);
225
+ }
226
+
211
227
  export function logAuditMetrics(metrics) {
212
228
  const historyDir = path.resolve(process.cwd(), ".agent/history");
213
229
  if (!fs.existsSync(historyDir)) {
@@ -275,7 +291,9 @@ export function runSelfAudit() {
275
291
 
276
292
  if (commandFileChanges.length > 0 && process.env.JULES_ALLOW_COMMAND_FILE_CHANGES !== "true" && process.env.JULES_ALLOW_COMMAND_FILE_CHANGES !== "1") {
277
293
  const err = new Error(
278
- "COMMAND FILE CHANGE DETECTED: " + commandFileChanges.join(", ") + ". Refusing to execute verification scripts from untrusted branch."
294
+ "COMMAND / BUILD CONFIG FILE CHANGE DETECTED: [" + commandFileChanges.join(", ") + "]. " +
295
+ "Refusing to execute verification scripts from untrusted branch. " +
296
+ "To override for legitimate dependency updates, set JULES_ALLOW_COMMAND_FILE_CHANGES=true."
279
297
  );
280
298
  err.code = 3;
281
299
  throw err;
@@ -287,7 +305,9 @@ export function runSelfAudit() {
287
305
 
288
306
  if (agentRuleChanges.length > 0 && process.env.JULES_ALLOW_AGENT_RULE_CHANGES !== "true" && process.env.JULES_ALLOW_AGENT_RULE_CHANGES !== "1") {
289
307
  const err = new Error(
290
- "AGENT RULE FILE CHANGE DETECTED: " + agentRuleChanges.join(", ") + ". Refusing to load agent rules from untrusted branch."
308
+ "AGENT RULE FILE CHANGE DETECTED: [" + agentRuleChanges.join(", ") + "]. " +
309
+ "Refusing to load agent rules from untrusted branch. " +
310
+ "To override for legitimate rule updates, set JULES_ALLOW_AGENT_RULE_CHANGES=true."
291
311
  );
292
312
  err.code = 3;
293
313
  throw err;
@@ -442,22 +462,6 @@ export function runSelfAudit() {
442
462
  throw err;
443
463
  }
444
464
 
445
- function getOodaStateFile(mergeBase = "main") {
446
- const oodaDir = path.resolve(process.cwd(), ".agent/state/ooda");
447
- if (!fs.existsSync(oodaDir)) fs.mkdirSync(oodaDir, { recursive: true });
448
-
449
- const keyInput = [
450
- process.env.GITHUB_REPOSITORY || "",
451
- process.env.GITHUB_REF || "",
452
- process.env.GITHUB_HEAD_REF || "",
453
- mergeBase,
454
- process.cwd()
455
- ].join("\u0000");
456
-
457
- const key = crypto.createHash("sha256").update(keyInput).digest("hex").slice(0, 16);
458
- return path.join(oodaDir, `${key}.json`);
459
- }
460
-
461
465
  let oodaRetries = 0;
462
466
  const oodaStateFile = getOodaStateFile(mergeBase);
463
467
  try {
package/scripts/utils.mjs CHANGED
@@ -165,10 +165,29 @@ export function redactSecrets(text) {
165
165
  return sanitized;
166
166
  }
167
167
 
168
+ export function pruneOldLedgers(stateDir, retentionDays = 30) {
169
+ try {
170
+ if (!fs.existsSync(stateDir)) return;
171
+ const now = Date.now();
172
+ const maxAgeMs = retentionDays * 24 * 60 * 60 * 1000;
173
+ const files = fs.readdirSync(stateDir);
174
+ files.forEach((file) => {
175
+ if (file.endsWith(".jsonl")) {
176
+ const filePath = path.join(stateDir, file);
177
+ const stat = fs.statSync(filePath);
178
+ if (now - stat.mtimeMs > maxAgeMs) {
179
+ try { fs.unlinkSync(filePath); } catch (_) {}
180
+ }
181
+ }
182
+ });
183
+ } catch (_) {}
184
+ }
185
+
168
186
  export function getDailyLedgerPath(ledgerName = "sessions") {
169
187
  const dateStr = new Date().toISOString().split("T")[0];
170
188
  const stateDir = path.resolve(process.env.JULES_PROJECT_ROOT || process.cwd(), ".agent/state", ledgerName);
171
189
  ensureDir(stateDir);
190
+ pruneOldLedgers(stateDir, 30);
172
191
  return path.join(stateDir, `${dateStr}.jsonl`);
173
192
  }
174
193
 
@@ -183,7 +202,6 @@ export function appendLedger(ledgerName, payload) {
183
202
  }
184
203
 
185
204
  export function checkDailyBudget(maxSessions = 300) {
186
- const dateStr = new Date().toISOString().split("T")[0];
187
205
  const ledgerPath = getDailyLedgerPath("sessions");
188
206
 
189
207
  if (!fs.existsSync(ledgerPath)) {