jules-orchestrator-kit 0.9.4 → 0.20.0

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,319 +1,114 @@
1
1
  # jules-orchestrator-kit
2
2
 
3
- *Disclaimer: This is an independent open-source orchestration tool and is not officially affiliated with or endorsed by Google.*
3
+ *Disclaimer: This is an independent open-source orchestration tool for Google Jules and is not officially affiliated with or endorsed by Google.*
4
4
 
5
5
  [![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
6
  [![npm version](https://img.shields.io/npm/v/jules-orchestrator-kit.svg)](https://www.npmjs.com/package/jules-orchestrator-kit)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
 
9
- **Turn Google Jules into an autonomous code builder that writes, tests, and fixes itself.**
10
- The orchestrator automates verification, scopes file boundaries, and prevents Jules from breaking your CI.
9
+ > **v0.20.0 Early Community Release Candidate**
10
+ > High-volume autonomous orchestration engine for Google Jules. Built specifically to handle 300+ daily sessions and parallel agent swarms with zero external runtime dependencies.
11
11
 
12
- > [!WARNING]
13
- > **Alpha Release:** This kit is in active development. Please exercise caution before integrating it into critical production pipelines.
12
+ ---
14
13
 
15
- > [!CAUTION]
16
- > **Task Limit Warning:** Autonomous loops can quickly consume your daily API limits. We strongly recommend starting with `JULES_DRY_RUN=1` to understand the workflow before scaling up.
14
+ ## ⚡ 2-Minute Quickstart
17
15
 
18
- ## Prerequisites
19
- To use this kit, you will need:
20
- - Node.js `18.0.0` or higher
21
- - `git` installed and available in your PATH
22
- - A Google Jules REST API key (set as `JULES_API_KEY`) **OR** the native `jules` binary in your PATH.
16
+ Get up and running in under 2 minutes:
23
17
 
24
- ## Quick Start
25
- Initialize your repository:
26
18
  ```bash
19
+ # 1. Install or initialize in your target codebase
27
20
  npx jules-orchestrator-kit init
28
- ```
29
-
30
- Dispatch a task or run the queue using `agentctl`:
31
- ```bash
32
- agentctl dispatch --title "Refactor Auth" --prompt "Implement JWT verification in auth handler"
33
- agentctl queue
34
- agentctl gate
35
- ```
36
21
 
37
- > 💡 **Unified Engine CLI (`agentctl`)**:
38
- > `agentctl` is the zero-dependency CLI executable that powers dispatching, safety gate auditing, mutex locks, and swarm management across all project types (Node, Rust, Go, Python, etc.).
39
- > Legacy `scripts/jules-*.mjs` shims are preserved for backward compatibility.
22
+ # 2. Dispatch a task (Dry-Run mode for testing)
23
+ JULES_DRY_RUN=1 npx agentctl dispatch \
24
+ --title "Add JWT Validator" \
25
+ --prompt "Implement JWT validation middleware with unit tests"
40
26
 
41
- ---
27
+ # 3. Run security & verification gatekeeper
28
+ npx agentctl gate
42
29
 
43
- ## How It Works
44
-
45
- 1. **You Assign Task:** Define what needs fixing or building (supports text and multimodal image mockups).
46
- 2. **Jules Writes Code:** Proposes changes in an isolated Git worktree sandbox.
47
- 3. **Run Tests & Linters:** The Gatekeeper runs your test suite, linters, and type checks.
48
- 4. **Self-Correction:** If anything fails, Jules automatically retries with fixes (OODA loop).
49
- 5. **Safe Delivery:** Once tests pass, the PR is verified and ready for review.
50
-
51
- ```mermaid
52
- sequenceDiagram
53
- autonumber
54
-
55
- box "Client Edge" #F4F4F4
56
- actor Trigger as Client (CLI / CI / SDK)
57
- end
58
-
59
- box "Control Plane" #E8F4F8
60
- participant Orc as Orchestrator Core
61
- participant Envelope as Task Envelope Validator
62
- participant Gate as Self-Audit Gatekeeper
63
- end
64
-
65
- box "Execution Sandbox" #F8E8E8
66
- participant API as Google Jules API
67
- participant Git as Git Worktree Sandbox
68
- end
69
-
70
- Trigger->>+Orc: Dispatch Task Payload / Envelope
71
-
72
- note over Orc,Envelope: Phase 1: Pre-Flight Premise & Security Validation
73
- Orc->>+Envelope: Validate Envelope (referenced_paths exist? base commit fresh?)
74
- alt Premise Failure / Missing Referenced Paths / Stale Base
75
- Envelope-->>Orc: Validation Failed (Premise Error)
76
- Orc-->>Trigger: Abort Pre-Dispatch (Exit 1)
77
- else Envelope Validated
78
- Envelope-->>-Orc: Envelope Approved
79
- Orc->>Orc: Redact Secrets (Entropy > 3.6) & Enforce Dynamic Guardrails
80
- Orc->>+Git: Provision Isolation Sandbox (git worktree)
81
- Git-->>-Orc: Sandbox Ready
82
- end
83
-
84
- loop OODA Repair Cycle (Max 3 Retries)
85
- note over Orc,Git: Phase 2: Agent Execution & Dispatch
86
- Orc->>+API: Dispatch Task + <MCP_DIRECTIVE> & Target Scope
87
- API->>+Git: Apply Proposed Code Changes
88
- Git-->>-API: Changes Written
89
- API-->>-Orc: Execution Complete
90
-
91
- note over Orc,Gate: Phase 3: Multi-Gate Verification & Risk Classification
92
- Orc->>+Gate: Trigger Self-Audit (trusted origin/main rules)
93
-
94
- Gate->>+Git: Run Scope, Payload & Stale-Base Audit (`git diff`)
95
- Git-->>-Gate: Diff Stats & Base Commit Age
96
-
97
- alt Scope Breach / Payload > 75 KB / Stale Base > 25 commits
98
- Gate-->>Orc: Security / Scope / Stale-Base Violation
99
- Orc->>Orc: Record Telemetry (metrics.jsonl)
100
- Orc-->>Trigger: Abort Execution (Exit 3)
101
- break Fatal Security or Base Error
102
- Orc->>Git: Teardown Worktree Sandbox
103
- end
104
- else Static Scope & Base Passed
105
- Gate->>Gate: Classify Risk Tier (R0 Cosmetic, R1 Routine, R2 Consequential, R3 Restricted)
106
- Gate->>+Git: Run Integrity Checks (Asset Magic-Bytes, Rules Budget, testCmd & buildCmd)
107
- Git-->>-Gate: Execution & Integrity Proof (stdout/stderr)
108
- end
109
-
110
- alt 100% Verification Suite & Integrity Passed
111
- Gate-->>-Orc: Verification Success + Risk Tier Metadata
112
- Orc->>+Git: Commit & Push to Remote Branch / PR
113
- Git-->>-Orc: PR Ready (Attached Execution Receipts)
114
- Orc->>Orc: Record Telemetry (metrics.jsonl)
115
- Orc-->>Trigger: Dispatch Succeeded (Exit 0)
116
- break Task Completed
117
- Orc->>Git: Teardown Worktree Sandbox
118
- end
119
- else Verification Failed
120
- Gate-->>Orc: Verification Failed (Stderr Trace output)
121
- Orc->>Orc: Record Failure Telemetry
122
-
123
- alt Retries Remaining (< 3)
124
- Orc->>Orc: Construct Repair Prompt with Stderr Trace
125
- else Max Retries Exceeded (3/3)
126
- Orc-->>-Trigger: Abort & Log Diagnostic Feedback (Exit 4)
127
- Orc->>Git: Teardown Worktree Sandbox
128
- end
129
- end
130
- end
30
+ # 4. Start stdio MCP Server (for Cursor, Claude Code, or AGY integration)
31
+ npx agentctl mcp
131
32
  ```
132
33
 
133
- > 💡 **Core Architectural Invariants**:
134
- > - **Task Envelope Premise Validation**: Pre-flight checks verify referenced paths and base freshness before dispatching tasks to prevent session burnout.
135
- > - **Risk Tier Classification Engine (R0–R3)**: Classifies changes by blast radius to route auto-merge vs mandatory human review.
136
- > - **Zero-Trust Base-Branch Security**: Security rules (`forbidden_paths`) are fetched exclusively from `origin/main` (never untrusted PR branches).
137
- > - **Asset & Rules Integrity**: Guards binary assets against HTML corruption and enforces character/line budgets on rule files.
138
- > - **Automatic PII & Secret Redaction**: Outbound task prompts are automatically sanitized to redact API secrets and mask sensitive PII (emails, IPs, phone numbers).
139
- > - **Ledger Hash-Chain Integrity**: Hashing over JSONL event streams detects unauthorized log tampering or record deletions.
140
- >
141
- > 🔍 For a deep dive into the execution protocol, see the [Architecture & Pipeline Flow](docs/architecture.md).
142
-
143
34
  ---
144
35
 
145
- ## Configuration
146
-
147
- The orchestrator automatically detects your tech stack, but you can edit `.agent/jules.yml` for fine-grained control:
148
-
149
- ```yaml
150
- # Google Jules Repository Configuration (Version 2)
151
- version: 2
152
- test_cmd: "npm test"
153
- build_cmd: "npm run build"
154
- forbidden_paths:
155
- - ".github/**"
156
- - "**/secrets/**"
157
- - "**/*.pem"
158
- - "**/lock-manager/**"
159
- - "scripts/jules-*"
160
- - ".agent/jules.yml"
161
- allow_paths: []
162
- ```
36
+ ## 🚀 Built for High-Volume Jules Swarms
163
37
 
164
- ---
38
+ `jules-orchestrator-kit` provides the production-hardened control plane needed to execute parallel Google Jules agent swarms safely:
165
39
 
166
- ## Expand with MCP
40
+ - **Parallel Agent Swarms**: Execute multi-agent task batches concurrently with deterministic lock management and automatic collision prevention.
41
+ - **Self-Healing OODA Loop**: Automatic test/build verification and repair loop with sliding-window thrash detection to halt non-convergent agent loops ($A \rightarrow B \rightarrow A \rightarrow B$) and preserve API token budgets.
42
+ - **4-Phase Security Gatekeeper**: Fails closed on untrusted PRs by verifying Scope (`forbidden_paths`), Diff Payload Size, Secret Entropy (> 3.6 bits), and Trusted Build/Test Execution.
167
43
 
168
- All task dispatches dynamically inject `<MCP_DIRECTIVE>` envelopes into task prompts. This forces Jules to adhere to strict read-before-write invariants.
44
+ ---
169
45
 
170
- You can supercharge Jules with external MCP servers by connecting them to your environment, granting Jules direct access to your infrastructure and real-time documentation. Examples:
171
- * **SaaS APIs & Tooling:** Context 7, Linear, and v0.
172
- * **Databases & Cloud:** Render, Neon, Supabase, Stitch.
173
- * **Framework Documentation:** Astro Docs, Cloudflare Docs, Next.js Docs.
46
+ ## ⚙️ Core Technical Architecture
174
47
 
175
- ---
48
+ Built strictly on native Node.js 18+ ESM with **Zero External Runtime Dependencies** (`"node": ">=18.0.0"`):
176
49
 
177
- ## Integration Interfaces
50
+ - **Linearizable VFS Mutex (`src/state.mjs`)**: Kernel-level directory mutex (`withVfsMutex`) guaranteeing serial linearizability for SHA-256 hash-chained session ledgers under high concurrency.
51
+ - **PID Recycling & Stale Lock Protection (`src/state.mjs`)**: Linux `/proc/<pid>/stat` launch-time validation prevents false-positive lock reaps from recycled OS process IDs.
52
+ - **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.
53
+ - **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.
54
+ - **TOCTOU & Symlink Defense (`src/security.mjs`)**: `safeAtomicWrite()` uses `O_CREAT | O_EXCL | O_WRONLY` temp files with `fsyncSync` + `renameSync` and `lstatSync`/`realpathSync` symlink checks.
55
+ - **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()`).
178
56
 
179
- The orchestrator supports three primary integration channels:
57
+ ---
180
58
 
181
- **1. Direct REST API Mode (`jules.googleapis.com`)**
182
- When `JULES_API_KEY` and `JULES_REPO` are present in your environment, payloads are dispatched directly to the official Google Jules REST API endpoint. Handles HTTP 429 rate limits gracefully.
59
+ ## 🛠️ CLI Command Reference (`agentctl`)
183
60
 
184
- **2. Native Jules CLI Fallback**
185
- If no API key is configured, the kit seamlessly falls back to invoking your local `jules` CLI binary via standard streams.
61
+ | Command | Usage | Description |
62
+ | :--- | :--- | :--- |
63
+ | `agentctl dispatch` | `agentctl dispatch --title "..." --prompt "..."` | Dispatches an autonomous task to Jules |
64
+ | `agentctl gate` | `agentctl gate [--fix] [--base main]` | Runs 4-Phase Safety Gatekeeper against workspace |
65
+ | `agentctl queue` | `agentctl queue` | Processes pending task queue from `.agent/jules-queue/` |
66
+ | `agentctl swarm` | `agentctl swarm` | Executes parallel swarm task queue |
67
+ | `agentctl merge-swarm` | `agentctl merge-swarm` | Performs 3-way structural merge on completed swarm PRs |
68
+ | `agentctl mcp` | `agentctl mcp` | Starts stdio Model Context Protocol (MCP) server |
69
+ | `agentctl doctor` | `agentctl doctor` | Verifies stack configuration, environment, and budget |
186
70
 
187
- **3. Programmatic Node.js SDK (`index.mjs`)**
188
- Downstream Node.js tools, MCP servers, and LLM orchestrators can import kit functions directly:
189
- ```js
190
- import { gate, dispatch, validateEnvelope, classifyRiskTier, checkAssetIntegrity, checkRulesBudget, redactSecrets } from "jules-orchestrator-kit";
71
+ ---
191
72
 
192
- // Anonymize sensitive PII (emails, IPs, phone numbers) before sending prompts
193
- const cleanPrompt = anonymizePii("Contact support at john@example.com");
73
+ ## 📋 Exit Code Protocol
194
74
 
195
- // Programmatically dispatch tasks
196
- await dispatch({ title: "Refactor Auth", prompt: cleanPrompt });
75
+ Standardized exit codes enforced across all CLI utilities:
197
76
 
198
- // Run 4-phase safety gate audit
199
- const audit = await gate({ base: "main" });
200
- ```
77
+ | Code | Status | Description |
78
+ | :---: | :--- | :--- |
79
+ | `0` | **Success** | Task completed cleanly; verification passed 100%. |
80
+ | `1` | **Arg / Pre-Dispatch Failure** | Invalid arguments, prompt > 50 KB, or pre-dispatch error. |
81
+ | `2` | **API / Network Failure** | Jules API rate-limit (429), `FAILED_PRECONDITION`, or timeout. |
82
+ | `3` | **Scope Violation** | Attempted modification of protected/forbidden files. |
83
+ | `4` | **OODA Exhausted / Regression** | Verification failed after max repair attempts or thrash loop. |
84
+ | `5` | **Diff Payload Exceeded** | Git diff exceeds payload budget (`limits.diffKb`, default 75 KB). |
85
+ | `6` | **Secret Detected** | High-confidence secret or token detected in patch diff. |
86
+ | `7` | **Budget Exhausted** | Daily task session quota limit reached (`limits.dailyTasks`, default 300). |
201
87
 
202
88
  ---
203
89
 
204
- ## Known Limitations
205
-
206
- **Code Suggestions (Web UI Only)**
207
- Currently, there is no way to automatically extract "Suggestions" (the inline code review comments Jules sometimes proposes instead of direct commits) via the CLI or API. Suggestions can only be read directly inside the **Jules Web UI**.
90
+ ## 🤝 Join the Community & Field-Testing
208
91
 
209
- *Workaround for Local LLM Users:* If you are tinkering with Jules alongside a local LLM and Jules leaves a Suggestion, the easiest workflow is to open the Jules Web UI, copy the suggestion block, and paste it back into your local LLM.
92
+ We are actively field-testing `v0.20.0` across 300+ daily autonomous sessions and opening the kit to the Google Jules developer community for feedback and contributions!
210
93
 
211
- ---
94
+ - **Test & Benchmark**: Clone the repository, test your edge cases, and run parallel swarms against your codebases.
95
+ - **Report Issues**: Found a bug, state race condition, or edge-case failure? Open an issue on GitHub.
96
+ - **Submit PRs**: We welcome contributions! Ensure all additions preserve our Zero Runtime Dependency invariant and pass `npm test` & `npm run lint`.
212
97
 
213
- ## Supported Tech Stacks
214
-
215
- | Stack / Ecosystem | Manifest / Workspace File | Test Command | Build Command |
216
- | --------------------------- | ------------------------------------- | ---------------------------------------- | ---------------------------------------- |
217
- | **Turborepo** | `turbo.json` | `npx turbo run test --filter=...` | `npx turbo run build --filter=...` |
218
- | **pnpm Workspace** | `pnpm-workspace.yaml` | `pnpm --filter=... test` | `pnpm --filter=... build` |
219
- | **Nx Workspace** | `nx.json` | `npx nx run-many -t test -p ...` | `npx nx run-many -t build -p ...` |
220
- | **Bun** | `bunfig.toml` / `bun.lockb` | `bun test` | `bun run build` |
221
- | **Deno** | `deno.json` / `deno.jsonc` | `deno test` | `deno task build` |
222
- | **JavaScript / TypeScript** | `package.json` | `npm test` | `npm run build` |
223
- | **Rust** | `Cargo.toml` | `cargo test --workspace` | `cargo build` |
224
- | **Go** | `go.mod` | `go test ./...` | `go build ./...` |
225
- | **Python** | `pyproject.toml` / `requirements.txt` | `pytest` | *(none)* |
226
- | **Elixir** | `mix.exs` | `mix test` | `mix compile` |
227
- | **Ruby** | `Gemfile` | `bundle exec rake test` | *(none)* |
228
- | **Swift** | `Package.swift` | `swift test` | `swift build` |
229
- | **Java (Maven/Gradle)** | `pom.xml` / `build.gradle` | `mvn test` / `./gradlew test` | `mvn compile` / `./gradlew assemble` |
230
- | **C / C++** | `Makefile` | `make test` | `make build` |
98
+ ### Running Tests Locally
231
99
 
232
- ---
100
+ ```bash
101
+ # Clone the repository
102
+ git clone https://github.com/FullThrottle83/jules-orchestrator-kit.git
103
+ cd jules-orchestrator-kit
233
104
 
234
- ## Reference Material
235
-
236
- ### CLI Commands
237
- All commands are registered in `package.json` and can be run via `npm run <command>`.
238
-
239
- | Command | Description |
240
- | ------- | ----------- |
241
- | `npm run init` | Initializes the orchestrator and `.agent/` directory |
242
- | `npm run test` | Runs the orchestrator kit's own unit tests |
243
- | `npm run jules:dispatch` | Dispatches a single task directly to Jules |
244
- | `npm run jules:queue` | Runs the local queue processor (picks up tasks from `.agent/jules-queue`) |
245
- | `npm run jules:create` | Scaffolds a new boilerplate task markdown file |
246
- | `npm run jules:status` | Shows real-time 3-bucket status (Action Required, In Progress, Completed) |
247
- | `npm run jules:audit` | Runs the self-audit gatekeeper (verifies tests, forbidden paths, and scope) |
248
- | `npm run jules:cleanup` | Audits and closes merged or stale REST sessions |
249
- | `npm run jules:scan` | Scans the codebase for TODO/FIXME comments and generates a suggested tasks file |
250
- | `npm run jules:swarm` | Launches a multi-agent swarm in parallel across isolated worktrees |
251
- | `npm run jules:merge-swarm` | Autonomous PR merge engine with Safety Gate lock verification |
252
- | `npm run jules:nightly` | Nightly maintenance job (usually triggered in CI) |
253
-
254
- ### Specialist Agent Prompts & Templates (`.agent/prompts/`)
255
- The kit includes pre-configured single-responsibility prompt presets in `.agent/prompts/`:
256
- - **`Overseer.md`**: Codebase architecture auditor & technical debt mapper.
257
- - **`Bolt.md`**: Performance micro-optimizer and payload governor (enforces < 75 KB payload diff limits).
258
- - **`Sentinel.md`**: Security audit specialist for input sanitization and secret scanning.
259
- - **`Task_Template.md`**: Machine-readable master task prompt template schema.
260
-
261
- ### Environment Variables
262
-
263
- | Variable | Description |
264
- | -------- | ----------- |
265
- | `JULES_API_KEY` | Your Google Jules REST API key (required for API mode) |
266
- | `GEMINI_API_KEY` | Alias for `JULES_API_KEY` (fallback) |
267
- | `JULES_API_URL` | Override the Jules REST API URL |
268
- | `JULES_REPO` | Target GitHub Repository (Format: `owner/repo`) |
269
- | `JULES_REPOLESS` | Set to `true` or `1` to run in repoless/serverless mode |
270
- | `JULES_DRY_RUN` | Set to `true` or `1` to simulate dispatching without making API calls |
271
- | `JULES_DAILY_BUDGET` | Daily max session budget for autonomous dispatches (Default: `300`) |
272
- | `JULES_MAX_DIFF_KB` | Maximum git diff payload size in KB before aborting with Exit Code 5 (Default: `50`) |
273
- | `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`) |
274
- | `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`) |
275
- | `BASE_BRANCH` | Base branch for PR Audits & Merge-Base calculations (Default: `main`) |
276
- | `GITHUB_HEAD_REF` | PR Head Branch (Used dynamically by CI during OODA repair) |
277
- | `JULES_PROJECT_ROOT` | Root directory of the project (Auto-assigned during swarm executions) |
278
- | `JULES_SWARM_CONCURRENCY` | Maximum parallel dispatches for swarm runs (Default: `3`) |
279
- | `JULES_SWARM_STAGGER_MS` | Dispatch stagger interval in milliseconds (Default: `1500`) |
280
- | `JULES_PACE_MS` | Rate-limit for the `jules:queue` command (Default: `500`) |
281
- | `JULES_USE_WORKTREES` | Use Git Worktrees instead of cloning for swarm isolation (Default: `false`) |
282
- | `JULES_SLOT_INDEX` | Current slot index for partitioning tasks (Swarm mode) |
283
- | `JULES_SLOT_TOTAL` | Total number of slots for partitioning tasks (Swarm mode) |
284
- | `CI` | Set to `true` to change log output and fail-fast behaviors for CI environments |
285
- | `ALLOW_AUTO_REPAIR` | Set to `true` to allow OODA Auto-Repair even when running in CI |
286
- | `GITHUB_STEP_SUMMARY` | GitHub Actions Step Summary File Path |
287
- | `NO_COLOR` | Set to `true` to disable ANSI color output |
288
-
289
- > [!NOTE]
290
- > **Scope Enforcement via `allow_paths`**
291
- > In `.agent/jules.yml`, defining `allow_paths` acts as a strict allowlist (deny-by-default). When non-empty, any file modified outside `allow_paths` will trigger a security violation (Exit Code 3). `forbidden_paths` always take absolute precedence and cannot be overridden.
292
-
293
- ### Exit Codes
294
- The Gatekeeper (`jules-self-audit.mjs` and related scripts) uses standard exit codes to signal status to CI systems.
295
-
296
- | Code | Meaning | Action Taken |
297
- | ---- | ------- | ------------ |
298
- | `0` | **Success** | All tests and security checks passed. |
299
- | `1` | **Pre-Dispatch / Arg Error** | Missing dependencies, prompt > 50 KB, syntax error, or pre-dispatch failure. |
300
- | `2` | **API / Network / Quota Error** | REST API HTTP 429 rate limit, HTTP 400 `FAILED_PRECONDITION` quota (~30 active limit), or connection timeout. |
301
- | `3` | **Security / Scope Violation** | Modified file breached `forbidden_paths` or changed command-defining files (`package.json`, `Cargo.toml`). Fails closed immediately. |
302
- | `4` | **Verification Exhausted** | Tests failed and the OODA Auto-Repair loop either exhausted its max retries (3) or is disabled. |
303
- | `5` | **Diff Payload Limit** | Diff payload size exceeded payload governor budget (`JULES_MAX_DIFF_KB`, default 50 KB). Split task. |
304
- | `6` | **Secret Leak Prevented** | High-confidence secret or private key pattern detected in diff. Aborted immediately. |
305
- | `7` | **Budget Exhausted** | Daily session budget limit reached or budget state locked. |
105
+ # Run ESLint & node unit test suite (100% zero external runtime deps)
106
+ npm run lint
107
+ npm test
108
+ ```
306
109
 
307
110
  ---
308
111
 
309
- ## Contributing
310
- We welcome contributions! Please follow these core principles:
311
- 1. **Zero Runtime Dependencies**: Use ONLY native Node.js built-in modules (`node:fs`, `node:path`, `node:child_process`, `node:crypto`, `node:util`).
312
- 2. **Verification Suite**: Ensure 100% of unit tests pass cleanly (`npm test`).
313
- 3. **Conventional Commits**: Use standardized prefixes (`feat:`, `fix:`, `docs:`, `test:`, `chore:`).
314
- 4. **Cross-Platform Compatibility**: Normalize Windows backslashes (`\`) to POSIX slashes (`/`) for glob patterns and paths.
315
-
316
- Please read our [Code of Conduct](CODE_OF_CONDUCT.md) before participating.
112
+ ## 📄 License
317
113
 
318
- ## License
319
- MIT License - feel free to use, modify, and share!
114
+ Distributed under the [MIT License](LICENSE).
package/bin/agentctl.mjs CHANGED
@@ -13,7 +13,7 @@ const command = args[0];
13
13
 
14
14
  function printHelp() {
15
15
  console.log(`
16
- 🚀 agentctl v0.9.4 — Universal Agent Orchestrator & Safety Gatekeeper
16
+ 🚀 agentctl v0.20.0 — Universal Agent Orchestrator & Safety Gatekeeper
17
17
 
18
18
  Usage: agentctl <command> [options]
19
19
 
package/index.mjs CHANGED
@@ -5,7 +5,7 @@
5
5
  * security auditing, repo gating, and state operations.
6
6
  */
7
7
 
8
- export { loadConfig, detectStack, resolveVerify, resolveRoot, normalizePath } from "./src/config.mjs";
8
+ export { loadConfig, detectStack, resolveVerify, resolveRoot, normalizePath, TIER_PRESETS } from "./src/config.mjs";
9
9
  export {
10
10
  shannonEntropy,
11
11
  redactSecrets,
@@ -19,14 +19,23 @@ export { createProvider, JULES_PRESET, CLAUDE_PRESET, CODEX_PRESET } from "./src
19
19
  export {
20
20
  appendLedger,
21
21
  readLedger,
22
+ verifyLedgerIntegrity,
23
+ reserveBudget,
24
+ commitBudgetReservation,
22
25
  withBudget,
23
26
  checkDailyBudget,
24
27
  acquireLock,
25
28
  releaseLock,
26
29
  lockStatus,
27
30
  } from "./src/state.mjs";
28
- export { gate, dispatch, repair, run } from "./src/engine.mjs";
31
+ export { gate, dispatch, repair, run, fingerprintFailureState } from "./src/engine.mjs";
29
32
  export { validateEnvelope } from "./src/envelope.mjs";
33
+ export {
34
+ createExecutionEnvelope,
35
+ verifyExecutionEnvelope,
36
+ freezeExecutionEnvelope,
37
+ hashExecutionEnvelope,
38
+ } from "./src/execution_envelope.mjs";
30
39
  export { checkAssetIntegrity } from "./src/asset_integrity.mjs";
31
40
  export { classifyRiskTier, RISK_TIERS } from "./src/risk.mjs";
32
41
  export { checkRulesBudget } from "./src/rules_budget.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jules-orchestrator-kit",
3
- "version": "0.9.4",
3
+ "version": "0.20.0",
4
4
  "description": "Orchestration kit for running Google Jules autonomous agents.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,30 +1,198 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Backward compatibility shim for jules-merge-swarm.mjs in v0.9.0.
4
+ * Swarm branch merger with 3-way structural JSON/object merge, git merge-file execution
5
+ * isolated in os.tmpdir(), and safety gate checking.
5
6
  */
6
7
 
7
- import { existsSync, readdirSync, readFileSync } from "node:fs";
8
+ import { writeFileSync, readFileSync, existsSync, readdirSync, rmSync, mkdtempSync } from "node:fs";
8
9
  import { join } from "node:path";
10
+ import { tmpdir } from "node:os";
11
+ import { spawnSync } from "node:child_process";
12
+ import { classifyRiskTier, RISK_TIERS } from "../src/risk.mjs";
13
+ import { changedFiles } from "../src/git.mjs";
9
14
 
15
+ export const EXIT = Object.freeze({
16
+ SUCCESS: 0,
17
+ R3_RESTRICTED: 3,
18
+ MERGE_CONFLICT: 4,
19
+ });
20
+
21
+ const BLOCKED_KEYS = new Set(["__proto__", "constructor", "prototype"]);
22
+
23
+ function isPlainObject(v) {
24
+ return (
25
+ v !== null &&
26
+ typeof v === "object" &&
27
+ !Array.isArray(v) &&
28
+ (Object.getPrototypeOf(v) === null || Object.getPrototypeOf(v) === Object.prototype)
29
+ );
30
+ }
31
+
32
+ function deepClone(v) {
33
+ if (v === undefined) return undefined;
34
+ return JSON.parse(JSON.stringify(v));
35
+ }
36
+
37
+ function stableStringify(v) {
38
+ if (v === null || typeof v !== "object") return JSON.stringify(v);
39
+ if (Array.isArray(v)) return `[${v.map(stableStringify).join(",")}]`;
40
+ const keys = Object.keys(v).filter((k) => !BLOCKED_KEYS.has(k)).sort();
41
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(v[k])}`).join(",")}}`;
42
+ }
43
+
44
+ function deepEqual(a, b) {
45
+ return stableStringify(a) === stableStringify(b);
46
+ }
47
+
48
+ /**
49
+ * Recursive 3-way structural object & array merge.
50
+ */
51
+ export function deepMerge3Way(base, ours, theirs, path = "$") {
52
+ const conflicts = [];
53
+ if (deepEqual(ours, theirs)) return { merged: deepClone(ours), conflicts };
54
+ if (deepEqual(ours, base)) return { merged: deepClone(theirs), conflicts };
55
+ if (deepEqual(theirs, base)) return { merged: deepClone(ours), conflicts };
56
+
57
+ if (isPlainObject(base) && isPlainObject(ours) && isPlainObject(theirs)) {
58
+ const merged = {};
59
+ const safeBase = base || {};
60
+ const safeOurs = ours || {};
61
+ const safeTheirs = theirs || {};
62
+ const keys = new Set([
63
+ ...Object.keys(safeBase),
64
+ ...Object.keys(safeOurs),
65
+ ...Object.keys(safeTheirs),
66
+ ]);
67
+ for (const k of keys) {
68
+ if (BLOCKED_KEYS.has(k)) continue;
69
+ const childPath = `${path}.${k}`;
70
+ const b = safeBase[k], o = safeOurs[k], t = safeTheirs[k];
71
+ if (o === undefined && t === undefined) continue;
72
+ if (o === undefined) { merged[k] = deepClone(t); continue; }
73
+ if (t === undefined) { merged[k] = deepClone(o); continue; }
74
+ const sub = deepMerge3Way(b, o, t, childPath);
75
+ merged[k] = sub.merged;
76
+ conflicts.push(...sub.conflicts);
77
+ }
78
+ return { merged, conflicts };
79
+ }
80
+
81
+ if (Array.isArray(ours) && Array.isArray(theirs)) {
82
+ const baseArr = Array.isArray(base) ? base : [];
83
+ const minLen = Math.min(ours.length, theirs.length);
84
+ const merged = [];
85
+ for (let i = 0; i < minLen; i++) {
86
+ const sub = deepMerge3Way(baseArr[i], ours[i], theirs[i], `${path}[${i}]`);
87
+ merged.push(sub.merged);
88
+ conflicts.push(...sub.conflicts);
89
+ }
90
+ if (ours.length > minLen && theirs.length <= minLen) {
91
+ merged.push(...ours.slice(minLen).map(deepClone));
92
+ } else if (theirs.length > minLen && ours.length <= minLen) {
93
+ merged.push(...theirs.slice(minLen).map(deepClone));
94
+ } else if (ours.length > minLen && theirs.length > minLen) {
95
+ conflicts.push({
96
+ path: `${path}[tail]`,
97
+ base: baseArr.slice(minLen),
98
+ ours: ours.slice(minLen),
99
+ theirs: theirs.slice(minLen),
100
+ });
101
+ merged.push(...ours.slice(minLen).map(deepClone));
102
+ }
103
+ return { merged, conflicts };
104
+ }
105
+
106
+ conflicts.push({ path, base: deepClone(base), ours: deepClone(ours), theirs: deepClone(theirs) });
107
+ return { merged: deepClone(ours), conflicts };
108
+ }
109
+
110
+ /**
111
+ * Executes a function inside a temporary directory in os.tmpdir() with guaranteed cleanup.
112
+ */
113
+ export function withTempDir(prefix, fn) {
114
+ const dir = mkdtempSync(join(tmpdir(), prefix || "jules-merge-"));
115
+ try {
116
+ return fn(dir);
117
+ } finally {
118
+ try {
119
+ rmSync(dir, { recursive: true, force: true });
120
+ } catch (_) {}
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Attempts a 3-way text file merge using git merge-file inside os.tmpdir().
126
+ */
127
+ export function attemptCodeMergeFile(repoRoot, relPath, oursContent, baseContent, theirsContent) {
128
+ return withTempDir("jules-merge-", (tmp) => {
129
+ const baseFile = join(tmp, "base");
130
+ const oursFile = join(tmp, "ours");
131
+ const theirsFile = join(tmp, "theirs");
132
+ writeFileSync(baseFile, baseContent ?? "", "utf-8");
133
+ writeFileSync(oursFile, oursContent ?? "", "utf-8");
134
+ writeFileSync(theirsFile, theirsContent ?? "", "utf-8");
135
+
136
+ const res = spawnSync("git", ["merge-file", "-p", "--diff3", oursFile, baseFile, theirsFile], {
137
+ encoding: "utf-8",
138
+ maxBuffer: 64 * 1024 * 1024,
139
+ });
140
+
141
+ if (res.error) {
142
+ return { ok: false, conflicts: 0, error: res.error.message };
143
+ }
144
+ const conflictCount = res.status && res.status > 0 ? res.status : 0;
145
+ const merged = res.stdout ?? "";
146
+ return {
147
+ ok: conflictCount === 0,
148
+ merged,
149
+ conflicts: conflictCount,
150
+ };
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Checks safety gate against active worker locks and risk tiers.
156
+ */
10
157
  export function checkSafetyGate(branchName = "", projectRoot = process.cwd()) {
11
158
  const locksDir = join(projectRoot, ".agent/state/locks");
12
- if (!existsSync(locksDir)) return { safe: true };
159
+ if (existsSync(locksDir)) {
160
+ try {
161
+ const files = readdirSync(locksDir);
162
+ for (const f of files) {
163
+ if (!f.endsWith(".json")) continue;
164
+ const content = readFileSync(join(locksDir, f), "utf-8");
165
+ const parsed = JSON.parse(content);
166
+ if (parsed.branch === branchName) {
167
+ return { safe: false, reason: `Active lock held by worker ${parsed.agent || "unknown"}` };
168
+ }
169
+ }
170
+ } catch (_) {}
171
+ }
172
+
173
+ // Check R3 risk tier if files modified
13
174
  try {
14
- const files = readdirSync(locksDir);
15
- for (const f of files) {
16
- if (!f.endsWith(".json")) continue;
17
- const content = readFileSync(join(locksDir, f), "utf-8");
18
- const parsed = JSON.parse(content);
19
- if (parsed.branch === branchName) {
20
- return { safe: false, reason: `Active lock held by worker ${parsed.agent || "unknown"}` };
175
+ const files = changedFiles(projectRoot, process.env.BASE_BRANCH || "main");
176
+ if (files.length > 0) {
177
+ const tier = classifyRiskTier(files);
178
+ if (tier.tier === RISK_TIERS.R3) {
179
+ return { safe: false, reason: `R3 Restricted Path violation: ${tier.reason}` };
21
180
  }
22
181
  }
23
182
  } catch (_) {}
183
+
24
184
  return { safe: true };
25
185
  }
26
186
 
27
187
  if (process.argv[1] && process.argv[1].endsWith("jules-merge-swarm.mjs")) {
188
+ const branchName = process.argv[2] || "";
189
+ const gateResult = checkSafetyGate(branchName);
190
+ if (!gateResult.safe) {
191
+ console.error(`[merge-swarm] Safety gate check failed: ${gateResult.reason}`);
192
+ process.exit(EXIT.R3_RESTRICTED);
193
+ }
194
+
28
195
  console.log("No open Jules PRs found.");
29
- process.exit(0);
196
+ process.exit(EXIT.SUCCESS);
30
197
  }
198
+