@iinm/plain-agent
Advanced tools
| /** | ||
| * @import { ProviderTokenUsage } from "./model" | ||
| */ | ||
| /** | ||
| * Extract the input (prompt/context) token count from a single turn's | ||
| * provider token usage object by summing the values of the specified keys. | ||
| * | ||
| * Returns `undefined` when no specified key yields a positive number. | ||
| * | ||
| * @param {ProviderTokenUsage} usage | ||
| * @param {string[]} inputTokensKeys - Keys whose numeric values are summed. | ||
| * @returns {number | undefined} | ||
| */ | ||
| export function extractInputTokenCount(usage, inputTokensKeys) { | ||
| let total = 0; | ||
| let found = false; | ||
| for (const key of inputTokensKeys) { | ||
| const value = usage[key]; | ||
| if (typeof value === "number" && value > 0) { | ||
| total += value; | ||
| found = true; | ||
| } | ||
| } | ||
| return found ? total : undefined; | ||
| } |
| { | ||
| "autoCompact": { "softLimit": 120000 }, | ||
| "autoApproval": { | ||
@@ -157,3 +158,6 @@ "defaultAction": "ask", | ||
| "desc": "find should be denied", | ||
| "toolUse": { "toolName": "exec_command", "input": { "command": "find" } }, | ||
| "toolUse": { | ||
| "toolName": "exec_command", | ||
| "input": { "command": "find" } | ||
| }, | ||
| "expectedAction": "deny" | ||
@@ -163,3 +167,6 @@ }, | ||
| "desc": "grep should be denied", | ||
| "toolUse": { "toolName": "exec_command", "input": { "command": "grep" } }, | ||
| "toolUse": { | ||
| "toolName": "exec_command", | ||
| "input": { "command": "grep" } | ||
| }, | ||
| "expectedAction": "deny" | ||
@@ -367,3 +374,8 @@ }, | ||
| "command": "gh", | ||
| "args": ["api", "--method", "GET", "repos/owner/repo/pulls/123/comments"] | ||
| "args": [ | ||
| "api", | ||
| "--method", | ||
| "GET", | ||
| "repos/owner/repo/pulls/123/comments" | ||
| ] | ||
| } | ||
@@ -377,3 +389,6 @@ }, | ||
| "toolName": "exec_command", | ||
| "input": { "command": "gh", "args": ["api", "repos/owner/repo/pulls"] } | ||
| "input": { | ||
| "command": "gh", | ||
| "args": ["api", "repos/owner/repo/pulls"] | ||
| } | ||
| }, | ||
@@ -405,3 +420,6 @@ "expectedAction": "deny" | ||
| "toolName": "exec_command", | ||
| "input": { "command": "bash", "args": ["-c", "echo hello | grep hello"] } | ||
| "input": { | ||
| "command": "bash", | ||
| "args": ["-c", "echo hello | grep hello"] | ||
| } | ||
| }, | ||
@@ -414,3 +432,6 @@ "expectedAction": null | ||
| "toolName": "exec_command", | ||
| "input": { "command": "bash", "args": ["-c", "echo hello > file.txt"] } | ||
| "input": { | ||
| "command": "bash", | ||
| "args": ["-c", "echo hello > file.txt"] | ||
| } | ||
| }, | ||
@@ -431,3 +452,6 @@ "expectedAction": null | ||
| "toolName": "exec_command", | ||
| "input": { "command": "bash", "args": ["-c", "for i in *.txt; do echo $i; done"] } | ||
| "input": { | ||
| "command": "bash", | ||
| "args": ["-c", "for i in *.txt; do echo $i; done"] | ||
| } | ||
| }, | ||
@@ -480,2 +504,5 @@ "expectedAction": null | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -508,2 +535,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -537,2 +567,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -566,2 +599,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -595,2 +631,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -624,2 +663,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -651,4 +693,7 @@ }, | ||
| "cache_read_input_tokens": 1, | ||
| "cache_creation_input_tokens": 12.50 | ||
| "cache_creation_input_tokens": 12.5 | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -680,4 +725,7 @@ }, | ||
| "cache_read_input_tokens": 1, | ||
| "cache_creation_input_tokens": 12.50 | ||
| "cache_creation_input_tokens": 12.5 | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -710,2 +758,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -737,2 +788,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -765,2 +819,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -793,2 +850,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -821,2 +881,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -849,2 +912,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -875,4 +941,7 @@ }, | ||
| "cache_read_input_tokens": 1, | ||
| "cache_creation_input_tokens": 12.50 | ||
| "cache_creation_input_tokens": 12.5 | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -903,4 +972,7 @@ }, | ||
| "cache_read_input_tokens": 1, | ||
| "cache_creation_input_tokens": 12.50 | ||
| "cache_creation_input_tokens": 12.5 | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["input_tokens", "cache_read_input_tokens"] | ||
| } | ||
@@ -938,3 +1010,4 @@ }, | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["promptTokenCount"] } | ||
| }, | ||
@@ -970,3 +1043,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["promptTokenCount"] } | ||
| }, | ||
@@ -1002,3 +1076,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["promptTokenCount"] } | ||
| }, | ||
@@ -1034,3 +1109,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["promptTokenCount"] } | ||
| }, | ||
@@ -1066,3 +1142,4 @@ | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["promptTokenCount"] } | ||
| }, | ||
@@ -1097,3 +1174,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["promptTokenCount"] } | ||
| }, | ||
@@ -1128,3 +1206,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["promptTokenCount"] } | ||
| }, | ||
@@ -1159,3 +1238,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["promptTokenCount"] } | ||
| }, | ||
@@ -1188,3 +1268,4 @@ | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1216,3 +1297,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1244,3 +1326,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1272,3 +1355,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1300,3 +1384,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1328,3 +1413,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1356,3 +1442,4 @@ | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1383,3 +1470,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1410,3 +1498,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1437,3 +1526,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1464,3 +1554,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1491,3 +1582,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["input_tokens"] } | ||
| }, | ||
@@ -1514,5 +1606,6 @@ | ||
| "prompt_tokens_details.cached_tokens": -0.14, | ||
| "completion_tokens": 0.6 | ||
| "completion_tokens": 0.6 | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1541,3 +1634,4 @@ | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1565,3 +1659,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1590,3 +1685,4 @@ | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1607,3 +1703,4 @@ | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1631,3 +1728,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1656,3 +1754,4 @@ | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1672,3 +1771,4 @@ { | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1697,3 +1797,4 @@ | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1722,3 +1823,4 @@ | ||
| } | ||
| } | ||
| }, | ||
| "autoCompact": { "inputTokensKeys": ["prompt_tokens"] } | ||
| }, | ||
@@ -1745,2 +1847,5 @@ | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["inputTokens", "cacheReadInputTokens"] | ||
| } | ||
@@ -1770,2 +1875,5 @@ }, | ||
| } | ||
| }, | ||
| "autoCompact": { | ||
| "inputTokensKeys": ["inputTokens", "cacheReadInputTokens"] | ||
| } | ||
@@ -1772,0 +1880,0 @@ } |
+1
-1
| { | ||
| "name": "@iinm/plain-agent", | ||
| "version": "1.13.1", | ||
| "version": "1.14.0", | ||
| "description": "A lightweight terminal-based coding agent focused on safety and low token cost", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+13
-2
| # Plain Agent | ||
| [](https://github.com/iinm/plain-agent/actions/workflows/github-code-scanning/codeql) | ||
| [](https://socket.dev/npm/package/@iinm/plain-agent) | ||
| [](https://socket.dev/npm/package/@iinm/plain-agent) | ||
| [](https://packagephobia.com/result?p=@iinm/plain-agent) | ||
@@ -232,3 +232,3 @@ | ||
| - Output truncation — when a command or MCP tool produces large output, it is truncated and saved to a file. The agent can then read only the relevant parts. | ||
| - Context compaction — when the context grows large, you can run `/compact` to discard old messages and reload task state from a memory file. | ||
| - Context compaction — run `/compact` to discard old messages and reload task state from a memory file. This also happens automatically when input tokens exceed a configurable soft limit. | ||
| - MCP tool filtering — MCP servers often expose many tools. Use `enabledTools` in the server config to enable only the ones you need, which reduces the number of tool definitions sent to the model. | ||
@@ -749,2 +749,13 @@ | ||
| // Auto-compact: when input tokens exceed the soft limit after a tool execution, | ||
| // the agent is prompted to update the memory file and call compact_context. | ||
| // Reduces noise and token costs before hitting the model's hard limit. | ||
| "autoCompact": { | ||
| "softLimit": 120000, | ||
| // Optional: override per model (prefix match on name+variant) | ||
| "softLimitPerModelPrefix": { | ||
| "gemini-2.5-pro": 500000 | ||
| } | ||
| }, | ||
| // Override the default notification command | ||
@@ -751,0 +762,0 @@ "notifyCmd": { "command": "plain-notify-desktop", "args": [] } |
+4
-0
@@ -64,2 +64,6 @@ import type { EventEmitter } from "node:events"; | ||
| initialState?: SessionState | null; | ||
| /** Soft limit on input tokens; triggers auto-compact prompt when exceeded. */ | ||
| contextSoftLimit?: number; | ||
| /** Keys in providerTokenUsage to sum for input token count. */ | ||
| inputTokensKeys?: string[]; | ||
| }; |
+4
-0
@@ -37,2 +37,4 @@ /** | ||
| initialState, | ||
| contextSoftLimit, | ||
| inputTokensKeys, | ||
| }) { | ||
@@ -199,2 +201,4 @@ /** @type {UserEventEmitter} */ | ||
| pauseSignal, | ||
| contextSoftLimit, | ||
| inputTokensKeys, | ||
| }); | ||
@@ -201,0 +205,0 @@ |
+42
-1
@@ -10,2 +10,4 @@ /** | ||
| import { styleText } from "node:util"; | ||
| import { buildCompactPrompt } from "./prompt.mjs"; | ||
| import { extractInputTokenCount } from "./tokenUsage.mjs"; | ||
| import { compactContextToolName } from "./tools/compactContext.mjs"; | ||
@@ -59,2 +61,4 @@ | ||
| * @property {PauseSignal} pauseSignal - Signal to pause auto-approve after current tool completes | ||
| * @property {number} [contextSoftLimit] - Soft limit on input tokens for auto-compact | ||
| * @property {string[]} [inputTokensKeys] - Keys in providerTokenUsage to sum for input token count | ||
| */ | ||
@@ -79,2 +83,4 @@ | ||
| pauseSignal, | ||
| contextSoftLimit, | ||
| inputTokensKeys, | ||
| }) { | ||
@@ -108,3 +114,3 @@ const inputHandler = createInputHandler({ | ||
| const maxThinkingLoops = 5; | ||
| let turnsAfterCompactPrompt = -1; | ||
| while (true) { | ||
@@ -247,2 +253,3 @@ // Check if auto-approve was paused by Ctrl-C during tool execution | ||
| ) { | ||
| turnsAfterCompactPrompt = -1; | ||
| continue; | ||
@@ -262,2 +269,36 @@ } | ||
| } | ||
| // Auto-compact: insert prompt if context exceeds soft limit | ||
| if (contextSoftLimit && inputTokensKeys && providerTokenUsage) { | ||
| const inputTokens = extractInputTokenCount( | ||
| providerTokenUsage, | ||
| inputTokensKeys, | ||
| ); | ||
| if (inputTokens !== undefined && inputTokens > contextSoftLimit) { | ||
| if (0 <= turnsAfterCompactPrompt && turnsAfterCompactPrompt < 3) { | ||
| turnsAfterCompactPrompt += 1; | ||
| } else { | ||
| stateManager.appendMessages([ | ||
| { | ||
| role: "user", | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: buildCompactPrompt({ | ||
| isSubagent: subagentManager.isSubagentActive(), | ||
| }), | ||
| }, | ||
| ], | ||
| }, | ||
| ]); | ||
| turnsAfterCompactPrompt = 0; | ||
| console.error( | ||
| styleText( | ||
| "yellow", | ||
| `\nContext exceeded soft limit (${inputTokens.toLocaleString()} / ${contextSoftLimit.toLocaleString()} tokens). Auto-compact prompt inserted.`, | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| } | ||
@@ -264,0 +305,0 @@ } |
@@ -11,3 +11,6 @@ /** | ||
| import { loadUserMessageContext } from "../context/loadUserMessageContext.mjs"; | ||
| import { CLAUDE_CODE_COMPATIBILITY_NOTES } from "../prompt.mjs"; | ||
| import { | ||
| buildCompactPrompt, | ||
| CLAUDE_CODE_COMPATIBILITY_NOTES, | ||
| } from "../prompt.mjs"; | ||
| import { parseFileRange } from "../utils/parseFileRange.mjs"; | ||
@@ -144,10 +147,6 @@ import { readFileRange } from "../utils/readFileRange.mjs"; | ||
| if (/^\/compact( |$)/i.test(inputTrimmed)) { | ||
| const invocation = inputTrimmed; | ||
| const message = [ | ||
| `System: This prompt was invoked as "${invocation}".`, | ||
| "", | ||
| "Compact the conversation context:", | ||
| "1. Update the memory file for the current task so it fully captures the task overview, progress, decisions, and next steps in a self-contained way.", | ||
| '2. Then call the "compact_context" tool alone with that memory file path and a brief reason.', | ||
| ].join("\n"); | ||
| const message = buildCompactPrompt({ | ||
| invocation: inputTrimmed, | ||
| isSubagent: agentCommands.getActiveSubagent() !== null, | ||
| }); | ||
| userEventEmitter.emit("userInput", [{ type: "text", text: message }]); | ||
@@ -154,0 +153,0 @@ return "continue"; |
+4
-0
@@ -90,2 +90,6 @@ import { ClaudeCodePluginRepo } from "./claudeCodePlugin.mjs"; | ||
| notifyCmd?: { command: string; args?: string[] }; | ||
| autoCompact?: { | ||
| softLimit?: number; | ||
| softLimitPerModelPrefix?: Record<string, number>; | ||
| }; | ||
| claudeCodePlugins?: ClaudeCodePluginRepo[]; | ||
@@ -92,0 +96,0 @@ }; |
+28
-0
@@ -127,2 +127,12 @@ /** | ||
| }, | ||
| autoCompact: config.autoCompact | ||
| ? { | ||
| softLimit: | ||
| config.autoCompact.softLimit ?? merged.autoCompact?.softLimit, | ||
| softLimitPerModelPrefix: { | ||
| ...(merged.autoCompact?.softLimitPerModelPrefix ?? {}), | ||
| ...(config.autoCompact.softLimitPerModelPrefix ?? {}), | ||
| }, | ||
| } | ||
| : merged.autoCompact, | ||
| notifyCmd: config.notifyCmd ?? merged.notifyCmd, | ||
@@ -227,1 +237,19 @@ claudeCodePlugins: [ | ||
| } | ||
| /** | ||
| * Resolve the effective context soft limit for the given model. | ||
| * | ||
| * @param {AppConfig["autoCompact"]} autoCompact | ||
| * @param {string} modelNameWithVariant - e.g. "claude-sonnet-4-6+thinking-high" | ||
| * @returns {number | undefined} | ||
| */ | ||
| export function resolveContextSoftLimit(autoCompact, modelNameWithVariant) { | ||
| if (!autoCompact) return undefined; | ||
| const perPrefix = autoCompact.softLimitPerModelPrefix; | ||
| const matched = perPrefix | ||
| ? Object.entries(perPrefix).find(([prefix]) => | ||
| modelNameWithVariant.startsWith(prefix), | ||
| )?.[1] | ||
| : undefined; | ||
| return matched ?? autoCompact.softLimit ?? undefined; | ||
| } |
+9
-1
@@ -19,3 +19,3 @@ /** | ||
| import { runTestApprovalCommand } from "./cli/testApproval.mjs"; | ||
| import { loadAppConfig } from "./config.mjs"; | ||
| import { loadAppConfig, resolveContextSoftLimit } from "./config.mjs"; | ||
| import { loadAgentRoles } from "./context/loadAgentRoles.mjs"; | ||
@@ -432,2 +432,8 @@ import { loadPrompts } from "./context/loadPrompts.mjs"; | ||
| // Resolve context soft limit from autoCompact config | ||
| const contextSoftLimit = resolveContextSoftLimit( | ||
| appConfig.autoCompact, | ||
| modelNameWithVariant, | ||
| ); | ||
| const { userEventEmitter, agentEventEmitter, agentCommands } = createAgent({ | ||
@@ -447,2 +453,4 @@ callModel: agentCallModel, | ||
| initialState: resumedState, | ||
| contextSoftLimit, | ||
| inputTokensKeys: modelDef.autoCompact?.inputTokensKeys, | ||
| }); | ||
@@ -449,0 +457,0 @@ |
@@ -13,4 +13,10 @@ import { AnthropicModelConfig } from "./providers/anthropic"; | ||
| cost?: CostConfig; | ||
| autoCompact?: ModelAutoCompactConfig; | ||
| }; | ||
| export type ModelAutoCompactConfig = { | ||
| /** Keys in providerTokenUsage whose values are summed to get the input token count. */ | ||
| inputTokensKeys: string[]; | ||
| }; | ||
| export type PlatformConfig = | ||
@@ -17,0 +23,0 @@ | { |
+39
-0
@@ -108,1 +108,40 @@ import { toOneLine } from "./utils/toOneLine.mjs"; | ||
| - If instructed to use "haiku agent", "sonnet agent", or "opus agent", use "worker" instead.`; | ||
| /** | ||
| * Build the compact context prompt message text. | ||
| * | ||
| * Shared between the interactive `/compact` command and the automatic | ||
| * soft-limit prompt insertion in the agent loop. | ||
| * | ||
| * @param {object} [options] | ||
| * @param {string} [options.invocation] - The invocation string (e.g., "/compact reason"). | ||
| * When omitted, a generic auto-compact preamble is used instead. | ||
| * @param {boolean} [options.isSubagent] - When true, instructs the model to | ||
| * return to the main agent via `switch_to_main_agent` before compacting. | ||
| * @returns {string} | ||
| */ | ||
| export function buildCompactPrompt(options) { | ||
| const { invocation, isSubagent } = options ?? {}; | ||
| const preamble = invocation | ||
| ? `System: This prompt was invoked as "${invocation}".` | ||
| : "System: Context is approaching the soft limit."; | ||
| if (isSubagent) { | ||
| return [ | ||
| preamble, | ||
| "", | ||
| "The context is growing large. Wrap up your current work:", | ||
| "1. Summarize your progress and findings so far in the memory file.", | ||
| '2. Call "switch_to_main_agent" to hand back control to the main agent.', | ||
| ].join("\n"); | ||
| } | ||
| return [ | ||
| preamble, | ||
| "", | ||
| "Compact the conversation context:", | ||
| "1. Update the memory file for the current task so it fully captures the task overview, progress, decisions, and next steps in a self-contained way.", | ||
| '2. Then call the "compact_context" tool alone with that memory file path and a brief reason.', | ||
| ].join("\n"); | ||
| } |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
578134
1.74%96
1.05%16235
1.62%1028
1.08%44
2.33%