@sabaiway/agent-workflow-kit 3.0.0 → 3.2.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/CHANGELOG.md +82 -1
- package/README.md +1 -0
- package/SKILL.md +7 -3
- package/bin/install.mjs +10 -1
- package/bridges/antigravity-cli-bridge/SKILL.md +6 -4
- package/bridges/antigravity-cli-bridge/bin/agy-review-honesty.test.mjs +20 -7
- package/bridges/antigravity-cli-bridge/bin/agy-review.sh +84 -7
- package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +189 -18
- package/bridges/antigravity-cli-bridge/bin/agy.sh +55 -6
- package/bridges/antigravity-cli-bridge/bin/agy.test.mjs +77 -41
- package/bridges/antigravity-cli-bridge/capability.json +4 -2
- package/bridges/antigravity-cli-bridge/references/driving-agy.md +5 -0
- package/bridges/codex-cli-bridge/SKILL.md +8 -4
- package/bridges/codex-cli-bridge/bin/codex-exec.sh +129 -11
- package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +278 -23
- package/bridges/codex-cli-bridge/bin/codex-review-honesty.test.mjs +20 -7
- package/bridges/codex-cli-bridge/bin/codex-review.sh +76 -13
- package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +112 -17
- package/bridges/codex-cli-bridge/capability.json +19 -4
- package/bridges/codex-cli-bridge/references/driving-codex.md +11 -0
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/bootstrap.md +3 -2
- package/references/modes/recommendations.md +1 -0
- package/references/modes/upgrade.md +9 -4
- package/references/modes/velocity.md +2 -0
- package/references/modes/worktrees.md +120 -0
- package/references/scripts/archive-decisions.mjs +2 -2
- package/references/scripts/archive-decisions.test.mjs +5 -5
- package/references/scripts/check-docs-size-cli.test.mjs +41 -0
- package/references/scripts/check-docs-size.mjs +46 -29
- package/references/shared/command-shapes.md +22 -0
- package/references/shared/composition-handoff.md +7 -2
- package/references/templates/agent_rules.md +10 -1
- package/tools/autonomy-doctor.mjs +1 -1
- package/tools/bridge-settings-read.mjs +25 -5
- package/tools/changed-surface.mjs +8 -8
- package/tools/commands.mjs +9 -0
- package/tools/delegation.mjs +2 -2
- package/tools/detect-backends.mjs +10 -0
- package/tools/grounding.mjs +13 -13
- package/tools/inject-methodology.mjs +71 -40
- package/tools/lens-region.mjs +113 -36
- package/tools/migrate-adr-store.mjs +3 -3
- package/tools/procedures.mjs +1 -1
- package/tools/recipes.mjs +2 -2
- package/tools/recommendations.mjs +34 -7
- package/tools/release-scan.mjs +12 -2
- package/tools/review-state.mjs +3 -3
- package/tools/sandbox-masks.mjs +13 -13
- package/tools/set-recipe.mjs +1 -1
- package/tools/velocity-profile.mjs +7 -7
- package/tools/worktrees.mjs +2292 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,87 @@ Semantically versioned ([semver](https://semver.org)), newest first. The `versio
|
|
|
4
4
|
is the current release. `upgrade` mode reads a project's `docs/ai/.workflow-version` and applies
|
|
5
5
|
every `migrations/<version>-<slug>.md` newer than it, in semver order.
|
|
6
6
|
|
|
7
|
+
## 3.2.0 — plain language + posture as code: the friction cluster shipped as mechanism (AD-061)
|
|
8
|
+
|
|
9
|
+
Carrier release for a friction cluster (kit 3.2.0 bundles the bridges + shared contract + the
|
|
10
|
+
plain-language template bar; memory 3.1.0, codex bridge 3.1.0, agy bridge 4.1.0 ride alongside;
|
|
11
|
+
engine unbumped). The packaged fixes below ship as MECHANISM for every user, never as one agent's
|
|
12
|
+
discipline.
|
|
13
|
+
|
|
14
|
+
- **Shared command-shapes contract.** New `references/shared/command-shapes.md` states the
|
|
15
|
+
promptless bar for improvised reads/probes — the host's file-read tool, else ONE plain
|
|
16
|
+
undecorated command (no compounds, redirects, pipes, or command substitution; improvised writes
|
|
17
|
+
use the host's file-edit tools). Bound via the existing `Requires:` mechanism to exactly the
|
|
18
|
+
probe-instructing modes (bootstrap / upgrade / velocity, closed-world pinned) + a one-sentence
|
|
19
|
+
inline bar on the SKILL.md router stamp-read. The honest residual is stated (a decorated form is
|
|
20
|
+
host/config-dependent) — never a false "always prompts" guarantee.
|
|
21
|
+
- **Plain-language §2.5 Communication bar** in BOTH deployed `agent_rules` templates,
|
|
22
|
+
byte-identically (the kit fallback deploy path communicates under the same bar): user-facing
|
|
23
|
+
narration is short and plain in the dialogue language; transliterated jargon is banned; an
|
|
24
|
+
English term survives only as the NAME of a thing (flag / command / file / test), glossed.
|
|
25
|
+
`lens-region.mjs` now reconciles a SECOND region (Communication, canon from the kit's own bundled
|
|
26
|
+
template) so EXISTING deployments gain the bar on `upgrade`, not only new bootstraps.
|
|
27
|
+
- **Posture as code on the codex-exec dispatch and both review dispatches** (bridges bundled here).
|
|
28
|
+
`codex-exec` gains a D5 exec posture banner (fresh + resume, resolved post-validation values, a
|
|
29
|
+
validated resume-id grammar + raw-byte NUL screen, control-byte refusal on every field). Both
|
|
30
|
+
review banners gain a BANNER-ONLY `timeout` field — outside D5 banner↔receipt parity — from ONE
|
|
31
|
+
shared effective-timeout resolver (byte-identical across the four wrappers) that closes the old
|
|
32
|
+
env-validation bypass and resolves the timeout binary shadow-proof; no capping binary →
|
|
33
|
+
`timeout=uncapped`, never fabricated. (The raw `agy-run` probe lane keeps no posture banner —
|
|
34
|
+
out of scope this release.) The quote-the-banner-verbatim duty rides both bridges' driving
|
|
35
|
+
contracts, capability notes, and the kit registry mirror, drift-guarded by a new cross-file test;
|
|
36
|
+
the threat-model boundary is stated on the codex execute capability and `codex-exec --help`
|
|
37
|
+
surfaces, including the kit mirror. The read-only `bridge-settings` advisor mirrors the wrapper's
|
|
38
|
+
control-byte refusal semantics with escaped output.
|
|
39
|
+
|
|
40
|
+
## 3.1.0 — parallel feature worktrees v1: provision · list · land --prepare · cleanup (AD-060)
|
|
41
|
+
|
|
42
|
+
New `worktrees` mode + `tools/worktrees.mjs` — several features implemented simultaneously in
|
|
43
|
+
DIFFERENT agent sessions on one machine/repo, zero interference on working-tree files (the ONE
|
|
44
|
+
exception: the default `node_modules` symlink is a shared MUTABLE dependency cache — the printed
|
|
45
|
+
isolated install is the isolation lane), unambiguous ownership. One thin dependency-free tool over git: every verification datum is recomputed live
|
|
46
|
+
from git, never read from stored metadata (the ONE exception: the PREPARED OID in the handoff,
|
|
47
|
+
read back only for recovery). Git >= 2.36 floor (NUL-terminated worktree porcelain); typed STOPs,
|
|
48
|
+
never a guess.
|
|
49
|
+
|
|
50
|
+
- **`provision <slug> --plan <path>`** — visible sibling worktree + `aw/<slug>` branch: the
|
|
51
|
+
registry-derived footprint copy-if-missing (a tracked file is NEVER overwritten), exactly one
|
|
52
|
+
seeded feature plan, the `handoff-<slug>.md` stub, shared-`node_modules` symlink advice
|
|
53
|
+
(`--install` only PRINTS the isolated install — zero spawn, zero write). `--resume` completes a
|
|
54
|
+
half-done provision behind fail-closed identity: at most one handoff, slug AND branch must
|
|
55
|
+
match, the section-required `## Provision record` (a duplicated field or section is a STOP,
|
|
56
|
+
never last-wins), user content preserved byte-exact.
|
|
57
|
+
- **`list`** — read-only and honest: any read failure renders `handoff: (unreadable)` — a silent
|
|
58
|
+
"no" never appears.
|
|
59
|
+
- **`land <slug> --prepare`** — the transient common-git-dir lock (shared with cleanup),
|
|
60
|
+
dirty-main / graph-divergence / docs-ai-drift / red-review-state refusals, the satellite
|
|
61
|
+
working-tree diff versus its base — staged AND unstaged inspected, every unstaged or
|
|
62
|
+
untracked-not-ignored leftover listed and refused (ignored content is outside observation by
|
|
63
|
+
design) — binary-safe transfer excluding exactly `docs/ai` + `docs/plans`, the optional
|
|
64
|
+
porcelain-visible sync adapter, the main gate matrix, and an OID report (main HEAD · TRANSFER ·
|
|
65
|
+
PREPARED · sync delta). The commit is NEVER run by the tool — it stays a dialogue ask. A second
|
|
66
|
+
prepare is reset-only against the recorded PREPARED OID; transfer-apply and post-launch sync
|
|
67
|
+
failures attempt a byte-clean rollback (rollback failures are composed without losing the
|
|
68
|
+
primary error), while a red gate matrix that leaves the snapshot unchanged intentionally KEEPS
|
|
69
|
+
the prepared tree and names both recovery lanes.
|
|
70
|
+
- **`cleanup <slug>`** — live landed-verification against main HEAD (exact land-exclusion
|
|
71
|
+
parity), typed-EXACT ownership of ignored content (file/glob roots own only files), literal
|
|
72
|
+
pathspecs, branch `-d`, prune; foreign content stops it. `--abandon` is the ONE destructive arm
|
|
73
|
+
(`-D`; requires the handoff identity).
|
|
74
|
+
- **Hardening:** every content read and regular-file copy goes through two no-follow descriptor
|
|
75
|
+
doors (identity-bound source · exclusive destination · descriptor-mode update), pinned as the
|
|
76
|
+
only paths by tripwire tests; strict parsers everywhere (NUL porcelain fields,
|
|
77
|
+
scan-before-parse JSON with any-depth duplicate-key refusal, atomic section surgery).
|
|
78
|
+
- **release-scan:** the reviewer-round-identity rung — an `agy` or `codex` name followed by an
|
|
79
|
+
R-number reference is refused; shippable finding IDs use neutral
|
|
80
|
+
`review-<scope>-rNN-<severity>-NN` IDs; the clean line now reads "no AI attribution or
|
|
81
|
+
reviewer-round identity found".
|
|
82
|
+
- **recommendations:** the worktrees parent-dir item — write access stays "not confirmed" without
|
|
83
|
+
a trusted host-capability signal; HAND-APPLY lines are host-qualified.
|
|
84
|
+
- **Docs:** the `references/modes/worktrees.md` contract — the MAIN/SATELLITE ownership matrix
|
|
85
|
+
(shared git state included), satellite forbidden verbs (the v1 docs-only bar), the
|
|
86
|
+
host-specific consent lane, and the other-harnesses PROVEN/ASSUMED split.
|
|
87
|
+
|
|
7
88
|
## 3.0.0 — strip-the-kit: the hardened computed core replaces the ledger machinery (AD-059)
|
|
8
89
|
|
|
9
90
|
> ### ⚠ BREAKING — the review loop is now computed, not recorded
|
|
@@ -502,7 +583,7 @@ was blocked on an honest degrade.
|
|
|
502
583
|
|
|
503
584
|
Contract surfaces moved in lockstep (the tool header, `--help`, the human render, the `--await`
|
|
504
585
|
comment, `references/modes/review-state.md`). Council-converged both segments (S1 codex + agy SHIP; S2
|
|
505
|
-
|
|
586
|
+
review round 1 revise → allPresent fold → review round 2 both SHIP), the two-gate agreement dogfooded live + pinned by a
|
|
506
587
|
detector-independent `two-gate-agreement.test.mjs` + a 17-case matrix; full suite 2969 green.
|
|
507
588
|
|
|
508
589
|
## 1.40.0 — Universal verification profile + session-loop economics (a)–(h) (BUGFREE-3, AD-049)
|
package/README.md
CHANGED
|
@@ -243,6 +243,7 @@ file), or run the guarded `/agent-workflow-kit uninstall`.
|
|
|
243
243
|
| `/agent-workflow-kit commit-guard` | any time | **the read-only pre-commit guard** (D10) — binds the LATEST completed `run-gates --final` receipt to the EXACT current tree: refuses on a missing/red/stale receipt, fingerprint drift under the run, a dangling later attempt, declaration content drift, evidence-hash or lcov drift, or unsatisfied review obligations (the same review-state decision, recomputed over a sanitized env — forged out-of-repo stores never satisfy). Re-runs NO gate or test. Wire it into `.git/hooks/pre-commit` (the installer writes the RESOLVED invocation). `git commit --no-verify` stays the stated residual. |
|
|
244
244
|
| `/agent-workflow-kit recommendations` | any time (every `upgrade` ends with it) | **read-only deployment advisor** (AD-044) — computes what in THIS deployment is configured sub-optimally (allowlist not seeded, autonomy render drifted, OS sandbox unavailable, gates undeclared, bridge friction, sandbox-mask clutter, an unacknowledged sandbox recipe) and renders **verdict-first**: one composed verdict line (does anything need attention?), then each item as **{severity · what · one-line benefit · an optional `recipe:` line (sandbox-lane only) · the exact consent-gated apply one-liner}**. The agent PRESENTS the section in the user's conversational language — every fact and count, nothing added or dropped; commands, paths, hosts and rule strings byte-exact; raw tool block on request — and runs EXACTLY the rendered one-liners only on your yes, surfacing each item's posture note first. Renders **present-even-when-empty** (`no recommendations — flow optimal.`); a failed probe degrades to a stated skip line. Registry strings are fact-true frozen one-line data (posture/risk notes live in the mode doc at the consent moment); the kit never seeds `sandbox.network.allowedDomains` / `filesystem.allowWrite` (**HAND-APPLY** territory), and the sandbox-lane item's convergence is a neutral fingerprint acknowledgement recorded by a consent-gated ack writer into `docs/ai/acks.json` — never a security key (the recipe is documented per bridge in `capability.json` `networkHosts` + `writableDirs`). `--cwd` is required (the target project is explicit); never writes, never commits, never runs a subscription CLI. |
|
|
245
245
|
| `/agent-workflow-kit doc-parity` | any time | **read-only doc-parity lint** (AD-049) — kills the doc-drift class where a mode-contract doc silently lags a code constant (a `--check` doc still reading `300` after the diff cap moved to `400`): a **closed, exported registry** binds each live constant (review caps, schema versions, the ledger's own class/scope vocabulary, and the autonomy-doctor EXIT/status/trusted-dir contract) to the exact token its `references/modes/*.md` contract must carry, and asserts the CURRENT value renders into every bound file — a drifted doc, an unreadable file, or an absent token **fails closed**. The values are sourced from the live imports (never re-typed), so the lint can't itself go stale; adding a binding is adding a checked entry (closed-world, edit-safe). `--check` is a gate exit code for `docs/ai/gates.json`. Never writes, never commits, never runs a subscription CLI. |
|
|
246
|
+
| `/agent-workflow-kit worktrees` | any time | **parallel feature worktrees** — run several features in DIFFERENT agent sessions on one repo, zero interference on working-tree files (the ONE exception is the dependency cache, below): `provision <slug> --plan <file>` creates a sibling git worktree on branch `aw/<slug>` and populates it (registry-derived footprint copy-if-missing — a tracked file is never overwritten; EXACTLY ONE seeded feature plan; the `handoff-<slug>.md` record from minute zero; `node_modules` symlinked where the link stays ignored — a shared MUTABLE dependency cache: writes through it hit MAIN's node_modules; for isolation run the printed isolated-install command (`--install` only PRINTS it; on `--resume` run the printed unlink-first recovery first); absolute root-pinned gate commands rebased on untracked copies only, and only while their bytes equal the MAIN source or its rebased form — user-modified copies stay untouched); `list` is read-only (slug, branch, base, dirty, handoff); `land <slug> --prepare` locks the common git dir, fail-closes on divergence or incomplete satellite state, transfers the complete accepted satellite diff onto a CLEAN main, runs sync plus the declared gates, and reports HEAD/TRANSFER/PREPARED OIDs — the commit ALWAYS stays a dialogue ask; `cleanup <slug>` takes the same lock and removes a LANDED worktree only after live landed-verification against main HEAD, while `--abandon` is the ONE destructive arm (destroys unlanded work; **no preview step** on any writer). The parent dir is the `docs/ai/worktrees.json` `parentDir` setting (default: the repo's sibling parent); an unwritable parent degrades to printed maintainer-pasted commands, and the one-time host consent that makes it promptless surfaces via `recommendations`. Never commits, never pushes, never runs a subscription CLI. |
|
|
246
247
|
| `/agent-workflow-kit uninstall` | opt-in, any time | **guarded teardown** — the inverse of `init` / `setup`. Removes only what's **provably ours** (managed skill dirs + bridge wrappers; in a project, the hidden-mode git-ignore block it added + the pre-commit hook it installed); **never deletes** your `docs/ai` / `AGENTS.md` (prints the exact `rm` to run by hand) or your `.claude/settings.json` (prints an **edit** — remove the attribution key, review any velocity `permissions.*` — never an `rm`). Always `--dry-run` first; preflight-then-mutate; never commits. |
|
|
247
248
|
| `/agent-workflow-kit velocity` | Claude Code · opt-in | **onboarding velocity profile** — seeds a fixed, audited **read-only** allowlist into `.claude/settings.json` so routine read-only commands stop idling on approval prompts while you're away; opt-in `acceptEdits`; plus a **read-only advisory** of likely project gate commands to add by hand. Writes **only** `.claude/settings.json` — **never** allowlists commit/push/publish, never writes `settings.local.json`, never commits. A seeded entry is a **trust posture, not a sandbox** (a runtime residual remains at the settings level — its guard ships as the opt-in `hook` command); a direct commit/push/publish still asks. `--dry-run` first. |
|
|
248
249
|
| `/agent-workflow-kit agents` | Claude Code · opt-in | **cheap-lane subagents** — places bundled subagent definitions (`.claude/agents/`) pinned to a **cheap model** (haiku, low effort, read-only tools) for mechanical work: extraction sweeps, changelog fact-skeletons, gate-failure triage. Judgment, review, and real code stay on your main lane — these vehicles only extract and draft, and their output is verified. Preview by default (`--apply` writes); an existing customized file is **preserved, never overwritten**; never touches `settings*.json`, never commits. |
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: agent-workflow-kit
|
|
|
3
3
|
description: Deploy or upgrade a portable AI-agent memory-and-workflow system in any project. Use when the user wants to bootstrap `docs/ai/` + an entry-point `AGENTS.md` (+ `CLAUDE.md` alias) + cap/archive/index enforcement in a new or existing repo, set up the Memory Map and session protocols, install the docs-rotation pre-commit hook, or run `/agent-workflow-kit` / `/agent-workflow-kit upgrade`. Triggers on phrases like "set up the memory system", "deploy the AI workflow here", "bootstrap docs/ai", "upgrade the workflow".
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
metadata:
|
|
6
|
-
version: '3.
|
|
6
|
+
version: '3.2.0'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# agent-workflow-kit
|
|
@@ -68,7 +68,7 @@ Pick the mode from the user's invocation — the mapping is pinned by `tools/com
|
|
|
68
68
|
|
|
69
69
|
**Safe-routing rule (which mode did the user invoke?).** Map the invocation token with `tools/commands.mjs` `routeInvocation`: a **known** subcommand → its mode; the **bare/empty** invocation → `bootstrap` — the one writer reachable without a token, and only on an undeployed project (if `docs/ai/` already exists, **ask upgrade-vs-bootstrap**, never overwrite); **any unrecognized/ambiguous** token → `help`, which is **read-only**. The invariant: **no unrecognized/garbage invocation ever triggers a write** (only an explicit known token or the acknowledged bare-bootstrap exception can).
|
|
70
70
|
|
|
71
|
-
Before acting, read `docs/ai/.workflow-version` (the project's stamp) to decide the route — an **internal** routing decision, **not** a line you print (version display: *Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`). Route:
|
|
71
|
+
Before acting, read `docs/ai/.workflow-version` (the project's stamp) — via the host's file-read tool, else ONE plain undecorated command (no compounds/redirects/pipes) — to decide the route — an **internal** routing decision, **not** a line you print (version display: *Version disclosure* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`). Route:
|
|
72
72
|
|
|
73
73
|
- **absent** → bootstrap (a fresh deployment).
|
|
74
74
|
- **stamp < `3.0.0`** (the deployment-lineage head) → `upgrade`.
|
|
@@ -187,11 +187,15 @@ read-only — read `${CLAUDE_SKILL_DIR}/references/modes/commit-guard.md` before
|
|
|
187
187
|
|
|
188
188
|
read-only — read `${CLAUDE_SKILL_DIR}/references/modes/doc-parity.md` before acting.
|
|
189
189
|
|
|
190
|
+
### Mode: worktrees
|
|
191
|
+
|
|
192
|
+
writer — read `${CLAUDE_SKILL_DIR}/references/modes/worktrees.md` before acting.
|
|
193
|
+
|
|
190
194
|
---
|
|
191
195
|
|
|
192
196
|
## References
|
|
193
197
|
|
|
194
|
-
- [`references/modes/`](${CLAUDE_SKILL_DIR}/references/modes/) — one file per mode; [`references/shared/`](${CLAUDE_SKILL_DIR}/references/shared/) — the shared point-of-use contracts (report footer · composition hand-off · deploy tail).
|
|
198
|
+
- [`references/modes/`](${CLAUDE_SKILL_DIR}/references/modes/) — one file per mode; [`references/shared/`](${CLAUDE_SKILL_DIR}/references/shared/) — the shared point-of-use contracts (report footer · composition hand-off · deploy tail · command shapes).
|
|
195
199
|
- [`references/contracts.md`](${CLAUDE_SKILL_DIR}/references/contracts.md) — the three setup contracts.
|
|
196
200
|
- [`references/templates/`](${CLAUDE_SKILL_DIR}/references/templates/) — the `AGENTS.md` + `docs/ai/` templates + seeded configs; [`references/scripts/`](${CLAUDE_SKILL_DIR}/references/scripts/) — the Node enforcement scripts; [`migrations/`](${CLAUDE_SKILL_DIR}/migrations/) — per-version upgrade steps; [`launchers/`](${CLAUDE_SKILL_DIR}/launchers/) — non-Claude agent launchers.
|
|
197
201
|
- [`tools/`](${CLAUDE_SKILL_DIR}/tools/) — the family-wide tooling; each tool's contract lives with its mode file and its own header; bridge mirrors under [`bridges/`](${CLAUDE_SKILL_DIR}/bridges/).
|
package/bin/install.mjs
CHANGED
|
@@ -211,7 +211,16 @@ const spawnMember = ({ command, args, options }) => spawnSync(command, args, opt
|
|
|
211
211
|
// npm/network blip (rate-limit, registry hiccup, momentary DNS) — an immediate retry tends to hit the
|
|
212
212
|
// same blip, so wait briefly first. Atomics.wait is a dependency-free sync sleep (the install flow is
|
|
213
213
|
// already synchronous here). Injected in tests as a 0ms no-op so the suite never actually sleeps.
|
|
214
|
-
|
|
214
|
+
// AW_INSTALL_RETRY_DELAY_MS is a test seam (the subprocess cascade suite zeroes the wait; the
|
|
215
|
+
// retry POLICY itself stays exercised). Unset, empty, malformed and negative all keep the shipped
|
|
216
|
+
// default — `Number('')` is 0, so the raw value is screened BEFORE coercion (the R1 council bug).
|
|
217
|
+
export const resolveRetryDelayMs = (env) => {
|
|
218
|
+
const raw = env.AW_INSTALL_RETRY_DELAY_MS;
|
|
219
|
+
if (typeof raw !== 'string' || raw.trim() === '') return 1500;
|
|
220
|
+
const parsed = Number(raw);
|
|
221
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1500;
|
|
222
|
+
};
|
|
223
|
+
const RETRY_DELAY_MS = resolveRetryDelayMs(process.env);
|
|
215
224
|
const sleepSync = (ms) => {
|
|
216
225
|
if (ms > 0) Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
217
226
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: antigravity-cli-bridge
|
|
3
3
|
description: Delegate work to Google's Antigravity CLI (`agy`) — the successor to Gemini CLI — to reach Gemini, Claude, and GPT-OSS models under a Google AI Pro/Ultra subscription from the terminal. Use when the user wants to run a headless `agy` prompt, hand a focused task or second-opinion review to `agy`, install or authenticate Antigravity CLI, check or economise its quota/models, bridge project context into `agy`, set up a second delegated-execution backend beside Codex, or troubleshoot `agy` flags, models, auth, conversations, or its no-JSON headless behaviour.
|
|
4
4
|
metadata:
|
|
5
|
-
version: '4.
|
|
5
|
+
version: '4.1.0'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# antigravity-cli-bridge
|
|
@@ -134,10 +134,12 @@ agy-review --continue --decided @round1.md --focus "still-open items" # round-
|
|
|
134
134
|
|
|
135
135
|
**Honesty + posture (D4/D5):** a run with no recognized `### Verdict` section — empty output
|
|
136
136
|
included — **exits 4 with NO receipt**: treat it as a *failed review to re-run*, never a fatal
|
|
137
|
-
session error. One stderr banner states the actual posture (`review posture: model=…`)
|
|
138
|
-
receipt records the same `posture {model}`; an attesting review with `AGY_MODEL` explicitly
|
|
137
|
+
session error. One stderr banner states the actual posture (`review posture: model=… timeout=…`)
|
|
138
|
+
and the receipt records the same `posture {model}`; an attesting review with `AGY_MODEL` explicitly
|
|
139
139
|
emptied refuses pre-spend (`AGY_PROBE=1` exempt), and control bytes in a model string refuse
|
|
140
|
-
pre-spend in every mode.
|
|
140
|
+
pre-spend in every mode. The `timeout=` field is **banner-only** (exactly the duration `agy-run`
|
|
141
|
+
hands to `timeout(1)`, or `uncapped`) — informational, never a receipt field. **Quote the posture
|
|
142
|
+
banner verbatim** when labeling a dispatch.
|
|
141
143
|
|
|
142
144
|
Frontier default `Gemini 3.1 Pro (High)`; **any** model is allowed (a sub-frontier one earns a
|
|
143
145
|
silenceable `AGY_PROBE=1` advisory). An oversized `code` review trips the byte ceiling with trim/split
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
// posture.model null). Colocated separately from agy-review.test.mjs — that file is
|
|
6
6
|
// red-proof-frozen; this one carries its own minimal standalone harness (the family idiom).
|
|
7
7
|
|
|
8
|
-
import { describe, it } from 'node:test';
|
|
8
|
+
import { describe, it, after } from 'node:test';
|
|
9
9
|
import assert from 'node:assert/strict';
|
|
10
|
-
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, chmodSync, existsSync, readdirSync, symlinkSync } from 'node:fs';
|
|
10
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, chmodSync, existsSync, readdirSync, symlinkSync, cpSync } from 'node:fs';
|
|
11
11
|
import { tmpdir } from 'node:os';
|
|
12
12
|
import { join, dirname, resolve } from 'node:path';
|
|
13
13
|
import { fileURLToPath } from 'node:url';
|
|
@@ -41,12 +41,17 @@ const makePathWithout = (root, exclude = []) => {
|
|
|
41
41
|
return dir;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
// Farm + sandbox base are READ-ONLY per invocation — built ONCE, shared (a per-run farm rebuild
|
|
45
|
+
// plus a per-test `git init`+commit dominate the wall otherwise).
|
|
46
|
+
const SHARED_ROOT = mkdtempSync(join(tmpdir(), 'agy-honesty-shared-'));
|
|
47
|
+
after(() => rmSync(SHARED_ROOT, { recursive: true, force: true }));
|
|
48
|
+
const FARM = makePathWithout(SHARED_ROOT, ['agy', 'agy-run']);
|
|
49
|
+
|
|
50
|
+
const TEMPLATE_HOME = (() => {
|
|
51
|
+
const home = join(SHARED_ROOT, 'template-home');
|
|
46
52
|
const bin = join(home, '.local', 'bin');
|
|
47
53
|
mkdirSync(bin, { recursive: true });
|
|
48
54
|
writeFileSync(join(bin, 'agy'), FAKE_AGY, { mode: 0o755 });
|
|
49
|
-
chmodSync(join(bin, 'agy'), 0o755);
|
|
50
55
|
const repo = join(home, 'repo');
|
|
51
56
|
mkdirSync(repo);
|
|
52
57
|
const g = (...args) => spawnSync('git', args, { cwd: repo, encoding: 'utf8' });
|
|
@@ -58,7 +63,15 @@ const makeSandbox = () => {
|
|
|
58
63
|
g('commit', '-qm', 'base');
|
|
59
64
|
writeFileSync(join(repo, 'pending.txt'), 'PENDING\n');
|
|
60
65
|
writeFileSync(join(repo, 'plan.md'), '# a plan artifact\n');
|
|
61
|
-
return
|
|
66
|
+
return home;
|
|
67
|
+
})();
|
|
68
|
+
|
|
69
|
+
const makeSandbox = () => {
|
|
70
|
+
const home = mkdtempSync(join(tmpdir(), 'agy-honesty-'));
|
|
71
|
+
cpSync(TEMPLATE_HOME, home, { recursive: true });
|
|
72
|
+
const bin = join(home, '.local', 'bin');
|
|
73
|
+
chmodSync(join(bin, 'agy'), 0o755);
|
|
74
|
+
return { home, bin, repo: join(home, 'repo') };
|
|
62
75
|
};
|
|
63
76
|
|
|
64
77
|
const run = (sb, { args, env = {} } = {}) => {
|
|
@@ -69,7 +82,7 @@ const run = (sb, { args, env = {} } = {}) => {
|
|
|
69
82
|
timeout: 30000,
|
|
70
83
|
env: {
|
|
71
84
|
HOME: sb.home,
|
|
72
|
-
PATH: `${sb.bin}:${
|
|
85
|
+
PATH: `${sb.bin}:${FARM}`,
|
|
73
86
|
TMPDIR: process.env.TMPDIR ?? '/tmp',
|
|
74
87
|
AGY_FAKE_SENTINEL: sentinel,
|
|
75
88
|
...env,
|
|
@@ -77,6 +77,11 @@ Notes:
|
|
|
77
77
|
pre-dispatch host-diff: before the FIRST dispatch of this bridge, diff its declared networkHosts
|
|
78
78
|
against the live sandbox allow-list — a missing host is surfaced to the maintainer BEFORE
|
|
79
79
|
dispatching, never fired into a known prompt
|
|
80
|
+
the review posture banner appends a banner-only timeout=<duration|uncapped> field — exactly the
|
|
81
|
+
duration agy-run hands to timeout(1), uncapped when no timeout/gtimeout binary caps the run;
|
|
82
|
+
INFORMATIONAL only: it never enters the receipt posture or the D5 banner↔receipt parity
|
|
83
|
+
quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated
|
|
84
|
+
posture; a prose re-type drifts
|
|
80
85
|
|
|
81
86
|
Round-2 / resume:
|
|
82
87
|
agy-review --continue [--decided @f] [--focus "…"]
|
|
@@ -115,10 +120,12 @@ Settings file (KEY=VALUE, parsed never sourced; env wins over file, file wins ov
|
|
|
115
120
|
Honesty + posture (D4/D5):
|
|
116
121
|
a run whose output carries NO recognized '### Verdict' section — empty output included — exits 4
|
|
117
122
|
with NO receipt: a FAILED review to RE-RUN, never a fatal session error. One stderr banner line
|
|
118
|
-
states the ACTUAL run posture (review posture: model=…) and the receipt records the
|
|
119
|
-
posture {model} (agy has no tier
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
states the ACTUAL run posture (review posture: model=… timeout=…) and the receipt records the
|
|
124
|
+
same posture {model} (agy has no tier; the timeout field is banner-only — never a receipt
|
|
125
|
+
field). Quote the posture banner verbatim when labeling this dispatch. An ATTESTING review with
|
|
126
|
+
AGY_MODEL explicitly emptied refuses pre-spend (the actual model would be unknowable;
|
|
127
|
+
AGY_PROBE=1 is exempt), and a model string carrying control bytes refuses pre-spend in every
|
|
128
|
+
mode.
|
|
122
129
|
|
|
123
130
|
Closed grammar: unknown flags are rejected; no '--' passthrough (the flag escape is --ungrounded; the env escapes are AGY_PROBE=1 and AGY_REVIEW_ALLOW_ADDDIR=1).
|
|
124
131
|
Requires at run time: the agy CLI on PATH + a Google AI subscription login (--help needs neither).
|
|
@@ -235,16 +242,66 @@ aw_apply_settings() {
|
|
|
235
242
|
}
|
|
236
243
|
aw_apply_settings
|
|
237
244
|
|
|
245
|
+
# --- Effective-timeout resolver (D5 banner honesty; AD-061) --------------------
|
|
246
|
+
# ONE rule, both bridges: the posture banner prints EXACTLY the duration handed to timeout(1) —
|
|
247
|
+
# an integer-seconds value rendered with the `s` suffix, a duration string verbatim — and
|
|
248
|
+
# `timeout=uncapped` when no timeout/gtimeout binary can cap the run; never a fabricated number.
|
|
249
|
+
# The EFFECTIVE value (env included — closing the aw_settings_valid env bypass) is validated by
|
|
250
|
+
# the same per-key rule as the settings file, plus a 7-digit integer-part bound (overflow); an
|
|
251
|
+
# invalid value warns + falls back to the built-in default — a typo never silently masquerades
|
|
252
|
+
# as a cap. AGY_TIMEOUT shares AGY_HARD_TIMEOUT's duration rule (it has no settings-file arm).
|
|
253
|
+
aw_effective_timeout() {
|
|
254
|
+
local key="$1" default="$2" value="${!1:-}" rule="$1" intpart
|
|
255
|
+
[[ "$rule" == "AGY_TIMEOUT" ]] && rule="AGY_HARD_TIMEOUT"
|
|
256
|
+
[[ -n "$value" ]] || { printf '%s' "$default"; return 0; }
|
|
257
|
+
intpart="${value%%[!0-9]*}"
|
|
258
|
+
if ! aw_settings_valid "$rule" "$value" || (( ${#intpart} > 7 )); then
|
|
259
|
+
# %q escapes the raw value so a control byte in it can never forge an extra diagnostic line
|
|
260
|
+
# (the direct agy-run lane has no pre-spend screen — the warning itself must be injection-proof).
|
|
261
|
+
printf "warning: invalid value '%q' for %s — using the built-in default %s.\n" "$value" "$key" "$default" >&2
|
|
262
|
+
printf '%s' "$default"
|
|
263
|
+
return 0
|
|
264
|
+
fi
|
|
265
|
+
printf '%s' "$value"
|
|
266
|
+
}
|
|
267
|
+
aw_timeout_label() {
|
|
268
|
+
local bin="$1" value="$2"
|
|
269
|
+
[[ -n "$bin" ]] || { printf 'uncapped'; return 0; }
|
|
270
|
+
case "$value" in
|
|
271
|
+
*[!0-9]*) printf '%s' "$value" ;;
|
|
272
|
+
*) printf '%ss' "$value" ;;
|
|
273
|
+
esac
|
|
274
|
+
}
|
|
275
|
+
aw_resolve_timeout_bin() {
|
|
276
|
+
local bin dir base
|
|
277
|
+
bin="$(builtin type -P timeout 2>/dev/null || true)"
|
|
278
|
+
[[ -n "$bin" ]] || bin="$(builtin type -P gtimeout 2>/dev/null || true)"
|
|
279
|
+
[[ -n "$bin" ]] || { printf ''; return 0; }
|
|
280
|
+
case "$bin" in
|
|
281
|
+
/*) ;;
|
|
282
|
+
*)
|
|
283
|
+
case "$bin" in
|
|
284
|
+
*/*) dir="${bin%/*}"; base="${bin##*/}" ;;
|
|
285
|
+
*) dir="."; base="$bin" ;;
|
|
286
|
+
esac
|
|
287
|
+
dir="$(builtin cd -- "$dir" 2>/dev/null && builtin pwd -P)" || { printf ''; return 0; }
|
|
288
|
+
bin="$dir/$base"
|
|
289
|
+
;;
|
|
290
|
+
esac
|
|
291
|
+
[[ -f "$bin" && -x "$bin" ]] || { printf ''; return 0; }
|
|
292
|
+
printf '%s' "$bin"
|
|
293
|
+
}
|
|
294
|
+
|
|
238
295
|
DEFAULT_AGY_REVIEW_MODEL="Gemini 3.1 Pro (High)"
|
|
239
296
|
# Review-receipt identity (AD-038). AW_BRIDGE_VERSION mirrors this bridge's SKILL.md/capability.json
|
|
240
297
|
# version (drift-guarded by agy-review.test.mjs against capability.json).
|
|
241
298
|
AW_RECEIPT_BACKEND="agy"
|
|
242
|
-
AW_BRIDGE_VERSION="4.
|
|
299
|
+
AW_BRIDGE_VERSION="4.1.0"
|
|
243
300
|
# `-` not `:-` so an EXPLICIT empty AGY_MODEL= survives (drop --model, use settings.json — agy.sh:52).
|
|
244
301
|
AGY_MODEL="${AGY_MODEL-$DEFAULT_AGY_REVIEW_MODEL}"
|
|
245
302
|
# D5 control-byte screen — IMMEDIATELY after resolution, BEFORE the off-frontier advisory (or any
|
|
246
303
|
# other interpolation) can echo raw newline/ESC bytes into stderr/the terminal (round-2 fold).
|
|
247
|
-
if [[ "$AGY_MODEL" == *[$'\x01'-$'\x1f']* ]]; then
|
|
304
|
+
if [[ "$AGY_MODEL" == *[$'\x01'-$'\x1f'$'\x7f']* ]]; then
|
|
248
305
|
echo "error: AGY_MODEL contains control bytes — fix the setting (env or bridge-settings.conf) and re-run." >&2
|
|
249
306
|
exit 2
|
|
250
307
|
fi
|
|
@@ -254,7 +311,20 @@ FRONTIER_SET=("Gemini 3.1 Pro (High)" "Claude Opus 4.6 (Thinking)" "Claude Sonne
|
|
|
254
311
|
# Duration-string timeouts (NOT codex's bare seconds): agy-run forwards a duration to --print-timeout,
|
|
255
312
|
# and the timeout(1) hard cap is a duration too — never numerically compared, so 30m vs 2h is fine.
|
|
256
313
|
AGY_HARD_TIMEOUT="${AGY_HARD_TIMEOUT:-30m}"
|
|
314
|
+
# D5 banner-field control-byte screen (AD-061) — like AGY_MODEL above, BEFORE any interpolation.
|
|
315
|
+
if [[ "$AGY_HARD_TIMEOUT" == *[$'\x01'-$'\x1f'$'\x7f']* ]]; then
|
|
316
|
+
echo "error: AGY_HARD_TIMEOUT contains control bytes — fix the setting (env or bridge-settings.conf) and re-run." >&2
|
|
317
|
+
exit 2
|
|
318
|
+
fi
|
|
319
|
+
AGY_HARD_TIMEOUT="$(aw_effective_timeout AGY_HARD_TIMEOUT 30m)"
|
|
257
320
|
AGY_TIMEOUT="${AGY_TIMEOUT:-$AGY_HARD_TIMEOUT}"
|
|
321
|
+
# D5 banner-field control-byte screen for AGY_TIMEOUT (AD-061): agy-review forwards it to the
|
|
322
|
+
# child agy-run's --print-timeout, so a control byte must refuse HERE, pre-spawn — the child's
|
|
323
|
+
# own diagnostic would otherwise echo it. (agy-run screens its own direct-lane env separately.)
|
|
324
|
+
if [[ "$AGY_TIMEOUT" == *[$'\x01'-$'\x1f'$'\x7f']* ]]; then
|
|
325
|
+
echo "error: AGY_TIMEOUT contains control bytes — fix the setting (env or bridge-settings.conf) and re-run." >&2
|
|
326
|
+
exit 2
|
|
327
|
+
fi
|
|
258
328
|
AGY_PROBE="${AGY_PROBE:-0}"
|
|
259
329
|
# A probe run is RECORDED, not just silently allowed: the receipt carries probe:true so the kit's
|
|
260
330
|
# review-state gate rejects it — a guards-relaxed review must never attest a tree.
|
|
@@ -882,7 +952,14 @@ if [[ -z "$AGY_MODEL" && -z "$resume_mode" && "$REVIEW_PROBE" != "true" && "$REV
|
|
|
882
952
|
echo " AGY_MODEL (wrapper default), or set the real model display string; AGY_PROBE=1 is exempt." >&2
|
|
883
953
|
exit 2
|
|
884
954
|
fi
|
|
885
|
-
|
|
955
|
+
# The timeout field is BANNER-ONLY (AD-061): it prints exactly the duration agy-run hands to
|
|
956
|
+
# timeout(1), or `uncapped` without a capping binary, and never enters the receipt posture.
|
|
957
|
+
# aw_resolve_timeout_bin: builtin type -P (an exported function can shadow neither `timeout` nor
|
|
958
|
+
# `type` itself), normalized to an absolute path fail-closed — the banner can never claim a cap
|
|
959
|
+
# the agy-run child would not have.
|
|
960
|
+
aw_banner_timeout_bin="$(aw_resolve_timeout_bin)"
|
|
961
|
+
aw_timeout_banner="$(aw_timeout_label "$aw_banner_timeout_bin" "$AGY_HARD_TIMEOUT")"
|
|
962
|
+
echo "review posture: model=${AGY_MODEL:-<agy settings default>} timeout=$aw_timeout_banner" >&2
|
|
886
963
|
|
|
887
964
|
# --- Execute via agy-run (single home of timeout + subscription + byte ceiling) ---
|
|
888
965
|
# The output is teed into the private staging dir so the mandated '### Verdict' section can be
|