jules-orchestrator-kit 0.29.0 β†’ 0.29.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
@@ -332,19 +332,21 @@ Native stdio server exposing task dispatch, gate verification, and risk auditing
332
332
 
333
333
  | Command | Usage | Description | Exit Codes |
334
334
  | :--- | :--- | :--- | :--- |
335
+ | `init` | `agentctl init [--interactive] [--tier pro]` | Interactive onboarding wizard & stack oracle inspector generating `.agent/config.yml`. | `0` (Created) |
336
+ | `task create` | `agentctl task create [--title <t>] [--prompt <p>]` | Interactively authors & scopes falsifiable task envelopes with secret scrubbing & preflight gate checks. | `0` (Queued), `1` (Unfalsifiable / Secret leak) |
335
337
  | `dispatch` | `agentctl dispatch --title <t> --prompt <p>` | Dispatches a single task to an AI agent in an isolated worktree. | `0` (Success), `1` (Arg error), `2` (429 Rate limit), `3` (Scope deny), `4` (OODA exhausted), `5` (Diff > 75KB), `6` (Secret leak) |
338
+ | `scan` | `agentctl scan` | Scans codebase for TODO/FIXME annotations to seed task authoring. | `0` (Scanned) |
336
339
  | `review-repair`| `agentctl review-repair <pr-comments.json>`| Parses GitHub PR review comments and synthesizes actionable OODA repair tasks. | `0` (Parsed), `1` (Missing file) |
337
340
  | `dashboard` | `agentctl dashboard [port]` | Starts zero-dependency local HTTP telemetry and audit visualizer dashboard. | `0` (Running) |
338
- | `gate` / `audit`| `agentctl gate --base main --json` | Runs security, secret scanning, and verification gate against current branch. | `0` (Approved), `3` (Scope violation), `5` (Diff limit), `6` (Secret leak) |
341
+ | `gate` / `audit`| `agentctl gate --mode working-tree --json` | Runs security, secret scanning, and verification gate against working tree or branch. | `0` (Approved), `3` (Scope violation), `5` (Diff limit), `6` (Secret leak) |
339
342
  | `bootstrap` | `agentctl bootstrap [--force] [--json]` | Inspects an untested repository and synthesizes `.agent/config.yml` with a zero-test verification oracle (`php -l`, `compileall`, `dotnet build`, `tsc`, `smoke`). | `0` (Bootstrapped / Existing) |
340
343
  | `queue` | `agentctl queue` | Consumes and executes pending markdown task envelopes in `.agent/queue/`. | `0` (Complete) |
341
344
  | `swarm` | `agentctl swarm` | Runs parallel multi-agent swarm across queued tasks with token-bucket concurrency. | `0` (Complete) |
342
345
  | `doctor` | `agentctl doctor` | Diagnostic inspect: displays detected stack, container wrapper, test command, and daily session budget. | `0` (Healthy) |
343
346
  | `lock` | `agentctl lock <acquire\|release\|status>`| Manages VFS mutex locks for multi-agent non-overlapping file ownership. | `0` (Locked/Released), `1` (Conflict) |
344
347
  | `clean` | `agentctl clean` | Prunes stale git worktrees, lockfiles, and temporary ledgers. | `0` (Clean) |
345
- | `init` | `agentctl init` | Scaffolds `.agent/` directory structure and default `.agent/config.yml`. | `0` (Created) |
346
348
  | `mcp` | `agentctl mcp` | Starts stdio Model Context Protocol (MCP) server for tool integration. | `0` / Stdio stream |
347
- | `version` | `agentctl version` | Outputs orchestrator kit semantic version (`v0.27.0`). | `0` |
349
+ | `version` | `agentctl version` | Outputs orchestrator kit semantic version (`v0.29.0`). | `0` |
348
350
 
349
351
  <br/>
350
352
 
@@ -353,14 +355,15 @@ Native stdio server exposing task dispatch, gate verification, and risk auditing
353
355
  <br/>
