feat: add dynamic truncation to rules/readme/agents injectors (#257)
- Apply dynamic truncation to rules-injector, directory-readme-injector, and directory-agents-injector - Add truncation notice encouraging users to read full content - Save context window space while maintaining awareness of complete documentation - Resolves #221 (part 1) Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
clearInjectedPaths,
|
clearInjectedPaths,
|
||||||
} from "./storage";
|
} from "./storage";
|
||||||
import { AGENTS_FILENAME } from "./constants";
|
import { AGENTS_FILENAME } from "./constants";
|
||||||
|
import { createDynamicTruncator } from "../../shared/dynamic-truncator";
|
||||||
|
|
||||||
interface ToolExecuteInput {
|
interface ToolExecuteInput {
|
||||||
tool: string;
|
tool: string;
|
||||||
@@ -39,6 +40,7 @@ interface EventInput {
|
|||||||
export function createDirectoryAgentsInjectorHook(ctx: PluginInput) {
|
export function createDirectoryAgentsInjectorHook(ctx: PluginInput) {
|
||||||
const sessionCaches = new Map<string, Set<string>>();
|
const sessionCaches = new Map<string, Set<string>>();
|
||||||
const pendingBatchReads = new Map<string, string[]>();
|
const pendingBatchReads = new Map<string, string[]>();
|
||||||
|
const truncator = createDynamicTruncator(ctx);
|
||||||
|
|
||||||
function getSessionCache(sessionID: string): Set<string> {
|
function getSessionCache(sessionID: string): Set<string> {
|
||||||
if (!sessionCaches.has(sessionID)) {
|
if (!sessionCaches.has(sessionID)) {
|
||||||
@@ -73,11 +75,11 @@ export function createDirectoryAgentsInjectorHook(ctx: PluginInput) {
|
|||||||
return found.reverse();
|
return found.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
function processFilePathForInjection(
|
async function processFilePathForInjection(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
sessionID: string,
|
sessionID: string,
|
||||||
output: ToolExecuteOutput,
|
output: ToolExecuteOutput,
|
||||||
): void {
|
): Promise<void> {
|
||||||
const resolved = resolveFilePath(filePath);
|
const resolved = resolveFilePath(filePath);
|
||||||
if (!resolved) return;
|
if (!resolved) return;
|
||||||
|
|
||||||
@@ -91,7 +93,11 @@ export function createDirectoryAgentsInjectorHook(ctx: PluginInput) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const content = readFileSync(agentsPath, "utf-8");
|
const content = readFileSync(agentsPath, "utf-8");
|
||||||
output.output += `\n\n[Directory Context: ${agentsPath}]\n${content}`;
|
const { result, truncated } = await truncator.truncate(sessionID, content);
|
||||||
|
const truncationNotice = truncated
|
||||||
|
? `\n\n[Note: Content was truncated to save context window space. For full context, please read the file directly: ${agentsPath}]`
|
||||||
|
: "";
|
||||||
|
output.output += `\n\n[Directory Context: ${agentsPath}]\n${result}${truncationNotice}`;
|
||||||
cache.add(agentsDir);
|
cache.add(agentsDir);
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
@@ -127,7 +133,7 @@ export function createDirectoryAgentsInjectorHook(ctx: PluginInput) {
|
|||||||
const toolName = input.tool.toLowerCase();
|
const toolName = input.tool.toLowerCase();
|
||||||
|
|
||||||
if (toolName === "read") {
|
if (toolName === "read") {
|
||||||
processFilePathForInjection(output.title, input.sessionID, output);
|
await processFilePathForInjection(output.title, input.sessionID, output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +141,7 @@ export function createDirectoryAgentsInjectorHook(ctx: PluginInput) {
|
|||||||
const filePaths = pendingBatchReads.get(input.callID);
|
const filePaths = pendingBatchReads.get(input.callID);
|
||||||
if (filePaths) {
|
if (filePaths) {
|
||||||
for (const filePath of filePaths) {
|
for (const filePath of filePaths) {
|
||||||
processFilePathForInjection(filePath, input.sessionID, output);
|
await processFilePathForInjection(filePath, input.sessionID, output);
|
||||||
}
|
}
|
||||||
pendingBatchReads.delete(input.callID);
|
pendingBatchReads.delete(input.callID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
clearInjectedPaths,
|
clearInjectedPaths,
|
||||||
} from "./storage";
|
} from "./storage";
|
||||||
import { README_FILENAME } from "./constants";
|
import { README_FILENAME } from "./constants";
|
||||||
|
import { createDynamicTruncator } from "../../shared/dynamic-truncator";
|
||||||
|
|
||||||
interface ToolExecuteInput {
|
interface ToolExecuteInput {
|
||||||
tool: string;
|
tool: string;
|
||||||
@@ -39,6 +40,7 @@ interface EventInput {
|
|||||||
export function createDirectoryReadmeInjectorHook(ctx: PluginInput) {
|
export function createDirectoryReadmeInjectorHook(ctx: PluginInput) {
|
||||||
const sessionCaches = new Map<string, Set<string>>();
|
const sessionCaches = new Map<string, Set<string>>();
|
||||||
const pendingBatchReads = new Map<string, string[]>();
|
const pendingBatchReads = new Map<string, string[]>();
|
||||||
|
const truncator = createDynamicTruncator(ctx);
|
||||||
|
|
||||||
function getSessionCache(sessionID: string): Set<string> {
|
function getSessionCache(sessionID: string): Set<string> {
|
||||||
if (!sessionCaches.has(sessionID)) {
|
if (!sessionCaches.has(sessionID)) {
|
||||||
@@ -73,11 +75,11 @@ export function createDirectoryReadmeInjectorHook(ctx: PluginInput) {
|
|||||||
return found.reverse();
|
return found.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
function processFilePathForInjection(
|
async function processFilePathForInjection(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
sessionID: string,
|
sessionID: string,
|
||||||
output: ToolExecuteOutput,
|
output: ToolExecuteOutput,
|
||||||
): void {
|
): Promise<void> {
|
||||||
const resolved = resolveFilePath(filePath);
|
const resolved = resolveFilePath(filePath);
|
||||||
if (!resolved) return;
|
if (!resolved) return;
|
||||||
|
|
||||||
@@ -91,7 +93,11 @@ export function createDirectoryReadmeInjectorHook(ctx: PluginInput) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const content = readFileSync(readmePath, "utf-8");
|
const content = readFileSync(readmePath, "utf-8");
|
||||||
output.output += `\n\n[Project README: ${readmePath}]\n${content}`;
|
const { result, truncated } = await truncator.truncate(sessionID, content);
|
||||||
|
const truncationNotice = truncated
|
||||||
|
? `\n\n[Note: Content was truncated to save context window space. For full context, please read the file directly: ${readmePath}]`
|
||||||
|
: "";
|
||||||
|
output.output += `\n\n[Project README: ${readmePath}]\n${result}${truncationNotice}`;
|
||||||
cache.add(readmeDir);
|
cache.add(readmeDir);
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
@@ -127,7 +133,7 @@ export function createDirectoryReadmeInjectorHook(ctx: PluginInput) {
|
|||||||
const toolName = input.tool.toLowerCase();
|
const toolName = input.tool.toLowerCase();
|
||||||
|
|
||||||
if (toolName === "read") {
|
if (toolName === "read") {
|
||||||
processFilePathForInjection(output.title, input.sessionID, output);
|
await processFilePathForInjection(output.title, input.sessionID, output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +141,7 @@ export function createDirectoryReadmeInjectorHook(ctx: PluginInput) {
|
|||||||
const filePaths = pendingBatchReads.get(input.callID);
|
const filePaths = pendingBatchReads.get(input.callID);
|
||||||
if (filePaths) {
|
if (filePaths) {
|
||||||
for (const filePath of filePaths) {
|
for (const filePath of filePaths) {
|
||||||
processFilePathForInjection(filePath, input.sessionID, output);
|
await processFilePathForInjection(filePath, input.sessionID, output);
|
||||||
}
|
}
|
||||||
pendingBatchReads.delete(input.callID);
|
pendingBatchReads.delete(input.callID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
loadInjectedRules,
|
loadInjectedRules,
|
||||||
saveInjectedRules,
|
saveInjectedRules,
|
||||||
} from "./storage";
|
} from "./storage";
|
||||||
|
import { createDynamicTruncator } from "../../shared/dynamic-truncator";
|
||||||
|
|
||||||
interface ToolExecuteInput {
|
interface ToolExecuteInput {
|
||||||
tool: string;
|
tool: string;
|
||||||
@@ -59,6 +60,7 @@ export function createRulesInjectorHook(ctx: PluginInput) {
|
|||||||
{ contentHashes: Set<string>; realPaths: Set<string> }
|
{ contentHashes: Set<string>; realPaths: Set<string> }
|
||||||
>();
|
>();
|
||||||
const pendingBatchFiles = new Map<string, string[]>();
|
const pendingBatchFiles = new Map<string, string[]>();
|
||||||
|
const truncator = createDynamicTruncator(ctx);
|
||||||
|
|
||||||
function getSessionCache(sessionID: string): {
|
function getSessionCache(sessionID: string): {
|
||||||
contentHashes: Set<string>;
|
contentHashes: Set<string>;
|
||||||
@@ -76,11 +78,11 @@ export function createRulesInjectorHook(ctx: PluginInput) {
|
|||||||
return resolve(ctx.directory, path);
|
return resolve(ctx.directory, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
function processFilePathForInjection(
|
async function processFilePathForInjection(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
sessionID: string,
|
sessionID: string,
|
||||||
output: ToolExecuteOutput
|
output: ToolExecuteOutput
|
||||||
): void {
|
): Promise<void> {
|
||||||
const resolved = resolveFilePath(filePath);
|
const resolved = resolveFilePath(filePath);
|
||||||
if (!resolved) return;
|
if (!resolved) return;
|
||||||
|
|
||||||
@@ -125,7 +127,11 @@ export function createRulesInjectorHook(ctx: PluginInput) {
|
|||||||
toInject.sort((a, b) => a.distance - b.distance);
|
toInject.sort((a, b) => a.distance - b.distance);
|
||||||
|
|
||||||
for (const rule of toInject) {
|
for (const rule of toInject) {
|
||||||
output.output += `\n\n[Rule: ${rule.relativePath}]\n[Match: ${rule.matchReason}]\n${rule.content}`;
|
const { result, truncated } = await truncator.truncate(sessionID, rule.content);
|
||||||
|
const truncationNotice = truncated
|
||||||
|
? `\n\n[Note: Content was truncated to save context window space. For full context, please read the file directly: ${rule.relativePath}]`
|
||||||
|
: "";
|
||||||
|
output.output += `\n\n[Rule: ${rule.relativePath}]\n[Match: ${rule.matchReason}]\n${result}${truncationNotice}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveInjectedRules(sessionID, cache);
|
saveInjectedRules(sessionID, cache);
|
||||||
@@ -167,7 +173,7 @@ export function createRulesInjectorHook(ctx: PluginInput) {
|
|||||||
const toolName = input.tool.toLowerCase();
|
const toolName = input.tool.toLowerCase();
|
||||||
|
|
||||||
if (TRACKED_TOOLS.includes(toolName)) {
|
if (TRACKED_TOOLS.includes(toolName)) {
|
||||||
processFilePathForInjection(output.title, input.sessionID, output);
|
await processFilePathForInjection(output.title, input.sessionID, output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +181,7 @@ export function createRulesInjectorHook(ctx: PluginInput) {
|
|||||||
const filePaths = pendingBatchFiles.get(input.callID);
|
const filePaths = pendingBatchFiles.get(input.callID);
|
||||||
if (filePaths) {
|
if (filePaths) {
|
||||||
for (const filePath of filePaths) {
|
for (const filePath of filePaths) {
|
||||||
processFilePathForInjection(filePath, input.sessionID, output);
|
await processFilePathForInjection(filePath, input.sessionID, output);
|
||||||
}
|
}
|
||||||
pendingBatchFiles.delete(input.callID);
|
pendingBatchFiles.delete(input.callID);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user