jules-orchestrator-kit 0.38.1 → 0.38.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.
- package/README.md +3 -2
- package/package.json +1 -1
- package/src/dag-engine.mjs +44 -6
- package/src/engine.mjs +9 -2
package/README.md
CHANGED
|
@@ -131,7 +131,7 @@ To maximize PR merge rates, dispatch tasks according to deterministic boundaries
|
|
|
131
131
|
* **Fail-Closed Security & Secret Redaction:** Evaluates explicit Deny rules before Allow rules against canonicalized, case-folded paths. Redacts high-entropy keys and base64-encoded credentials (such as Kubernetes `Secret` manifests).
|
|
132
132
|
* **Complexity & Cost Router:** Zero-dependency heuristic classifier (`src/router.mjs`) routing mechanical tasks to lightweight models while reserving primary models for complex refactors.
|
|
133
133
|
* **Terminal UI & Diagnostic Matrix (`agentctl doctor`):** Interactive terminal dashboard, task sidecar manager, and automated transactional self-repair.
|
|
134
|
-
* **Verified Test Suite:** Tested with **
|
|
134
|
+
* **Verified Test Suite:** Tested with **572 unit tests across 81 suites passing in < 10.0s**.
|
|
135
135
|
|
|
136
136
|
<br/>
|
|
137
137
|
|
|
@@ -151,7 +151,7 @@ To maximize PR merge rates, dispatch tasks according to deterministic boundaries
|
|
|
151
151
|
| `task template` | `agentctl task template [<id>] [--list] [--json]` | Lists and synthesizes pre-calibrated web task envelopes (`web-cwv`, `web-wcag`, `web-seo`, `web-playwright`, `web-flaky-heal`, `web-i18n`, `web-ai-access`). | `0` (Listed/Synthesized) |
|
|
152
152
|
| `dispatch` | `agentctl dispatch [-p <prompt>] [-f <file>] [-r <role>] [-t <tier>] [--auto-pr] [--repoless] [--dry-run]` | Dispatches autonomous task to the active provider with payload limits and role prompt resolution. | `0` (Dispatched), `1` (Error) |
|
|
153
153
|
| `doctor` | `agentctl doctor [--json]` | Diagnostic DAG check runner & automated transactional self-repair engine. | `0` (Healthy), `1` (Failures) |
|
|
154
|
-
| `queue` | `agentctl queue [--dag] [--concurrency <n>] [--dry-run] [--json]` | Consumes and executes task envelopes in `.agent/jules-queue/` with Kahn's DAG dependency resolution. | `0` (Complete) |
|
|
154
|
+
| `queue` | `agentctl queue [--dag] [--concurrency <n>] [--dry-run] [--json]` | Consumes and executes task envelopes in `.agent/jules-queue/` with Kahn's DAG dependency resolution. Non-task files (manifests, `README.md`) are skipped, and `--dry-run` previews without moving anything. | `0` (Complete) |
|
|
155
155
|
| `swarm` | `agentctl swarm [--json]` | Runs parallel multi-agent swarm across worker slots with PID liveness detection. | `0` (Complete) |
|
|
156
156
|
| `gate` / `audit`| `agentctl gate --mode working-tree [--json]` | Runs security, secret scanning, and verification gates against working tree or branch. | `0` (Approved), `3` (Scope), `5` (Diff >75K), `6` (Secret) |
|
|
157
157
|
| `rollback` | `agentctl rollback [sessionId \| --latest]` | Restores exact commit, uncommitted files, and cleans orphan task worktrees from pre-flight checkpoints. | `0` (Restored), `1` (Error) |
|
|
@@ -319,6 +319,7 @@ console.log(classification.tier); // "fast" | "complex"
|
|
|
319
319
|
|
|
320
320
|
| Feature | Module / Command | Architectural Description | Status |
|
|
321
321
|
| :--- | :--- | :--- | :---: |
|
|
322
|
+
| **Queue Runner Fidelity** | `src/dag-engine.mjs`, `src/engine.mjs` | Queue selection is by task shape rather than file extension, so manifests and READMEs are skipped instead of dispatched, and `--dry-run` leaves the queue untouched. | **v0.38.2** *(Shipped)* |
|
|
322
323
|
| **Release Gate Enforcement & Wizard Smoke Test** | `.github/workflows/jules-audit.yml`, `scripts/release.mjs`, `test/wizard-smoke.test.mjs` | Doc-sync gate runs in CI rather than by hand, releases block on a green CI matrix for `HEAD`, per-test deadlines turn a hang into a failure, and the real `init` wizard is driven end to end over a fake TTY. | **v0.38.1** *(Shipped)* |
|
|
323
324
|
| **Multi-OS CI Matrix & TUI Hardening** | `scripts/run-tests.mjs`, `src/state.mjs`, `src/git.mjs` | Automated 9-job CI matrix across Linux, macOS, and Windows on Node 20/22/24 with raw-mode TUI resilience and native Windows command quoting. | **v0.38.0** *(Shipped)* |
|
|
324
325
|
| **Base64 Secret Detection & Budget Fix** | `src/security.mjs`, `src/budget.mjs` | Secret scanner decodes base64 before matching structured patterns (K8s secrets), and `budget reset` preserves confirmed provider sessions. | **v0.37.0** *(Shipped)* |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jules-orchestrator-kit",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.2",
|
|
4
4
|
"description": "Zero-dependency safety gatekeeper, test oracle generator, and multi-agent coordination protocol for Google Jules (jules) autonomous agents.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
package/src/dag-engine.mjs
CHANGED
|
@@ -417,9 +417,42 @@ export function resolveAffectedTests(modifiedFiles = [], options = {}) {
|
|
|
417
417
|
* @param {Object} [options.config]
|
|
418
418
|
* @returns {Promise<{ processed: number, results: Array<any> }>}
|
|
419
419
|
*/
|
|
420
|
+
/**
|
|
421
|
+
* Decides whether a queue-directory entry is a task this runner should execute.
|
|
422
|
+
*
|
|
423
|
+
* The DAG runner accepts more shapes than `isTaskFile` does — `.json` and
|
|
424
|
+
* `.task` envelopes as well as Markdown — but "every `.json` in the queue
|
|
425
|
+
* folder" also swept up manifests (swarm run files, indexes). A manifest has no
|
|
426
|
+
* `prompt`, so the whole file became the prompt and a large one blew the
|
|
427
|
+
* provider payload limit. Shape decides, not the extension.
|
|
428
|
+
*
|
|
429
|
+
* @param {string} fileName
|
|
430
|
+
* @param {string} content
|
|
431
|
+
* @param {Function} isTaskFile - `isTaskFile` from engine.mjs, passed in to avoid a circular import.
|
|
432
|
+
* @returns {boolean}
|
|
433
|
+
*/
|
|
434
|
+
function isDagTaskFile(fileName, content, isTaskFile) {
|
|
435
|
+
if (fileName.endsWith(".task")) return true;
|
|
436
|
+
if (fileName.endsWith(".md")) return isTaskFile(fileName, content);
|
|
437
|
+
if (fileName.endsWith(".json")) {
|
|
438
|
+
let parsed;
|
|
439
|
+
try {
|
|
440
|
+
parsed = JSON.parse(content);
|
|
441
|
+
} catch (_) {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return false;
|
|
445
|
+
// A manifest *describes* tasks; a task envelope *is* one.
|
|
446
|
+
if (Array.isArray(parsed.tasks) || Array.isArray(parsed.envelopes)) return false;
|
|
447
|
+
return typeof parsed.prompt === "string" && parsed.prompt.length > 0;
|
|
448
|
+
}
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
|
|
420
452
|
export async function executeQueueDag(root = process.cwd(), options = {}) {
|
|
421
453
|
const { readdirSync, renameSync, mkdirSync } = await import("node:fs");
|
|
422
454
|
const { join, basename } = await import("node:path");
|
|
455
|
+
const { isTaskFile } = await import("./engine.mjs");
|
|
423
456
|
|
|
424
457
|
const queueDir = join(root, ".agent", "jules-queue");
|
|
425
458
|
const fallbackQueueDir = join(root, ".agent", "queue");
|
|
@@ -429,7 +462,7 @@ export async function executeQueueDag(root = process.cwd(), options = {}) {
|
|
|
429
462
|
if (!existsSync(actualQueueDir)) {
|
|
430
463
|
return { processed: 0, results: [] };
|
|
431
464
|
}
|
|
432
|
-
if (!existsSync(completedDir)) {
|
|
465
|
+
if (!existsSync(completedDir) && !options.dryRun) {
|
|
433
466
|
try {
|
|
434
467
|
mkdirSync(completedDir, { recursive: true });
|
|
435
468
|
} catch (_) {}
|
|
@@ -452,6 +485,7 @@ export async function executeQueueDag(root = process.cwd(), options = {}) {
|
|
|
452
485
|
if (basename(file) !== file) continue;
|
|
453
486
|
const fullPath = join(actualQueueDir, file);
|
|
454
487
|
const content = readFileSync(fullPath, "utf-8");
|
|
488
|
+
if (!isDagTaskFile(file, content, isTaskFile)) continue;
|
|
455
489
|
let taskId = file.replace(/\.(md|json|task)$/, "");
|
|
456
490
|
let dependsOn = [];
|
|
457
491
|
let title = taskId;
|
|
@@ -508,13 +542,17 @@ export async function executeQueueDag(root = process.cwd(), options = {}) {
|
|
|
508
542
|
results.push({ file: t.file, taskId: t.taskId, ok: false, status: session.status, error: session.error, session });
|
|
509
543
|
throw new Error(`DAG Task '${t.taskId}' failed: ${session.error || session.status || "Unknown error"}`);
|
|
510
544
|
} else {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
545
|
+
// A dry run simulates; it must leave the queue exactly as it found it,
|
|
546
|
+
// or the second `--dry-run` sees an empty queue.
|
|
547
|
+
if (!options.dryRun) {
|
|
548
|
+
const dstPath = join(completedDir, t.file);
|
|
549
|
+
if (existsSync(srcPath)) {
|
|
550
|
+
if (!existsSync(dstPath)) {
|
|
551
|
+
renameSync(srcPath, dstPath);
|
|
552
|
+
}
|
|
515
553
|
}
|
|
516
554
|
}
|
|
517
|
-
results.push({ file: t.file, taskId: t.taskId, ok: true, session });
|
|
555
|
+
results.push({ file: t.file, taskId: t.taskId, ok: true, dryRun: Boolean(options.dryRun), session });
|
|
518
556
|
}
|
|
519
557
|
},
|
|
520
558
|
});
|
package/src/engine.mjs
CHANGED
|
@@ -824,7 +824,7 @@ export async function run(tasksOrOpts = {}, opts = {}) {
|
|
|
824
824
|
|
|
825
825
|
const queueDir = getQueueDir(root);
|
|
826
826
|
const completedDir = join(queueDir, "completed");
|
|
827
|
-
ensureDir(completedDir);
|
|
827
|
+
if (!isDry) ensureDir(completedDir);
|
|
828
828
|
|
|
829
829
|
let filesToProcess = [];
|
|
830
830
|
if (Array.isArray(tasks)) {
|
|
@@ -858,10 +858,17 @@ export async function run(tasksOrOpts = {}, opts = {}) {
|
|
|
858
858
|
return;
|
|
859
859
|
}
|
|
860
860
|
|
|
861
|
-
|
|
861
|
+
// options.provider mirrors dispatch()'s existing injection point so a
|
|
862
|
+
// caller can exercise the real queue lifecycle without a live provider.
|
|
863
|
+
const session = await dispatch(task, { root, config, dryRun: isDry, provider: options.provider });
|
|
862
864
|
|
|
863
865
|
if (session && session.ok === false) {
|
|
864
866
|
results.push({ file: fileName, ok: false, status: session.status, error: session.error, session });
|
|
867
|
+
} else if (isDry) {
|
|
868
|
+
// A dry run simulates; it must leave the queue exactly as it found
|
|
869
|
+
// it, or the second `--dry-run` sees an empty queue. Nothing
|
|
870
|
+
// completed either, so nothing is written to the ledger.
|
|
871
|
+
results.push({ file: fileName, ok: true, dryRun: true, session });
|
|
865
872
|
} else {
|
|
866
873
|
const dstPath = join(completedDir, fileName);
|
|
867
874
|
if (existsSync(srcPath)) {
|