354
356
 
355
357
  <a id="roadmap"></a>
356
- ## πŸ—ΊοΈ v0.27+ Next-Gen Feature Roadmap
358
+ ## πŸ—ΊοΈ v0.29+ Feature Roadmap
357
359
 
358
360
  | Feature | Module / Command | Architectural Blueprint | Target Release |
359
361
  | :--- | :--- | :--- | :---: |
362
+ | **Onboarding & Stack Oracle Wizard** | `agentctl init --interactive` (`src/wizard-init.mjs`) | Zero-dependency interactive CLI wizard auto-detecting verification oracles, quota tiers, and preset workflows. | **v0.29.0** *(Shipped)* |
363
+ | **Guided Task Authoring Subsystem** | `agentctl task create` (`src/wizard-task.mjs`) | Guided task authoring with TODO candidate harvesting, Shannon entropy secret scrubbing, and guardrail footer synthesis. | **v0.29.0** *(Shipped)* |
360
364
  | **PR Review Auto-Remediation Loop** | `agentctl review-repair` (`src/review-repair.mjs`) | Ingests GitHub PR review comments (`CHANGES_REQUESTED`), extracts line/file context, and dispatches automated OODA repair turns until reviewer comments are resolved. | **v0.27.0** *(Shipped)* |
361
365
  | **Multi-Provider Failover Router** | `createFailoverProvider` (`src/provider.mjs`) | Ordered router (`["jules", "claude-code", "local-mcp"]`) that seamlessly falls back to secondary LLMs on HTTP 429 rate limits or 5xx service unavailability. | **v0.27.0** *(Shipped)* |
362
366
  | **Telemetry & Audit Web Dashboard**| `agentctl dashboard` (`src/dashboard.mjs`) | Zero-dependency local HTTP server displaying real-time DAG execution graphs, Wilson-Score flaky test ledgers, and SHA-256 telemetry chains. | **v0.27.0** *(Shipped)* |
363
- | **Cross-Language Contract Guard** | `hashCrossLanguageInterface` (`src/merge-blocks.mjs`)| Canonical SHA-256 schema hashing for OpenAPI/Protobuf specs across polyglot task dependencies in `DagExecutor`. | **v0.26.0** *(Shipped)* |
364
367
 
365
368
  <br/>
366
369
 
@@ -371,11 +374,10 @@ Native stdio server exposing task dispatch, gate verification, and risk auditing
371
374
  ## πŸ“– Recipes, Documentation & Prior Art
372
375
 
