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 +3 -0
- package/README.md +2 -1
- package/bin/init.js +3 -0
- package/package.json +1 -1
- package/scripts/jules-queue-runner.mjs +16 -10
- package/scripts/jules-self-audit.mjs +22 -18
- package/scripts/utils.mjs +19 -1
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
|
|
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
|
@@ -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"
|
|
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 (
|
|
192
|
-
|
|
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}]
|
|
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(", ") + ".
|
|
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(", ") + ".
|
|
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)) {
|