jules-orchestrator-kit 0.24.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 +113 -80
- package/bin/agentctl.mjs +3 -3
- package/index.mjs +12 -2
- package/package.json +7 -11
- package/src/engine.mjs +45 -7
- package/src/mcp.mjs +1 -1
- package/src/provider.mjs +88 -6
- package/src/state.mjs +41 -9
- package/.agent/jules-queue/README.md +0 -30
- package/.agent/jules.yml +0 -13
- package/.env.example +0 -73
- package/.github/workflows/jules-audit.yml +0 -49
package/README.md
CHANGED
|
@@ -1,24 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# 🚀 jules-orchestrator-kit
|
|
4
|
+
|
|
5
|
+
### High-Volume Autonomous AI Agent Orchestration Engine for Google Jules
|
|
4
6
|
|
|
5
7
|
[](https://github.com/FullThrottle83/jules-orchestrator-kit/actions/workflows/jules-audit.yml)
|
|
6
8
|
[](https://www.npmjs.com/package/jules-orchestrator-kit)
|
|
7
9
|
[](https://opensource.org/licenses/MIT)
|
|
8
10
|
[](https://nodejs.org)
|
|
11
|
+
[](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
|
-
>
|
|
11
|
-
> Built specifically to execute 300+ daily agent sessions and parallel swarms safely. Zero external runtime dependencies. Built strictly on native Node.js 20+ ESM.
|
|
18
|
+
</div>
|
|
12
19
|
|
|
13
20
|
---
|
|
14
21
|
|
|
15
|
-
|
|
22
|
+
<p align="center">
|
|
23
|
+
<img src="docs/assets/hero-flow.svg?v=3" alt="Autonomous Orchestration Pipeline" width="100%" />
|
|
24
|
+
</p>
|
|
16
25
|
|
|
17
26
|
---
|
|
18
27
|
|
|
19
28
|
## ⚡ 2-Minute Quickstart
|
|
20
29
|
|
|
21
|
-
|
|
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.
|
|
22
31
|
|
|
23
32
|
```bash
|
|
24
33
|
# 1. Initialize orchestrator structure in your target codebase
|
|
@@ -36,7 +45,7 @@ npx agentctl gate
|
|
|
36
45
|
npx agentctl mcp
|
|
37
46
|
```
|
|
38
47
|
|
|
39
|
-
> 📖 **Looking for production deployment patterns?**
|
|
48
|
+
> 📖 **NOTE**: **Looking for production deployment patterns?**
|
|
40
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).
|
|
41
50
|
|
|
42
51
|
---
|
|
@@ -55,17 +64,19 @@ Whether you are dispatching your first automated coding task or managing high-th
|
|
|
55
64
|
|
|
56
65
|
---
|
|
57
66
|
|
|
67
|
+
## 🏛️ System Architecture & Visual Diagrams
|
|
68
|
+
|
|
58
69
|
<details open>
|
|
59
|
-
<summary><b
|
|
70
|
+
<summary><b>📐 1. Control Plane Architecture Layers</b></summary>
|
|
60
71
|
|
|
61
72
|
<br/>
|
|
62
73
|
|
|
63
|
-
|
|
74
|
+
<p align="center">
|
|
75
|
+
<img src="docs/assets/architecture-layers.svg?v=3" alt="Control Plane Architecture Layers" width="100%" />
|
|
76
|
+
</p>
|
|
64
77
|
|
|
65
78
|
<br/>
|
|
66
79
|
|
|
67
|
-

|
|
68
|
-
|
|
69
80
|
### Engine System Highlights
|
|
70
81
|
|
|
71
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`).
|
|
@@ -80,12 +91,31 @@ Whether you are dispatching your first automated coding task or managing high-th
|
|
|
80
91
|
|
|
81
92
|
</details>
|
|
82
93
|
|
|
83
|
-
<details>
|
|
84
|
-
<summary><b
|
|
94
|
+
<details open>
|
|
95
|
+
<summary><b>🔁 2. Self-Healing OODA Loop Cycle</b></summary>
|
|
85
96
|
|
|
86
97
|
<br/>
|
|
87
98
|
|
|
88
|
-
|
|
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/>
|
|
89
119
|
|
|
90
120
|
### The 4-Phase Safety Audit & Security Boundary (`agentctl gate`)
|
|
91
121
|
|
|
@@ -96,17 +126,35 @@ Whether you are dispatching your first automated coding task or managing high-th
|
|
|
96
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]`).
|
|
97
127
|
6. **MCP Stream Isolation (`src/mcp.mjs`)**: Seals `process.stdout.write` framing stream to prevent log output from corrupting JSON-RPC stdio frames.
|
|
98
128
|
|
|
99
|
-
>
|
|
100
|
-
> All security rules are fetched strictly from `origin/main` (never untrusted PR branches) to prevent prompt-injection attacks from altering security rules.
|
|
129
|
+
> ⚠️ **WARNING**: All security rules are fetched strictly from `origin/main` (never untrusted PR branches) to prevent prompt-injection attacks from altering security rules.
|
|
101
130
|
|
|
102
131
|
</details>
|
|
103
132
|
|
|
104
|
-
<details>
|
|
105
|
-
<summary><b
|
|
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>
|
|
106
141
|
|
|
107
142
|
<br/>
|
|
108
143
|
|
|
109
|
-
|
|
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/>
|
|
110
158
|
|
|
111
159
|
### Connecting to Claude Desktop, Cursor, or Antigravity
|
|
112
160
|
|
|
@@ -121,15 +169,40 @@ npx agentctl mcp
|
|
|
121
169
|
- `audit_jules_gate`: Execute the 4-phase safety gate against the workspace.
|
|
122
170
|
- `check_risk_tier`: Classify workspace changes into Risk Tiers (R0 Cosmetic to R3 Restricted).
|
|
123
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.
|
|
124
173
|
|
|
125
174
|
</details>
|
|
126
175
|
|
|
127
|
-
<details>
|
|
128
|
-
<summary><b
|
|
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>
|
|
129
184
|
|
|
130
185
|
<br/>
|
|
131
186
|
|
|
132
|
-
|
|
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
|
+
---
|
|
202
|
+
|
|
203
|
+
## 🤖 Specialist Agent Prompt Presets (`.agent/prompts/`)
|
|
204
|
+
|
|
205
|
+
Specialized prompt presets enforce payload limits (< 75 KB) and domain guardrails out of the box:
|
|
133
206
|
|
|
134
207
|
| Preset | Role & Domain | Primary Focus |
|
|
135
208
|
| :--- | :--- | :--- |
|
|
@@ -144,12 +217,9 @@ npx agentctl dispatch \
|
|
|
144
217
|
--prompt "$(cat .agent/prompts/Janitor.md) Prune unused helper methods in src/utils.mjs"
|
|
145
218
|
```
|
|
146
219
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
<details>
|
|
150
|
-
<summary><b>⚡ GitHub Actions Composite Action (.github/actions/setup-jules)</b></summary>
|
|
220
|
+
---
|
|
151
221
|
|
|
152
|
-
|
|
222
|
+
## ⚡ GitHub Actions Composite Action (`.github/actions/setup-jules`)
|
|
153
223
|
|
|
154
224
|
Integrate `jules-orchestrator-kit` into any GitHub Actions workflow with 3 lines of YAML:
|
|
155
225
|
|
|
@@ -165,12 +235,9 @@ steps:
|
|
|
165
235
|
JULES_API_KEY: ${{ secrets.JULES_API_KEY }}
|
|
166
236
|
```
|
|
167
237
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
<details>
|
|
171
|
-
<summary><b>🛠️ CLI Command Reference (`agentctl`)</b></summary>
|
|
238
|
+
---
|
|
172
239
|
|
|
173
|
-
|
|
240
|
+
## 🛠️ CLI Command Reference (`agentctl`)
|
|
174
241
|
|
|
175
242
|
| Command | Usage Example | Description |
|
|
176
243
|
| :--- | :--- | :--- |
|
|
@@ -185,35 +252,9 @@ steps:
|
|
|
185
252
|
| **`scan`** | `agentctl scan` | Scans codebase for `TODO` and `FIXME` comments and generates task queue |
|
|
186
253
|
| **`clean`** | `agentctl clean` | Audits and cleans up stale git worktrees, orphaned intents, locks, and temporary state files |
|
|
187
254
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
<details>
|
|
191
|
-
<summary><b>💳 Subscription Tier Presets (Free / Pro / Ultra)</b></summary>
|
|
192
|
-
|
|
193
|
-
<br/>
|
|
194
|
-
|
|
195
|
-

|
|
196
|
-
|
|
197
|
-
<br/>
|
|
198
|
-
|
|
199
|
-
Tailor session limits and rate-limiting behavior to your Google Jules API subscription tier:
|
|
200
|
-
|
|
201
|
-
| Tier | `dailyTasks` | `repairAttempts` | `concurrency` | `staggerMs` | Target Usage |
|
|
202
|
-
| :--- | :---: | :---: | :---: | :---: | :--- |
|
|
203
|
-
| **`free`** | `15` | `1` | `1` | `3000 ms` | **Hobby / Free Tier:** Conserves quota, prevents HTTP 429 rate limits. |
|
|
204
|
-
| **`pro`** | `100` | `2` | `2` | `1500 ms` | **Developer Pro:** Balanced throughput for everyday work. |
|
|
205
|
-
| **`ultra`** *(default)* | `300` | `3` | `3` | `1000 ms` | **Swarm / Enterprise:** Maximum parallel throughput & CI/CD. |
|
|
206
|
-
|
|
207
|
-
**How to activate:**
|
|
208
|
-
- **Environment Variable:** `export JULES_TIER=free` (or set in `.env`)
|
|
209
|
-
- **Config File (`.agent/jules.yml`):** Set `tier: free`
|
|
210
|
-
|
|
211
|
-
</details>
|
|
212
|
-
|
|
213
|
-
<details>
|
|
214
|
-
<summary><b>📝 Configuration Reference (`.agent/jules.yml`)</b></summary>
|
|
255
|
+
---
|
|
215
256
|
|
|
216
|
-
|
|
257
|
+
## 📝 Configuration Reference (`.agent/jules.yml`)
|
|
217
258
|
|
|
218
259
|
Auto-generated by `agentctl init` at the root of your project:
|
|
219
260
|
|
|
@@ -233,16 +274,13 @@ limits:
|
|
|
233
274
|
diffKb: 75
|
|
234
275
|
```
|
|
235
276
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
<details>
|
|
239
|
-
<summary><b>🚦 Exit Code Registry & Troubleshooting</b></summary>
|
|
277
|
+
---
|
|
240
278
|
|
|
241
|
-
|
|
279
|
+
## 🚦 Exit Code Registry & Troubleshooting
|
|
242
280
|
|
|
243
|
-
Standardized exit codes enforced across all CLI utilities:
|
|
281
|
+
Standardized exit codes enforced across all CLI utilities and CI pipelines:
|
|
244
282
|
|
|
245
|
-
| Exit Code |
|
|
283
|
+
| Exit Code | Classification | Description & Immediate Remediation Action |
|
|
246
284
|
| :---: | :--- | :--- |
|
|
247
285
|
| `0` | **Success** | Task completed cleanly; PR opened or verification passed. |
|
|
248
286
|
| `1` | **Pre-Dispatch / Arg Error** | Invalid arguments, prompt > 50 KB, or pre-dispatch validation error. |
|
|
@@ -250,15 +288,15 @@ Standardized exit codes enforced across all CLI utilities:
|
|
|
250
288
|
| `3` | **Scope Violation** | Attempted modification of restricted files (`.github/`, command files, agent rules). |
|
|
251
289
|
| `4` | **OODA Exhausted / Thrash** | Verification suite failed after 3 repair attempts or hit deterministic regression. |
|
|
252
290
|
| `5` | **Diff Payload Limit** | Post-change git diff exceeds payload budget (`limits.diffKb`, default 75 KB). |
|
|
253
|
-
| `6` | **Secret Detected** | High-confidence secret or private key detected in patch diff. |
|
|
291
|
+
| `6` | **Secret Detected** | High-confidence secret or private key detected in patch diff (Shannon entropy > 3.6 bits). |
|
|
254
292
|
| `7` | **Budget Exhausted** | Daily task session quota limit reached (`limits.dailyTasks`, default 300). |
|
|
293
|
+
| `8` | **FLAKY_QUARANTINE** | Statistical test flakiness detected (oscillation >= 0.4, Wilson CI); OODA repair suppressed. |
|
|
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. |
|
|
255
296
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
<details>
|
|
259
|
-
<summary><b>🔐 Environment Variables Reference</b></summary>
|
|
297
|
+
---
|
|
260
298
|
|
|
261
|
-
|
|
299
|
+
## 🔐 Environment Variables Reference
|
|
262
300
|
|
|
263
301
|
| Variable | Description | Default |
|
|
264
302
|
| :--- | :--- | :--- |
|
|
@@ -273,12 +311,9 @@ Standardized exit codes enforced across all CLI utilities:
|
|
|
273
311
|
| `BASE_BRANCH` | Base branch for PR Audits & Merge-Base checks | `main` |
|
|
274
312
|
| `NO_COLOR` | Set to `true` to disable ANSI color output | `false` |
|
|
275
313
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
<details>
|
|
279
|
-
<summary><b>🌐 Supported Tech Stacks & Auto-Detection</b></summary>
|
|
314
|
+
---
|
|
280
315
|
|
|
281
|
-
|
|
316
|
+
## 🌐 Supported Tech Stacks & Auto-Detection
|
|
282
317
|
|
|
283
318
|
The orchestrator automatically infers verification and build commands across ecosystems:
|
|
284
319
|
|
|
@@ -295,8 +330,6 @@ The orchestrator automatically infers verification and build commands across eco
|
|
|
295
330
|
| **Elixir / Ruby** | `mix.exs` / `Gemfile` | `mix test` / `rake test` | *(standard build)* |
|
|
296
331
|
| **Java / C / C++** | `pom.xml` / `Makefile` | `mvn test` / `make test` | `mvn compile` / `make` |
|
|
297
332
|
|
|
298
|
-
</details>
|
|
299
|
-
|
|
300
333
|
---
|
|
301
334
|
|
|
302
335
|
## 🤝 Contributing & Standards
|
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
|
|
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
|
|
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 (
|
|
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 (
|
|
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 {
|
|
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": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Orchestration kit for running Google Jules autonomous agents.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,20 +23,16 @@
|
|
|
23
23
|
".": "./index.mjs"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
|
-
"index.mjs",
|
|
27
|
-
"src/",
|
|
28
26
|
"bin/",
|
|
27
|
+
"src/",
|
|
29
28
|
"scripts/",
|
|
30
|
-
".
|
|
29
|
+
"index.mjs",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"README.md",
|
|
32
|
+
"JULES_RULES_TEMPLATE.md",
|
|
31
33
|
".agent/rules/",
|
|
32
34
|
".agent/prompts/",
|
|
33
|
-
".agent/workflows/"
|
|
34
|
-
".agent/jules-queue/README.md",
|
|
35
|
-
".github/workflows/jules-audit.yml",
|
|
36
|
-
".env.example",
|
|
37
|
-
"JULES_RULES_TEMPLATE.md",
|
|
38
|
-
"README.md",
|
|
39
|
-
"LICENSE"
|
|
35
|
+
".agent/workflows/"
|
|
40
36
|
],
|
|
41
37
|
"scripts": {
|
|
42
38
|
"init": "node bin/init.js",
|
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
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
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
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
202
|
-
used
|
|
213
|
+
ok: used < limit,
|
|
214
|
+
used,
|
|
203
215
|
budget: limit,
|
|
204
|
-
remaining: Math.max(0, limit -
|
|
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
|
-
|
|
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
|
-
|
|
250
|
-
|
|
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 - (
|
|
271
|
-
used:
|
|
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
|
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# Jules Task Queue Directory
|
|
2
|
-
|
|
3
|
-
Drop markdown task specification files here (e.g. `TASK-001-feature-name.md`) to queue tasks for background execution by Google Jules.
|
|
4
|
-
|
|
5
|
-
### File Format Example
|
|
6
|
-
|
|
7
|
-
```markdown
|
|
8
|
-
# TASK-001: Implement User Rate Limiting
|
|
9
|
-
|
|
10
|
-
## Objective
|
|
11
|
-
Implement sliding window rate limiting for public API routes.
|
|
12
|
-
|
|
13
|
-
## Execution Rules
|
|
14
|
-
- Use Redis / memory store for tracking hit counts.
|
|
15
|
-
- Must return HTTP 429 Too Many Requests when limit is exceeded.
|
|
16
|
-
- Must pass `npm test` before submitting PR.
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Process all queued tasks in batch:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
agentctl queue
|
|
23
|
-
# or npm run jules:queue
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Or dispatch a single task using `agentctl dispatch`:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
agentctl dispatch --title "TASK-001 Rate Limiting" --prompt "$(cat .agent/jules-queue/TASK-001-rate-limiting.md)"
|
|
30
|
-
```
|
package/.agent/jules.yml
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# Google Jules Repository Configuration (Version 2)
|
|
2
|
-
version: 2
|
|
3
|
-
test_cmd: "npm test"
|
|
4
|
-
build_cmd: ""
|
|
5
|
-
forbidden_paths:
|
|
6
|
-
- ".github/**"
|
|
7
|
-
- "**/secrets/**"
|
|
8
|
-
- "**/*.pem"
|
|
9
|
-
- "**/lock-manager/**"
|
|
10
|
-
- "scripts/jules-self-audit.mjs"
|
|
11
|
-
- ".agent/jules.yml"
|
|
12
|
-
allow_paths: []
|
|
13
|
-
|
package/.env.example
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# Google Jules Orchestration Kit - Environment Configuration Example
|
|
2
|
-
# Copy this file to .env and populate with your credentials/settings.
|
|
3
|
-
|
|
4
|
-
# --- Authentication & Core Settings ---
|
|
5
|
-
# Google Jules API Key (Required for direct REST API dispatches)
|
|
6
|
-
JULES_API_KEY=your_google_jules_api_key_here
|
|
7
|
-
|
|
8
|
-
# Alias for API key (fallback if JULES_API_KEY is not set)
|
|
9
|
-
# GEMINI_API_KEY=your_gemini_api_key_here
|
|
10
|
-
|
|
11
|
-
# Override the Jules REST API URL
|
|
12
|
-
# JULES_API_URL=https://jules.googleapis.com/v1alpha/sessions
|
|
13
|
-
|
|
14
|
-
# Target GitHub Repository (Format: owner/repo)
|
|
15
|
-
JULES_REPO=owner/repo
|
|
16
|
-
|
|
17
|
-
# Set to "true" or "1" to run in repoless/serverless mode
|
|
18
|
-
# JULES_REPOLESS=false
|
|
19
|
-
|
|
20
|
-
# Enable Dry-Run mode to simulate payload dispatch without making API calls
|
|
21
|
-
# JULES_DRY_RUN=false
|
|
22
|
-
|
|
23
|
-
# --- Budget & Security Gatekeeper ---
|
|
24
|
-
# Daily max session limit for autonomous dispatches (Default: 300)
|
|
25
|
-
# JULES_DAILY_BUDGET=300
|
|
26
|
-
|
|
27
|
-
# Allow modifications to command-defining files (package.json, Cargo.toml, etc.) in PRs (Default: false)
|
|
28
|
-
# JULES_ALLOW_COMMAND_FILE_CHANGES=false
|
|
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
|
-
|
|
33
|
-
# --- Execution Scope & Git ---
|
|
34
|
-
# Base Branch for PR Audits & Merge-Base Calculations (Default: main)
|
|
35
|
-
BASE_BRANCH=main
|
|
36
|
-
|
|
37
|
-
# PR Head Branch (Used by CI to dynamically target branches during OODA repair)
|
|
38
|
-
# GITHUB_HEAD_REF=my-feature-branch
|
|
39
|
-
|
|
40
|
-
# Root directory of the project (Auto-assigned during swarm executions)
|
|
41
|
-
# JULES_PROJECT_ROOT=/path/to/project
|
|
42
|
-
|
|
43
|
-
# --- Swarm & Concurrency ---
|
|
44
|
-
# Maximum parallel dispatches for swarm runs (Default: 3)
|
|
45
|
-
JULES_SWARM_CONCURRENCY=3
|
|
46
|
-
|
|
47
|
-
# Dispatch Stagger Interval in Milliseconds (Default: 1500)
|
|
48
|
-
JULES_SWARM_STAGGER_MS=1500
|
|
49
|
-
|
|
50
|
-
# Rate-limit for the jules:queue command (Default: 500)
|
|
51
|
-
# JULES_PACE_MS=500
|
|
52
|
-
|
|
53
|
-
# Use Git Worktrees instead of cloning for swarm isolation
|
|
54
|
-
# JULES_USE_WORKTREES=false
|
|
55
|
-
|
|
56
|
-
# Current slot index for partitioning tasks (Swarm mode)
|
|
57
|
-
# JULES_SLOT_INDEX=1
|
|
58
|
-
|
|
59
|
-
# Total number of slots for partitioning tasks (Swarm mode)
|
|
60
|
-
# JULES_SLOT_TOTAL=3
|
|
61
|
-
|
|
62
|
-
# --- CI & Output ---
|
|
63
|
-
# Is this running in a CI environment? (Changes log output and fail-fast behaviors)
|
|
64
|
-
# CI=true
|
|
65
|
-
|
|
66
|
-
# Allow OODA Auto-Repair even in CI environments
|
|
67
|
-
# ALLOW_AUTO_REPAIR=true
|
|
68
|
-
|
|
69
|
-
# GitHub Actions Step Summary File Path
|
|
70
|
-
# GITHUB_STEP_SUMMARY=/path/to/step_summary.md
|
|
71
|
-
|
|
72
|
-
# Disable color in terminal output
|
|
73
|
-
# NO_COLOR=true
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
name: Jules PR Audit & Test Gatekeeper
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [ main ]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [ main ]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
audit-and-test:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
strategy:
|
|
13
|
-
matrix:
|
|
14
|
-
node-version: ['20.x', '22.x', '24.x']
|
|
15
|
-
steps:
|
|
16
|
-
- name: Checkout repository
|
|
17
|
-
uses: actions/checkout@v4
|
|
18
|
-
with:
|
|
19
|
-
fetch-depth: 0
|
|
20
|
-
|
|
21
|
-
- name: Setup Node.js
|
|
22
|
-
uses: actions/setup-node@v4
|
|
23
|
-
with:
|
|
24
|
-
node-version: ${{ matrix.node-version }}
|
|
25
|
-
|
|
26
|
-
- name: Cache OODA state & ledgers
|
|
27
|
-
uses: actions/cache@v4
|
|
28
|
-
with:
|
|
29
|
-
path: .agent/state/
|
|
30
|
-
key: ooda-state-${{ runner.os }}-${{ github.run_id }}
|
|
31
|
-
restore-keys: |
|
|
32
|
-
ooda-state-${{ runner.os }}-
|
|
33
|
-
|
|
34
|
-
- name: Install dependencies
|
|
35
|
-
run: npm install
|
|
36
|
-
|
|
37
|
-
- name: Run Linter
|
|
38
|
-
run: npm run lint --if-present
|
|
39
|
-
|
|
40
|
-
- name: Run Unit Tests
|
|
41
|
-
run: npm test --if-present
|
|
42
|
-
|
|
43
|
-
- name: Run Jules PR Self-Audit Gatekeeper
|
|
44
|
-
if: matrix.node-version == '22.x' && github.event_name == 'pull_request'
|
|
45
|
-
run: node scripts/jules-self-audit.mjs
|
|
46
|
-
env:
|
|
47
|
-
CI: "true"
|
|
48
|
-
BASE_BRANCH: "main"
|
|
49
|
-
|