373
376
  - [**Google Jules Official Documentation**](https://jules.google) β€” Official platform overview and API specifications for Google Jules.
374
- - [**Google Labs Code Repositories**](https://github.com/google-labs-code) β€” Official Google Labs public GitHub organization (jules-action, jules-sdk).
377
+ - [**Onboarding & Task Wizard Specification**](./docs/ONBOARDING_AND_TASK_WIZARD_SPEC.md) β€” Normative architecture specification for zero-dependency TUI, stack oracle, and guided task authoring.
375
378
  - [**Universal Polyglot Architecture & Zero-Test Specification**](./docs/UNIVERSAL_POLYGLOT_ARCHITECTURE.md) β€” Comprehensive technical report on boundary resolution, OODA math, and B2B workflows.
376
379
  - [**v0.27.0 Architectural Audit & Platform Evolution**](./docs/V0.27_ARCHITECTURAL_AUDIT_AND_EVOLUTION.md) β€” PR review remediation, failover router, and local dashboard specs.
377
380
  - [**Examples & Task Envelope Recipes**](./EXAMPLES.md) β€” Production YAML and Markdown task envelopes.
378
- - [**Adversarial Security Audit Phase 4 Report**](./docs/AUDIT_REPORT.md) β€” CWE-77, CWE-1321, and CWE-183 security hardening analysis.
379
381
  - [**Changelog**](./CHANGELOG.md) β€” Full release history and migration guides.
380
382
 
381
383
  <br/>
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 v0.29.0 β€” Universal Agent Orchestrator & Safety Gatekeeper
17
+ πŸš€ agentctl v0.29.1 β€” Universal Agent Orchestrator & Safety Gatekeeper
18
18
 
19
19
  Usage: agentctl <command> [options]
20
20
 
@@ -54,7 +54,7 @@ async function main() {
54
54
  }
55
55
 
56
56
  if (command === "version" || command === "--version" || command === "-v") {
57
- console.log("agentctl v0.29.0");
57
+ console.log("agentctl v0.29.1");
58
58
  process.exit(0);
59
59
  }
60
60
 
@@ -265,7 +265,7 @@ async function main() {
265
265
  }
266
266
 
267
267
  case "doctor": {
268
- console.log(`\nπŸ” agentctl System Diagnostics (v0.29.0)`);
268
+ console.log(`\nπŸ” agentctl System Diagnostics (v0.29.1)`);
269
269
  console.log(`--------------------------------------------------`);
270
270
  console.log(` Project Root : ${root}`);
271
271
  console.log(` Config File : ${config._file || "None (Using defaults)"}`);
@@ -401,7 +401,7 @@ async function main() {
401
401
  case "status": {
402
402
  const queueDir = getQueueDir(root);
403
403
  const files = existsSync(queueDir) ? readdirSync(queueDir).filter((f) => isTaskFile(f, queueDir)) : [];
404
- console.log(`\nπŸ“Š agentctl Status Summary (v0.29.0)`);
404
+ console.log(`\nπŸ“Š agentctl Status Summary (v0.29.1)`);
405
405
  console.log(`--------------------------------------------------`);
406
406
  console.log(` Project Root : ${root}`);
407
407
  console.log(` Pending Tasks : ${files.length}`);
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Google Jules Orchestrator Kit - Node.js SDK (v0.29.0)
2
+ * Google Jules Orchestrator Kit - Node.js SDK (v0.29.1)
3
3
  *
4
4
  * Exposes core orchestrator functions for programmatically driving agent tasks,
5
5
  * security auditing, repo gating, and state operations.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jules-orchestrator-kit",
3
- "version": "0.29.0",
3
+ "version": "0.29.1",
4
4
  "description": "Orchestration kit for running Google Jules autonomous agents.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/dashboard.mjs CHANGED
@@ -32,7 +32,7 @@ export function getDashboardHtml(root = process.cwd()) {
32
32
  </style>
33
33
  </head>
34
34
  <body>
35
- <h1>πŸš€ jules-orchestrator-kit Dashboard (v0.29.0)</h1>
35
+ <h1>πŸš€ jules-orchestrator-kit Dashboard (v0.29.1)</h1>
36
36
  <div class="subtitle">Repository: <code>${root}</code></div>
37
37
 
38
38
  <div class="grid">
@@ -87,7 +87,7 @@ export function createDashboardServer({ root = process.cwd(), port: _port = 4100
87
87
 
88
88
  if (url.pathname === "/api/status") {
89
89
  res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
90
- return res.end(JSON.stringify({ ok: true, version: "0.29.0", root, ts: new Date().toISOString() }));
90
+ return res.end(JSON.stringify({ ok: true, version: "0.29.1", root, ts: new Date().toISOString() }));
91
91
  }
92
92
 
93
93
  if (url.pathname === "/api/telemetry") {
package/src/engine.mjs CHANGED
@@ -46,14 +46,8 @@ export function isTaskFile(fileName, queueDirOrContent = null) {
46
46
  }
47
47
  if (typeof content === "string") {
48
48
  const trimmed = content.trimStart();
49
- if (trimmed.startsWith("---")) {
50
- const endFm = trimmed.indexOf("\n---", 3);
51
- if (endFm !== -1) {
52
- const frontMatter = trimmed.slice(3, endFm);
53
- if (/(?:taskId|envelopeId|baseRef|baseSha|task|title|id|version|capabilities|scope|verify):/i.test(frontMatter)) {
54
- return true;
55
- }
56
- }
49
+ if (trimmed.includes("JULES_TASK_ENVELOPE") || trimmed.includes("# Task ID:") || trimmed.startsWith("---")) {
50
+ return true;
57
51
  }
58
52
  }
59
53
  } catch (_) {}
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: "0.29.0",
12
+ version: "0.29.1",
13
13
  };
14
14
 
15
15
  export const MAX_MCP_FRAME_SIZE = 4 * 1024 * 1024; // 4 MB memory safety ceiling
package/src/security.mjs CHANGED
@@ -13,7 +13,7 @@ export const HIGH_CONFIDENCE_PATTERNS = [
13
13
 
14
14
  /\bAKIA[0-9A-Z]{16}\b/g,
15
15
  /\bASIA[0-9A-Z]{16}\b/g,
16
- /\baws_secret_access_key\s*=\s*['"]?[A-Za-z0-9\/+=]{40}['"]?/g,
16
+ /\baws_secret_access_key\s*=\s*['"]?[A-Za-z0-9\/+=]{40}['"]?/gi,
17
17
 
18
18
  /-----BEGIN (?:RSA|DSA|EC|OPENSSH|PRIVATE)(?:\s+PRIVATE)? KEY-----[\s\S]*?-----END (?:RSA|DSA|EC|OPENSSH|PRIVATE)(?:\s+PRIVATE)? KEY-----/g,
19
19
  /PuTTY-User-Key-File-[0-9]:[^\n]+/g,
package/src/tui.mjs CHANGED
@@ -41,6 +41,13 @@ export function styleText(text, style) {
41
41
  return `${style}${text}${ANSI.reset}`;
42
42
  }
43
43
 
44
+ export class WizardCancelledError extends Error {
45
+ constructor(message = "Wizard cancelled by user") {
46
+ super(message);
47
+ this.name = "WizardCancelledError";
48
+ }
49
+ }
50
+
44
51
  /**
45
52
  * Read keypress in raw mode from TTY input stream.
46
53
  * @param {import("node:stream").Readable} stream
@@ -60,7 +67,7 @@ async function* readKeypresses(stream = process.stdin) {
60
67
  if (str === "\u0003") {
61
68
  // Ctrl+C SIGINT
62
69
  process.stdout.write(ANSI.showCursor + "\n");
63
- process.exit(130);
70
+ throw new WizardCancelledError("Wizard operation cancelled by user (SIGINT).");
64
71
  }
65
72
  yield str;
66
73
  }
@@ -1,9 +1,26 @@
1
- import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from "node:fs";
1
+ import { existsSync, readFileSync, writeFileSync, openSync, fsyncSync, closeSync, renameSync, mkdirSync, readdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { parseYaml } from "./config.mjs";
4
4
  import { detectStackOracles, runVerificationProbe } from "./wizard-oracle.mjs";
5
5
  import { select, multiSelect, input, confirm, spinner, isTTY } from "./tui.mjs";
6
6
 
7
+ /**
8
+ * Write a file atomically using a temporary file and atomic rename.
9
+ * @param {string} filePath
10
+ * @param {string} content
11
+ */
12
+ function writeAtomic(filePath, content) {
13
+ const tmpPath = `${filePath}.tmp.${Date.now()}.${Math.random().toString(36).slice(2)}`;
14
+ const fd = openSync(tmpPath, "w");
15
+ try {
16
+ writeFileSync(fd, content, "utf-8");
17
+ fsyncSync(fd);
18
+ } finally {
19
+ closeSync(fd);
20
+ }
21
+ renameSync(tmpPath, filePath);
22
+ }
23
+
7
24
  export const TIER_PROFILES = {
8
25
  free: {
9
26
  concurrency: 1,
@@ -71,21 +88,30 @@ export function planInit(root = process.cwd(), options = {}) {
71
88
  const tierName = options.tier || "pro";
72
89
  const limits = TIER_PROFILES[tierName] || TIER_PROFILES.pro;
73
90
 
91
+ // Preserve existing config if present
92
+ let existingConfig = {};
93
+ const existingConfigPath = join(root, ".agent", "config.yml");
94
+ if (existsSync(existingConfigPath)) {
95
+ try {
96
+ existingConfig = parseYaml(readFileSync(existingConfigPath, "utf-8")) || {};
97
+ } catch (_) {}
98
+ }
99
+
74
100
  const verify = {
75
- test: options.testCmd || oracle.candidates.testCmd || "",
76
- build: options.buildCmd || oracle.candidates.buildCmd || "",
77
- lint: options.lintCmd || oracle.candidates.lintCmd || "",
78
- typecheck: options.typecheckCmd || oracle.candidates.typecheckCmd || "",
101
+ test: options.testCmd || existingConfig.verify?.test || oracle.candidates.testCmd || "",
102
+ build: options.buildCmd || existingConfig.verify?.build || oracle.candidates.buildCmd || "",
103
+ lint: options.lintCmd || existingConfig.verify?.lint || oracle.candidates.lintCmd || "",
104
+ typecheck: options.typecheckCmd || existingConfig.verify?.typecheck || oracle.candidates.typecheckCmd || "",
79
105
  };
80
106
 
81
- const selectedPresets = options.presets || ["nightly-security-audit", "flaky-test-quarantine"];
107
+ const selectedPresets = options.presets || existingConfig.presets || ["nightly-security-audit", "flaky-test-quarantine"];
82
108
 
83
109
  const configYaml = `# Google Jules Orchestrator Kit Config (v0.29.0)
84
110
  version: 1
85
- provider: jules
111
+ provider: ${existingConfig.provider || "jules"}
86
112
  tier: ${tierName}
87
- base_branch: ${options.baseBranch || "main"}
88
- branch_prefix: agent/
113
+ base_branch: ${options.baseBranch || existingConfig.base_branch || "main"}
114
+ branch_prefix: ${existingConfig.branch_prefix || "agent/"}
89
115
 
90
116
  limits:
91
117
  concurrency: ${limits.concurrency}
@@ -161,6 +187,10 @@ export function loadPresets(root = process.cwd()) {
161
187
  export async function runInitWizard(root = process.cwd(), options = {}) {
162
188
  const interactive = options.interactive !== false && isTTY(options.stdin || process.stdin);
163
189
 
190
+ if (!interactive && !options.allowDefaults && !options.tier) {
191
+ throw new Error("Non-interactive init requires explicit options or allowDefaults: true");
192
+ }
193
+
164
194
  let selectedTier = options.tier || "pro";
165
195
  let testCmd = options.testCmd;
166
196
  let buildCmd = options.buildCmd;
@@ -232,8 +262,8 @@ export async function runInitWizard(root = process.cwd(), options = {}) {
232
262
  const configPath = join(agentDir, "config.yml");
233
263
  const julesPath = join(agentDir, "jules.yml");
234
264
 
235
- writeFileSync(configPath, plan.configYaml, "utf-8");
236
- writeFileSync(julesPath, plan.julesYaml, "utf-8");
265
+ writeAtomic(configPath, plan.configYaml);
266
+ writeAtomic(julesPath, plan.julesYaml);
237
267
 
238
268
  return {
239
269
  ok: true,
@@ -1,8 +1,9 @@
1
1
  import { writeFileSync, mkdirSync, existsSync } from "node:fs";
2
- import { join } from "node:path";
2
+ import { resolve } from "node:path";
3
3
  import { loadConfig } from "./config.mjs";
4
4
  import { gate } from "./engine.mjs";
5
5
  import { scanDiff, shannonEntropy } from "./security.mjs";
6
+ import { getQueueDir } from "./state.mjs";
6
7
  import { scanCodebaseForTodos } from "../scripts/jules-scan-todos.mjs";
7
8
  import { select, input, confirm, spinner, isTTY } from "./tui.mjs";
8
9
 
@@ -16,6 +17,8 @@ HARD CONSTRAINTS:
16
17
  - BEFORE opening the PR: Run \`git fetch origin main && git rebase origin/main\`, then re-verify.
17
18
  `;
18
19
 
20
+ const TRIVIAL_ORACLES = new Set(["true", "echo", ":", "false", "exit 0", "exit 1", "echo ok"]);
21
+
19
22
  /**
20
23
  * Pure planning core for task creation & envelope synthesis.
21
24
  * @param {string} root
@@ -29,7 +32,6 @@ HARD CONSTRAINTS:
29
32
  * verifyCmd: string,
30
33
  * flags: { autoPr: boolean, requirePlanApproval: boolean, repoless: boolean, startingBranch: string },
31
34
  * secretFindings: Array<any>,
32
- * gateResult: object,
33
35
  * taskFileContent: string
34
36
  * }}
35
37
  */
@@ -38,28 +40,37 @@ export function planTaskCreate(root = process.cwd(), inputObj = {}) {
38
40
 
39
41
  const title = inputObj.title || "Agent Task";
40
42
  const rawPrompt = inputObj.prompt || "";
41
- const verifyCmd = inputObj.verifyCmd || config.verify.test || config.verify.build || "";
43
+ const verifyCmd = (inputObj.verifyCmd || config.verify.test || config.verify.build || "").trim();
42
44
 
43
45
  // 1. Falsifiability check
44
46
  if (!rawPrompt.trim()) {
45
47
  throw new Error("Task prompt cannot be empty.");
46
48
  }
47
- if (!verifyCmd && !inputObj.allowUnverifiable) {
49
+ const cleanCmd = verifyCmd.toLowerCase().replace(/['"]/g, "").trim();
50
+ if ((!verifyCmd || TRIVIAL_ORACLES.has(cleanCmd)) && !inputObj.allowUnverifiable) {
48
51
  throw new Error(
49
- "Unfalsifiable Task Rejected: Task must include a concrete verification test/build command. Configure verify.test in .agent/config.yml or pass --verify-cmd."
52
+ "Unfalsifiable Task Rejected: Task must include a non-trivial verification test/build command. Configure verify.test in .agent/config.yml or pass --verify-cmd."
50
53
  );
51
54
  }
52
55
 
53
- // 2. Secret Scrubbing Preflight
56
+ // 2. Secret Scrubbing Preflight (multiline diff formatting)
54
57
  const secretFindings = [];
55
- const secretScan = scanDiff(`+${rawPrompt}`);
58
+ const multilineDiff = rawPrompt.split("\n").map((line) => `+${line}`).join("\n");
59
+ const secretScan = scanDiff(multilineDiff);
56
60
  if (!secretScan.ok) {
57
- secretScan.findings.forEach((f) => secretFindings.push(f));
61
+ secretScan.findings.forEach((f) => secretFindings.push({ id: f.type || f.id || "SECRET_LEAK", ...f }));
58
62
  }
59
63
  if (shannonEntropy(rawPrompt) > 4.5 && !inputObj.allowHighEntropy) {
60
64
  secretFindings.push({ id: "HIGH_ENTROPY_PROMPT", line: 1 });
61
65
  }
62
66
 
67
+ // High-confidence secrets cannot be bypassed
68
+ const highConfidenceFindings = secretFindings.filter((f) => f.id !== "HIGH_ENTROPY_PROMPT");
69
+ if (highConfidenceFindings.length > 0) {
70
+ throw new Error(
71
+ `Pre-Dispatch Secret Leak Blocked: Prompt contains ${highConfidenceFindings.length} high-confidence secret finding(s). High-confidence credentials cannot be bypassed.`
72
+ );
73
+ }
63
74
  if (secretFindings.length > 0 && !inputObj.allowSecrets) {
64
75
  throw new Error(
65
76
  `Pre-Dispatch Secret Leak Blocked: Prompt contains potential secrets or credentials (${secretFindings.length} finding(s)). Scrub keys before dispatching.`
@@ -74,7 +85,11 @@ export function planTaskCreate(root = process.cwd(), inputObj = {}) {
74
85
  startingBranch: inputObj.startingBranch || config.baseBranch || "main",
75
86
  };
76
87
 
77
- // 4. Prompt Envelope & Guardrail Footer Synthesis
88
+ // 4. Task ID Sanitization (Path Traversal Guard)
89
+ const rawId = inputObj.id || `TASK-${Date.now().toString(36).toUpperCase()}`;
90
+ const taskId = String(rawId).replace(/[^a-zA-Z0-9_-]/g, "_");
91
+
92
+ // 5. Prompt Envelope & Guardrail Footer Synthesis
78
93
  const fullPrompt = `[TASK INSTRUCTIONS]
79
94
  ${rawPrompt}
80
95
 
@@ -83,9 +98,16 @@ Test/Verification Command: ${verifyCmd || "(None)"}
83
98
 
84
99
  ${GUARDRAIL_FOOTER}`;
85
100
 
86
- const taskId = inputObj.id || `TASK-${Date.now().toString(36).toUpperCase()}`;
101
+ const envelopeMetadata = {
102
+ version: 1,
103
+ id: taskId,
104
+ title,
105
+ flags,
106
+ verifyCmd,
107
+ };
87
108
 
88
- const taskFileContent = `# ${title}
109
+ const taskFileContent = `<!-- JULES_TASK_ENVELOPE: ${JSON.stringify(envelopeMetadata)} -->
110
+ # ${title}
89
111
  # Task ID: ${taskId}
90
112
  # Auto-PR: ${flags.autoPr} | Plan Approval: ${flags.requirePlanApproval} | Repoless: ${flags.repoless}
91
113
 
@@ -173,16 +195,24 @@ export async function runTaskCreateWizard(root = process.cwd(), options = {}) {
173
195
  // Perform Gate Preflight
174
196
  const gateRes = await gate({ root, mode: "working-tree" });
175
197
  if (!gateRes.ok && (gateRes.code === 3 || gateRes.code === 6) && !options.allowGateFailure) {
176
- throw new Error(`Gate Preflight Rejected Task: Repository contains scope or secret violations (Exit ${gateRes.code}).`);
198
+ // Filter out uncommitted .agent/ config scope warnings if user initialized locally
199
+ const realViolations = gateRes.phases[0]?.violations?.filter((v) => !v.file.startsWith(".agent/")) || [];
200
+ if (realViolations.length > 0 || gateRes.code === 6) {
201
+ throw new Error(`Gate Preflight Rejected Task: Repository contains scope or secret violations (Exit ${gateRes.code}).`);
202
+ }
177
203
  }
178
204
 
179
- // Write Task File to .agent/queue/
180
- const queueDir = join(root, ".agent", "queue");
205
+ // Write Task File to canonical queue directory (getQueueDir)
206
+ const queueDir = getQueueDir(root);
181
207
  if (!existsSync(queueDir)) {
182
208
  mkdirSync(queueDir, { recursive: true });
183
209
  }
184
210
 
185
- const taskFile = join(queueDir, `${plan.taskId}.md`);
211
+ const taskFile = resolve(queueDir, `${plan.taskId}.md`);
212
+ if (!taskFile.startsWith(resolve(queueDir))) {
213
+ throw new Error("Task ID path traversal blocked.");
214
+ }
215
+
186
216
  writeFileSync(taskFile, plan.taskFileContent, "utf-8");
187
217
 
188
218
  return {