agy-context-saver 1.7.0 β 1.8.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 +17 -0
- package/README.md +16 -20
- package/mcp/index.js +112 -232
- package/mcp/instructions.md +0 -1
- package/package.json +2 -3
- package/plugin.json +3 -3
- package/rules/AGENTS.md +3 -8
- package/scripts/execution-guard-hook.mjs +30 -5
- package/scripts/install-register.mjs +26 -42
- package/scripts/provision-rtk.mjs +66 -0
- package/scripts/state-dir.mjs +51 -0
- package/skills/context-governance/SKILL.md +9 -19
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to `Agy-Context-Saver` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.8.0] - 2026-10-15
|
|
9
|
+
|
|
10
|
+
### Removed
|
|
11
|
+
- **`subagent_brief` moved to the separate [Agy-Squad](https://github.com/paragon-ux/agy-squad) package.** Agy-Context-Saver now exposes 5 MCP tools. Agy-Squad is under construction, and the two will install side by side. Removed with it: the tool schema, `tests/test-subagent-trials.mjs`, and the subagent delegation and Pointer-Over-Wire guidance in `rules/AGENTS.md`, `skills/context-governance/SKILL.md`, `mcp/instructions.md` and the docs.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **Hook state moved out of the shared temp folder.** The polling ledger and RTK rewrite cache now live in a per-user folder: `%LOCALAPPDATA%\agy-context-saver` on Windows, `$XDG_STATE_HOME/agy-context-saver` (default `~/.local/state/agy-context-saver`) elsewhere. Other users and processes on the machine can no longer read or tamper with them.
|
|
15
|
+
- **Uninstall removes all hook state.** It deletes the state folder and any files older versions left in the temp folder (`agy-session-*.json`, `agy-rtk-rewrite-cache.json` and their temp files). Install removes those legacy temp files too. `test-lifecycle-rollback` seeds all of them and checks that they're removed, and that unrelated temp files are left alone.
|
|
16
|
+
- **The installer retires stale subagent schemas.** Re-installing removes `subagent_brief.json` and `dispatch_squad.json` from `~/.gemini/antigravity/mcp/agy-context-saver/`. `test-lifecycle-rollback` seeds both and checks that they're removed.
|
|
17
|
+
- **The `check_context_health` turn-budget warning** now recommends summarizing and continuing in a fresh conversation instead of delegating to subagents.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- **The MCP server exits when its parent goes away.** It exits on stdin end/close, on stdout `EPIPE`, and when its parent process is gone (15 s watchdog), so no orphaned server processes are left behind.
|
|
21
|
+
- **`mcp/index.js` can be imported without side effects.** The CLI and the stdio loop run only when it's the entry point, and it exports `handleRequest`.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
8
25
|
## [1.7.0] - 2026-10-13
|
|
9
26
|
|
|
10
27
|
### Added
|
package/README.md
CHANGED
|
@@ -21,7 +21,6 @@ It permanently eliminates session degradation, memory bloat, and context window
|
|
|
21
21
|
| **Workspace File Inspection** | Massive uncompressed view_file dumps | Compact `rtk read`, `rtk grep`, `rtk find` |
|
|
22
22
|
| **Long Command Execution** | Detached after 5s $\to$ busy-wait loop | Synchronous window expanded to 10s $\to$ finishes in-turn |
|
|
23
23
|
| **Background Task Monitoring** | Repetitive busy-polling loops | Enforced Reactive Wakeup with 5-strike circuit breaker |
|
|
24
|
-
| **Subagent Delegation** | In-band truncated >16KB JSON dumps | Pointer-Over-Wire (POW) protocols via `subagent_brief` |
|
|
25
24
|
| **Configuration Safety** | Risk of third-party config clobber | Non-destructive preservation of foreign hooks/MCP with `.bak` backups |
|
|
26
25
|
| **Transcript Review** | Raw JSONL dumped into context | Clean human-readable Markdown via `read_transcript` |
|
|
27
26
|
| **Runtime Dependencies** | N/A | **Zero** external npm dependencies (100% Node.js stdlib) + native RTK binary |
|
|
@@ -32,7 +31,9 @@ It permanently eliminates session degradation, memory bloat, and context window
|
|
|
32
31
|
|
|
33
32
|
1. **Layer 1: Reactive Lifecycle & Polling Immunity**: Replaces destructive busy-polling loops with native Antigravity **Reactive Wakeup**, governed by an exponential Proportional Backoff curve and a 5-strike circuit breaker.
|
|
34
33
|
2. **Layer 2: Transparent CLI & Codebase Streamlining**: Intercepts shell execution via PreToolUse hooks, transparently rewriting commands to RTK (`rtk git`, `rtk pytest`), hard-routing workspace searches to `rtk read`/`find`/`rg`, and shielding internal `.system_generated` state.
|
|
35
|
-
3. **Layer 3:
|
|
34
|
+
3. **Layer 3: Synchronous Execution**: Expands synchronous execution windows to 10s so routine commands finish in-turn instead of detaching.
|
|
35
|
+
|
|
36
|
+
> **Subagent tools moved.** `subagent_brief` and squad dispatch now live in the separate [Agy-Squad](https://github.com/paragon-ux/agy-squad) package (under construction). Agy-Context-Saver is complete without them, and the two will install side by side.
|
|
36
37
|
|
|
37
38
|
---
|
|
38
39
|
|
|
@@ -54,6 +55,9 @@ npm run status
|
|
|
54
55
|
> [!TIP]
|
|
55
56
|
> **Enterprise Non-Destructive Guarantee**: Agy-Context-Saver requires **zero external npm dependencies** (100% native Node.js standard library), strictly preserves existing foreign hooks (e.g. `waymark-continuity`) and foreign MCP servers, automatically writes atomic `.bak` configuration backups, and supports clean 1-step rollback.
|
|
56
57
|
|
|
58
|
+
> [!NOTE]
|
|
59
|
+
> **RTK Default Configuration Notice**: To protect agent context and command execution from unprompted marketing banners (`[rtk] /!\ No hook installed...`), `Agy-Context-Saver` standardizes `suppress_hook_warning = true` in RTK's user configuration (`%APPDATA%\rtk\config.toml` on Windows / `~/.config/rtk/config.toml` on POSIX) during installation. Existing user configurations are safely preserved. See [RTK Support Matrix](docs/RTK-SUPPORT-MATRIX.md#rtk-configuration-standardization--default-modifications).
|
|
60
|
+
|
|
57
61
|
---
|
|
58
62
|
|
|
59
63
|
## Architectural Division of Responsibilities
|
|
@@ -94,7 +98,6 @@ npm run status
|
|
|
94
98
|
- Background task polling governance & 5-strike circuit breaker
|
|
95
99
|
- Antigravity internal state protection (`.system_generated/`)
|
|
96
100
|
- Zero-JSON Markdown transcript extraction (`read_transcript`, `query_transcript`)
|
|
97
|
-
- Subagent delegation formulation (`subagent_brief`)
|
|
98
101
|
- Benchmark and evaluation artifact mutation protection
|
|
99
102
|
|
|
100
103
|
---
|
|
@@ -105,14 +108,12 @@ npm run status
|
|
|
105
108
|
2. **The Terminal Bloat Trap**: Routine test runs and git operations dump hundreds of lines of noise into context. RTK compresses these outputs natively before they enter model context.
|
|
106
109
|
3. **The Raw Transcript Trap**: Reading `transcript.jsonl` dumps megabytes of raw JSON into context. Agy blocks direct reads of `.system_generated` and provides high-density Markdown streaming tools.
|
|
107
110
|
4. **Native Inspection Bypass**: Native inspection tools (`view_file`, `grep_search`, `find_by_name`, `list_dir`) bypass token reduction. Agy hard-routes workspace inspection to RTK's canonical CLI (`rtk read`, `rtk grep`, `rtk find`, `rtk ls`).
|
|
108
|
-
5. **The Subagent Truncation Trap**: When subagents return massive audits or code in-band over `send_message`, payloads exceed transport limits (~16 KB) and pollute parent context. Agy enforces **Pointer-Over-Wire (POW)**: subagents dump detailed reports to disk and transmit only high-signal summaries and clickable links.
|
|
109
111
|
|
|
110
112
|
---
|
|
111
113
|
|
|
112
114
|
## Key Capabilities
|
|
113
115
|
|
|
114
116
|
- **Transparent RTK Rewriting**: Native `run_command` calls are automatically rewritten (e.g. `pytest` $\to$ `rtk pytest`) via Antigravity PreToolUse hooks.
|
|
115
|
-
- **Pointer-Over-Wire (POW) Subagent Architecture**: Equips `subagent_brief` payloads with isolated workspaces and file-pointer reporting, eliminating context pollution and message truncation during multi-agent workflows.
|
|
116
117
|
- **Canonical Codebase Inspection**:
|
|
117
118
|
- `rtk read <file>`: Read file with intelligent token filtering and line ranges.
|
|
118
119
|
- `rtk find <path>`: Compact file search tree and directory listing (cross-platform, works on Windows & POSIX).
|
|
@@ -138,8 +139,8 @@ For complete technical references, architectural specifications, and governance
|
|
|
138
139
|
- π **[Getting Started & Installation](docs/installation.md)**: Zero-delay setup, native plugin linking, hooks registration, and verification matrix.
|
|
139
140
|
- ποΈ **[Architecture & Internals](docs/architecture.md)**: Closed Execution Topology, RTK separation of concerns, and the Four Non-Fragile Architectural Pillars.
|
|
140
141
|
- π‘οΈ **[Lifecycle Governance & Hooks](docs/governance.md)**: Proportional Backoff curve, Reactive Wakeup invariants, and the 5-strike circuit breaker.
|
|
141
|
-
- π§° **[MCP Tools & CLI Reference](docs/tools.md)**: Complete specifications for all
|
|
142
|
-
- π¬ **[Forensic & Output Inspection](docs/architecture.md#6-output-governance-spillover-safe-harbor-0-new-tools)**: Ephemeral tool spillover resolution, post-mortem task retrieval
|
|
142
|
+
- π§° **[MCP Tools & CLI Reference](docs/tools.md)**: Complete specifications for all 5 canonical autonomous MCP tools, slash commands, and diagnostic utilities.
|
|
143
|
+
- π¬ **[Forensic & Output Inspection](docs/architecture.md#6-output-governance-spillover-safe-harbor-0-new-tools)**: Ephemeral tool spillover resolution, and post-mortem task retrieval.
|
|
143
144
|
- π **[Publishing & Verification Playbook](docs/publishing.md)**: Clean-room test runs, release procedures, and multi-suite verification matrix.
|
|
144
145
|
- πΊοΈ **[Product Roadmap](docs/roadmap.md)**: Phased strategic roadmap focusing on affordable and reliable multi-agent workflows across general tasks.
|
|
145
146
|
|
|
@@ -175,11 +176,11 @@ node mcp/index.js status
|
|
|
175
176
|
```bash
|
|
176
177
|
npm test
|
|
177
178
|
```
|
|
178
|
-
*Runs all
|
|
179
|
+
*Runs all 10 test suites (hook governance, MCP protocol, output inspection governance, E2E probes, performance probes, installed verification, lifecycle rollback, check-only read-only safety, malformed configuration preservation, and RTK ledger fresh install verification).*
|
|
179
180
|
|
|
180
181
|
### 3. Run Targeted Tests Individually
|
|
181
182
|
```bash
|
|
182
|
-
# Governance hook unit tests (
|
|
183
|
+
# Governance hook unit tests (27 closed-topology tests)
|
|
183
184
|
node tests/test-hook.mjs
|
|
184
185
|
|
|
185
186
|
# MCP server protocol & method tests
|
|
@@ -200,17 +201,14 @@ node tests/test-installed-verification.mjs
|
|
|
200
201
|
# Clean install/uninstall/restore lifecycle test
|
|
201
202
|
node tests/test-lifecycle-rollback.mjs
|
|
202
203
|
|
|
203
|
-
# Subagent delegation trials & batching
|
|
204
|
-
node tests/test-subagent-trials.mjs
|
|
205
|
-
|
|
206
204
|
# Check-only read-only isolation regression test
|
|
207
205
|
node tests/test-check-only-readonly.mjs
|
|
208
206
|
|
|
209
207
|
# Malformed config preservation fail-closed regression test
|
|
210
208
|
node tests/test-malformed-config-preservation.mjs
|
|
211
209
|
|
|
212
|
-
#
|
|
213
|
-
node tests/test-
|
|
210
|
+
# RTK ledger & fresh install verification test
|
|
211
|
+
node tests/test-rtk-ledger-fresh-install.mjs
|
|
214
212
|
```
|
|
215
213
|
|
|
216
214
|
### 4. Test MCP Tools from Antigravity Agent
|
|
@@ -218,7 +216,6 @@ node tests/test-waymark-synergy.mjs
|
|
|
218
216
|
- `check_context_health`: Call with `{ "transcriptPath": "<path-to-transcript.jsonl>" }`.
|
|
219
217
|
- `read_transcript`: Call with `{ "conversationId": "<uuid>", "lastTurns": 3, "mode": "compact" }`.
|
|
220
218
|
- `query_transcript`: Call with `{ "conversationId": "<uuid>", "query": "<term>", "summaryOnly": true }`.
|
|
221
|
-
- `subagent_brief`: Call with `{ "objective": "<goal>", "scopeFiles": ["..."] }`.
|
|
222
219
|
|
|
223
220
|
### 5. Testing Pitfalls & Anti-Patterns to Avoid
|
|
224
221
|
- β **Do NOT run `npm start` or `node mcp/index.js` interactively**: It starts the stdio JSON-RPC daemon and waits indefinitely on `stdin`.
|
|
@@ -234,11 +231,10 @@ node tests/test-waymark-synergy.mjs
|
|
|
234
231
|
- `/mcp:agy-context-saver:context_shield`: Injects 3-layer governance rules and RTK standards into any session with one click. *(MCP Prompts appear in your chat autocomplete menu; MCP Tools are called autonomously by the agent).*
|
|
235
232
|
* **Agent MCP Tools**:
|
|
236
233
|
1. `check_context_health`: Diagnoses conversation transcript turn budgets, payload size, RTK status, and polling loops.
|
|
237
|
-
2. `
|
|
238
|
-
3. `
|
|
239
|
-
4. `
|
|
240
|
-
5. `
|
|
241
|
-
6. `sync_installation`: Re-verifies and repairs all integration layers in ~25ms.
|
|
234
|
+
2. `read_transcript`: Reads recent conversation history in clean Markdown format (compact/full).
|
|
235
|
+
3. `query_transcript`: Forensic filtering, regex search engine, and surgical single-step dereferencing (`stepIndex`, `field`) from full transcripts.
|
|
236
|
+
4. `get_installation_status`: Inspects live Antigravity plugin link, hook, MCP server, tool schemas, and RTK binary.
|
|
237
|
+
5. `sync_installation`: Re-verifies and repairs all integration layers in ~25ms.
|
|
242
238
|
|
|
243
239
|
* **Streamlined Output Inspection (0 New Tools)**:
|
|
244
240
|
- **Runtime Step Spillovers** (`.system_generated/steps/<step>/output.txt`): Inspected via canonical `rtk read <path>` (safe-harbored in the PreToolUse hook; native `view_file` calls automatically route to `rtk read`).
|
package/mcp/index.js
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* - Tools:
|
|
13
13
|
* - check_context_health: Inspects conversation transcripts using async streaming
|
|
14
14
|
* to diagnose turn count, tool polling loops, and context degradation with minimal memory.
|
|
15
|
-
* - subagent_brief: Formulates scope-isolated prompts for delegated subagents.
|
|
16
15
|
* - get_installation_status: Audits live Antigravity plugin, hook, MCP, and RTK integration.
|
|
17
16
|
* - sync_installation: Re-verifies and synchronizes installation and tool schemas.
|
|
18
17
|
* - read_transcript: Clean Markdown streaming reader for compact and full transcripts.
|
|
@@ -31,48 +30,57 @@ import { fileURLToPath } from "node:url";
|
|
|
31
30
|
import { runInstall, runUninstall, runStatus, runRestoreBackups, detectExistingInstallation } from "../scripts/install-register.mjs";
|
|
32
31
|
import { findExistingRtk } from "../scripts/provision-rtk.mjs";
|
|
33
32
|
|
|
33
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
34
|
+
const realOrSelf = (p) => { try { return fs.realpathSync(p); } catch { return path.resolve(p); } };
|
|
35
|
+
const isEntryPoint = Boolean(
|
|
36
|
+
process.argv[1] && realOrSelf(process.argv[1]) === realOrSelf(__filename)
|
|
37
|
+
);
|
|
38
|
+
|
|
34
39
|
// Handle CLI subcommands (e.g. npx agy-context-saver install)
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
40
|
+
if (isEntryPoint) {
|
|
41
|
+
const cliArgs = process.argv.slice(2);
|
|
42
|
+
const cliArg = cliArgs[0];
|
|
43
|
+
const checkFlag = cliArgs.includes("--check") || cliArgs.includes("-c");
|
|
44
|
+
const statusFlag = cliArgs.includes("--status") || cliArgs.includes("status");
|
|
45
|
+
const uninstallFlag = cliArgs.includes("--uninstall") || cliArgs.includes("uninstall");
|
|
46
|
+
const restoreFlag = cliArgs.includes("--restore-backups") || cliArgs.includes("restore-backups");
|
|
47
|
+
const helpFlag = cliArgs.includes("--help") || cliArgs.includes("-h") || cliArgs.includes("help");
|
|
48
|
+
const allowDegraded = cliArgs.includes("--allow-degraded");
|
|
49
|
+
|
|
50
|
+
if (helpFlag) {
|
|
51
|
+
printHelp();
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
if ((checkFlag || statusFlag) && (uninstallFlag || restoreFlag)) {
|
|
55
|
+
console.error("Error: Read-only flags (--check, --status) cannot be combined with mutating flags (--uninstall, --restore-backups).");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
if (checkFlag && statusFlag) {
|
|
59
|
+
console.error("Error: Conflicting flags: specify either --check or --status, not both.");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
if (checkFlag) {
|
|
63
|
+
await runInstall({ checkOnly: true, allowDegraded });
|
|
64
|
+
process.exit(0);
|
|
65
|
+
} else if (statusFlag) {
|
|
66
|
+
runStatus();
|
|
67
|
+
process.exit(0);
|
|
68
|
+
} else if (uninstallFlag) {
|
|
69
|
+
await runUninstall({ restoreBackups: restoreFlag });
|
|
70
|
+
process.exit(0);
|
|
71
|
+
} else if (restoreFlag) {
|
|
72
|
+
await runRestoreBackups();
|
|
73
|
+
process.exit(0);
|
|
74
|
+
} else if (cliArg === "install" || cliArg === "--install") {
|
|
75
|
+
await runInstall({ allowDegraded });
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
71
78
|
}
|
|
72
79
|
|
|
80
|
+
|
|
73
81
|
function printHelp() {
|
|
74
82
|
console.log(`
|
|
75
|
-
Agy-Context-Saver π‘οΈ (v1.
|
|
83
|
+
Agy-Context-Saver π‘οΈ (v1.8.0)
|
|
76
84
|
Universal MCP Server & Lifecycle Governor for Google Antigravity (Powered by RTK)
|
|
77
85
|
|
|
78
86
|
Usage:
|
|
@@ -119,7 +127,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
119
127
|
const rulesPath = path.resolve(__dirname, "../rules/AGENTS.md");
|
|
120
128
|
|
|
121
129
|
const SERVER_NAME = "agy-context-saver";
|
|
122
|
-
const SERVER_VERSION = "1.
|
|
130
|
+
const SERVER_VERSION = "1.8.0";
|
|
123
131
|
|
|
124
132
|
// --- Tools Specification ---
|
|
125
133
|
const TOOLS = [
|
|
@@ -140,68 +148,6 @@ const TOOLS = [
|
|
|
140
148
|
}
|
|
141
149
|
}
|
|
142
150
|
},
|
|
143
|
-
{
|
|
144
|
-
name: "subagent_brief",
|
|
145
|
-
description: "Generates scope-isolated, anti-bloat prompts and ready-to-use invoke_subagent JSON payloads for single or multiple delegated subagents. Instructs subagents to absorb intermediate file reads/searches and return only high-signal findings.",
|
|
146
|
-
inputSchema: {
|
|
147
|
-
type: "object",
|
|
148
|
-
properties: {
|
|
149
|
-
objective: {
|
|
150
|
-
type: "string",
|
|
151
|
-
description: "The core research or investigative objective (for single subagent)."
|
|
152
|
-
},
|
|
153
|
-
role: {
|
|
154
|
-
type: "string",
|
|
155
|
-
description: "2-5 word role description (e.g. 'Comprehensive Test Engineer', 'Codebase Security Auditor'). Defaults to 'Scope-Isolated Deep Research Subagent'."
|
|
156
|
-
},
|
|
157
|
-
instructions: {
|
|
158
|
-
type: "string",
|
|
159
|
-
description: "Optional step-by-step instructions or specialized directives."
|
|
160
|
-
},
|
|
161
|
-
scopeFiles: {
|
|
162
|
-
type: "array",
|
|
163
|
-
items: { type: "string" },
|
|
164
|
-
description: "List of files or directories to investigate."
|
|
165
|
-
},
|
|
166
|
-
expectedDeliverable: {
|
|
167
|
-
type: "string",
|
|
168
|
-
description: "The concise format expected back by the parent agent."
|
|
169
|
-
},
|
|
170
|
-
typeName: {
|
|
171
|
-
type: "string",
|
|
172
|
-
description: "Subagent type name: 'self' (default, inherits parent tools/prompt/model) or 'research' (read-only)."
|
|
173
|
-
},
|
|
174
|
-
model: {
|
|
175
|
-
type: "string",
|
|
176
|
-
enum: ["inherit", "flash_lite", "flash", "pro"],
|
|
177
|
-
description: "Model tier: 'inherit' (default), 'flash_lite', 'flash', or 'pro'."
|
|
178
|
-
},
|
|
179
|
-
workspace: {
|
|
180
|
-
type: "string",
|
|
181
|
-
enum: ["inherit", "branch", "share"],
|
|
182
|
-
description: "Workspace isolation: 'inherit' (default), 'branch', or 'share'."
|
|
183
|
-
},
|
|
184
|
-
subagents: {
|
|
185
|
-
type: "array",
|
|
186
|
-
description: "Optional array of subagent configurations to formulate multiple subagents at once.",
|
|
187
|
-
items: {
|
|
188
|
-
type: "object",
|
|
189
|
-
properties: {
|
|
190
|
-
role: { type: "string", description: "2-5 word role description." },
|
|
191
|
-
objective: { type: "string", description: "The core objective for this subagent." },
|
|
192
|
-
instructions: { type: "string", description: "Optional step-by-step instructions." },
|
|
193
|
-
scopeFiles: { type: "array", items: { type: "string" }, description: "Files/directories to investigate." },
|
|
194
|
-
expectedDeliverable: { type: "string", description: "Expected deliverable format." },
|
|
195
|
-
typeName: { type: "string", description: "Subagent type name: 'self' or 'research'." },
|
|
196
|
-
model: { type: "string", enum: ["inherit", "flash_lite", "flash", "pro"], description: "Model tier." },
|
|
197
|
-
workspace: { type: "string", enum: ["inherit", "branch", "share"], description: "Workspace isolation." }
|
|
198
|
-
},
|
|
199
|
-
required: ["objective"]
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
151
|
{
|
|
206
152
|
name: "get_installation_status",
|
|
207
153
|
description: "Inspect the live installation status of Agy-Context-Saver across all Antigravity integration points (Native Plugin Link, Governor Hook in hooks.json, MCP Server in mcp_config.json, and Tool Schemas).",
|
|
@@ -323,7 +269,7 @@ const RESOURCES = [
|
|
|
323
269
|
{
|
|
324
270
|
uri: "context-saver://rules/governance",
|
|
325
271
|
name: "Antigravity Execution & Context Governance Rulebook",
|
|
326
|
-
description: "Authoritative 3-Layer rules preventing background polling loops, maximizing synchronous execution, and
|
|
272
|
+
description: "Authoritative 3-Layer rules preventing background polling loops, maximizing synchronous execution, and keeping command output compact.",
|
|
327
273
|
mimeType: "text/markdown"
|
|
328
274
|
}
|
|
329
275
|
];
|
|
@@ -449,7 +395,7 @@ async function handleCheckContextHealth(args = {}) {
|
|
|
449
395
|
reportLines.push("β οΈ Recommendation: Background polling detected! Cease manage_task(status) calls and yield execution to native Reactive Wakeup.");
|
|
450
396
|
}
|
|
451
397
|
if (userTurns >= 35) {
|
|
452
|
-
reportLines.push("π‘ Recommendation: Turn budget approaching threshold.
|
|
398
|
+
reportLines.push("π‘ Recommendation: Turn budget approaching threshold. Summarize progress and continue in a fresh conversation to preserve context.");
|
|
453
399
|
} else {
|
|
454
400
|
reportLines.push("β Context footprint is well-managed.");
|
|
455
401
|
}
|
|
@@ -463,108 +409,10 @@ async function handleCheckContextHealth(args = {}) {
|
|
|
463
409
|
}
|
|
464
410
|
}
|
|
465
411
|
|
|
466
|
-
function formatSingleBrief({
|
|
467
|
-
role = "Scope-Isolated Deep Research Subagent",
|
|
468
|
-
objective = "",
|
|
469
|
-
instructions = "",
|
|
470
|
-
scopeFiles = [],
|
|
471
|
-
expectedDeliverable = "Synthesized factual findings"
|
|
472
|
-
} = {}) {
|
|
473
|
-
const scopeStr = scopeFiles && scopeFiles.length ? `\n\nTarget files:\n${scopeFiles.map(f => `- ${f}`).join("\n")}` : "";
|
|
474
|
-
const instrStr = instructions && instructions.trim() ? `\n\nSPECIFIC INSTRUCTIONS:\n${instructions.trim()}` : "";
|
|
475
|
-
const hasBrainUuidPath = /[/\\]\.gemini[/\\]antigravity[/\\]brain[/\\][0-9a-f-]{36}/i.test(`${objective}\n${instructions}\n${expectedDeliverable}`);
|
|
476
|
-
const brainPathWarning = hasBrainUuidPath
|
|
477
|
-
? `\n\nβ οΈ ARTIFACT DIRECTORY WRITE GUARD: Any 'brain/<uuid>/' path referenced above belongs to another conversation. You may READ from it via 'view_file', but when calling 'write_to_file' or 'replace_file_content', you MUST either write to workspace '<workspace>/scratch/...' (OMIT ArtifactMetadata) or substitute YOUR OWN Artifact Directory Path from <user_information> (INCLUDE ArtifactMetadata).`
|
|
478
|
-
: "";
|
|
479
|
-
return [
|
|
480
|
-
`ROLE: ${role}`,
|
|
481
|
-
`OBJECTIVE: ${objective}${scopeStr}${instrStr}${brainPathWarning}`,
|
|
482
|
-
``,
|
|
483
|
-
`CRITICAL CONTEXT GOVERNANCE INSTRUCTIONS (POINTER-OVER-WIRE):`,
|
|
484
|
-
`1. Isolated Workspace & Bounded File Slices: Do all multi-file exploration in this subagent sandbox. Prefer 'view_file' with bounded slices (StartLine and EndLine, max 150 lines) over 'rtk read' so line numbers are preserved and terminal buffers never truncate file headers.`,
|
|
485
|
-
`2. Pointer-Over-Wire (POW) Protocol: In-band messaging channels (send_message) have strict transport payload limits (~16-20 KB). If your findings or deliverable exceed 1 KB:`,
|
|
486
|
-
` a. Write the full comprehensive deliverable using 'write_to_file' following Antigravity's strict path rules:`,
|
|
487
|
-
` - Option A (Workspace 'scratch/' β Recommended): Set TargetFile to '<workspace>/scratch/<task>_findings.md' and OMIT ArtifactMetadata completely (passing ArtifactMetadata on a workspace path throws "files must be written to the correct artifact directory").`,
|
|
488
|
-
` - Option B (Your Own Brain 'scratch/'): Set TargetFile to '<your-Artifact-Directory-Path>/scratch/<task>_findings.md' using the exact Artifact Directory Path from YOUR <user_information> block (NEVER a parent's brain/<other-uuid>/ path) and INCLUDE ArtifactMetadata: { Summary: "...", UserFacing: false, RequestFeedback: false }.`,
|
|
489
|
-
` b. In 'send_message', transmit ONLY:`,
|
|
490
|
-
` - Executive Summary (3-5 high-signal sentences)`,
|
|
491
|
-
` - Verdict / Scoreboard Table`,
|
|
492
|
-
` - Clickable File Link: [Full Deliverable](file:///absolute/path/to/findings.md)`,
|
|
493
|
-
`3. Fast Turn Termination: Once 'send_message' is dispatched, stop calling tools and conclude your turn immediately so the parent agent wakes up without latency.`,
|
|
494
|
-
`4. Format: ${expectedDeliverable}`,
|
|
495
|
-
`5. Citation Mandate: Keep your final response concise, structured, and cite exact file:line references.`
|
|
496
|
-
].join("\n");
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
function handleSubagentBrief(args = {}) {
|
|
500
|
-
let specs = [];
|
|
501
|
-
if (Array.isArray(args.subagents) && args.subagents.length > 0) {
|
|
502
|
-
specs = args.subagents;
|
|
503
|
-
} else if (args.objective || args.role || args.instructions) {
|
|
504
|
-
specs = [args];
|
|
505
|
-
} else {
|
|
506
|
-
return {
|
|
507
|
-
isError: true,
|
|
508
|
-
content: [{ type: "text", text: "subagent_brief error: provide either 'objective' (for a single subagent) or 'subagents' array (for multiple subagents)." }]
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
const subagentsPayload = [];
|
|
513
|
-
const renderedBriefs = [];
|
|
514
|
-
|
|
515
|
-
for (let i = 0; i < specs.length; i++) {
|
|
516
|
-
const spec = specs[i] || {};
|
|
517
|
-
const role = spec.role || spec.Role || "Scope-Isolated Deep Research Subagent";
|
|
518
|
-
const objective = spec.objective || spec.Objective || "";
|
|
519
|
-
const instructions = spec.instructions || spec.Instructions || "";
|
|
520
|
-
const scopeFiles = spec.scopeFiles || spec.ScopeFiles || [];
|
|
521
|
-
const expectedDeliverable = spec.expectedDeliverable || spec.ExpectedDeliverable || "Synthesized factual findings";
|
|
522
|
-
const typeName = spec.typeName || spec.TypeName || "self";
|
|
523
|
-
const model = spec.model || spec.Model || "inherit";
|
|
524
|
-
const workspace = spec.workspace || spec.Workspace || "inherit";
|
|
525
|
-
|
|
526
|
-
const promptText = formatSingleBrief({
|
|
527
|
-
role,
|
|
528
|
-
objective,
|
|
529
|
-
instructions,
|
|
530
|
-
scopeFiles,
|
|
531
|
-
expectedDeliverable
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
subagentsPayload.push({
|
|
535
|
-
TypeName: typeName,
|
|
536
|
-
Role: role,
|
|
537
|
-
Model: model,
|
|
538
|
-
Workspace: workspace,
|
|
539
|
-
Prompt: promptText
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
if (specs.length === 1) {
|
|
543
|
-
renderedBriefs.push(promptText);
|
|
544
|
-
} else {
|
|
545
|
-
renderedBriefs.push(`### Subagent ${i + 1}: ${role}\n${promptText}`);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
const payloadJson = JSON.stringify({ Subagents: subagentsPayload }, null, 2);
|
|
550
|
-
|
|
551
|
-
const fullText = [
|
|
552
|
-
renderedBriefs.join("\n\n---\n\n"),
|
|
553
|
-
"",
|
|
554
|
-
"==================================================",
|
|
555
|
-
"READY-TO-USE invoke_subagent PAYLOAD:",
|
|
556
|
-
"```json",
|
|
557
|
-
payloadJson,
|
|
558
|
-
"```"
|
|
559
|
-
].join("\n");
|
|
560
|
-
|
|
561
|
-
return { content: [{ type: "text", text: fullText }] };
|
|
562
|
-
}
|
|
563
|
-
|
|
564
412
|
function handleGetInstallationStatus() {
|
|
565
413
|
const existing = detectExistingInstallation();
|
|
566
414
|
const rtkInfo = findExistingRtk();
|
|
567
|
-
const schemaText = existing.details.schemas.exists ? "ALL
|
|
415
|
+
const schemaText = existing.details.schemas.exists ? "ALL 5 SCHEMAS PRESENT" : "MISSING";
|
|
568
416
|
const report = [
|
|
569
417
|
`### Agy-Context-Saver Installation Status: ${existing.isComplete ? "HEALTHY & ACTIVE π‘οΈ" : existing.isInstalled ? "PARTIAL INSTALLATION β οΈ" : "NOT INSTALLED β"}`,
|
|
570
418
|
`- Native Plugin Link: ${existing.details.plugin.exists ? `ACTIVE (${existing.details.plugin.target})` : "NOT LINKED"}`,
|
|
@@ -594,7 +442,7 @@ async function handleSyncInstallation({ checkOnly = false } = {}) {
|
|
|
594
442
|
`- Plugin Link: ${result.details?.plugin?.exists ? "Verified" : "Updated"}`,
|
|
595
443
|
`- Lifecycle Hook: Registered in hooks.json`,
|
|
596
444
|
`- MCP Server: Registered in mcp_config.json`,
|
|
597
|
-
`- Tool Schemas:
|
|
445
|
+
`- Tool Schemas: 5 Schemas mirrored to ~/.gemini/antigravity/mcp/agy-context-saver`,
|
|
598
446
|
`- RTK Binary: ${rtkInfo ? `Verified (${rtkInfo.version})` : "Provisioned"}`,
|
|
599
447
|
"",
|
|
600
448
|
checkOnly ? "β Pre-flight check complete (dry-run)." : "β Installation fully synchronized and up-to-date in Zero-Delay mode."
|
|
@@ -740,6 +588,11 @@ function cleanMessageContent(content, maxChars = MAX_TURN_CHARS) {
|
|
|
740
588
|
text = text.replace(/[A-Za-z0-9+/=]{200,}/g, "[Binary/Base64 Payload Omitted]");
|
|
741
589
|
}
|
|
742
590
|
|
|
591
|
+
// Strip Git CRLF/LF line-ending diagnostic warning noise (wastes context window tokens)
|
|
592
|
+
if (text.includes("will be replaced by")) {
|
|
593
|
+
text = text.replace(/(?:#\s*)?warning:\s*(?:in the working copy of [^,\r\n]+,\s*)?(?:CRLF will be replaced by LF|LF will be replaced by CRLF)[^\r\n]*(?:\r?\n)?/gi, "");
|
|
594
|
+
}
|
|
595
|
+
|
|
743
596
|
// Clamp lines exceeding MAX_LINE_CHARS only if newline exists and text is long enough
|
|
744
597
|
if (text.length > MAX_LINE_CHARS && text.includes("\n")) {
|
|
745
598
|
const lines = text.split("\n");
|
|
@@ -1325,8 +1178,6 @@ async function handleRequest(request) {
|
|
|
1325
1178
|
|
|
1326
1179
|
if (name === "check_context_health") {
|
|
1327
1180
|
toolResult = await handleCheckContextHealth(args);
|
|
1328
|
-
} else if (name === "subagent_brief") {
|
|
1329
|
-
toolResult = handleSubagentBrief(args);
|
|
1330
1181
|
} else if (name === "get_installation_status") {
|
|
1331
1182
|
toolResult = handleGetInstallationStatus();
|
|
1332
1183
|
} else if (name === "sync_installation") {
|
|
@@ -1414,33 +1265,62 @@ async function handleRequest(request) {
|
|
|
1414
1265
|
};
|
|
1415
1266
|
}
|
|
1416
1267
|
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
const
|
|
1268
|
+
if (isEntryPoint) {
|
|
1269
|
+
// Stdio JSON-RPC line loop
|
|
1270
|
+
const MAX_STDIO_PAYLOAD_BYTES = Number(process.env.AGY_MAX_STDIO_PAYLOAD_BYTES) || (10 * 1024 * 1024); // 10 MB ceiling default
|
|
1271
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false });
|
|
1420
1272
|
|
|
1421
|
-
rl.on("line", async (line) => {
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
}
|
|
1432
|
-
try {
|
|
1433
|
-
const req = JSON.parse(line);
|
|
1434
|
-
const resp = await handleRequest(req);
|
|
1435
|
-
if (resp) {
|
|
1436
|
-
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
1273
|
+
rl.on("line", async (line) => {
|
|
1274
|
+
if (!line || !line.trim()) return;
|
|
1275
|
+
if (line.length > MAX_STDIO_PAYLOAD_BYTES) {
|
|
1276
|
+
const errResp = {
|
|
1277
|
+
jsonrpc: "2.0",
|
|
1278
|
+
id: null,
|
|
1279
|
+
error: { code: -32600, message: "Invalid Request: Payload exceeds 10MB safety ceiling" }
|
|
1280
|
+
};
|
|
1281
|
+
process.stdout.write(JSON.stringify(errResp) + "\n");
|
|
1282
|
+
return;
|
|
1437
1283
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1284
|
+
try {
|
|
1285
|
+
const req = JSON.parse(line);
|
|
1286
|
+
const resp = await handleRequest(req);
|
|
1287
|
+
if (resp) {
|
|
1288
|
+
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
1289
|
+
}
|
|
1290
|
+
} catch (err) {
|
|
1291
|
+
const errResp = {
|
|
1292
|
+
jsonrpc: "2.0",
|
|
1293
|
+
id: null,
|
|
1294
|
+
error: { code: -32700, message: `Parse error: ${err.message}` }
|
|
1295
|
+
};
|
|
1296
|
+
process.stdout.write(JSON.stringify(errResp) + "\n");
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
// Anti-Orphan Lifecycle Governance: Prevent lingering Node processes when parent IDE closes
|
|
1301
|
+
rl.on("close", () => process.exit(0));
|
|
1302
|
+
process.stdin.on("end", () => process.exit(0));
|
|
1303
|
+
process.stdin.on("close", () => process.exit(0));
|
|
1304
|
+
process.stdout.on("error", (err) => {
|
|
1305
|
+
if (err && err.code === "EPIPE") {
|
|
1306
|
+
process.exit(0);
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1309
|
+
|
|
1310
|
+
// Lightweight parent-process watchdog: If parent IDE process terminates, exit child
|
|
1311
|
+
const initialPpid = process.ppid;
|
|
1312
|
+
if (initialPpid && initialPpid > 1) {
|
|
1313
|
+
const watchdog = setInterval(() => {
|
|
1314
|
+
try {
|
|
1315
|
+
process.kill(initialPpid, 0);
|
|
1316
|
+
} catch (err) {
|
|
1317
|
+
if (err && err.code === "ESRCH") {
|
|
1318
|
+
process.exit(0);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}, 15000);
|
|
1322
|
+
watchdog.unref();
|
|
1445
1323
|
}
|
|
1446
|
-
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
export { handleRequest };
|
package/mcp/instructions.md
CHANGED
|
@@ -18,7 +18,6 @@ Agy-Context-Saver is the universal Model Context Protocol server and lifecycle g
|
|
|
18
18
|
|
|
19
19
|
## Available MCP Tools
|
|
20
20
|
- `check_context_health`: Diagnoses transcript turn counts, byte payload, and polling loops. Accepts `transcriptPath` or `conversationId`.
|
|
21
|
-
- `subagent_brief`: Generates scope-isolated instructions for delegated subagents to prevent parent context bloat.
|
|
22
21
|
- `read_transcript`: Streams recent turns from `transcript.jsonl` or `transcript_full.jsonl` formatted as clean Markdown.
|
|
23
22
|
- `query_transcript`: Forensic regex search and filtering engine across transcript steps with optional `summaryOnly: true`.
|
|
24
23
|
- `get_installation_status`: Audits live health of the plugin link, hook, MCP server, tool schemas, and RTK.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agy-context-saver",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Universal Model Context Protocol (MCP) server & lifecycle governor purpose-built for Google Antigravity",
|
|
5
5
|
"main": "mcp/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"uninstall": "node scripts/install-register.mjs --uninstall",
|
|
14
14
|
"status": "node scripts/install-register.mjs --status",
|
|
15
15
|
"preflight": "node scripts/release-preflight.mjs",
|
|
16
|
-
"test
|
|
17
|
-
"test": "node tests/test-hook.mjs && node tests/test-mcp.mjs && node tests/test-output-governance.mjs && node tests/test-e2e-probes.mjs && node tests/test-performance-probes.mjs && node tests/test-installed-verification.mjs && node tests/test-lifecycle-rollback.mjs && node tests/test-subagent-trials.mjs && node tests/test-check-only-readonly.mjs && node tests/test-malformed-config-preservation.mjs && node tests/test-waymark-synergy.mjs"
|
|
16
|
+
"test": "node tests/test-hook.mjs && node tests/test-mcp.mjs && node tests/test-output-governance.mjs && node tests/test-e2e-probes.mjs && node tests/test-performance-probes.mjs && node tests/test-installed-verification.mjs && node tests/test-lifecycle-rollback.mjs && node tests/test-check-only-readonly.mjs && node tests/test-malformed-config-preservation.mjs && node tests/test-rtk-ledger-fresh-install.mjs"
|
|
18
17
|
},
|
|
19
18
|
"repository": {
|
|
20
19
|
"type": "git",
|
package/plugin.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agy-context-saver",
|
|
3
3
|
"displayName": "Antigravity Context Shield & Governor",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "Deterministic background polling governor, RTK terminal optimizer, and
|
|
4
|
+
"version": "1.8.0",
|
|
5
|
+
"description": "Deterministic background polling governor, RTK terminal optimizer, and transcript context shield for Antigravity.",
|
|
6
6
|
"logo": "assets/logo.svg",
|
|
7
7
|
"suggestedPrompts": [
|
|
8
8
|
"Audit context health and token consumption for this session",
|
|
9
|
-
"
|
|
9
|
+
"Search this conversation's transcript for earlier tool errors",
|
|
10
10
|
"Inspect recent conversation turns without raw JSON transcript bloat"
|
|
11
11
|
]
|
|
12
12
|
}
|
package/rules/AGENTS.md
CHANGED
|
@@ -45,19 +45,16 @@
|
|
|
45
45
|
- `rtk summary <cmd>`: Run command and produce a 2-line heuristic summary.
|
|
46
46
|
- **Antigravity Special Files Safe Harbor**: Documented Antigravity special files (`SKILL.md`, brain artifacts, `.gemini/config/` configs) are permitted via `view_file` without slice bounds.
|
|
47
47
|
- **Protected Internal State**: Direct native inspection of `.system_generated/` (transcripts, task logs, progress files) is strictly forbidden. Use Agy MCP tools `read_transcript` and `query_transcript`.
|
|
48
|
+
- **Git CRLF/LF Warning Hygiene**: Git's line-ending conversion notices (`warning: LF will be replaced by CRLF the next time Git touches it` / `warning: CRLF will be replaced by LF...`) are non-actionable diagnostic noise. Suppress them during repo operations (via `git -c core.safecrlf=false` or `git config --global core.safecrlf false`) and do not propagate them into subagent briefs, task outputs, or conversation context.
|
|
48
49
|
|
|
49
50
|
---
|
|
50
51
|
|
|
51
|
-
## Layer 3: Synchronous Execution
|
|
52
|
+
## Layer 3: Synchronous Execution
|
|
52
53
|
- **Maximum Synchronous Window**: Non-daemon `run_command` calls are automatically set to `WaitMsBeforeAsync: 10000` to complete synchronously and prevent unnecessary background detachment.
|
|
53
54
|
- **Targeted Test Execution & Opt-In Fail-Fast (LH-11)**:
|
|
54
55
|
- **Targeted Invocations**: In interactive turns, prefer targeting specific test files or directories (e.g. `pytest tests/test_core.py`) rather than running full multi-minute repository sweeps.
|
|
55
56
|
- **Fail-Fast Flags are Opt-In**: Fail-fast flags (`-x`, `--maxfail=1`) are opt-in. Use them when quickly debugging a localized failure, but omit them when assessing the full blast radius across the codebase to avoid the "whack-a-mole" loop.
|
|
56
57
|
- **Coordinated Watchdog Mandatory for Full Sweeps**: When running full multi-minute test sweeps without `-x`, agents **MUST** pair execution with the **Coordinated Watchdog Protocol** (`schedule(DurationSeconds=45, ...)`) and output a **Pre-Yield Status Card**. Never yield silently.
|
|
57
|
-
- **Subagent Delegation for Deep Audits**: When a comprehensive repository-wide test audit is required, delegate the sweep to a subagent (`invoke_subagent`) to keep the main thread responsive.
|
|
58
|
-
- **Subagent Delegation & Pointer-Over-Wire (POW)**:
|
|
59
|
-
- Delegate heavy multi-file exploration, deep research, and repo sweeps to subagents (`invoke_subagent`). Subagents absorb intermediate steps and return high-signal summaries.
|
|
60
|
-
- **POW Protocol & `write_to_file` Isolation**: Payloads or deliverables exceeding 1 KB must be written either to workspace scratch files (`<workspace>/scratch/<task>_findings.md` with `ArtifactMetadata` **omitted**) or to the **subagent's own** `<Artifact Directory Path>/scratch/<task>_findings.md` (with `ArtifactMetadata: { Summary, UserFacing: false, RequestFeedback: false }`). Never instruct a subagent to `write_to_file` into the parent conversation's `brain/<parent-uuid>/` directory, as Antigravity enforces per-conversation `brain/<uuid>` write isolation. Transmit **ONLY** an Executive Summary (3β5 sentences), a Scoreboard / Verdict table, and a clickable file link (`file:///...`) in `send_message` to prevent transport truncation and parent context bloat.
|
|
61
58
|
- **Benchmark / Evaluator Integrity**: Never mutate benchmark definitions, prompt templates, or scoring artifacts during an active evaluation without explicit user confirmation.
|
|
62
59
|
|
|
63
60
|
---
|
|
@@ -77,7 +74,7 @@ When an agent or developer tests this framework, follow this exact sequence with
|
|
|
77
74
|
```bash
|
|
78
75
|
npm test
|
|
79
76
|
```
|
|
80
|
-
*Expected*: All
|
|
77
|
+
*Expected*: All 10 test suites pass (hook unit tests, MCP protocol tests, output governance, E2E probes, performance probes, live system validation, lifecycle rollback, check-only read-only safety, malformed config preservation, RTK ledger fresh install).
|
|
81
78
|
|
|
82
79
|
3. **Verify Individual Layers On-Demand**:
|
|
83
80
|
- Governance Hook: `node tests/test-hook.mjs`
|
|
@@ -87,14 +84,12 @@ When an agent or developer tests this framework, follow this exact sequence with
|
|
|
87
84
|
- Performance Probes: `node tests/test-performance-probes.mjs`
|
|
88
85
|
- Live System Checks: `node tests/test-installed-verification.mjs`
|
|
89
86
|
- Lifecycle & Rollback: `node tests/test-lifecycle-rollback.mjs`
|
|
90
|
-
- Subagent Delegation Trials: `node tests/test-subagent-trials.mjs`
|
|
91
87
|
|
|
92
88
|
4. **Verify MCP Tools in Antigravity**:
|
|
93
89
|
- `get_installation_status`: Inspect live integration layers.
|
|
94
90
|
- `check_context_health`: Pass `{ "transcriptPath": "<path-to-transcript.jsonl>" }`.
|
|
95
91
|
- `read_transcript`: Pass `{ "conversationId": "<uuid>", "lastTurns": 3, "mode": "compact" }`.
|
|
96
92
|
- `query_transcript`: Pass `{ "conversationId": "<uuid>", "query": "<pattern>", "summaryOnly": true }`.
|
|
97
|
-
- `subagent_brief`: Pass `{ "objective": "<goal>", "role": "<role>", "scopeFiles": ["..."] }` or pass multiple via `{ "subagents": [...] }` to generate ready-to-use `invoke_subagent` JSON payloads.
|
|
98
93
|
|
|
99
94
|
5. **Operational Anti-Patterns (What NOT to do)**:
|
|
100
95
|
- β **Do NOT run `npm start` or `node mcp/index.js` interactively**: It starts the stdio JSON-RPC daemon and waits indefinitely on stdin.
|
|
@@ -56,12 +56,32 @@ const GOVERNED_TOOLS = new Set([
|
|
|
56
56
|
const STATE_TTL_MS = 3600000; // 1 hour TTL
|
|
57
57
|
const MAX_STATE_ENTRIES = 50;
|
|
58
58
|
|
|
59
|
+
// Per-user state folder, never the shared os.tmpdir(). Keep identical to scripts/state-dir.mjs:
|
|
60
|
+
// this hook is mirrored as a standalone file, so it can't import it.
|
|
61
|
+
function getStateDir() {
|
|
62
|
+
const profileHome = process.env.AGY_TEST_PROFILE_DIR;
|
|
63
|
+
if (os.platform() === "win32") {
|
|
64
|
+
const localAppData = profileHome
|
|
65
|
+
? path.join(profileHome, "AppData", "Local")
|
|
66
|
+
: (process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"));
|
|
67
|
+
return path.join(localAppData, "agy-context-saver");
|
|
68
|
+
}
|
|
69
|
+
const stateHome = profileHome
|
|
70
|
+
? path.join(profileHome, ".local", "state")
|
|
71
|
+
: (process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local", "state"));
|
|
72
|
+
return path.join(stateHome, "agy-context-saver");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function ensureStateDir() {
|
|
76
|
+
try { fs.mkdirSync(getStateDir(), { recursive: true }); } catch {}
|
|
77
|
+
}
|
|
78
|
+
|
|
59
79
|
function getStateFilePath(convId = "global") {
|
|
60
80
|
if (!convId || convId === "global") {
|
|
61
|
-
return path.join(
|
|
81
|
+
return path.join(getStateDir(), "agy-session-governance-state.json");
|
|
62
82
|
}
|
|
63
83
|
const safeId = String(convId).replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
64
|
-
return path.join(
|
|
84
|
+
return path.join(getStateDir(), `agy-session-${safeId}.json`);
|
|
65
85
|
}
|
|
66
86
|
|
|
67
87
|
function getSessionState(convId = "global") {
|
|
@@ -79,6 +99,7 @@ function getSessionState(convId = "global") {
|
|
|
79
99
|
|
|
80
100
|
function saveSessionState(convId = "global", entry) {
|
|
81
101
|
const file = getStateFilePath(convId);
|
|
102
|
+
ensureStateDir();
|
|
82
103
|
try {
|
|
83
104
|
const now = Date.now();
|
|
84
105
|
entry.lastPollTime = now;
|
|
@@ -176,6 +197,7 @@ function isProtectedBenchmarkPath(targetPath) {
|
|
|
176
197
|
);
|
|
177
198
|
}
|
|
178
199
|
|
|
200
|
+
|
|
179
201
|
/**
|
|
180
202
|
* Checks if target path references runtime step output spillover file.
|
|
181
203
|
*/
|
|
@@ -355,7 +377,7 @@ const NON_REWRITABLE_BINARIES = new Set([
|
|
|
355
377
|
"export"
|
|
356
378
|
]);
|
|
357
379
|
|
|
358
|
-
const REWRITE_CACHE_FILE = path.join(
|
|
380
|
+
const REWRITE_CACHE_FILE = path.join(getStateDir(), "agy-rtk-rewrite-cache.json");
|
|
359
381
|
const REWRITE_CACHE_MAX = 100;
|
|
360
382
|
let inMemoryRewriteCache = null;
|
|
361
383
|
|
|
@@ -403,6 +425,7 @@ function resolveCompatibleRtk() {
|
|
|
403
425
|
|
|
404
426
|
cache.__envSig = envSig;
|
|
405
427
|
cache.__rtkResolved = resolved;
|
|
428
|
+
ensureStateDir();
|
|
406
429
|
try {
|
|
407
430
|
const tmp = `${REWRITE_CACHE_FILE}.${process.pid}.tmp`;
|
|
408
431
|
fs.writeFileSync(tmp, JSON.stringify(cache), "utf-8");
|
|
@@ -443,6 +466,7 @@ function rememberRewrite(cmd, rewritten) {
|
|
|
443
466
|
for (const k of keys.slice(0, Math.max(0, keys.length - REWRITE_CACHE_MAX))) delete disk[k];
|
|
444
467
|
if (envSig !== undefined) disk.__envSig = envSig;
|
|
445
468
|
if (rtkResolved !== undefined) disk.__rtkResolved = rtkResolved;
|
|
469
|
+
ensureStateDir();
|
|
446
470
|
const tmp = `${REWRITE_CACHE_FILE}.${process.pid}.tmp`;
|
|
447
471
|
fs.writeFileSync(tmp, JSON.stringify(disk), "utf-8");
|
|
448
472
|
try {
|
|
@@ -616,6 +640,7 @@ try {
|
|
|
616
640
|
});
|
|
617
641
|
}
|
|
618
642
|
|
|
643
|
+
|
|
619
644
|
// LH-09-B: Shell command mutation of protected benchmark artifacts
|
|
620
645
|
if (
|
|
621
646
|
(rawCmd.includes(">") || /\b(?:rm|del|Remove-Item)\b/i.test(rawCmd)) &&
|
|
@@ -800,10 +825,10 @@ try {
|
|
|
800
825
|
});
|
|
801
826
|
}
|
|
802
827
|
|
|
803
|
-
// Default when no slice attempted:
|
|
828
|
+
// Default when no slice attempted: guide agent/subagent to use bounded slices or rtk read
|
|
804
829
|
respond({
|
|
805
830
|
decision: "deny",
|
|
806
|
-
reason: `Antigravity Governance:
|
|
831
|
+
reason: `Antigravity Governance: Full-file inspection via '${toolName}' bypasses token optimization. Provide bounded slices ('StartLine' and 'EndLine', max 150 lines) via 'view_file' to view code with line numbers, or Run 'rtk read ${target || "<file>"}' via run_command instead.`
|
|
807
832
|
});
|
|
808
833
|
}
|
|
809
834
|
|
|
@@ -17,6 +17,7 @@ import os from "node:os";
|
|
|
17
17
|
import path from "node:path";
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
19
|
import { ensureRtkInstalled, findExistingRtk } from "./provision-rtk.mjs";
|
|
20
|
+
import { getStateDir, removeLegacyTmpState } from "./state-dir.mjs";
|
|
20
21
|
|
|
21
22
|
const fsPromises = fs.promises;
|
|
22
23
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -115,7 +116,6 @@ export function detectExistingInstallation() {
|
|
|
115
116
|
const scriptExists = fs.existsSync(destHook);
|
|
116
117
|
const schemasExist =
|
|
117
118
|
fs.existsSync(path.join(antigravityMcpDir, "check_context_health.json")) &&
|
|
118
|
-
fs.existsSync(path.join(antigravityMcpDir, "subagent_brief.json")) &&
|
|
119
119
|
fs.existsSync(path.join(antigravityMcpDir, "get_installation_status.json")) &&
|
|
120
120
|
fs.existsSync(path.join(antigravityMcpDir, "sync_installation.json")) &&
|
|
121
121
|
fs.existsSync(path.join(antigravityMcpDir, "read_transcript.json")) &&
|
|
@@ -338,7 +338,10 @@ export async function runInstall(options = {}) {
|
|
|
338
338
|
"safe_command.json",
|
|
339
339
|
"get_spillover_content.json",
|
|
340
340
|
"read_task_output.json",
|
|
341
|
-
"get_step_detail.json"
|
|
341
|
+
"get_step_detail.json",
|
|
342
|
+
// Subagent tools moved to the separate Agy-Squad package (2026-10-15)
|
|
343
|
+
"subagent_brief.json",
|
|
344
|
+
"dispatch_squad.json"
|
|
342
345
|
];
|
|
343
346
|
for (const s of retiredSchemas) {
|
|
344
347
|
const sPath = path.join(antigravityMcpDir, s);
|
|
@@ -350,6 +353,11 @@ export async function runInstall(options = {}) {
|
|
|
350
353
|
}
|
|
351
354
|
}
|
|
352
355
|
|
|
356
|
+
// Versions up to 1.7.0 kept hook state in the shared temp folder; it now lives in getStateDir()
|
|
357
|
+
for (const legacy of removeLegacyTmpState()) {
|
|
358
|
+
log(`[OK] Removed legacy temp-folder state: ${legacy}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
353
361
|
const instructionsContent = `# Agy-Context-Saver MCP Server Instructions
|
|
354
362
|
|
|
355
363
|
## Overview
|
|
@@ -371,7 +379,6 @@ Agy-Context-Saver is the universal Model Context Protocol server and lifecycle g
|
|
|
371
379
|
|
|
372
380
|
## Available MCP Tools
|
|
373
381
|
- \`check_context_health\`: Diagnoses transcript turn counts, byte payload, and polling loops. Accepts \`transcriptPath\` or \`conversationId\`.
|
|
374
|
-
- \`subagent_brief\`: Formulates scope-isolated prompts and ready-to-use invoke_subagent JSON payloads for single or multiple delegated subagents with Pointer-Over-Wire delivery contract.
|
|
375
382
|
- \`read_transcript\`: Streams recent turns from \`transcript.jsonl\` or \`transcript_full.jsonl\` formatted as clean Markdown.
|
|
376
383
|
- \`query_transcript\`: Forensic regex search and filtering engine across transcript steps with optional \`summaryOnly: true\`, and surgical step extraction via \`stepIndex\` and \`field\`.
|
|
377
384
|
- \`get_installation_status\`: Audits live health of the plugin link, hook, MCP server, tool schemas, and RTK.
|
|
@@ -402,42 +409,6 @@ Agy-Context-Saver is the universal Model Context Protocol server and lifecycle g
|
|
|
402
409
|
}
|
|
403
410
|
};
|
|
404
411
|
|
|
405
|
-
const subagentBriefSchema = {
|
|
406
|
-
name: "subagent_brief",
|
|
407
|
-
description: "Generates scope-isolated, anti-bloat prompts and ready-to-use invoke_subagent JSON payloads for single or multiple delegated subagents. Instructs subagents to absorb intermediate file reads/searches and return only high-signal findings.",
|
|
408
|
-
parameters: {
|
|
409
|
-
type: "object",
|
|
410
|
-
properties: {
|
|
411
|
-
objective: { type: "string", description: "The core research or investigative objective (for single subagent)." },
|
|
412
|
-
role: { type: "string", description: "2-5 word role description (e.g. 'Comprehensive Test Engineer', 'Codebase Security Auditor'). Defaults to 'Scope-Isolated Deep Research Subagent'." },
|
|
413
|
-
instructions: { type: "string", description: "Optional step-by-step instructions or specialized directives." },
|
|
414
|
-
scopeFiles: { type: "array", items: { type: "string" }, description: "List of files or directories to investigate." },
|
|
415
|
-
expectedDeliverable: { type: "string", description: "The concise format expected back by the parent agent." },
|
|
416
|
-
typeName: { type: "string", description: "Subagent type name: 'self' (default, inherits parent tools/prompt/model) or 'research' (read-only)." },
|
|
417
|
-
model: { type: "string", enum: ["inherit", "flash_lite", "flash", "pro"], description: "Model tier: 'inherit' (default), 'flash_lite', 'flash', or 'pro'." },
|
|
418
|
-
workspace: { type: "string", enum: ["inherit", "branch", "share"], description: "Workspace isolation: 'inherit' (default), 'branch', or 'share'." },
|
|
419
|
-
subagents: {
|
|
420
|
-
type: "array",
|
|
421
|
-
description: "Optional array of subagent configurations to formulate multiple subagents at once.",
|
|
422
|
-
items: {
|
|
423
|
-
type: "object",
|
|
424
|
-
properties: {
|
|
425
|
-
role: { type: "string", description: "2-5 word role description." },
|
|
426
|
-
objective: { type: "string", description: "The core objective for this subagent." },
|
|
427
|
-
instructions: { type: "string", description: "Optional step-by-step instructions." },
|
|
428
|
-
scopeFiles: { type: "array", items: { type: "string" }, description: "Files/directories to investigate." },
|
|
429
|
-
expectedDeliverable: { type: "string", description: "Expected deliverable format." },
|
|
430
|
-
typeName: { type: "string", description: "Subagent type name: 'self' or 'research'." },
|
|
431
|
-
model: { type: "string", enum: ["inherit", "flash_lite", "flash", "pro"], description: "Model tier." },
|
|
432
|
-
workspace: { type: "string", enum: ["inherit", "branch", "share"], description: "Workspace isolation." }
|
|
433
|
-
},
|
|
434
|
-
required: ["objective"]
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
|
|
441
412
|
const getInstallationStatusSchema = {
|
|
442
413
|
name: "get_installation_status",
|
|
443
414
|
description: "Inspect the live installation status of Agy-Context-Saver across all Antigravity integration points (Native Plugin Link, Governor Hook, MCP Server, and Tool Schemas).",
|
|
@@ -559,7 +530,6 @@ Agy-Context-Saver is the universal Model Context Protocol server and lifecycle g
|
|
|
559
530
|
await Promise.all([
|
|
560
531
|
safeWriteFileIfChanged(path.join(antigravityMcpDir, "instructions.md"), instructionsContent),
|
|
561
532
|
safeWriteFileIfChanged(path.join(antigravityMcpDir, "check_context_health.json"), JSON.stringify(checkHealthSchema, null, 2)),
|
|
562
|
-
safeWriteFileIfChanged(path.join(antigravityMcpDir, "subagent_brief.json"), JSON.stringify(subagentBriefSchema, null, 2)),
|
|
563
533
|
safeWriteFileIfChanged(path.join(antigravityMcpDir, "get_installation_status.json"), JSON.stringify(getInstallationStatusSchema, null, 2)),
|
|
564
534
|
safeWriteFileIfChanged(path.join(antigravityMcpDir, "sync_installation.json"), JSON.stringify(syncInstallationSchema, null, 2)),
|
|
565
535
|
safeWriteFileIfChanged(path.join(antigravityMcpDir, "read_transcript.json"), JSON.stringify(readTranscriptSchema, null, 2)),
|
|
@@ -631,7 +601,21 @@ export async function runUninstall(options = {}) {
|
|
|
631
601
|
}
|
|
632
602
|
}
|
|
633
603
|
|
|
634
|
-
// 6.
|
|
604
|
+
// 6. Remove hook state: the per-user state folder and anything older versions left in the temp folder
|
|
605
|
+
const stateDir = getStateDir();
|
|
606
|
+
if (fs.existsSync(stateDir)) {
|
|
607
|
+
try {
|
|
608
|
+
await fsPromises.rm(stateDir, { recursive: true, force: true });
|
|
609
|
+
log(`[OK] Removed hook state folder: ${stateDir}`);
|
|
610
|
+
} catch (err) {
|
|
611
|
+
warn(`[WARN] Could not remove hook state folder: ${err.message}`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
for (const legacy of removeLegacyTmpState()) {
|
|
615
|
+
log(`[OK] Removed legacy temp-folder state: ${legacy}`);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// 7. Optional: Restore configuration files from backups if requested
|
|
635
619
|
if (options.restoreBackups) {
|
|
636
620
|
await runRestoreBackups(options);
|
|
637
621
|
}
|
|
@@ -678,7 +662,7 @@ export function runStatus(options = {}) {
|
|
|
678
662
|
log(`- Native Plugin Link: ${existing.details.plugin.exists ? "ACTIVE (" + pluginDest + ")" : "NOT LINKED"}`);
|
|
679
663
|
log(`- Execution Governor Hook: ${existing.details.hook.registered ? "REGISTERED in hooks.json" : "NOT REGISTERED"}`);
|
|
680
664
|
log(`- Universal MCP Server: ${existing.details.mcp.registered ? "CONFIGURED in mcp_config.json" : "NOT CONFIGURED"}`);
|
|
681
|
-
log(`- Antigravity Tool Schemas: ${existing.details.schemas.exists ? "PRESENT (
|
|
665
|
+
log(`- Antigravity Tool Schemas: ${existing.details.schemas.exists ? "PRESENT (5 Schemas)" : "MISSING"}`);
|
|
682
666
|
log(`- RTK Command Optimizer: ${existing.details.rtk ? "INSTALLED (" + existing.details.rtk.version + ")" : "MISSING"}`);
|
|
683
667
|
|
|
684
668
|
log(`\nOverall Status: ${existing.isComplete ? "HEALTHY & ACTIVE π‘οΈ" : existing.isInstalled ? "PARTIAL INSTALLATION" : "NOT INSTALLED"}\n`);
|
|
@@ -92,6 +92,7 @@ export async function ensureRtkInstalled(options = {}) {
|
|
|
92
92
|
const log = options.silent ? () => {} : console.log;
|
|
93
93
|
const existing = options.existingRtk || findExistingRtk(options);
|
|
94
94
|
if (existing) {
|
|
95
|
+
ensureRtkConfigured(options);
|
|
95
96
|
log(`β RTK is already installed and verified: ${existing.version} (${existing.path})`);
|
|
96
97
|
return existing;
|
|
97
98
|
}
|
|
@@ -160,10 +161,75 @@ export async function ensureRtkInstalled(options = {}) {
|
|
|
160
161
|
throw new Error(`RTK installation verification failed: could not run ${targetExe}`);
|
|
161
162
|
}
|
|
162
163
|
|
|
164
|
+
ensureRtkConfigured(options);
|
|
165
|
+
|
|
163
166
|
log(`β Successfully installed and verified RTK: ${verified.version} at ${verified.path}`);
|
|
164
167
|
return verified;
|
|
165
168
|
}
|
|
166
169
|
|
|
170
|
+
export function getRtkConfigPath() {
|
|
171
|
+
const profileHome = process.env.AGY_TEST_PROFILE_DIR;
|
|
172
|
+
if (profileHome) {
|
|
173
|
+
if (os.platform() === "win32") {
|
|
174
|
+
return path.join(profileHome, "AppData", "Roaming", "rtk", "config.toml");
|
|
175
|
+
}
|
|
176
|
+
return path.join(profileHome, ".config", "rtk", "config.toml");
|
|
177
|
+
}
|
|
178
|
+
if (os.platform() === "win32") {
|
|
179
|
+
const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
|
|
180
|
+
return path.join(appData, "rtk", "config.toml");
|
|
181
|
+
}
|
|
182
|
+
const configHome = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
183
|
+
return path.join(configHome, "rtk", "config.toml");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function ensureRtkConfigured(options = {}) {
|
|
187
|
+
const log = options.silent ? () => {} : console.log;
|
|
188
|
+
const configPath = getRtkConfigPath();
|
|
189
|
+
const configDir = path.dirname(configPath);
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
if (!fs.existsSync(configDir)) {
|
|
193
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!fs.existsSync(configPath)) {
|
|
197
|
+
const defaultContent = `[hooks]\nsuppress_hook_warning = true\n`;
|
|
198
|
+
fs.writeFileSync(configPath, defaultContent, "utf-8");
|
|
199
|
+
log(`β Initialized RTK configuration with suppress_hook_warning = true at ${configPath}`);
|
|
200
|
+
} else {
|
|
201
|
+
let content = fs.readFileSync(configPath, "utf-8");
|
|
202
|
+
if (/suppress_hook_warning\s*=\s*false/i.test(content)) {
|
|
203
|
+
content = content.replace(/suppress_hook_warning\s*=\s*false/i, "suppress_hook_warning = true");
|
|
204
|
+
fs.writeFileSync(configPath, content, "utf-8");
|
|
205
|
+
log(`β Updated RTK configuration to suppress_hook_warning = true in ${configPath}`);
|
|
206
|
+
} else if (!/suppress_hook_warning/i.test(content)) {
|
|
207
|
+
if (/\[hooks\]/i.test(content)) {
|
|
208
|
+
content = content.replace(/\[hooks\]/i, "[hooks]\nsuppress_hook_warning = true");
|
|
209
|
+
} else {
|
|
210
|
+
content = (content.trim() + "\n\n[hooks]\nsuppress_hook_warning = true\n").trimStart();
|
|
211
|
+
}
|
|
212
|
+
fs.writeFileSync(configPath, content, "utf-8");
|
|
213
|
+
log(`β Configured suppress_hook_warning = true in ${configPath}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} catch (err) {
|
|
217
|
+
if (!options.silent) {
|
|
218
|
+
console.warn(`[WARN] Could not standardize RTK config: ${err.message}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
ensureGitLineEndingConfigured(options);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function ensureGitLineEndingConfigured(options = {}) {
|
|
226
|
+
const log = options.silent ? () => {} : console.log;
|
|
227
|
+
try {
|
|
228
|
+
execSync("git config --global core.safecrlf false", { windowsHide: true, stdio: "ignore" });
|
|
229
|
+
log("β Standardized git core.safecrlf = false to suppress line-ending warning noise");
|
|
230
|
+
} catch {}
|
|
231
|
+
}
|
|
232
|
+
|
|
167
233
|
// Auto-run if executed directly
|
|
168
234
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
169
235
|
ensureRtkInstalled().catch((err) => {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-user state location for Agy-Context-Saver (governance ledger, RTK rewrite cache).
|
|
3
|
+
*
|
|
4
|
+
* State lives in a folder owned by this user, never in the shared os.tmpdir():
|
|
5
|
+
* Windows: %LOCALAPPDATA%\agy-context-saver
|
|
6
|
+
* POSIX: $XDG_STATE_HOME/agy-context-saver (default ~/.local/state/agy-context-saver)
|
|
7
|
+
* Under AGY_TEST_PROFILE_DIR the same layout is rooted in the test profile.
|
|
8
|
+
*
|
|
9
|
+
* The hook is mirrored into ~/.gemini/config/scripts as a standalone file, so it keeps an
|
|
10
|
+
* identical inline copy of getStateDir(). tests/test-hook.mjs checks that the two agree.
|
|
11
|
+
* No side effects on import.
|
|
12
|
+
*/
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import os from "node:os";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
|
|
17
|
+
export function getStateDir() {
|
|
18
|
+
const profileHome = process.env.AGY_TEST_PROFILE_DIR;
|
|
19
|
+
if (os.platform() === "win32") {
|
|
20
|
+
const localAppData = profileHome
|
|
21
|
+
? path.join(profileHome, "AppData", "Local")
|
|
22
|
+
: (process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"));
|
|
23
|
+
return path.join(localAppData, "agy-context-saver");
|
|
24
|
+
}
|
|
25
|
+
const stateHome = profileHome
|
|
26
|
+
? path.join(profileHome, ".local", "state")
|
|
27
|
+
: (process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local", "state"));
|
|
28
|
+
return path.join(stateHome, "agy-context-saver");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Files that versions up to 1.7.0 left in the shared temp folder, plus their atomic-write temps.
|
|
32
|
+
const LEGACY_TMP_STATE = /^(agy-session-.+\.json|agy-rtk-rewrite-cache\.json)(\..+\.tmp)?$/;
|
|
33
|
+
|
|
34
|
+
/** Removes state files that older versions wrote to os.tmpdir(). Returns the paths removed. */
|
|
35
|
+
export function removeLegacyTmpState() {
|
|
36
|
+
const removed = [];
|
|
37
|
+
const tmp = os.tmpdir();
|
|
38
|
+
let names = [];
|
|
39
|
+
try { names = fs.readdirSync(tmp); } catch { return removed; }
|
|
40
|
+
for (const name of names) {
|
|
41
|
+
if (!LEGACY_TMP_STATE.test(name)) continue;
|
|
42
|
+
const p = path.join(tmp, name);
|
|
43
|
+
try {
|
|
44
|
+
if (fs.lstatSync(p).isFile()) {
|
|
45
|
+
fs.unlinkSync(p);
|
|
46
|
+
removed.push(p);
|
|
47
|
+
}
|
|
48
|
+
} catch {}
|
|
49
|
+
}
|
|
50
|
+
return removed;
|
|
51
|
+
}
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
name: context-governance
|
|
3
3
|
description: >-
|
|
4
4
|
Use when running shell commands with large outputs, inspecting session transcript health,
|
|
5
|
-
preventing repetitive background polling loops, inspecting codebases with RTK
|
|
5
|
+
preventing repetitive background polling loops, or inspecting codebases with RTK.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Context Governance & Execution Guard (Agy-Context-Saver + RTK)
|
|
9
9
|
|
|
10
|
-
Agy-Context-Saver and RTK protect session transcript health and LLM attention by transparently compressing command outputs, preventing repetitive background polling loops, routing native inspection to compact CLI interfaces
|
|
10
|
+
Agy-Context-Saver and RTK protect session transcript health and LLM attention by transparently compressing command outputs, preventing repetitive background polling loops, and routing native inspection to compact CLI interfaces.
|
|
11
11
|
|
|
12
12
|
## Architectural Boundaries
|
|
13
13
|
|
|
@@ -42,23 +42,14 @@ Performs streaming inspection of `transcript.jsonl` to calculate turn budgets, s
|
|
|
42
42
|
- **Parameters**:
|
|
43
43
|
- `transcriptPath` (string, required): Absolute path to `transcript.jsonl`.
|
|
44
44
|
|
|
45
|
-
### 2. `
|
|
46
|
-
Generates a strictly formatted, scope-isolated prompt for delegating research or debugging to a subagent (`invoke_subagent`). Enforces the **Pointer-Over-Wire (POW)** protocol: deliverables exceeding 1 KB must be written to workspace scratch files (e.g. `scratch/<task>_findings.md`), transmitting only an executive summary, scoreboard, and clickable `file:///` link in-band.
|
|
47
|
-
- **Parameters**:
|
|
48
|
-
- `objective` (string, required): Core investigative or implementation goal.
|
|
49
|
-
- `role` (string, optional): Subagent role / title.
|
|
50
|
-
- `scopeFiles` (array of strings, optional): Specific file targets.
|
|
51
|
-
- `expectedDeliverable` (string, optional): Expected concise synthesis format.
|
|
52
|
-
- `subagents` (array, optional): Batch of subagent objects for multi-role delegation.
|
|
53
|
-
|
|
54
|
-
### 3. `read_transcript`
|
|
45
|
+
### 2. `read_transcript`
|
|
55
46
|
Quickly reads recent conversation turns from an Antigravity transcript in clean Markdown format with zero JSON noise.
|
|
56
47
|
- **Parameters**:
|
|
57
48
|
- `conversationId` (string, required): Conversation UUID or path.
|
|
58
49
|
- `mode` (string, default: "compact"): "compact" or "full".
|
|
59
50
|
- `lastTurns` (number, default: 3): Number of turns to display.
|
|
60
51
|
|
|
61
|
-
###
|
|
52
|
+
### 3. `query_transcript`
|
|
62
53
|
Granular query and forensic filtering engine for transcripts with keyword/regex search, auto-dereferencing of truncated payloads, and surgical step projection.
|
|
63
54
|
- **Parameters**:
|
|
64
55
|
- `conversationId` (string, required): Conversation UUID or path.
|
|
@@ -67,10 +58,10 @@ Granular query and forensic filtering engine for transcripts with keyword/regex
|
|
|
67
58
|
- `stepIndex` (number, optional): Specific step to dereference from `transcript_full.jsonl`.
|
|
68
59
|
- `field` (string, optional): Field projection (`"content"`, `"thinking"`, `"tool_calls"`).
|
|
69
60
|
|
|
70
|
-
###
|
|
61
|
+
### 4. `get_installation_status`
|
|
71
62
|
Audits the Antigravity integration layers (Native Plugin Link, Lifecycle Hook in `hooks.json`, MCP Server in `mcp_config.json`, Tool Schemas, and RTK binary/hook).
|
|
72
63
|
|
|
73
|
-
###
|
|
64
|
+
### 5. `sync_installation`
|
|
74
65
|
Re-verifies, repairs, and synchronizes the installation and tool schemas in ~25ms.
|
|
75
66
|
- **Parameters**:
|
|
76
67
|
- `checkOnly` (boolean, default: false): Dry-run audit mode.
|
|
@@ -90,11 +81,10 @@ Re-verifies, repairs, and synchronizes the installation and tool schemas in ~25m
|
|
|
90
81
|
- Shell commands transparently rewrite via RTK (`rtk git`, `rtk pytest`, `rtk cargo`, etc.).
|
|
91
82
|
- Codebase inspection is hard-routed to `rtk read`, `rtk find`, and `rtk rg "<pattern>" .`.
|
|
92
83
|
- Direct access to `.system_generated/` is strictly denied; use `read_transcript` and `query_transcript`.
|
|
84
|
+
- **Log Hygiene (Git CRLF/LF Notice Stripping)**: Repetitive Git line-ending notices (`warning: LF will be replaced by CRLF` / `warning: CRLF will be replaced by LF...`) are non-actionable environment noise. Agy-Context-Saver automatically strips these warning lines across forensic transcript parsers, and agents suppress them at the source (`git -c core.safecrlf=false`) to protect context window tokens.
|
|
93
85
|
|
|
94
|
-
3. **Layer 3:
|
|
86
|
+
3. **Layer 3: Synchronous Execution**:
|
|
95
87
|
- Synchronous wait window expands to 10,000ms to eliminate unnecessary detachment.
|
|
96
|
-
- Heavy exploratory work is delegated to subagents (`invoke_subagent`).
|
|
97
|
-
- Subagents strictly follow Pointer-Over-Wire: write reports $>1\text{ KB}$ to disk; transmit only summary, scoreboard, and clickable file link.
|
|
98
88
|
|
|
99
89
|
---
|
|
100
90
|
|
|
@@ -102,5 +92,5 @@ Re-verifies, repairs, and synchronizes the installation and tool schemas in ~25m
|
|
|
102
92
|
|
|
103
93
|
To verify installation health and run test validation:
|
|
104
94
|
- **Health Check**: `npm run status` (verifies plugin link, hook, MCP config, schemas, RTK).
|
|
105
|
-
- **Test Suite**: `npm test` (runs all
|
|
95
|
+
- **Test Suite**: `npm test` (runs all 10 test suites).
|
|
106
96
|
- Detailed verification playbooks and release steps reside in `docs/publishing.md`.
|