agy-context-saver 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +33 -34
- package/mcp/index.js +135 -249
- package/mcp/instructions.md +8 -6
- package/package.json +2 -3
- package/plugin.json +3 -3
- package/rules/AGENTS.md +16 -20
- package/scripts/execution-guard-hook.mjs +104 -14
- package/scripts/install-register.mjs +30 -76
- package/scripts/provision-rtk.mjs +66 -0
- package/scripts/state-dir.mjs +51 -0
- package/skills/context-governance/SKILL.md +26 -32
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.1)
|
|
76
84
|
Universal MCP Server & Lifecycle Governor for Google Antigravity (Powered by RTK)
|
|
77
85
|
|
|
78
86
|
Usage:
|
|
@@ -99,19 +107,17 @@ MCP Server Stdio Mode:
|
|
|
99
107
|
|
|
100
108
|
Testing & Verification Playbook:
|
|
101
109
|
npm run status Verify live 4-layer integration status
|
|
102
|
-
npm test Run complete
|
|
103
|
-
node tests/test-hook.mjs Run
|
|
110
|
+
npm test Run complete 10-suite automated test suite
|
|
111
|
+
node tests/test-hook.mjs Run 27 closed-topology governance rule tests
|
|
104
112
|
node tests/test-mcp.mjs Run MCP protocol, method, and schema tests
|
|
105
113
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
-
|
|
111
|
-
|
|
112
|
-
- Search:
|
|
113
|
-
- Read: rtk read <file>
|
|
114
|
-
- Directory: rtk ls <path> or rtk tree
|
|
114
|
+
Unified 3-Tier Inspection Directives (Waymark + Hybrid view_file + RTK):
|
|
115
|
+
Tier 1 (Structure): waymark_ask(..., plain: true) across all 23 supported formats
|
|
116
|
+
Tier 2 (Read/Edit): view_file(StartLine, EndLine) (1-150L default; or explicit 1..TotalLines full-file audit <= 800L & <= 45 KiB)
|
|
117
|
+
Tier 3 (RTK CLI):
|
|
118
|
+
- Spillovers/Logs: rtk read <path> (--head-lines / --tail-lines)
|
|
119
|
+
- Directory: rtk find <path> (Windows & POSIX) or rtk ls <path> (POSIX)
|
|
120
|
+
- Prose Search: rtk rg "<pattern>" . (Windows trailing dot required) or rtk grep "<pattern>" (POSIX)
|
|
115
121
|
`);
|
|
116
122
|
}
|
|
117
123
|
|
|
@@ -119,7 +125,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
119
125
|
const rulesPath = path.resolve(__dirname, "../rules/AGENTS.md");
|
|
120
126
|
|
|
121
127
|
const SERVER_NAME = "agy-context-saver";
|
|
122
|
-
const SERVER_VERSION = "1.
|
|
128
|
+
const SERVER_VERSION = "1.8.1";
|
|
123
129
|
|
|
124
130
|
// --- Tools Specification ---
|
|
125
131
|
const TOOLS = [
|
|
@@ -140,68 +146,6 @@ const TOOLS = [
|
|
|
140
146
|
}
|
|
141
147
|
}
|
|
142
148
|
},
|
|
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
149
|
{
|
|
206
150
|
name: "get_installation_status",
|
|
207
151
|
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).",
|
|
@@ -254,7 +198,7 @@ const TOOLS = [
|
|
|
254
198
|
},
|
|
255
199
|
{
|
|
256
200
|
name: "query_transcript",
|
|
257
|
-
description: "Granular query and forensic filtering engine for Antigravity conversation transcripts. Searches by keyword or regex, filters by role (user, assistant, tool, error), slices step ranges, and automatically dereferences full content when truncated.",
|
|
201
|
+
description: "Granular query and forensic filtering engine for Antigravity conversation transcripts. Searches by keyword or regex, filters by role (user, assistant, subagent, tool, error), slices step ranges, and automatically dereferences full content when truncated.",
|
|
258
202
|
inputSchema: {
|
|
259
203
|
type: "object",
|
|
260
204
|
properties: {
|
|
@@ -269,7 +213,7 @@ const TOOLS = [
|
|
|
269
213
|
roles: {
|
|
270
214
|
type: "array",
|
|
271
215
|
items: { type: "string" },
|
|
272
|
-
description: "Filter steps by role/source: 'user', 'assistant', 'tool', 'error', or 'all' (default: ['user', 'assistant'])."
|
|
216
|
+
description: "Filter steps by role/source: 'user', 'assistant', 'subagent', 'tool', 'error', or 'all' (default: ['user', 'assistant'])."
|
|
273
217
|
},
|
|
274
218
|
mode: {
|
|
275
219
|
type: "string",
|
|
@@ -323,7 +267,7 @@ const RESOURCES = [
|
|
|
323
267
|
{
|
|
324
268
|
uri: "context-saver://rules/governance",
|
|
325
269
|
name: "Antigravity Execution & Context Governance Rulebook",
|
|
326
|
-
description: "Authoritative 3-Layer rules preventing background polling loops, maximizing synchronous execution, and
|
|
270
|
+
description: "Authoritative 3-Layer rules preventing background polling loops, maximizing synchronous execution, and keeping command output compact.",
|
|
327
271
|
mimeType: "text/markdown"
|
|
328
272
|
}
|
|
329
273
|
];
|
|
@@ -449,7 +393,7 @@ async function handleCheckContextHealth(args = {}) {
|
|
|
449
393
|
reportLines.push("⚠️ Recommendation: Background polling detected! Cease manage_task(status) calls and yield execution to native Reactive Wakeup.");
|
|
450
394
|
}
|
|
451
395
|
if (userTurns >= 35) {
|
|
452
|
-
reportLines.push("💡 Recommendation: Turn budget approaching threshold.
|
|
396
|
+
reportLines.push("💡 Recommendation: Turn budget approaching threshold. Summarize progress and continue in a fresh conversation to preserve context.");
|
|
453
397
|
} else {
|
|
454
398
|
reportLines.push("✓ Context footprint is well-managed.");
|
|
455
399
|
}
|
|
@@ -463,108 +407,10 @@ async function handleCheckContextHealth(args = {}) {
|
|
|
463
407
|
}
|
|
464
408
|
}
|
|
465
409
|
|
|
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
410
|
function handleGetInstallationStatus() {
|
|
565
411
|
const existing = detectExistingInstallation();
|
|
566
412
|
const rtkInfo = findExistingRtk();
|
|
567
|
-
const schemaText = existing.details.schemas.exists ? "ALL
|
|
413
|
+
const schemaText = existing.details.schemas.exists ? "ALL 5 SCHEMAS PRESENT" : "MISSING";
|
|
568
414
|
const report = [
|
|
569
415
|
`### Agy-Context-Saver Installation Status: ${existing.isComplete ? "HEALTHY & ACTIVE 🛡️" : existing.isInstalled ? "PARTIAL INSTALLATION ⚠️" : "NOT INSTALLED ❌"}`,
|
|
570
416
|
`- Native Plugin Link: ${existing.details.plugin.exists ? `ACTIVE (${existing.details.plugin.target})` : "NOT LINKED"}`,
|
|
@@ -594,7 +440,7 @@ async function handleSyncInstallation({ checkOnly = false } = {}) {
|
|
|
594
440
|
`- Plugin Link: ${result.details?.plugin?.exists ? "Verified" : "Updated"}`,
|
|
595
441
|
`- Lifecycle Hook: Registered in hooks.json`,
|
|
596
442
|
`- MCP Server: Registered in mcp_config.json`,
|
|
597
|
-
`- Tool Schemas:
|
|
443
|
+
`- Tool Schemas: 5 Schemas mirrored to ~/.gemini/antigravity/mcp/agy-context-saver`,
|
|
598
444
|
`- RTK Binary: ${rtkInfo ? `Verified (${rtkInfo.version})` : "Provisioned"}`,
|
|
599
445
|
"",
|
|
600
446
|
checkOnly ? "✓ Pre-flight check complete (dry-run)." : "✓ Installation fully synchronized and up-to-date in Zero-Delay mode."
|
|
@@ -740,6 +586,11 @@ function cleanMessageContent(content, maxChars = MAX_TURN_CHARS) {
|
|
|
740
586
|
text = text.replace(/[A-Za-z0-9+/=]{200,}/g, "[Binary/Base64 Payload Omitted]");
|
|
741
587
|
}
|
|
742
588
|
|
|
589
|
+
// Strip Git CRLF/LF line-ending diagnostic warning noise (wastes context window tokens)
|
|
590
|
+
if (text.includes("will be replaced by")) {
|
|
591
|
+
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, "");
|
|
592
|
+
}
|
|
593
|
+
|
|
743
594
|
// Clamp lines exceeding MAX_LINE_CHARS only if newline exists and text is long enough
|
|
744
595
|
if (text.length > MAX_LINE_CHARS && text.includes("\n")) {
|
|
745
596
|
const lines = text.split("\n");
|
|
@@ -958,13 +809,20 @@ async function handleReadTranscript({ conversationId, mode = "compact", lastTurn
|
|
|
958
809
|
const formattedBlocks = [];
|
|
959
810
|
let currentLength = header.length;
|
|
960
811
|
let clamped = false;
|
|
812
|
+
const maxWindowChars = mode === "compact" ? 3800 : MAX_OUTPUT_CHARS;
|
|
961
813
|
|
|
962
814
|
for (let i = selected.length - 1; i >= 0; i--) {
|
|
963
|
-
const
|
|
815
|
+
const item = selected[i];
|
|
816
|
+
const isDialogueTurn =
|
|
817
|
+
item.type === "USER_INPUT" ||
|
|
818
|
+
item.type === "PLANNER_RESPONSE" ||
|
|
819
|
+
item.type === "SUBAGENT_RESPONSE";
|
|
820
|
+
const block = formatTranscriptItem(item, {
|
|
964
821
|
includeThinking,
|
|
965
|
-
includeToolCalls: true
|
|
822
|
+
includeToolCalls: true,
|
|
823
|
+
maxContentChars: mode === "compact" && !isDialogueTurn ? 300 : MAX_TURN_CHARS
|
|
966
824
|
});
|
|
967
|
-
if (currentLength + block.length + 6 >
|
|
825
|
+
if (currentLength + block.length + 6 > maxWindowChars && formattedBlocks.length > 0) {
|
|
968
826
|
clamped = true;
|
|
969
827
|
break;
|
|
970
828
|
}
|
|
@@ -974,7 +832,8 @@ async function handleReadTranscript({ conversationId, mode = "compact", lastTurn
|
|
|
974
832
|
|
|
975
833
|
let fullOutput = header + formattedBlocks.join("\n\n---\n\n");
|
|
976
834
|
if (clamped) {
|
|
977
|
-
|
|
835
|
+
const ceilingLabel = mode === "compact" ? "3.8 KB inline" : "24 KB";
|
|
836
|
+
fullOutput += `\n\n... [Transcript display clamped at ${ceilingLabel} safety ceiling; use query_transcript(stepIndex: N) for specific steps] ...`;
|
|
978
837
|
}
|
|
979
838
|
|
|
980
839
|
return { content: [{ type: "text", text: fullOutput }] };
|
|
@@ -1325,8 +1184,6 @@ async function handleRequest(request) {
|
|
|
1325
1184
|
|
|
1326
1185
|
if (name === "check_context_health") {
|
|
1327
1186
|
toolResult = await handleCheckContextHealth(args);
|
|
1328
|
-
} else if (name === "subagent_brief") {
|
|
1329
|
-
toolResult = handleSubagentBrief(args);
|
|
1330
1187
|
} else if (name === "get_installation_status") {
|
|
1331
1188
|
toolResult = handleGetInstallationStatus();
|
|
1332
1189
|
} else if (name === "sync_installation") {
|
|
@@ -1414,33 +1271,62 @@ async function handleRequest(request) {
|
|
|
1414
1271
|
};
|
|
1415
1272
|
}
|
|
1416
1273
|
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
const
|
|
1274
|
+
if (isEntryPoint) {
|
|
1275
|
+
// Stdio JSON-RPC line loop
|
|
1276
|
+
const MAX_STDIO_PAYLOAD_BYTES = Number(process.env.AGY_MAX_STDIO_PAYLOAD_BYTES) || (10 * 1024 * 1024); // 10 MB ceiling default
|
|
1277
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false });
|
|
1420
1278
|
|
|
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");
|
|
1279
|
+
rl.on("line", async (line) => {
|
|
1280
|
+
if (!line || !line.trim()) return;
|
|
1281
|
+
if (line.length > MAX_STDIO_PAYLOAD_BYTES) {
|
|
1282
|
+
const errResp = {
|
|
1283
|
+
jsonrpc: "2.0",
|
|
1284
|
+
id: null,
|
|
1285
|
+
error: { code: -32600, message: "Invalid Request: Payload exceeds 10MB safety ceiling" }
|
|
1286
|
+
};
|
|
1287
|
+
process.stdout.write(JSON.stringify(errResp) + "\n");
|
|
1288
|
+
return;
|
|
1437
1289
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1290
|
+
try {
|
|
1291
|
+
const req = JSON.parse(line);
|
|
1292
|
+
const resp = await handleRequest(req);
|
|
1293
|
+
if (resp) {
|
|
1294
|
+
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
1295
|
+
}
|
|
1296
|
+
} catch (err) {
|
|
1297
|
+
const errResp = {
|
|
1298
|
+
jsonrpc: "2.0",
|
|
1299
|
+
id: null,
|
|
1300
|
+
error: { code: -32700, message: `Parse error: ${err.message}` }
|
|
1301
|
+
};
|
|
1302
|
+
process.stdout.write(JSON.stringify(errResp) + "\n");
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
// Anti-Orphan Lifecycle Governance: Prevent lingering Node processes when parent IDE closes
|
|
1307
|
+
rl.on("close", () => process.exit(0));
|
|
1308
|
+
process.stdin.on("end", () => process.exit(0));
|
|
1309
|
+
process.stdin.on("close", () => process.exit(0));
|
|
1310
|
+
process.stdout.on("error", (err) => {
|
|
1311
|
+
if (err && err.code === "EPIPE") {
|
|
1312
|
+
process.exit(0);
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
// Lightweight parent-process watchdog: If parent IDE process terminates, exit child
|
|
1317
|
+
const initialPpid = process.ppid;
|
|
1318
|
+
if (initialPpid && initialPpid > 1) {
|
|
1319
|
+
const watchdog = setInterval(() => {
|
|
1320
|
+
try {
|
|
1321
|
+
process.kill(initialPpid, 0);
|
|
1322
|
+
} catch (err) {
|
|
1323
|
+
if (err && err.code === "ESRCH") {
|
|
1324
|
+
process.exit(0);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}, 15000);
|
|
1328
|
+
watchdog.unref();
|
|
1445
1329
|
}
|
|
1446
|
-
}
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
export { handleRequest };
|
package/mcp/instructions.md
CHANGED
|
@@ -11,16 +11,18 @@ Agy-Context-Saver is the universal Model Context Protocol server and lifecycle g
|
|
|
11
11
|
2. **Never Read Internal State Directly**:
|
|
12
12
|
- Access to `.system_generated/` via `view_file` is strictly denied to prevent transcript explosion.
|
|
13
13
|
- Use `read_transcript` or `query_transcript` to inspect conversation history cleanly.
|
|
14
|
-
3. **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
14
|
+
3. **Unified 3-Tier Codebase & File Inspection**:
|
|
15
|
+
- **Tier 1 — Structural Discovery (`waymark_ask` with `plain: true`)**: First locate exact `[StartLine, EndLine]` coordinates via `waymark_ask(..., plain: true)` across all 23 supported formats (code ASTs + `markdown`, `yaml`, `json`, `toml`, `bash`). Always set `plain: true` (~19–466 tokens, zero shell quoting or stdin hazards).
|
|
16
|
+
- **Tier 2 — Precision Workspace File Reading (`view_file` Hybrid Slice Policy)**: Pass the `StartLine` and `EndLine` span from `waymark_ask` into `view_file` (bounded slices of `1–150` lines) to preserve 1-indexed line coordinates for `replace_file_content` and avoid terminal stdout truncation. Partial slices of `151–800` lines are denied; an explicit one-pass full-file audit (`StartLine: 1, EndLine: <TotalLines>`) is permitted only when the file on disk is $\le 800$ lines and $\le 45\text{ KiB}$ (`46,080` bytes).
|
|
17
|
+
- **Tier 3 — RTK Boundaries (`rtk read`, `rtk find`, `rtk rg`)**:
|
|
18
|
+
- **Spillovers & Task Logs**: Read `.system_generated/steps/<step>/output.txt` spillovers and completed `tasks/<taskId>.log` files via `rtk read <path>` (with `--head-lines` / `--tail-lines`). Never use `rtk read -l minimal|aggressive` or `-m` to derive line numbers for edits.
|
|
19
|
+
- **Directory/File Discovery**: Use `rtk find <path>` (Windows & POSIX) or `rtk ls <path>` (POSIX).
|
|
20
|
+
- **Prose / Regex Search**: Use `rtk rg "<pattern>" <path>` (explicit `<path>` required on Windows to avoid stdin stalls) strictly for free-text/regex searches inside body prose or string values when the symbol, heading, or config key name is unknown.
|
|
18
21
|
|
|
19
22
|
## Available MCP Tools
|
|
20
23
|
- `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
24
|
- `read_transcript`: Streams recent turns from `transcript.jsonl` or `transcript_full.jsonl` formatted as clean Markdown.
|
|
23
|
-
- `query_transcript`: Forensic regex search and filtering engine across transcript steps with optional `summaryOnly: true`.
|
|
25
|
+
- `query_transcript`: Forensic regex search and filtering engine across transcript steps with optional `summaryOnly: true`, plus surgical step extraction via `stepIndex` and `field`.
|
|
24
26
|
- `get_installation_status`: Audits live health of the plugin link, hook, MCP server, tool schemas, and RTK.
|
|
25
27
|
- `sync_installation`: Synchronizes and repairs all 4 integration layers in ~25ms.
|
|
26
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agy-context-saver",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
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.1",
|
|
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
|
}
|