jules-orchestrator-kit 1.0.0 → 1.0.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/README.md CHANGED
@@ -1,208 +1,353 @@
1
- # jules-orchestrator-kit
1
+ <div align="center">
2
2
 
3
- *Disclaimer: This is an independent open-source orchestration tool for Google Jules and is not officially affiliated with or endorsed by Google.*
3
+ # 🚀 jules-orchestrator-kit
4
+
5
+ ### High-Volume Autonomous AI Agent Orchestration Engine for Google Jules
4
6
 
5
7
  [![Jules PR Audit](https://github.com/FullThrottle83/jules-orchestrator-kit/actions/workflows/jules-audit.yml/badge.svg)](https://github.com/FullThrottle83/jules-orchestrator-kit/actions/workflows/jules-audit.yml)
6
8
  [![npm version](https://img.shields.io/npm/v/jules-orchestrator-kit.svg)](https://www.npmjs.com/package/jules-orchestrator-kit)
7
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
10
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen.svg)](https://nodejs.org)
11
+ [![Zero Dependencies](https://img.shields.io/badge/dependencies-0%20native-blue.svg)](https://nodejs.org)
12
+
13
+ <p align="center">
14
+ <b>Zero-dependency safety kernel and high-throughput orchestration engine for autonomous AI agent swarms.</b><br/>
15
+ Built specifically to execute 300+ daily agent sessions and parallel worktree swarms safely on native Node.js 20+ ESM.
16
+ </p>
9
17
 
10
- > **Zero-dependency safety kernel for autonomous AI agent swarms.**
11
- > Built specifically to execute 300+ daily agent sessions and parallel swarms safely with zero external runtime dependencies. Built strictly on native Node.js 20+ ESM standard library.
18
+ </div>
12
19
 
13
20
  ---
14
21
 
15
- ## 🏗️ Architecture Layer Diagram
16
-
17
- ```text
18
- ===================================================================================
19
- JULES ORCHESTRATOR KIT — ARCHITECTURE LAYERS
20
- ===================================================================================
21
-
22
- +-------------------------------------------------------------------------------+
23
- | GOVERNANCE PLANE |
24
- | +--------------------+ +--------------------+ +-------------------------+ |
25
- | | Scope Guard | | Secret Scanner | | Risk Classifier | |
26
- | | (builtin & custom) | | (entropy > 3.6b) | | (R0 Cosmetic - R3 Restr)| |
27
- | +--------------------+ +--------------------+ +-------------------------+ |
28
- | +--------------------+ +--------------------+ +-------------------------+ |
29
- | | Prompt Firewall | | Dynamic Guardrails | | Flaky Test Quarantine | |
30
- | | (injection strip) | | (rule budget sync) | | (Wilson CI / Exit 8) | |
31
- | +--------------------+ +--------------------+ +-------------------------+ |
32
- +----------------------------------------+--------------------------------------+
33
- |
34
- v
35
- +-------------------------------------------------------------------------------+
36
- | EXECUTION PLANE |
37
- | +--------------------+ +--------------------+ +-------------------------+ |
38
- | | Agent Envelope | | Task DAG Engine | | OODA Auto-Repair | |
39
- | | (sanitized payload)| | (Kahn's / Fingerpr)| | (3-strike thrash guard) | |
40
- | +--------------------+ +--------------------+ +-------------------------+ |
41
- | +--------------------+ +--------------------+ +-------------------------+ |
42
- | | Process Group Mgr | | Hermetic Net Guard | | 3-Way Structural Merger | |
43
- | | (detached / SIGKILL| | (ERR_UNMOCKED_NET) | | (AST/JSON & block diff) | |
44
- | +--------------------+ +--------------------+ +-------------------------+ |
45
- +----------------------------------------+--------------------------------------+
46
- |
47
- v
48
- +-------------------------------------------------------------------------------+
49
- | KERNEL PLANE |
50
- | +--------------------+ +--------------------+ +-------------------------+ |
51
- | | VFS Mutex | | Telemetry Spine | | Stdio MCP Stream | |
52
- | | (linearizable CAS) | | (O(1) SHA-256 chain)| | (Content-Length 4MB) | |
53
- | +--------------------+ +--------------------+ +-------------------------+ |
54
- | +--------------------+ +--------------------+ +-------------------------+ |
55
- | | Intent Journal | | Worktree Reaper | | Atomic Budget Ledger | |
56
- | | (append-only sync) | | (zombie PID prune) | | (sliding window / proc) | |
57
- | +--------------------+ +--------------------+ +-------------------------+ |
58
- +-------------------------------------------------------------------------------+
59
- ===================================================================================
60
- ```
22
+ <p align="center">
23
+ <img src="docs/assets/hero-flow.svg?v=3" alt="Autonomous Orchestration Pipeline" width="100%" />
24
+ </p>
61
25
 
62
26
  ---
63
27
 
64
- ## ⚡ Quick Start Guide
28
+ ## ⚡ 2-Minute Quickstart
65
29
 
66
- ### 1. Command Line Interface (`agentctl`)
67
-
68
- Install globally or run via `npx`:
30
+ > 💡 **TIP**: **New to Google Jules or agent automation?** You don't need any complex setup! `jules-orchestrator-kit` works out of the box with standard `npm test` and zero external dependencies.
69
31
 
70
32
  ```bash
71
- # Initialize orchestrator structure and configuration in target project
72
- npx agentctl init
33
+ # 1. Initialize orchestrator structure in your target codebase
34
+ npx jules-orchestrator-kit init
73
35
 
74
- # Dispatch an autonomous coding task (Dry-run mode for local simulation)
36
+ # 2. Dispatch an autonomous task (Dry-Run mode for local simulation)
75
37
  JULES_DRY_RUN=1 npx agentctl dispatch \
76
- --title "Implement JWT Validator" \
38
+ --title "Add JWT Validator" \
77
39
  --prompt "Implement JWT validation middleware with unit tests"
78
40
 
79
- # Run the 4-phase security and verification gatekeeper against current workspace
41
+ # 3. Run the 4-phase security & verification gatekeeper
80
42
  npx agentctl gate
81
43
 
82
- # Launch parallel multi-agent swarm in isolated git worktrees
83
- npx agentctl swarm
44
+ # 4. Connect as a native stdio MCP server (for Claude Code, Cursor, or Antigravity)
45
+ npx agentctl mcp
46
+ ```
47
+
48
+ > 📖 **NOTE**: **Looking for production deployment patterns?**
49
+ > Check out [**EXAMPLES.md**](./EXAMPLES.md) for 6 real-world recipes (Nightly TODO Scanner, Composite CI Action, Multi-Worktree Swarms, OODA Auto-Fix, MCP IDE setup, and Specialist Rosters).
50
+
51
+ ---
52
+
53
+ ## 🎯 Why jules-orchestrator-kit?
54
+
55
+ Whether you are dispatching your first automated coding task or managing high-throughput CI/CD swarms across large engineering teams, `jules-orchestrator-kit` provides total operational safety:
56
+
57
+ | Feature | 🐣 For Rookies & Beginners | 🛠️ For Senior Developers & Infrastructure Engineers |
58
+ | :--- | :--- | :--- |
59
+ | **Safety First** | Never breaks `main` branch or pushes failing code. | 4-Phase Safety Gatekeeper fails closed on scope drift, high entropy secrets, or test regressions. |
60
+ | **Token Budget Protection** | Prevents runaway loops from burning API quotas. | Sliding-window OODA thrash detector ($A \rightarrow B \rightarrow A \rightarrow B$) halts non-convergent repair cycles automatically. |
61
+ | **Zero Setup Hassle** | Works out of the box with standard `npm test`. | **Zero External Runtime Dependencies** (`node:fs`, `node:path`, `node:crypto`, `node:child_process`). |
62
+ | **Multi-Agent Swarms** | Run multiple tasks simultaneously without conflict. | Deterministic VFS mutex and 3-way structural merge engine resolve parallel worktree changes cleanly. |
63
+ | **IDE & Tooling** | Seamlessly connects to your favorite editor. | Native Model Context Protocol (MCP) server over memory-bounded stdio streams. |
64
+
65
+ ---
66
+
67
+ ## 🏛️ System Architecture & Visual Diagrams
68
+
69
+ <details open>
70
+ <summary><b>📐 1. Control Plane Architecture Layers</b></summary>
71
+
72
+ <br/>
73
+
74
+ <p align="center">
75
+ <img src="docs/assets/architecture-layers.svg?v=3" alt="Control Plane Architecture Layers" width="100%" />
76
+ </p>
77
+
78
+ <br/>
79
+
80
+ ### Engine System Highlights
81
+
82
+ - **Native Task DAG Executor (`src/dag-engine.mjs`)**: Zero-dependency `DagExecutor` with Kahn's topological sort algorithm, SHA-256 interface fingerprinting post-task execution, and pre-execution cycle detection (`DagCycleError`).
83
+ - **Intent Journaling & Zombie Worktree Reaper (`src/journal.mjs`)**: Automatic boot-time scan (`reapOrphanedIntents`) in `agentctl` and MCP server that tracks git operations in `.agent/state/journal.jsonl` and prunes orphaned worktrees left by crashed/recycled processes.
84
+ - **Hermetic Network Egress Guard (`src/preload-net-guard.mjs`)**: Intercepts and blocks unmocked outbound HTTP/HTTPS egress during test execution (`NODE_OPTIONS="--import ./src/preload-net-guard.mjs"`), enforcing hermetic testing while allowing local loopback (`localhost`, `127.0.0.1`).
85
+ - **Linearizable VFS Mutex (`src/state.mjs`)**: Kernel-level directory mutex (`withVfsMutex`) guaranteeing serial linearizability for SHA-256 hash-chained session ledgers with atomic budget reservation (`reserveBudgetAtomic`).
86
+ - **PID Recycling & Stale Lock Protection (`src/state.mjs`)**: Linux `/proc/<pid>/stat` launch-time validation and random UUID nonces prevent false-positive lock reaps from recycled OS process IDs.
87
+ - **Memory-Bounded Content-Length MCP Streaming (`src/mcp.mjs`)**: Native MCP server over stdio streams using `McpFrameDecoder` with a 4 MB memory safety ceiling and panic boundaries to prevent stdout stack trace leaks.
88
+ - **Process Group Isolation (`src/process-group.mjs`)**: `ProcessGroupManager` creates isolated process groups (`detached: true`) and catches `SIGINT`/`SIGTERM`/`exit` signals to execute `process.kill(-pgid)`, guaranteeing zero zombie processes.
89
+ - **TOCTOU & Symlink Defense (`src/security.mjs`)**: `safeAtomicWrite()` uses `O_CREAT | O_EXCL | O_WRONLY` temp files with `fsyncSync` + `renameSync` and `lstatSync`/`realpathSync` symlink checks.
90
+ - **3-Way Structural AST/JSON Merge (`scripts/jules-merge-swarm.mjs`)**: Pure Node `deepMerge3Way()` algorithm for recursive object and array merges executed in isolated temporary directories (`os.tmpdir()`).
91
+
92
+ </details>
93
+
94
+ <details open>
95
+ <summary><b>🔁 2. Self-Healing OODA Loop Cycle</b></summary>
96
+
97
+ <br/>
98
+
99
+ <p align="center">
100
+ <img src="docs/assets/ooda-loop-cycle.svg?v=3" alt="Self-Healing OODA Loop" width="100%" />
101
+ </p>
102
+
103
+ <br/>
104
+
105
+ > 🚨 **IMPORTANT**: The OODA (Observe-Orient-Decide-Act) loop executes up to 3 repair attempts when tests fail. If the failure output oscillates deterministically without progress, the OODA engine halts repair to save API tokens and returns Exit Code 4.
106
+
107
+ </details>
108
+
109
+ <details open>
110
+ <summary><b>🛡️ 3. Zero-Trust Security Shield & 4-Phase Gate</b></summary>
111
+
112
+ <br/>
113
+
114
+ <p align="center">
115
+ <img src="docs/assets/security-shield.svg?v=3" alt="Zero-Trust Security Guarantees" width="100%" />
116
+ </p>
117
+
118
+ <br/>
119
+
120
+ ### The 4-Phase Safety Audit & Security Boundary (`agentctl gate`)
121
+
122
+ 1. **Scope Fencing (`forbidden_paths`)**: Ensures agents cannot modify protected files (`package.json`, `.github/`, deployment keys) without explicit overrides.
123
+ 2. **Diff Payload Governor**: Rejects oversized diffs (> 75 KB) to prevent truncation and hidden payload injections.
124
+ 3. **Secret Entropy Scanner**: Scans diffs for high-confidence secrets (AWS keys, Stripe keys, GitHub tokens, SSH private keys) using Shannon Entropy analysis (> 3.6 bits).
125
+ 4. **Trusted Verification Suite**: Executes auto-detected unit tests and linters (`npm test`) inside a hermetic network sandbox to guarantee zero regressions before merging.
126
+ 5. **Prompt Guard Boundary (`src/prompt-guard.mjs`)**: `sanitizeUntrustedData` strips bidi control characters, ANSI escape sequences, zero-width unicode, and neutralizes prompt injection tags (`<|im_start|>`, `[INST]`).
127
+ 6. **MCP Stream Isolation (`src/mcp.mjs`)**: Seals `process.stdout.write` framing stream to prevent log output from corrupting JSON-RPC stdio frames.
128
+
129
+ > ⚠️ **WARNING**: All security rules are fetched strictly from `origin/main` (never untrusted PR branches) to prevent prompt-injection attacks from altering security rules.
130
+
131
+ </details>
132
+
133
+ <details open>
134
+ <summary><b>🐝 4. Parallel Swarm Topology & Isolated Worktrees</b></summary>
135
+
136
+ <br/>
137
+
138
+ <p align="center">
139
+ <img src="docs/assets/swarm-topology.svg?v=3" alt="Multi-Agent Swarm Topology" width="100%" />
140
+ </p>
141
+
142
+ <br/>
143
+
144
+ > 📌 **NOTE**: Swarm execution spawns dedicated git worktrees for each task in parallel, isolated by VFS locks. Completed tasks are verified and merged back using 3-way AST/JSON structural merging (`agentctl swarm`).
145
+
146
+ </details>
147
+
148
+ <details open>
149
+ <summary><b>🔌 5. Model Context Protocol (MCP) & IDE Integration</b></summary>
150
+
151
+ <br/>
152
+
153
+ <p align="center">
154
+ <img src="docs/assets/mcp-integration.svg?v=3" alt="Dual-Way MCP Integration" width="100%" />
155
+ </p>
156
+
157
+ <br/>
158
+
159
+ ### Connecting to Claude Desktop, Cursor, or Antigravity
160
+
161
+ Start the native stdio MCP server:
162
+
163
+ ```bash
164
+ npx agentctl mcp
84
165
  ```
85
166
 
86
- ### 2. Model Context Protocol (MCP) Client Configuration (`agentctl-mcp`)
167
+ #### MCP Tool Registry Exposed:
168
+ - `dispatch_jules_task`: Dispatch autonomous coding tasks directly from your LLM prompt.
169
+ - `audit_jules_gate`: Execute the 4-phase safety gate against the workspace.
170
+ - `check_risk_tier`: Classify workspace changes into Risk Tiers (R0 Cosmetic to R3 Restricted).
171
+ - `get_jules_status`: Fetch real-time status of active, pending, and completed tasks.
172
+ - `telemetry_tail`: Query last N real-time telemetry events from the SHA-256 hash spine.
87
173
 
88
- Connect `jules-orchestrator-kit` directly to **Claude Desktop**, **Cursor IDE**, or **Antigravity IDE** via native stdio MCP framing.
174
+ </details>
175
+
176
+ <details open>
177
+ <summary><b>💳 6. Subscription Tier Presets Matrix</b></summary>
178
+
179
+ <br/>
180
+
181
+ <p align="center">
182
+ <img src="docs/assets/tier-presets.svg?v=3" alt="Subscription Tier Presets Matrix" width="100%" />
183
+ </p>
184
+
185
+ <br/>
186
+
187
+ Tailor session limits and rate-limiting behavior to your Google Jules API subscription tier:
188
+
189
+ | Tier | `dailyTasks` | `repairAttempts` | `concurrency` | `staggerMs` | Target Usage |
190
+ | :--- | :---: | :---: | :---: | :---: | :--- |
191
+ | **`free`** | `15` | `1` | `1` | `3000 ms` | **Hobby / Free Tier:** Conserves quota, prevents HTTP 429 rate limits. |
192
+ | **`pro`** | `100` | `2` | `2` | `1500 ms` | **Developer Pro:** Balanced throughput for everyday work. |
193
+ | **`ultra`** *(default)* | `300` | `3` | `3` | `1000 ms` | **Swarm / Enterprise:** Maximum parallel throughput & CI/CD. |
194
+
195
+ **How to activate:**
196
+ - **Environment Variable:** `export JULES_TIER=free` (or set in `.env`)
197
+ - **Config File (`.agent/jules.yml`):** Set `tier: free`
198
+
199
+ </details>
200
+
201
+ ---
89
202
 
90
- #### Claude Desktop Configuration (`claude_desktop_config.json`)
203
+ ## 🤖 Specialist Agent Prompt Presets (`.agent/prompts/`)
91
204
 
92
- ```json
93
- {
94
- "mcpServers": {
95
- "jules-orchestrator": {
96
- "command": "npx",
97
- "args": ["agentctl-mcp"],
98
- "env": {
99
- "JULES_API_KEY": "YOUR_JULES_API_KEY"
100
- }
101
- }
102
- }
103
- }
205
+ Specialized prompt presets enforce payload limits (< 75 KB) and domain guardrails out of the box:
206
+
207
+ | Preset | Role & Domain | Primary Focus |
208
+ | :--- | :--- | :--- |
209
+ | **`Overseer.md`** | **Architect & Supervisor** | System-wide refactoring, linearizable state, and structural integrity. |
210
+ | **`Bolt.md`** | **Performance Engineer** | Bottleneck elimination, streaming optimization, and low-latency execution. |
211
+ | **`Sentinel.md`** | **Security Auditor** | Vulnerability patching, secret sanitization, and TOCTOU defense. |
212
+ | **`Janitor.md`** | **Technical Debt & Cleanup** | Dead code elimination, unused import pruning, and zero-dependency compliance. |
213
+
214
+ ```bash
215
+ # Example: Dispatch a cleanup task using the Janitor preset
216
+ npx agentctl dispatch \
217
+ --prompt "$(cat .agent/prompts/Janitor.md) Prune unused helper methods in src/utils.mjs"
104
218
  ```
105
219
 
106
- #### Cursor / VS Code MCP Configuration (`.vscode/mcp.json`)
107
-
108
- ```json
109
- {
110
- "mcpServers": {
111
- "agentctl-mcp": {
112
- "command": "npx",
113
- "args": ["-y", "jules-orchestrator-kit", "mcp"],
114
- "env": {
115
- "JULES_API_KEY": "YOUR_JULES_API_KEY"
116
- }
117
- }
118
- }
119
- }
220
+ ---
221
+
222
+ ## ⚡ GitHub Actions Composite Action (`.github/actions/setup-jules`)
223
+
224
+ Integrate `jules-orchestrator-kit` into any GitHub Actions workflow with 3 lines of YAML:
225
+
226
+ ```yaml
227
+ steps:
228
+ - uses: actions/checkout@v4
229
+ - uses: FullThrottle83/jules-orchestrator-kit/.github/actions/setup-jules@main
230
+ with:
231
+ action: 'gate'
232
+ base_branch: 'main'
233
+ tier: 'ultra'
234
+ env:
235
+ JULES_API_KEY: ${{ secrets.JULES_API_KEY }}
120
236
  ```
121
237
 
122
238
  ---
123
239
 
124
- ## 📝 Configuration Reference (`.agent/config.yml`)
240
+ ## 🛠️ CLI Command Reference (`agentctl`)
241
+
242
+ | Command | Usage Example | Description |
243
+ | :--- | :--- | :--- |
244
+ | **`init`** | `npx agentctl init` | Initializes `.agent/` configuration, workflows, and task queue directory |
245
+ | **`dispatch`** | `agentctl dispatch --title "Fix Bug" --prompt "..."` | Dispatches an autonomous task to Google Jules |
246
+ | **`gate`** | `agentctl gate [--fix] [--base main]` | Runs 4-phase safety gatekeeper audit against workspace |
247
+ | **`queue`** | `agentctl queue` | Processes pending task queue sequentially from `.agent/jules-queue/` |
248
+ | **`swarm`** | `agentctl swarm` | Launches parallel multi-agent swarm in isolated git worktrees |
249
+ | **`merge-swarm`** | `agentctl merge-swarm` | Performs 3-way structural merge on completed swarm PRs |
250
+ | **`mcp`** | `agentctl mcp` | Starts stdio Model Context Protocol (MCP) JSON-RPC 2.0 server |
251
+ | **`doctor`** | `agentctl doctor` | Verifies stack configuration, environment keys, and daily token budget |
252
+ | **`scan`** | `agentctl scan` | Scans codebase for `TODO` and `FIXME` comments and generates task queue |
253
+ | **`clean`** | `agentctl clean` | Audits and cleans up stale git worktrees, orphaned intents, locks, and temporary state files |
254
+
255
+ ---
125
256
 
126
- The orchestrator reads configuration from `.agent/config.yml` (or legacy `.agent/jules.yml`). Scaffolded automatically via `agentctl init`:
257
+ ## 📝 Configuration Reference (`.agent/jules.yml`)
258
+
259
+ Auto-generated by `agentctl init` at the root of your project:
127
260
 
128
261
  ```yaml
129
- # Google Jules Repository Configuration
130
262
  version: 2
131
- tier: "ultra" # Preset options: free, pro, ultra (default: ultra)
132
- provider: "jules"
133
-
134
- # Automated verification suite commands (auto-detected if omitted)
263
+ tier: "pro" # Options: free, pro, ultra (default: ultra)
135
264
  test_cmd: "npm test"
136
265
  build_cmd: "npm run build"
137
-
138
- # Scope protection boundaries (builtin protections are automatically merged)
139
266
  forbidden_paths:
140
- - ".github/**"
267
+ - ".github/"
141
268
  - "package.json"
142
- - ".agent/config.yml"
143
269
  - ".agent/jules.yml"
144
-
145
270
  allow_paths: []
146
-
147
- # Operational limits & budgets
148
271
  limits:
149
- dailyTasks: 300 # Maximum agent tasks per 24-hour cycle window
150
- repairAttempts: 3 # Maximum OODA auto-repair retries on test failure
151
- diffKb: 75 # Git diff payload ceiling in KB (Governor)
152
- concurrency: 3 # Maximum parallel worktree swarm workers
153
- staggerMs: 1000 # Dispatch stagger delay in milliseconds
272
+ dailyTasks: 300
273
+ repairAttempts: 3
274
+ diffKb: 75
154
275
  ```
155
276
 
156
277
  ---
157
278
 
158
- ## 🚦 Exit Code Reference Table
279
+ ## 🚦 Exit Code Registry & Troubleshooting
159
280
 
160
- Standardized exit codes enforced across all CLI commands (`agentctl`), scripts, and CI/CD pipelines:
281
+ Standardized exit codes enforced across all CLI utilities and CI pipelines:
161
282
 
162
283
  | Exit Code | Classification | Description & Immediate Remediation Action |
163
284
  | :---: | :--- | :--- |
164
- | `0` | **Gate Approved / Success** | Verification suite passed; task, audit, or gate operation completed cleanly. |
165
- | `1` | **Git Base / Config Error** | Pre-dispatch argument error, missing git repository root, invalid prompt (>50 KB), or bad configuration. |
166
- | `2` | **API / Network Failure** | Jules REST API HTTP 429 rate-limit, `FAILED_PRECONDITION` quota limit, or connection timeout. |
167
- | `3` | **Scope Guard Violation** | Attempted modification of protected/forbidden files (`package.json`, `.github/`, deployment keys). |
168
- | `4` | **Verification Failure** | Unit test or build command failed after 3 OODA auto-repair attempts or hit non-convergent loop. |
169
- | `5` | **Diff Payload Governor** | Post-change git diff exceeded maximum payload budget (`limits.diffKb`, default >75 KB limit). |
170
- | `6` | **Secret Scanner Finding** | High-confidence secret, API token, or SSH private key detected in diff (Shannon entropy > 3.6 bits). |
171
- | `7` | **Budget Exhausted** | Daily task session quota limit reached (`limits.dailyTasks`, default 300 per 24h cycle). |
285
+ | `0` | **Success** | Task completed cleanly; PR opened or verification passed. |
286
+ | `1` | **Pre-Dispatch / Arg Error** | Invalid arguments, prompt > 50 KB, or pre-dispatch validation error. |
287
+ | `2` | **API / Network Failure** | Jules API rate-limit (HTTP 429), `FAILED_PRECONDITION` quota, or timeout. |
288
+ | `3` | **Scope Violation** | Attempted modification of restricted files (`.github/`, command files, agent rules). |
289
+ | `4` | **OODA Exhausted / Thrash** | Verification suite failed after 3 repair attempts or hit deterministic regression. |
290
+ | `5` | **Diff Payload Limit** | Post-change git diff exceeds payload budget (`limits.diffKb`, default 75 KB). |
291
+ | `6` | **Secret Detected** | High-confidence secret or private key detected in patch diff (Shannon entropy > 3.6 bits). |
292
+ | `7` | **Budget Exhausted** | Daily task session quota limit reached (`limits.dailyTasks`, default 300). |
172
293
  | `8` | **FLAKY_QUARANTINE** | Statistical test flakiness detected (oscillation >= 0.4, Wilson CI); OODA repair suppressed. |
173
- | `124` | **Execution Timeout** | Subprocess execution exceeded hard execution timeout limit (default 10 minutes). |
174
- | `188` | **ERR_UNMOCKED_NET** | Unmocked outbound HTTP/HTTPS egress intercepted during test execution by hermetic network guard. |
294
+ | `124` | **Execution Timeout** | Subprocess execution exceeded hard timeout limit (default 10 minutes). |
295
+ | `188` | **ERR_UNMOCKED_NET** | Unmocked outbound HTTP/HTTPS egress intercepted by hermetic network guard. |
175
296
 
176
297
  ---
177
298
 
178
- ## 🛡️ Security Model & Honest Boundaries
299
+ ## 🔐 Environment Variables Reference
300
+
301
+ | Variable | Description | Default |
302
+ | :--- | :--- | :--- |
303
+ | `JULES_API_KEY` | Google Jules REST API key | *(none)* |
304
+ | `JULES_REPO` | Target GitHub Repository (`owner/repo`) | Auto-detected from `git remote` |
305
+ | `JULES_TIER` | Subscription tier preset (`free`, `pro`, `ultra`) | `ultra` |
306
+ | `JULES_DRY_RUN` | Set to `1` or `true` for dry-run simulation mode | `false` |
307
+ | `JULES_DAILY_BUDGET` | Custom daily session budget limit | `300` |
308
+ | `JULES_MAX_DIFF_KB` | Custom git diff payload limit in KB | `75` |
309
+ | `JULES_ALLOW_COMMAND_FILE_CHANGES` | Allow PR changes to command files (`package.json`, etc.) | `false` |
310
+ | `JULES_ALLOW_AGENT_RULE_CHANGES` | Allow PR changes to agent rule files (`AGENTS.md`, etc.) | `false` |
311
+ | `BASE_BRANCH` | Base branch for PR Audits & Merge-Base checks | `main` |
312
+ | `NO_COLOR` | Set to `true` to disable ANSI color output | `false` |
179
313
 
180
- ### Security Guarantees
314
+ ---
181
315
 
182
- 1. **Zero-Trust Scope Fencing**: Scope guard enforces strict forbidden path lists (`.github/`, credentials, agent rules). User additions complement builtin rules without override vulnerabilities.
183
- 2. **Shannon Entropy Secret Scanner**: Scans diffs for high-confidence secrets (AWS, Stripe, GitHub tokens, RSA private keys) using Shannon Entropy analysis (> 3.6 bits).
184
- 3. **Hermetic Network Guard (`ERR_UNMOCKED_NET`)**: Intercepts unmocked outbound HTTP/HTTPS egress during verification runs (`node:http`, `node:https`, `fetch`), enforcing offline-first hermetic execution while permitting local loopback.
185
- 4. **Prompt Firewall (`src/prompt-guard.mjs`)**: Neutralizes prompt injection patterns, strips zero-width unicode, bidi control characters, and ANSI escape codes before payloads reach LLM context boundaries.
186
- 5. **Linearizable VFS Directory Mutex (`src/state.mjs`)**: CAS directory mutex guarantees serial linearizability across multi-process swarms. Process start time validation against `/proc/<pid>/stat` prevents stale lock corruption from recycled OS process IDs.
187
- 6. **O(1) SHA-256 Telemetry Hash-Spine (`src/telemetry.mjs`)**: Cryptographically links all session events in an append-only ledger with automatic head-desync self-healing.
316
+ ## 🌐 Supported Tech Stacks & Auto-Detection
188
317
 
189
- ### Honest System Boundaries
318
+ The orchestrator automatically infers verification and build commands across ecosystems:
190
319
 
191
- - **Zero External Dependencies**: The orchestrator core uses **100% native Node.js ESM modules** (`node:fs`, `node:path`, `node:crypto`, `node:child_process`, `node:stream`). No supply-chain dependency risk.
192
- - **Diff Governor Envelope Ceiling**: Hard diff payload limit of 75 KB prevents hidden payload injection and API payload truncation.
193
- - **Process Subtree Cleanup**: `ProcessGroupManager` creates detached process groups and handles system termination signals (`SIGINT`/`SIGTERM`) to kill whole subtrees, guaranteeing zero zombie processes.
194
- - **Rule Verification Scope**: Security checks are evaluated strictly against `origin/main` baseline to prevent untrusted PR branches from modifying their own guardrails.
320
+ | Stack / Ecosystem | Manifest File | Inferred Test Command | Inferred Build Command |
321
+ | :--- | :--- | :--- | :--- |
322
+ | **Turborepo** | `turbo.json` | `npx turbo run test` | `npx turbo run build` |
323
+ | **pnpm Workspace** | `pnpm-workspace.yaml` | `pnpm test` | `pnpm build` |
324
+ | **Nx Workspace** | `nx.json` | `npx nx run-many -t test` | `npx nx run-many -t build` |
325
+ | **JavaScript / TypeScript** | `package.json` | `npm test` | `npm run build` |
326
+ | **Rust** | `Cargo.toml` | `cargo test --workspace` | `cargo build` |
327
+ | **Go** | `go.mod` | `go test ./...` | `go build ./...` |
328
+ | **Python** | `pyproject.toml` | `pytest` | *(none)* |
329
+ | **Bun / Deno** | `bunfig.toml` / `deno.json` | `bun test` / `deno test` | `bun run build` |
330
+ | **Elixir / Ruby** | `mix.exs` / `Gemfile` | `mix test` / `rake test` | *(standard build)* |
331
+ | **Java / C / C++** | `pom.xml` / `Makefile` | `mvn test` / `make test` | `mvn compile` / `make` |
195
332
 
196
333
  ---
197
334
 
198
- ## 🤝 Contributing & Testing
335
+ ## 🤝 Contributing & Standards
336
+
337
+ We welcome community contributions! Please adhere to our core engineering invariants:
338
+
339
+ 1. **Zero External Runtime Dependencies**: Use ONLY native Node.js ESM built-in modules (`node:fs`, `node:path`, `node:crypto`, `node:child_process`, `node:os`).
340
+ 2. **100% Verification Suite**: All test suites must pass cleanly with 0 errors.
341
+ 3. **Cross-Platform Compatibility**: Always normalize Windows backslashes (`\`) to POSIX slashes (`/`).
342
+
343
+ ### Running Tests Locally
199
344
 
200
345
  ```bash
201
- # Clone repository
346
+ # Clone the repository
202
347
  git clone https://github.com/FullThrottle83/jules-orchestrator-kit.git
203
348
  cd jules-orchestrator-kit
204
349
 
205
- # Run ESLint and comprehensive unit test suite
350
+ # Run ESLint & node unit test suite (100% zero external runtime deps)
206
351
  npm run lint
207
352
  npm test
208
353
  ```
package/bin/agentctl.mjs CHANGED
@@ -14,7 +14,7 @@ const command = args[0];
14
14
 
15
15
  function printHelp() {
16
16
  console.log(`
17
- 🚀 agentctl v1.0.0 — Universal Agent Orchestrator & Safety Gatekeeper
17
+ 🚀 agentctl v1.0.1 — Universal Agent Orchestrator & Safety Gatekeeper
18
18
 
19
19
  Usage: agentctl <command> [options]
20
20
 
@@ -44,7 +44,7 @@ async function main() {
44
44
  }
45
45
 
46
46
  if (command === "version" || command === "--version" || command === "-v") {
47
- console.log("agentctl v1.0.0");
47
+ console.log("agentctl v1.0.1");
48
48
  process.exit(0);
49
49
  }
50
50
 
@@ -226,7 +226,7 @@ async function main() {
226
226
  }
227
227
 
228
228
  case "doctor": {
229
- console.log(`\n🔍 agentctl System Diagnostics (v1.0.0)`);
229
+ console.log(`\n🔍 agentctl System Diagnostics (v1.0.1)`);
230
230
  console.log(`--------------------------------------------------`);
231
231
  console.log(` Project Root : ${root}`);
232
232
  console.log(` Config File : ${config._file || "None (Using defaults)"}`);
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Google Jules Orchestrator Kit - Node.js SDK (v1.0.0)
2
+ * Google Jules Orchestrator Kit - Node.js SDK (v1.0.1)
3
3
  *
4
4
  * Exposes core orchestrator functions for programmatically driving agent tasks,
5
5
  * security auditing, repo gating, and state operations.
@@ -17,7 +17,16 @@ export {
17
17
  export { sanitizeUntrustedData, buildAgentEnvelope } from "./src/prompt-guard.mjs";
18
18
  export { isolateMcpStdout, writeMcpFrame } from "./src/mcp.mjs";
19
19
  export { git, runCmd, resolveBase, changedFiles, diffBytes, diffText } from "./src/git.mjs";
20
- export { createProvider, JULES_PRESET, CLAUDE_PRESET, CODEX_PRESET } from "./src/provider.mjs";
20
+ export {
21
+ createProvider,
22
+ JULES_PRESET,
23
+ CLAUDE_PRESET,
24
+ CODEX_PRESET,
25
+ ProviderRateLimitError,
26
+ ProviderUnavailableError,
27
+ ProviderSchemaError,
28
+ parseRetryAfter,
29
+ } from "./src/provider.mjs";
21
30
  export {
22
31
  appendLedger,
23
32
  readLedger,
@@ -25,6 +34,7 @@ export {
25
34
  reserveBudget,
26
35
  reserveBudgetAtomic,
27
36
  commitBudgetReservation,
37
+ rollbackBudgetReservation,
28
38
  withBudget,
29
39
  checkDailyBudget,
30
40
  acquireLock,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jules-orchestrator-kit",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Orchestration kit for running Google Jules autonomous agents.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/engine.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { loadConfig, parseYaml, normalizeScope } from "./config.mjs";
2
2
  import { checkScope, scanDiff, redactSecrets } from "./security.mjs";
3
3
  import { changedFiles, diffBytes, diffText, showFromOrigin, runCmd } from "./git.mjs";
4
- import { createProvider } from "./provider.mjs";
5
- import { withBudget, appendLedger, getQueueDir, ensureDir } from "./state.mjs";
4
+ import { createProvider, ProviderRateLimitError, ProviderUnavailableError } from "./provider.mjs";
5
+ import { withBudget, appendLedger, getQueueDir, ensureDir, rollbackBudgetReservation } from "./state.mjs";
6
6
  import { sanitizeUntrustedData, buildAgentEnvelope } from "./prompt-guard.mjs";
7
7
  import { recordVerifyRun, readVerifyRuns, flakyVerdict } from "./flaky-ledger.mjs";
8
8
  import { readdirSync, readFileSync, renameSync, existsSync } from "node:fs";
@@ -346,6 +346,24 @@ export async function repair(failure, opts = {}) {
346
346
  config.limits.dailyTasks
347
347
  );
348
348
  } catch (err) {
349
+ if (err instanceof ProviderRateLimitError || err instanceof ProviderUnavailableError) {
350
+ if (err.reservationId) {
351
+ rollbackBudgetReservation(root, err.reservationId);
352
+ }
353
+ const retryAfterMs = err.retryAfterMs || 60000;
354
+ const backoffSec = Math.ceil(retryAfterMs / 1000);
355
+ console.warn(`[PROVIDER_INFRASTRUCTURE_FAILURE] ${err.name}: ${err.message}. Recommended backoff: ${backoffSec}s.`);
356
+ attempts.push({ n, ok: false, error: err.message, providerError: true, retryAfterMs });
357
+ appendTelemetry(root, "ooda_repair_attempt", { attempt: n, ok: false, error: err.message, providerError: true });
358
+ return {
359
+ ok: false,
360
+ attempts,
361
+ finalStatus: "PROVIDER_INFRASTRUCTURE_FAILURE",
362
+ error: err.message,
363
+ retryAfterMs,
364
+ providerError: true,
365
+ };
366
+ }
349
367
  attempts.push({ n, ok: false, error: err.message });
350
368
  appendTelemetry(root, "ooda_repair_attempt", { attempt: n, ok: false, error: err.message });
351
369
  break;
@@ -482,11 +500,31 @@ export async function dispatch(task, opts = {}) {
482
500
 
483
501
  const cleanTask = { ...task, prompt: envelopedPrompt };
484
502
 
485
- return withBudget(
486
- () => provider.dispatch(cleanTask, { root, dryRun: opts.dryRun }),
487
- root,
488
- config.limits.dailyTasks
489
- );
503
+ try {
504
+ return await withBudget(
505
+ () => provider.dispatch(cleanTask, { root, dryRun: opts.dryRun }),
506
+ root,
507
+ config.limits.dailyTasks
508
+ );
509
+ } catch (err) {
510
+ if (err instanceof ProviderRateLimitError || err instanceof ProviderUnavailableError) {
511
+ if (err.reservationId) {
512
+ rollbackBudgetReservation(root, err.reservationId);
513
+ }
514
+ const retryAfterMs = err.retryAfterMs || 60000;
515
+ const status = err instanceof ProviderRateLimitError ? "RATE_LIMITED" : "PROVIDER_UNAVAILABLE";
516
+ const backoffSec = Math.ceil(retryAfterMs / 1000);
517
+ console.warn(`[PROVIDER_INFRASTRUCTURE_FAILURE] ${err.name}: ${err.message}. Recommended backoff: ${backoffSec}s.`);
518
+ return {
519
+ ok: false,
520
+ status,
521
+ error: err.message,
522
+ retryAfterMs,
523
+ providerError: true,
524
+ };
525
+ }
526
+ throw err;
527
+ }
490
528
  }
491
529
 
492
530
  export async function run(tasksOrOpts = {}, opts = {}) {
package/src/mcp.mjs CHANGED
@@ -9,7 +9,7 @@ import { ProgressBus } from "./mcp-progress.mjs";
9
9
 
10
10
  export const MCP_SERVER_INFO = {
11
11
  name: "jules-orchestrator-kit",
12
- version: "1.0.0",
12
+ version: "1.0.1",
13
13
  };
14
14
 
15
15
  export const MAX_MCP_FRAME_SIZE = 4 * 1024 * 1024; // 4 MB memory safety ceiling
package/src/provider.mjs CHANGED
@@ -31,6 +31,45 @@ export const CODEX_PRESET = {
31
31
  promptViaStdin: false,
32
32
  };
33
33
 
34
+ export class ProviderRateLimitError extends Error {
35
+ constructor(message, opts = {}) {
36
+ super(message);
37
+ this.name = "ProviderRateLimitError";
38
+ this.retryAfterMs = opts.retryAfterMs ?? 60000;
39
+ this.status = opts.status || 429;
40
+ }
41
+ }
42
+
43
+ export class ProviderUnavailableError extends Error {
44
+ constructor(message, opts = {}) {
45
+ super(message);
46
+ this.name = "ProviderUnavailableError";
47
+ this.status = opts.status || 503;
48
+ this.retryAfterMs = opts.retryAfterMs;
49
+ }
50
+ }
51
+
52
+ export class ProviderSchemaError extends Error {
53
+ constructor(message, opts = {}) {
54
+ super(message);
55
+ this.name = "ProviderSchemaError";
56
+ this.status = opts.status;
57
+ }
58
+ }
59
+
60
+ export function parseRetryAfter(header) {
61
+ if (!header) return null;
62
+ const seconds = Number(header);
63
+ if (!isNaN(seconds)) {
64
+ return Math.max(0, Math.round(seconds * 1000));
65
+ }
66
+ const dateMs = Date.parse(header);
67
+ if (!isNaN(dateMs)) {
68
+ return Math.max(0, dateMs - Date.now());
69
+ }
70
+ return null;
71
+ }
72
+
34
73
  function interpolateString(template, data) {
35
74
  if (typeof template !== "string") return template;
36
75
  return template.replace(/\{(\w+)\}/g, (_, key) => data[key] ?? "");
@@ -111,20 +150,63 @@ export function createProvider(spec = "jules", config = {}) {
111
150
  body = JSON.stringify(data);
112
151
  }
113
152
 
114
- const res = await fetch(url, {
115
- method: providerSpec.method || "POST",
116
- headers,
117
- body,
118
- });
153
+ const timeoutMs = ctx.timeoutMs || config.timeoutMs || providerSpec.timeoutMs || 120_000;
154
+ let res;
155
+ try {
156
+ res = await fetch(url, {
157
+ method: providerSpec.method || "POST",
158
+ headers,
159
+ body,
160
+ signal: AbortSignal.timeout(timeoutMs),
161
+ });
162
+ } catch (err) {
163
+ if (err.name === "TimeoutError" || err.name === "AbortError" || err.code === "ABORT_ERR") {
164
+ throw new ProviderUnavailableError(`Provider HTTP Timeout (${timeoutMs}ms): ${err.message}`, {
165
+ status: 504,
166
+ });
167
+ }
168
+ throw err;
169
+ }
119
170
 
120
171
  if (!res.ok) {
121
172
  const text = await res.text();
122
173
  const cleanText = text.slice(0, 500);
123
174
  const sanitizedText = rawToken ? cleanText.split(rawToken).join("[REDACTED]") : cleanText;
175
+ const retryAfterHeader = res.headers ? res.headers.get("retry-after") : null;
176
+ const retryAfterMs = parseRetryAfter(retryAfterHeader);
177
+
178
+ if (res.status === 429) {
179
+ throw new ProviderRateLimitError(`Provider HTTP Error (429): ${sanitizedText}`, {
180
+ retryAfterMs: retryAfterMs ?? 60000,
181
+ status: 429,
182
+ });
183
+ }
184
+
185
+ if (res.status >= 500 && res.status < 600) {
186
+ throw new ProviderUnavailableError(`Provider HTTP Error (${res.status}): ${sanitizedText}`, {
187
+ status: res.status,
188
+ retryAfterMs: retryAfterMs ?? undefined,
189
+ });
190
+ }
191
+
124
192
  throw new Error(`Provider HTTP Error (${res.status}): ${sanitizedText}`);
125
193
  }
126
194
 
127
- const json = await res.json();
195
+ let json;
196
+ try {
197
+ json = await res.json();
198
+ } catch (err) {
199
+ throw new ProviderSchemaError(`Provider Payload Error: Invalid JSON response: ${err.message}`, {
200
+ status: res.status,
201
+ });
202
+ }
203
+
204
+ if (!json || typeof json !== "object") {
205
+ throw new ProviderSchemaError("Provider Payload Error: Expected JSON object response", {
206
+ status: res.status,
207
+ });
208
+ }
209
+
128
210
  return {
129
211
  id: json.id || json.name || "http-session",
130
212
  status: json.state || "active",
package/src/state.mjs CHANGED
@@ -189,19 +189,31 @@ export function checkDailyBudget(arg1 = resolveRoot(), arg2 = 300) {
189
189
  const content = readFileSync(filePath, "utf-8");
190
190
  const lines = content.split("\n").filter(Boolean);
191
191
  let count = 0;
192
+ const activeIds = new Set();
192
193
  for (const line of lines) {
193
194
  try {
194
195
  const entry = JSON.parse(line);
195
196
  if (entry && entry.event === "budget_reserved") {
196
- count++;
197
+ if (entry.reservationId) {
198
+ activeIds.add(entry.reservationId);
199
+ } else {
200
+ count++;
201
+ }
202
+ } else if (entry && (entry.event === "budget_rolled_back" || entry.event === "budget_released")) {
203
+ if (entry.reservationId) {
204
+ activeIds.delete(entry.reservationId);
205
+ } else {
206
+ count = Math.max(0, count - 1);
207
+ }
197
208
  }
198
209
  } catch (_) {}
199
210
  }
211
+ const used = count + activeIds.size;
200
212
  return {
201
- ok: count < limit,
202
- used: count,
213
+ ok: used < limit,
214
+ used,
203
215
  budget: limit,
204
- remaining: Math.max(0, limit - count),
216
+ remaining: Math.max(0, limit - used),
205
217
  };
206
218
  } catch (_) {
207
219
  return { ok: true, used: 0, budget: limit, remaining: limit };
@@ -226,6 +238,7 @@ export function reserveBudgetAtomic(stateDirOrRoot = resolveRoot(), limit = 300,
226
238
  const filePath = join(stateDir, `ledger-${dateStr}.jsonl`);
227
239
 
228
240
  let count = 0;
241
+ const activeIds = new Set();
229
242
  let prevHash = "0".repeat(64);
230
243
 
231
244
  if (existsSync(filePath)) {
@@ -236,7 +249,17 @@ export function reserveBudgetAtomic(stateDirOrRoot = resolveRoot(), limit = 300,
236
249
  try {
237
250
  const entry = JSON.parse(line);
238
251
  if (entry && entry.event === "budget_reserved") {
239
- count++;
252
+ if (entry.reservationId) {
253
+ activeIds.add(entry.reservationId);
254
+ } else {
255
+ count++;
256
+ }
257
+ } else if (entry && (entry.event === "budget_rolled_back" || entry.event === "budget_released")) {
258
+ if (entry.reservationId) {
259
+ activeIds.delete(entry.reservationId);
260
+ } else {
261
+ count = Math.max(0, count - 1);
262
+ }
240
263
  }
241
264
  if (entry && entry.hash) {
242
265
  prevHash = entry.hash;
@@ -246,8 +269,9 @@ export function reserveBudgetAtomic(stateDirOrRoot = resolveRoot(), limit = 300,
246
269
  } catch (_) {}
247
270
  }
248
271
 
249
- if (count >= limit) {
250
- throw new BudgetError(`Daily budget exhausted (${count}/${limit} tasks executed)`);
272
+ const used = count + activeIds.size;
273
+ if (used >= limit) {
274
+ throw new BudgetError(`Daily budget exhausted (${used}/${limit} tasks executed)`);
251
275
  }
252
276
 
253
277
  const timestamp = new Date().toISOString();
@@ -267,8 +291,8 @@ export function reserveBudgetAtomic(stateDirOrRoot = resolveRoot(), limit = 300,
267
291
  return {
268
292
  ok: true,
269
293
  reservationId,
270
- remaining: Math.max(0, limit - (count + 1)),
271
- used: count + 1,
294
+ remaining: Math.max(0, limit - (used + 1)),
295
+ used: used + 1,
272
296
  };
273
297
  }, opts);
274
298
  }
@@ -282,6 +306,11 @@ export function commitBudgetReservation(rootOrOpts = resolveRoot(), reservationI
282
306
  return appendLedger({ event: "budget_committed", reservationId }, root);
283
307
  }
284
308
 
309
+ export function rollbackBudgetReservation(rootOrOpts = resolveRoot(), reservationId = "") {
310
+ const root = typeof rootOrOpts === "string" ? rootOrOpts : resolveRoot();
311
+ return appendLedger({ event: "budget_rolled_back", reservationId }, root);
312
+ }
313
+
285
314
  export async function withBudget(fn, root = resolveRoot(), limit = 300) {
286
315
  const reservation = reserveBudget(root, limit);
287
316
  try {
@@ -289,6 +318,9 @@ export async function withBudget(fn, root = resolveRoot(), limit = 300) {
289
318
  commitBudgetReservation(root, reservation.reservationId);
290
319
  return result;
291
320
  } catch (err) {
321
+ if (err && typeof err === "object") {
322
+ err.reservationId = reservation.reservationId;
323
+ }
292
324
  appendLedger({ event: "budget_reservation_failed", reservationId: reservation.reservationId, error: err.message }, root);
293
325
  throw err;
294
326
  }