From fa4558fc8185a327df688ba6cc83ef209b99b929 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Fri, 19 Dec 2025 15:48:49 +0100
Subject: [PATCH 01/13] UI component
---
index.ts | 129 ++++++++++++++-----------
lib/ui/dcp-status.js | 12 +++
lib/ui/notification.ts | 208 ++++++++++++++++++++++++-----------------
3 files changed, 206 insertions(+), 143 deletions(-)
create mode 100644 lib/ui/dcp-status.js
diff --git a/index.ts b/index.ts
index ac87705..786a2a2 100644
--- a/index.ts
+++ b/index.ts
@@ -1,66 +1,83 @@
-import type { Plugin } from "@opencode-ai/plugin"
-import { getConfig } from "./lib/config"
-import { Logger } from "./lib/logger"
-import { loadPrompt } from "./lib/prompt"
-import { createSessionState } from "./lib/state"
-import { createPruneTool } from "./lib/strategies"
-import { createChatMessageTransformHandler, createEventHandler } from "./lib/hooks"
+import type { Plugin } from "@opencode-ai/plugin";
+import { getConfig } from "./lib/config";
+import { Logger } from "./lib/logger";
+import { loadPrompt } from "./lib/prompt";
+import { createSessionState } from "./lib/state";
+import { createPruneTool } from "./lib/strategies";
+import {
+ createChatMessageTransformHandler,
+ createEventHandler,
+} from "./lib/hooks";
const plugin: Plugin = (async (ctx) => {
- const config = getConfig(ctx)
+ const config = getConfig(ctx);
- if (!config.enabled) {
- return {}
- }
+ if (!config.enabled) {
+ return {};
+ }
- // Suppress AI SDK warnings
- if (typeof globalThis !== 'undefined') {
- (globalThis as any).AI_SDK_LOG_WARNINGS = false
- }
+ // Suppress AI SDK warnings
+ if (typeof globalThis !== "undefined") {
+ (globalThis as any).AI_SDK_LOG_WARNINGS = false;
+ }
- // Initialize core components
- const logger = new Logger(config.debug)
- const state = createSessionState()
+ // Initialize core components
+ const logger = new Logger(config.debug);
+ const state = createSessionState();
- // Log initialization
- logger.info("DCP initialized", {
- strategies: config.strategies,
- })
+ // Log initialization
+ logger.info("DCP initialized", {
+ strategies: config.strategies,
+ });
- return {
- "experimental.chat.system.transform": async (_input: unknown, output: { system: string[] }) => {
- const syntheticPrompt = loadPrompt("prune-system-prompt")
- output.system.push(syntheticPrompt)
- },
- "experimental.chat.messages.transform": createChatMessageTransformHandler(
- ctx.client,
+ return {
+ ui: [
+ {
+ name: "dcp-status",
+ path: "/home/spoon/.config/opencode/plugin/dcp/lib/ui/dcp-status.js",
+ },
+ ],
+ "experimental.chat.system.transform": async (
+ _input: unknown,
+ output: { system: string[] },
+ ) => {
+ const syntheticPrompt = loadPrompt("prune-system-prompt");
+ output.system.push(syntheticPrompt);
+ },
+ "experimental.chat.messages.transform": createChatMessageTransformHandler(
+ ctx.client,
+ state,
+ logger,
+ config,
+ ),
+ tool: config.strategies.pruneTool.enabled
+ ? {
+ prune: createPruneTool({
+ client: ctx.client,
state,
logger,
- config
- ),
- tool: config.strategies.pruneTool.enabled ? {
- prune: createPruneTool({
- client: ctx.client,
- state,
- logger,
- config,
- workingDirectory: ctx.directory
- }),
- } : undefined,
- config: async (opencodeConfig) => {
- // Add prune to primary_tools by mutating the opencode config
- // This works because config is cached and passed by reference
- if (config.strategies.pruneTool.enabled) {
- const existingPrimaryTools = opencodeConfig.experimental?.primary_tools ?? []
- opencodeConfig.experimental = {
- ...opencodeConfig.experimental,
- primary_tools: [...existingPrimaryTools, "prune"],
- }
- logger.info("Added 'prune' to experimental.primary_tools via config mutation")
- }
- },
- event: createEventHandler(ctx.client, config, state, logger, ctx.directory),
- }
-}) satisfies Plugin
+ config,
+ workingDirectory: ctx.directory,
+ }),
+ }
+ : undefined,
+ config: async (opencodeConfig) => {
+ // Add prune to primary_tools by mutating the opencode config
+ // This works because config is cached and passed by reference
+ if (config.strategies.pruneTool.enabled) {
+ const existingPrimaryTools =
+ opencodeConfig.experimental?.primary_tools ?? [];
+ opencodeConfig.experimental = {
+ ...opencodeConfig.experimental,
+ primary_tools: [...existingPrimaryTools, "prune"],
+ };
+ logger.info(
+ "Added 'prune' to experimental.primary_tools via config mutation",
+ );
+ }
+ },
+ event: createEventHandler(ctx.client, config, state, logger, ctx.directory),
+ };
+}) satisfies Plugin;
-export default plugin
+export default plugin;
diff --git a/lib/ui/dcp-status.js b/lib/ui/dcp-status.js
new file mode 100644
index 0000000..3eefcee
--- /dev/null
+++ b/lib/ui/dcp-status.js
@@ -0,0 +1,12 @@
+class DcpStatus extends HTMLElement {
+ connectedCallback() {
+ this.innerHTML = `
+
+
DCP Status
+
Tokens saved in this session: ${this.getAttribute("saved") || 0}
+
+ `;
+ }
+}
+
+customElements.define("dcp-status", DcpStatus);
diff --git a/lib/ui/notification.ts b/lib/ui/notification.ts
index ead50ac..08e296b 100644
--- a/lib/ui/notification.ts
+++ b/lib/ui/notification.ts
@@ -1,118 +1,152 @@
-import type { Logger } from "../logger"
-import type { SessionState } from "../state"
-import { formatPrunedItemsList, formatTokenCount } from "./utils"
-import { ToolParameterEntry } from "../state"
-import { PluginConfig } from "../config"
+import type { Logger } from "../logger";
+import type { SessionState } from "../state";
+import { formatPrunedItemsList, formatTokenCount } from "./utils";
+import { ToolParameterEntry } from "../state";
+import { PluginConfig } from "../config";
-export type PruneReason = "completion" | "noise" | "consolidation"
+export type PruneReason = "completion" | "noise" | "consolidation";
export const PRUNE_REASON_LABELS: Record = {
- completion: "Task Complete",
- noise: "Noise Removal",
- consolidation: "Consolidation"
-}
+ completion: "Task Complete",
+ noise: "Noise Removal",
+ consolidation: "Consolidation",
+};
function formatStatsHeader(
- totalTokensSaved: number,
- pruneTokenCounter: number
+ totalTokensSaved: number,
+ pruneTokenCounter: number,
): string {
- const totalTokensSavedStr = `~${formatTokenCount(totalTokensSaved + pruneTokenCounter)}`
- return [
- `▣ DCP | ${totalTokensSavedStr} saved total`,
- ].join('\n')
+ const totalTokensSavedStr = `~${formatTokenCount(totalTokensSaved + pruneTokenCounter)}`;
+ return [`▣ DCP | ${totalTokensSavedStr} saved total`].join("\n");
}
function buildMinimalMessage(
- state: SessionState,
- reason: PruneReason | undefined
+ state: SessionState,
+ reason: PruneReason | undefined,
): string {
- const reasonSuffix = reason ? ` [${PRUNE_REASON_LABELS[reason]}]` : ''
- return formatStatsHeader(
- state.stats.totalPruneTokens,
- state.stats.pruneTokenCounter
+ const reasonSuffix = reason ? ` [${PRUNE_REASON_LABELS[reason]}]` : "";
+ return (
+ formatStatsHeader(
+ state.stats.totalPruneTokens,
+ state.stats.pruneTokenCounter,
) + reasonSuffix
+ );
}
function buildDetailedMessage(
- state: SessionState,
- reason: PruneReason | undefined,
- pruneToolIds: string[],
- toolMetadata: Map,
- workingDirectory?: string
+ state: SessionState,
+ reason: PruneReason | undefined,
+ pruneToolIds: string[],
+ toolMetadata: Map,
+ workingDirectory?: string,
): string {
- let message = formatStatsHeader(state.stats.totalPruneTokens, state.stats.pruneTokenCounter)
+ let message = formatStatsHeader(
+ state.stats.totalPruneTokens,
+ state.stats.pruneTokenCounter,
+ );
- if (pruneToolIds.length > 0) {
- const pruneTokenCounterStr = `~${formatTokenCount(state.stats.pruneTokenCounter)}`
- const reasonLabel = reason ? ` — ${PRUNE_REASON_LABELS[reason]}` : ''
- message += `\n\n▣ Pruning (${pruneTokenCounterStr})${reasonLabel}`
+ if (pruneToolIds.length > 0) {
+ const pruneTokenCounterStr = `~${formatTokenCount(state.stats.pruneTokenCounter)}`;
+ const reasonLabel = reason ? ` — ${PRUNE_REASON_LABELS[reason]}` : "";
+ message += `\n\n▣ Pruning (${pruneTokenCounterStr})${reasonLabel}`;
- const itemLines = formatPrunedItemsList(pruneToolIds, toolMetadata, workingDirectory)
- message += '\n' + itemLines.join('\n')
- }
+ const itemLines = formatPrunedItemsList(
+ pruneToolIds,
+ toolMetadata,
+ workingDirectory,
+ );
+ message += "\n" + itemLines.join("\n");
+ }
- return message.trim()
+ return message.trim();
}
export async function sendUnifiedNotification(
- client: any,
- logger: Logger,
- config: PluginConfig,
- state: SessionState,
- sessionId: string,
- pruneToolIds: string[],
- toolMetadata: Map,
- reason: PruneReason | undefined,
- params: any,
- workingDirectory: string
+ client: any,
+ logger: Logger,
+ config: PluginConfig,
+ state: SessionState,
+ sessionId: string,
+ pruneToolIds: string[],
+ toolMetadata: Map,
+ reason: PruneReason | undefined,
+ params: any,
+ workingDirectory: string,
): Promise {
- const hasPruned = pruneToolIds.length > 0
- if (!hasPruned) {
- return false
- }
+ const hasPruned = pruneToolIds.length > 0;
+ if (!hasPruned) {
+ return false;
+ }
- if (config.pruningSummary === 'off') {
- return false
- }
+ if (config.pruningSummary === "off") {
+ return false;
+ }
- const message = config.pruningSummary === 'minimal'
- ? buildMinimalMessage(state, reason)
- : buildDetailedMessage(state, reason, pruneToolIds, toolMetadata, workingDirectory)
+ const message =
+ config.pruningSummary === "minimal"
+ ? buildMinimalMessage(state, reason)
+ : buildDetailedMessage(
+ state,
+ reason,
+ pruneToolIds,
+ toolMetadata,
+ workingDirectory,
+ );
- await sendIgnoredMessage(client, sessionId, message, params, logger)
- return true
+ await sendIgnoredMessage(
+ client,
+ sessionId,
+ message,
+ params,
+ logger,
+ state.stats.totalPruneTokens + state.stats.pruneTokenCounter,
+ );
+ return true;
}
export async function sendIgnoredMessage(
- client: any,
- sessionID: string,
- text: string,
- params: any,
- logger: Logger
+ client: any,
+ sessionID: string,
+ text: string,
+ params: any,
+ logger: Logger,
+ totalSaved?: number,
): Promise {
- const agent = params.agent || undefined
- const model = params.providerId && params.modelId ? {
- providerID: params.providerId,
- modelID: params.modelId
- } : undefined
+ const agent = params.agent || undefined;
+ const model =
+ params.providerId && params.modelId
+ ? {
+ providerID: params.providerId,
+ modelID: params.modelId,
+ }
+ : undefined;
- try {
- await client.session.prompt({
- path: {
- id: sessionID
+ try {
+ await client.session.prompt({
+ path: {
+ id: sessionID,
+ },
+ body: {
+ noReply: true,
+ agent: agent,
+ model: model,
+ parts: [
+ {
+ type: "text",
+ text: text,
+ ignored: true,
+ },
+ {
+ type: "text",
+ text: "dcp-status",
+ plugin: true,
+ metadata: {
+ saved: totalSaved ?? 0,
},
- body: {
- noReply: true,
- agent: agent,
- model: model,
- parts: [{
- type: 'text',
- text: text,
- ignored: true
- }]
- }
- })
- } catch (error: any) {
- logger.error("Failed to send notification", { error: error.message })
- }
+ },
+ ],
+ },
+ });
+ } catch (error: any) {
+ logger.error("Failed to send notification", { error: error.message });
+ }
}
-
From 537a1e7b92faa5337d9ce007844e6e0ea6961239 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Fri, 19 Dec 2025 17:15:21 +0100
Subject: [PATCH 02/13] check list
---
index.ts | 111 ++++++++++-
lib/strategies/prune-tool.ts | 345 +++++++++++++++++++++--------------
lib/ui/confirmation.ts | 95 ++++++++++
lib/ui/dcp-status.js | 12 --
lib/ui/notification.ts | 40 ++--
5 files changed, 434 insertions(+), 169 deletions(-)
create mode 100644 lib/ui/confirmation.ts
delete mode 100644 lib/ui/dcp-status.js
diff --git a/index.ts b/index.ts
index 786a2a2..3c45c7c 100644
--- a/index.ts
+++ b/index.ts
@@ -8,6 +8,7 @@ import {
createChatMessageTransformHandler,
createEventHandler,
} from "./lib/hooks";
+import { getPendingPrune, setPendingPrune } from "./lib/ui/confirmation";
const plugin: Plugin = (async (ctx) => {
const config = getConfig(ctx);
@@ -34,9 +35,117 @@ const plugin: Plugin = (async (ctx) => {
ui: [
{
name: "dcp-status",
- path: "/home/spoon/.config/opencode/plugin/dcp/lib/ui/dcp-status.js",
+ template: {
+ type: "box",
+ direction: "row",
+ border: ["right"],
+ borderStyle: "heavy",
+ borderColor: "accent",
+ bg: "#1a1a2e",
+ paddingX: 1,
+ paddingY: 0,
+ gap: 1,
+ children: [
+ { type: "text", content: "📦 DCP", fg: "accent", bold: true },
+ { type: "text", content: " │ ", fg: "textMuted" },
+ { type: "text", content: "{{saved}}", fg: "accent", bold: true },
+ { type: "text", content: " saved", fg: "textMuted" },
+ ],
+ },
+ },
+ {
+ name: "dcp-confirm",
+ template: {
+ type: "box",
+ direction: "column",
+ border: ["top", "bottom", "left", "right"],
+ borderStyle: "rounded",
+ borderColor: "accent",
+ bg: "#1a1a2e",
+ paddingX: 2,
+ paddingY: 1,
+ gap: 1,
+ children: [
+ {
+ type: "box",
+ direction: "row",
+ gap: 1,
+ children: [
+ { type: "text", content: "📦", fg: "accent" },
+ {
+ type: "text",
+ content: "DCP — Select files to prune",
+ fg: "accent",
+ bold: true,
+ },
+ ],
+ },
+ {
+ type: "text",
+ content: "─────────────────────────────────",
+ fg: "textMuted",
+ },
+ {
+ type: "checklist",
+ items: "{{items}}",
+ fg: "text",
+ fgChecked: "#2ecc71",
+ onToggle: "item-toggled",
+ },
+ {
+ type: "text",
+ content: "─────────────────────────────────",
+ fg: "textMuted",
+ },
+ {
+ type: "box",
+ direction: "row",
+ gap: 2,
+ children: [
+ {
+ type: "confirm-button",
+ label: " ✓ Confirm Prune ",
+ fg: "#1a1a2e",
+ bg: "#2ecc71",
+ onConfirm: "confirm-prune",
+ },
+ {
+ type: "confirm-button",
+ label: " ✗ Cancel ",
+ fg: "text",
+ bg: "#e94560",
+ onConfirm: "cancel-prune",
+ },
+ ],
+ },
+ ],
+ },
},
],
+ "ui.event": async (event: {
+ component: string;
+ event: string;
+ data: Record;
+ }) => {
+ logger.info("UI Event received", event);
+
+ if (event.component === "dcp-confirm") {
+ const pending = getPendingPrune();
+ if (event.event === "item-toggled" && event.data.items && pending) {
+ // Update the pending items state
+ pending.items = event.data.items;
+ } else if (event.event === "confirm-prune" && pending) {
+ const confirmed = pending.items
+ .filter((i: { checked: boolean }) => i.checked)
+ .map((i: { id: string }) => i.id);
+ pending.resolve(confirmed);
+ setPendingPrune(null);
+ } else if (event.event === "cancel-prune" && pending) {
+ pending.resolve([]);
+ setPendingPrune(null);
+ }
+ }
+ },
"experimental.chat.system.transform": async (
_input: unknown,
output: { system: string[] },
diff --git a/lib/strategies/prune-tool.ts b/lib/strategies/prune-tool.ts
index 285a88d..e6fd7a1 100644
--- a/lib/strategies/prune-tool.ts
+++ b/lib/strategies/prune-tool.ts
@@ -1,24 +1,25 @@
-import { tool } from "@opencode-ai/plugin"
-import type { SessionState, ToolParameterEntry, WithParts } from "../state"
-import type { PluginConfig } from "../config"
-import { buildToolIdList } from "../messages/utils"
-import { PruneReason, sendUnifiedNotification } from "../ui/notification"
-import { formatPruningResultForTool } from "../ui/utils"
-import { ensureSessionInitialized } from "../state"
-import { saveSessionState } from "../state/persistence"
-import type { Logger } from "../logger"
-import { loadPrompt } from "../prompt"
-import { calculateTokensSaved, getCurrentParams } from "./utils"
+import { tool } from "@opencode-ai/plugin";
+import type { SessionState, ToolParameterEntry, WithParts } from "../state";
+import type { PluginConfig } from "../config";
+import { buildToolIdList } from "../messages/utils";
+import { PruneReason, sendUnifiedNotification } from "../ui/notification";
+import { formatPruningResultForTool } from "../ui/utils";
+import { ensureSessionInitialized } from "../state";
+import { saveSessionState } from "../state/persistence";
+import type { Logger } from "../logger";
+import { loadPrompt } from "../prompt";
+import { calculateTokensSaved, getCurrentParams } from "./utils";
+import { requestPruneConfirmation } from "../ui/confirmation";
/** Tool description loaded from prompts/prune-tool-spec.txt */
-const TOOL_DESCRIPTION = loadPrompt("prune-tool-spec")
+const TOOL_DESCRIPTION = loadPrompt("prune-tool-spec");
export interface PruneToolContext {
- client: any
- state: SessionState
- logger: Logger
- config: PluginConfig
- workingDirectory: string
+ client: any;
+ state: SessionState;
+ logger: Logger;
+ config: PluginConfig;
+ workingDirectory: string;
}
/**
@@ -26,130 +27,192 @@ export interface PruneToolContext {
* Accepts numeric IDs from the list and prunes those tool outputs.
*/
export function createPruneTool(
- ctx: PruneToolContext,
+ ctx: PruneToolContext,
): ReturnType {
- return tool({
- description: TOOL_DESCRIPTION,
- args: {
- ids: tool.schema.array(
- tool.schema.string()
- ).describe(
- "Numeric IDs as strings to prune from the list"
+ return tool({
+ description: TOOL_DESCRIPTION,
+ args: {
+ ids: tool.schema
+ .array(tool.schema.string())
+ .describe(
+ "Numeric IDs as strings to prune from the list",
+ ),
+ metadata: tool.schema
+ .object({
+ reason: tool.schema
+ .enum(["completion", "noise", "consolidation"])
+ .describe("The reason for pruning"),
+ distillation: tool.schema
+ .record(tool.schema.string(), tool.schema.any())
+ .optional()
+ .describe(
+ "An object containing detailed summaries or extractions of the key findings from the tools being pruned. This is REQUIRED for 'consolidation'.",
),
- metadata: tool.schema.object({
- reason: tool.schema.enum(["completion", "noise", "consolidation"]).describe("The reason for pruning"),
- distillation: tool.schema.record(tool.schema.string(), tool.schema.any()).optional().describe(
- "An object containing detailed summaries or extractions of the key findings from the tools being pruned. This is REQUIRED for 'consolidation'."
- ),
- }).describe("Metadata about the pruning operation."),
- },
- async execute(args, toolCtx) {
- const { client, state, logger, config, workingDirectory } = ctx
- const sessionId = toolCtx.sessionID
-
- logger.info("Prune tool invoked")
- logger.info(JSON.stringify(args))
-
- if (!args.ids || args.ids.length === 0) {
- logger.debug("Prune tool called but args.ids is empty or undefined: " + JSON.stringify(args))
- return "No IDs provided. Check the list for available IDs to prune."
- }
-
- if (!args.metadata || !args.metadata.reason) {
- logger.debug("Prune tool called without metadata.reason: " + JSON.stringify(args))
- return "Missing metadata.reason. Provide metadata: { reason: 'completion' | 'noise' | 'consolidation' }"
- }
-
- const { reason, distillation } = args.metadata;
-
- const numericToolIds: number[] = args.ids
- .map(id => parseInt(id, 10))
- .filter((n): n is number => !isNaN(n))
-
- if (numericToolIds.length === 0) {
- logger.debug("No numeric tool IDs provided for pruning, yet prune tool was called: " + JSON.stringify(args))
- return "No numeric IDs provided. Format: ids: [id1, id2, ...]"
- }
-
- // Fetch messages to calculate tokens and find current agent
- const messagesResponse = await client.session.messages({
- path: { id: sessionId }
- })
- const messages: WithParts[] = messagesResponse.data || messagesResponse
-
- await ensureSessionInitialized(ctx.client, state, sessionId, logger, messages)
-
- const currentParams = getCurrentParams(messages, logger)
- const toolIdList: string[] = buildToolIdList(state, messages, logger)
-
- // Validate that all numeric IDs are within bounds
- if (numericToolIds.some(id => id < 0 || id >= toolIdList.length)) {
- logger.debug("Invalid tool IDs provided: " + numericToolIds.join(", "))
- return "Invalid IDs provided. Only use numeric IDs from the list."
- }
-
- // Validate that all IDs exist in cache and aren't protected
- // (rejects hallucinated IDs and turn-protected tools not shown in )
- for (const index of numericToolIds) {
- const id = toolIdList[index]
- const metadata = state.toolParameters.get(id)
- if (!metadata) {
- logger.debug("Rejecting prune request - ID not in cache (turn-protected or hallucinated)", { index, id })
- return "Invalid IDs provided. Only use numeric IDs from the list."
- }
- if (config.strategies.pruneTool.protectedTools.includes(metadata.tool)) {
- logger.debug("Rejecting prune request - protected tool", { index, id, tool: metadata.tool })
- return "Invalid IDs provided. Only use numeric IDs from the list."
- }
- }
-
- const pruneToolIds: string[] = numericToolIds.map(index => toolIdList[index])
- state.prune.toolIds.push(...pruneToolIds)
-
- const toolMetadata = new Map()
- for (const id of pruneToolIds) {
- const toolParameters = state.toolParameters.get(id)
- if (toolParameters) {
- toolMetadata.set(id, toolParameters)
- } else {
- logger.debug("No metadata found for ID", { id })
- }
- }
-
- state.stats.pruneTokenCounter += calculateTokensSaved(state, messages, pruneToolIds)
-
- await sendUnifiedNotification(
- client,
- logger,
- config,
- state,
- sessionId,
- pruneToolIds,
- toolMetadata,
- reason as PruneReason,
- currentParams,
- workingDirectory
- )
-
- state.stats.totalPruneTokens += state.stats.pruneTokenCounter
- state.stats.pruneTokenCounter = 0
- state.nudgeCounter = 0
-
- saveSessionState(state, logger)
- .catch(err => logger.error("Failed to persist state", { error: err.message }))
-
- const result = formatPruningResultForTool(
- pruneToolIds,
- toolMetadata,
- workingDirectory
- )
-
- if (distillation) {
- logger.info("Distillation data received:", distillation)
- }
-
- return result
- },
- })
+ })
+ .describe("Metadata about the pruning operation."),
+ },
+ async execute(args, toolCtx) {
+ const { client, state, logger, config, workingDirectory } = ctx;
+ const sessionId = toolCtx.sessionID;
+
+ logger.info("Prune tool invoked");
+ logger.info(JSON.stringify(args));
+
+ if (!args.ids || args.ids.length === 0) {
+ logger.debug(
+ "Prune tool called but args.ids is empty or undefined: " +
+ JSON.stringify(args),
+ );
+ return "No IDs provided. Check the list for available IDs to prune.";
+ }
+
+ if (!args.metadata || !args.metadata.reason) {
+ logger.debug(
+ "Prune tool called without metadata.reason: " + JSON.stringify(args),
+ );
+ return "Missing metadata.reason. Provide metadata: { reason: 'completion' | 'noise' | 'consolidation' }";
+ }
+
+ const { reason, distillation } = args.metadata;
+
+ const numericToolIds: number[] = args.ids
+ .map((id) => parseInt(id, 10))
+ .filter((n): n is number => !isNaN(n));
+
+ if (numericToolIds.length === 0) {
+ logger.debug(
+ "No numeric tool IDs provided for pruning, yet prune tool was called: " +
+ JSON.stringify(args),
+ );
+ return "No numeric IDs provided. Format: ids: [id1, id2, ...]";
+ }
+
+ // Fetch messages to calculate tokens and find current agent
+ const messagesResponse = await client.session.messages({
+ path: { id: sessionId },
+ });
+ const messages: WithParts[] = messagesResponse.data || messagesResponse;
+
+ await ensureSessionInitialized(
+ ctx.client,
+ state,
+ sessionId,
+ logger,
+ messages,
+ );
+
+ const currentParams = getCurrentParams(messages, logger);
+ const toolIdList: string[] = buildToolIdList(state, messages, logger);
+
+ // Validate that all numeric IDs are within bounds
+ if (numericToolIds.some((id) => id < 0 || id >= toolIdList.length)) {
+ logger.debug("Invalid tool IDs provided: " + numericToolIds.join(", "));
+ return "Invalid IDs provided. Only use numeric IDs from the list.";
+ }
+
+ // Validate that all IDs exist in cache and aren't protected
+ // (rejects hallucinated IDs and turn-protected tools not shown in )
+ for (const index of numericToolIds) {
+ const id = toolIdList[index];
+ const metadata = state.toolParameters.get(id);
+ if (!metadata) {
+ logger.debug(
+ "Rejecting prune request - ID not in cache (turn-protected or hallucinated)",
+ { index, id },
+ );
+ return "Invalid IDs provided. Only use numeric IDs from the list.";
+ }
+ if (
+ config.strategies.pruneTool.protectedTools.includes(metadata.tool)
+ ) {
+ logger.debug("Rejecting prune request - protected tool", {
+ index,
+ id,
+ tool: metadata.tool,
+ });
+ return "Invalid IDs provided. Only use numeric IDs from the list.";
+ }
+ }
+
+ const pruneToolIds: string[] = numericToolIds.map(
+ (index) => toolIdList[index],
+ );
+
+ const toolMetadata = new Map();
+ for (const id of pruneToolIds) {
+ const toolParameters = state.toolParameters.get(id);
+ if (toolParameters) {
+ toolMetadata.set(id, toolParameters);
+ } else {
+ logger.debug("No metadata found for ID", { id });
+ }
+ }
+
+ // Request user confirmation before pruning
+ const confirmedIds = await requestPruneConfirmation(
+ client,
+ sessionId,
+ pruneToolIds,
+ toolMetadata,
+ currentParams,
+ logger,
+ workingDirectory,
+ );
+
+ if (confirmedIds.length === 0) {
+ logger.info("Prune cancelled by user");
+ return "Prune cancelled by user.";
+ }
+
+ // Use only the confirmed IDs
+ const finalPruneIds = confirmedIds;
+ state.prune.toolIds.push(...finalPruneIds);
+
+ // Recalculate metadata for confirmed IDs only
+ const finalMetadata = new Map();
+ for (const id of finalPruneIds) {
+ const entry = toolMetadata.get(id);
+ if (entry) finalMetadata.set(id, entry);
+ }
+
+ state.stats.pruneTokenCounter += calculateTokensSaved(
+ state,
+ messages,
+ finalPruneIds,
+ );
+
+ await sendUnifiedNotification(
+ client,
+ logger,
+ config,
+ state,
+ sessionId,
+ finalPruneIds,
+ finalMetadata,
+ reason as PruneReason,
+ currentParams,
+ workingDirectory,
+ );
+
+ state.stats.totalPruneTokens += state.stats.pruneTokenCounter;
+ state.stats.pruneTokenCounter = 0;
+ state.nudgeCounter = 0;
+
+ saveSessionState(state, logger).catch((err) =>
+ logger.error("Failed to persist state", { error: err.message }),
+ );
+
+ const result = formatPruningResultForTool(
+ finalPruneIds,
+ finalMetadata,
+ workingDirectory,
+ );
+
+ if (distillation) {
+ logger.info("Distillation data received:", distillation);
+ }
+
+ return result;
+ },
+ });
}
-
diff --git a/lib/ui/confirmation.ts b/lib/ui/confirmation.ts
new file mode 100644
index 0000000..ebed9c7
--- /dev/null
+++ b/lib/ui/confirmation.ts
@@ -0,0 +1,95 @@
+import type { Logger } from "../logger";
+import type { ToolParameterEntry } from "../state";
+import { extractParameterKey } from "../messages/utils";
+import { shortenPath, truncate } from "./utils";
+
+export interface PendingConfirmation {
+ resolve: (confirmedIds: string[]) => void;
+ items: Array<{ id: string; label: string; checked: boolean }>;
+}
+
+// Shared state for pending confirmations
+let pendingPrune: PendingConfirmation | null = null;
+
+export function getPendingPrune(): PendingConfirmation | null {
+ return pendingPrune;
+}
+
+export function setPendingPrune(pending: PendingConfirmation | null): void {
+ pendingPrune = pending;
+}
+
+export function resolvePendingPrune(confirmedIds: string[]): void {
+ if (pendingPrune) {
+ pendingPrune.resolve(confirmedIds);
+ pendingPrune = null;
+ }
+}
+
+/**
+ * Shows a confirmation UI for pruning and returns a Promise that resolves
+ * with the list of confirmed tool IDs (or empty array if cancelled).
+ */
+export async function requestPruneConfirmation(
+ client: any,
+ sessionId: string,
+ pruneToolIds: string[],
+ toolMetadata: Map,
+ params: any,
+ logger: Logger,
+ workingDirectory: string,
+): Promise {
+ // Build checklist items from the tool metadata
+ const items = pruneToolIds.map((id) => {
+ const meta = toolMetadata.get(id);
+ let label = id;
+ if (meta) {
+ const paramKey = extractParameterKey(meta.tool, meta.parameters);
+ if (paramKey) {
+ label = `${meta.tool}: ${truncate(shortenPath(paramKey, workingDirectory), 50)}`;
+ } else {
+ label = meta.tool;
+ }
+ }
+ return { id, label, checked: true };
+ });
+
+ logger.info("Requesting prune confirmation", { itemCount: items.length });
+
+ // Create the promise that will be resolved by UI events
+ return new Promise((resolve) => {
+ setPendingPrune({ resolve, items });
+
+ const agent = params.agent || undefined;
+ const model =
+ params.providerId && params.modelId
+ ? { providerID: params.providerId, modelID: params.modelId }
+ : undefined;
+
+ // Send the confirmation UI message
+ client.session
+ .prompt({
+ path: { id: sessionId },
+ body: {
+ noReply: true,
+ agent,
+ model,
+ parts: [
+ {
+ type: "text",
+ text: "dcp-confirm",
+ plugin: true,
+ metadata: { items },
+ },
+ ],
+ },
+ })
+ .catch((error: any) => {
+ logger.error("Failed to send confirmation UI", {
+ error: error.message,
+ });
+ resolve([]); // Resolve with empty on error
+ setPendingPrune(null);
+ });
+ });
+}
diff --git a/lib/ui/dcp-status.js b/lib/ui/dcp-status.js
deleted file mode 100644
index 3eefcee..0000000
--- a/lib/ui/dcp-status.js
+++ /dev/null
@@ -1,12 +0,0 @@
-class DcpStatus extends HTMLElement {
- connectedCallback() {
- this.innerHTML = `
-
-
DCP Status
-
Tokens saved in this session: ${this.getAttribute("saved") || 0}
-
- `;
- }
-}
-
-customElements.define("dcp-status", DcpStatus);
diff --git a/lib/ui/notification.ts b/lib/ui/notification.ts
index 08e296b..f9327ca 100644
--- a/lib/ui/notification.ts
+++ b/lib/ui/notification.ts
@@ -120,6 +120,30 @@ export async function sendIgnoredMessage(
}
: undefined;
+ const parts: any[] = [
+ {
+ type: "text",
+ text: text,
+ ignored: true,
+ },
+ ];
+
+ // Add plugin UI part if we have saved tokens
+ if (totalSaved && totalSaved > 0) {
+ const formatted =
+ totalSaved >= 1000
+ ? `~${(totalSaved / 1000).toFixed(1)}K`
+ : `${totalSaved}`;
+ parts.push({
+ type: "text",
+ text: "dcp-status",
+ plugin: true,
+ metadata: {
+ saved: formatted,
+ },
+ });
+ }
+
try {
await client.session.prompt({
path: {
@@ -129,21 +153,7 @@ export async function sendIgnoredMessage(
noReply: true,
agent: agent,
model: model,
- parts: [
- {
- type: "text",
- text: text,
- ignored: true,
- },
- {
- type: "text",
- text: "dcp-status",
- plugin: true,
- metadata: {
- saved: totalSaved ?? 0,
- },
- },
- ],
+ parts: parts,
},
});
} catch (error: any) {
From 0ae469d616c7f40d8ee4313bdabd9f86689cf943 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Fri, 19 Dec 2025 17:34:28 +0100
Subject: [PATCH 03/13] component
---
index.ts | 48 +++++++++++++++++++++---------------------------
1 file changed, 21 insertions(+), 27 deletions(-)
diff --git a/index.ts b/index.ts
index 3c45c7c..bd3f0a6 100644
--- a/index.ts
+++ b/index.ts
@@ -58,12 +58,14 @@ const plugin: Plugin = (async (ctx) => {
template: {
type: "box",
direction: "column",
- border: ["top", "bottom", "left", "right"],
- borderStyle: "rounded",
+ border: ["left"],
+ borderStyle: "heavy",
borderColor: "accent",
- bg: "#1a1a2e",
+ bg: "backgroundPanel",
paddingX: 2,
- paddingY: 1,
+ paddingY: 0,
+ minWidth: 60,
+ alignSelf: "center",
gap: 1,
children: [
{
@@ -71,50 +73,42 @@ const plugin: Plugin = (async (ctx) => {
direction: "row",
gap: 1,
children: [
- { type: "text", content: "📦", fg: "accent" },
{
type: "text",
- content: "DCP — Select files to prune",
- fg: "accent",
+ content: "Select files to prune",
+ fg: "text",
bold: true,
},
],
},
- {
- type: "text",
- content: "─────────────────────────────────",
- fg: "textMuted",
- },
{
type: "checklist",
items: "{{items}}",
- fg: "text",
- fgChecked: "#2ecc71",
- onToggle: "item-toggled",
- },
- {
- type: "text",
- content: "─────────────────────────────────",
fg: "textMuted",
+ fgChecked: "text",
+ bgChecked: "backgroundElement",
+ borderColorChecked: "accent",
+ onToggle: "item-toggled",
},
{
type: "box",
direction: "row",
gap: 2,
+ justifyContent: "flex-end",
children: [
{
type: "confirm-button",
- label: " ✓ Confirm Prune ",
- fg: "#1a1a2e",
- bg: "#2ecc71",
- onConfirm: "confirm-prune",
+ label: " Cancel ",
+ fg: "textMuted",
+ bg: "backgroundPanel",
+ onConfirm: "cancel-prune",
},
{
type: "confirm-button",
- label: " ✗ Cancel ",
- fg: "text",
- bg: "#e94560",
- onConfirm: "cancel-prune",
+ label: " Confirm ",
+ fg: "background",
+ bg: "accent",
+ onConfirm: "confirm-prune",
},
],
},
From ea86954ef09ba9679ace4dec8d634ae46ee5d187 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Fri, 19 Dec 2025 17:55:41 +0100
Subject: [PATCH 04/13] feat: auto prune
---
index.ts | 30 ++++++++++-
lib/hooks.ts | 119 +++++++++++++++++++++--------------------
lib/ui/confirmation.ts | 18 +++++++
3 files changed, 107 insertions(+), 60 deletions(-)
diff --git a/index.ts b/index.ts
index bd3f0a6..fea8658 100644
--- a/index.ts
+++ b/index.ts
@@ -8,7 +8,11 @@ import {
createChatMessageTransformHandler,
createEventHandler,
} from "./lib/hooks";
-import { getPendingPrune, setPendingPrune } from "./lib/ui/confirmation";
+import {
+ getPendingPrune,
+ setPendingPrune,
+ setAutoConfirm,
+} from "./lib/ui/confirmation";
const plugin: Plugin = (async (ctx) => {
const config = getConfig(ctx);
@@ -103,6 +107,13 @@ const plugin: Plugin = (async (ctx) => {
bg: "backgroundPanel",
onConfirm: "cancel-prune",
},
+ {
+ type: "confirm-button",
+ label: " Auto ",
+ fg: "background",
+ bg: "warning",
+ onConfirm: "auto-prune",
+ },
{
type: "confirm-button",
label: " Confirm ",
@@ -137,6 +148,14 @@ const plugin: Plugin = (async (ctx) => {
} else if (event.event === "cancel-prune" && pending) {
pending.resolve([]);
setPendingPrune(null);
+ } else if (event.event === "auto-prune" && pending) {
+ // Enable auto-confirm and confirm this one
+ setAutoConfirm(true);
+ const confirmed = pending.items
+ .filter((i: { checked: boolean }) => i.checked)
+ .map((i: { id: string }) => i.id);
+ pending.resolve(confirmed);
+ setPendingPrune(null);
}
}
},
@@ -179,7 +198,14 @@ const plugin: Plugin = (async (ctx) => {
);
}
},
- event: createEventHandler(ctx.client, config, state, logger, ctx.directory),
+ event: createEventHandler(
+ ctx.client,
+ config,
+ state,
+ logger,
+ ctx.directory,
+ () => setAutoConfirm(false),
+ ),
};
}) satisfies Plugin;
diff --git a/lib/hooks.ts b/lib/hooks.ts
index c578e4b..0209ddb 100644
--- a/lib/hooks.ts
+++ b/lib/hooks.ts
@@ -1,74 +1,77 @@
-import type { SessionState, WithParts } from "./state"
-import type { Logger } from "./logger"
-import type { PluginConfig } from "./config"
-import { syncToolCache } from "./state/tool-cache"
-import { deduplicate, supersedeWrites } from "./strategies"
-import { prune, insertPruneToolContext } from "./messages"
-import { checkSession } from "./state"
-import { runOnIdle } from "./strategies/on-idle"
-
+import type { SessionState, WithParts } from "./state";
+import type { Logger } from "./logger";
+import type { PluginConfig } from "./config";
+import { syncToolCache } from "./state/tool-cache";
+import { deduplicate, supersedeWrites } from "./strategies";
+import { prune, insertPruneToolContext } from "./messages";
+import { checkSession } from "./state";
+import { runOnIdle } from "./strategies/on-idle";
export function createChatMessageTransformHandler(
- client: any,
- state: SessionState,
- logger: Logger,
- config: PluginConfig
+ client: any,
+ state: SessionState,
+ logger: Logger,
+ config: PluginConfig,
) {
- return async (
- input: {},
- output: { messages: WithParts[] }
- ) => {
- await checkSession(client, state, logger, output.messages)
+ return async (input: {}, output: { messages: WithParts[] }) => {
+ await checkSession(client, state, logger, output.messages);
- if (state.isSubAgent) {
- return
- }
+ if (state.isSubAgent) {
+ return;
+ }
- syncToolCache(state, config, logger, output.messages);
+ syncToolCache(state, config, logger, output.messages);
- deduplicate(state, logger, config, output.messages)
- supersedeWrites(state, logger, config, output.messages)
+ deduplicate(state, logger, config, output.messages);
+ supersedeWrites(state, logger, config, output.messages);
- prune(state, logger, config, output.messages)
+ prune(state, logger, config, output.messages);
- insertPruneToolContext(state, config, logger, output.messages)
- }
+ insertPruneToolContext(state, config, logger, output.messages);
+ };
}
export function createEventHandler(
- client: any,
- config: PluginConfig,
- state: SessionState,
- logger: Logger,
- workingDirectory?: string
+ client: any,
+ config: PluginConfig,
+ state: SessionState,
+ logger: Logger,
+ workingDirectory?: string,
+ onUserMessage?: () => void,
) {
- return async (
- { event }: { event: any }
- ) => {
- if (state.sessionId === null || state.isSubAgent) {
- return
- }
+ return async ({ event }: { event: any }) => {
+ if (state.sessionId === null || state.isSubAgent) {
+ return;
+ }
+
+ // Reset auto-confirm on user message
+ if (
+ event.type === "message.part.added" &&
+ event.properties?.part?.type === "text"
+ ) {
+ const role = event.properties?.role;
+ if (role === "user" && onUserMessage) {
+ onUserMessage();
+ }
+ }
- if (event.type === "session.status" && event.properties.status.type === "idle") {
- if (!config.strategies.onIdle.enabled) {
- return
- }
- if (state.lastToolPrune) {
- logger.info("Skipping OnIdle pruning - last tool was prune")
- return
- }
+ if (
+ event.type === "session.status" &&
+ event.properties.status.type === "idle"
+ ) {
+ if (!config.strategies.onIdle.enabled) {
+ return;
+ }
+ if (state.lastToolPrune) {
+ logger.info("Skipping OnIdle pruning - last tool was prune");
+ return;
+ }
- try {
- await runOnIdle(
- client,
- state,
- logger,
- config,
- workingDirectory
- )
- } catch (err: any) {
- logger.error("OnIdle pruning failed", { error: err.message })
- }
- }
+ try {
+ await runOnIdle(client, state, logger, config, workingDirectory);
+ } catch (err: any) {
+ logger.error("OnIdle pruning failed", { error: err.message });
+ }
}
+ };
}
diff --git a/lib/ui/confirmation.ts b/lib/ui/confirmation.ts
index ebed9c7..48f33b2 100644
--- a/lib/ui/confirmation.ts
+++ b/lib/ui/confirmation.ts
@@ -11,6 +11,9 @@ export interface PendingConfirmation {
// Shared state for pending confirmations
let pendingPrune: PendingConfirmation | null = null;
+// Auto-confirm mode - when true, automatically confirms all prunes
+let autoConfirmEnabled = false;
+
export function getPendingPrune(): PendingConfirmation | null {
return pendingPrune;
}
@@ -19,6 +22,14 @@ export function setPendingPrune(pending: PendingConfirmation | null): void {
pendingPrune = pending;
}
+export function isAutoConfirmEnabled(): boolean {
+ return autoConfirmEnabled;
+}
+
+export function setAutoConfirm(enabled: boolean): void {
+ autoConfirmEnabled = enabled;
+}
+
export function resolvePendingPrune(confirmedIds: string[]): void {
if (pendingPrune) {
pendingPrune.resolve(confirmedIds);
@@ -29,6 +40,7 @@ export function resolvePendingPrune(confirmedIds: string[]): void {
/**
* Shows a confirmation UI for pruning and returns a Promise that resolves
* with the list of confirmed tool IDs (or empty array if cancelled).
+ * If auto-confirm is enabled, immediately returns all IDs without showing UI.
*/
export async function requestPruneConfirmation(
client: any,
@@ -39,6 +51,12 @@ export async function requestPruneConfirmation(
logger: Logger,
workingDirectory: string,
): Promise {
+ // If auto-confirm is enabled, immediately return all IDs
+ if (autoConfirmEnabled) {
+ logger.info("Auto-confirming prune", { itemCount: pruneToolIds.length });
+ return pruneToolIds;
+ }
+
// Build checklist items from the tool metadata
const items = pruneToolIds.map((id) => {
const meta = toolMetadata.get(id);
From 0f6388021eec33f102796e679ce2868f67e190d6 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Sat, 20 Dec 2025 01:19:47 +0100
Subject: [PATCH 05/13] component
---
index.ts | 146 ++++++++++++++++++++++++++++++-------------------------
1 file changed, 79 insertions(+), 67 deletions(-)
diff --git a/index.ts b/index.ts
index fea8658..1edeaf6 100644
--- a/index.ts
+++ b/index.ts
@@ -1,9 +1,9 @@
-import type { Plugin } from "@opencode-ai/plugin";
-import { getConfig } from "./lib/config";
-import { Logger } from "./lib/logger";
-import { loadPrompt } from "./lib/prompt";
-import { createSessionState } from "./lib/state";
-import { createPruneTool } from "./lib/strategies";
+import type {Plugin} from "@opencode-ai/plugin";
+import {getConfig} from "./lib/config";
+import {Logger} from "./lib/logger";
+import {loadPrompt} from "./lib/prompt";
+import {createSessionState} from "./lib/state";
+import {createPruneTool} from "./lib/strategies";
import {
createChatMessageTransformHandler,
createEventHandler,
@@ -37,45 +37,21 @@ const plugin: Plugin = (async (ctx) => {
return {
ui: [
- {
- name: "dcp-status",
- template: {
- type: "box",
- direction: "row",
- border: ["right"],
- borderStyle: "heavy",
- borderColor: "accent",
- bg: "#1a1a2e",
- paddingX: 1,
- paddingY: 0,
- gap: 1,
- children: [
- { type: "text", content: "📦 DCP", fg: "accent", bold: true },
- { type: "text", content: " │ ", fg: "textMuted" },
- { type: "text", content: "{{saved}}", fg: "accent", bold: true },
- { type: "text", content: " saved", fg: "textMuted" },
- ],
- },
- },
{
name: "dcp-confirm",
template: {
type: "box",
direction: "column",
- border: ["left"],
- borderStyle: "heavy",
- borderColor: "accent",
bg: "backgroundPanel",
- paddingX: 2,
paddingY: 0,
- minWidth: 60,
- alignSelf: "center",
- gap: 1,
+ gap: 0,
children: [
{
type: "box",
direction: "row",
gap: 1,
+ paddingX: 2,
+ paddingY: 1,
children: [
{
type: "text",
@@ -85,41 +61,77 @@ const plugin: Plugin = (async (ctx) => {
},
],
},
- {
- type: "checklist",
- items: "{{items}}",
- fg: "textMuted",
- fgChecked: "text",
- bgChecked: "backgroundElement",
- borderColorChecked: "accent",
- onToggle: "item-toggled",
- },
{
type: "box",
- direction: "row",
- gap: 2,
- justifyContent: "flex-end",
+ direction: "column",
+ paddingX: 2,
+ paddingBottom: 1,
children: [
{
- type: "confirm-button",
- label: " Cancel ",
+ type: "checklist",
+ items: "{{items}}",
fg: "textMuted",
- bg: "backgroundPanel",
- onConfirm: "cancel-prune",
+ fgChecked: "text",
+ borderColorChecked: "warning",
+ onToggle: "item-toggled",
},
+ ],
+ },
+ {
+ type: "box",
+ direction: "row",
+ gap: 0,
+ justifyContent: "space-between",
+ bg: "backgroundElement",
+ paddingY: 1,
+ paddingX: 2,
+ children: [
{
- type: "confirm-button",
- label: " Auto ",
- fg: "background",
- bg: "warning",
- onConfirm: "auto-prune",
+ type: "box",
+ direction: "row",
+ gap: 0,
+ children: [
+ {
+ type: "text",
+ content: "click items to select",
+ fg: "textMuted",
+ },
+ ],
},
{
- type: "confirm-button",
- label: " Confirm ",
- fg: "background",
- bg: "accent",
- onConfirm: "confirm-prune",
+ type: "button-group",
+ gap: 1,
+ defaultIndex: 2,
+ bgColor: "backgroundElement",
+ children: [
+ {
+ type: "confirm-button",
+ label: "Reject",
+ fg: "textMuted",
+ bg: "backgroundPanel",
+ fgHover: "warning",
+ borderColorHover: "warning",
+ onConfirm: "cancel-prune",
+ },
+ {
+ type: "confirm-button",
+ label: "Auto",
+ fg: "textMuted",
+ bg: "backgroundPanel",
+ fgHover: "warning",
+ borderColorHover: "warning",
+ onConfirm: "auto-prune",
+ },
+ {
+ type: "confirm-button",
+ label: "Confirm",
+ fg: "textMuted",
+ bg: "backgroundPanel",
+ fgHover: "warning",
+ borderColorHover: "warning",
+ onConfirm: "confirm-prune",
+ },
+ ],
},
],
},
@@ -141,8 +153,8 @@ const plugin: Plugin = (async (ctx) => {
pending.items = event.data.items;
} else if (event.event === "confirm-prune" && pending) {
const confirmed = pending.items
- .filter((i: { checked: boolean }) => i.checked)
- .map((i: { id: string }) => i.id);
+ .filter((i: {checked: boolean}) => i.checked)
+ .map((i: {id: string}) => i.id);
pending.resolve(confirmed);
setPendingPrune(null);
} else if (event.event === "cancel-prune" && pending) {
@@ -152,8 +164,8 @@ const plugin: Plugin = (async (ctx) => {
// Enable auto-confirm and confirm this one
setAutoConfirm(true);
const confirmed = pending.items
- .filter((i: { checked: boolean }) => i.checked)
- .map((i: { id: string }) => i.id);
+ .filter((i: {checked: boolean}) => i.checked)
+ .map((i: {id: string}) => i.id);
pending.resolve(confirmed);
setPendingPrune(null);
}
@@ -161,7 +173,7 @@ const plugin: Plugin = (async (ctx) => {
},
"experimental.chat.system.transform": async (
_input: unknown,
- output: { system: string[] },
+ output: {system: string[]}
) => {
const syntheticPrompt = loadPrompt("prune-system-prompt");
output.system.push(syntheticPrompt);
@@ -170,7 +182,7 @@ const plugin: Plugin = (async (ctx) => {
ctx.client,
state,
logger,
- config,
+ config
),
tool: config.strategies.pruneTool.enabled
? {
@@ -194,7 +206,7 @@ const plugin: Plugin = (async (ctx) => {
primary_tools: [...existingPrimaryTools, "prune"],
};
logger.info(
- "Added 'prune' to experimental.primary_tools via config mutation",
+ "Added 'prune' to experimental.primary_tools via config mutation"
);
}
},
@@ -204,7 +216,7 @@ const plugin: Plugin = (async (ctx) => {
state,
logger,
ctx.directory,
- () => setAutoConfirm(false),
+ () => setAutoConfirm(false)
),
};
}) satisfies Plugin;
From 326a510c063398a2b67a9f3f4f624c8ee5e1c620 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Sat, 20 Dec 2025 19:07:43 +0100
Subject: [PATCH 06/13] stuff
---
index.ts | 28 ++++++++++++++--------------
lib/ui/confirmation.ts | 34 +++++++++++++++++++---------------
2 files changed, 33 insertions(+), 29 deletions(-)
diff --git a/index.ts b/index.ts
index 1edeaf6..8e00574 100644
--- a/index.ts
+++ b/index.ts
@@ -1,9 +1,9 @@
-import type {Plugin} from "@opencode-ai/plugin";
-import {getConfig} from "./lib/config";
-import {Logger} from "./lib/logger";
-import {loadPrompt} from "./lib/prompt";
-import {createSessionState} from "./lib/state";
-import {createPruneTool} from "./lib/strategies";
+import type { Plugin } from "@opencode-ai/plugin";
+import { getConfig } from "./lib/config";
+import { Logger } from "./lib/logger";
+import { loadPrompt } from "./lib/prompt";
+import { createSessionState } from "./lib/state";
+import { createPruneTool } from "./lib/strategies";
import {
createChatMessageTransformHandler,
createEventHandler,
@@ -153,8 +153,8 @@ const plugin: Plugin = (async (ctx) => {
pending.items = event.data.items;
} else if (event.event === "confirm-prune" && pending) {
const confirmed = pending.items
- .filter((i: {checked: boolean}) => i.checked)
- .map((i: {id: string}) => i.id);
+ .filter((i: { checked: boolean }) => i.checked)
+ .map((i: { id: string }) => i.id);
pending.resolve(confirmed);
setPendingPrune(null);
} else if (event.event === "cancel-prune" && pending) {
@@ -164,8 +164,8 @@ const plugin: Plugin = (async (ctx) => {
// Enable auto-confirm and confirm this one
setAutoConfirm(true);
const confirmed = pending.items
- .filter((i: {checked: boolean}) => i.checked)
- .map((i: {id: string}) => i.id);
+ .filter((i: { checked: boolean }) => i.checked)
+ .map((i: { id: string }) => i.id);
pending.resolve(confirmed);
setPendingPrune(null);
}
@@ -173,7 +173,7 @@ const plugin: Plugin = (async (ctx) => {
},
"experimental.chat.system.transform": async (
_input: unknown,
- output: {system: string[]}
+ output: { system: string[] },
) => {
const syntheticPrompt = loadPrompt("prune-system-prompt");
output.system.push(syntheticPrompt);
@@ -182,7 +182,7 @@ const plugin: Plugin = (async (ctx) => {
ctx.client,
state,
logger,
- config
+ config,
),
tool: config.strategies.pruneTool.enabled
? {
@@ -206,7 +206,7 @@ const plugin: Plugin = (async (ctx) => {
primary_tools: [...existingPrimaryTools, "prune"],
};
logger.info(
- "Added 'prune' to experimental.primary_tools via config mutation"
+ "Added 'prune' to experimental.primary_tools via config mutation",
);
}
},
@@ -216,7 +216,7 @@ const plugin: Plugin = (async (ctx) => {
state,
logger,
ctx.directory,
- () => setAutoConfirm(false)
+ () => setAutoConfirm(false),
),
};
}) satisfies Plugin;
diff --git a/lib/ui/confirmation.ts b/lib/ui/confirmation.ts
index 48f33b2..0eeca2c 100644
--- a/lib/ui/confirmation.ts
+++ b/lib/ui/confirmation.ts
@@ -1,11 +1,11 @@
-import type { Logger } from "../logger";
-import type { ToolParameterEntry } from "../state";
-import { extractParameterKey } from "../messages/utils";
-import { shortenPath, truncate } from "./utils";
+import type {Logger} from "../logger";
+import type {ToolParameterEntry} from "../state";
+import {extractParameterKey} from "../messages/utils";
+import {shortenPath, truncate} from "./utils";
export interface PendingConfirmation {
resolve: (confirmedIds: string[]) => void;
- items: Array<{ id: string; label: string; checked: boolean }>;
+ items: Array<{id: string; label: string; checked: boolean}>;
}
// Shared state for pending confirmations
@@ -49,11 +49,11 @@ export async function requestPruneConfirmation(
toolMetadata: Map,
params: any,
logger: Logger,
- workingDirectory: string,
+ workingDirectory: string
): Promise {
// If auto-confirm is enabled, immediately return all IDs
if (autoConfirmEnabled) {
- logger.info("Auto-confirming prune", { itemCount: pruneToolIds.length });
+ logger.info("Auto-confirming prune", {itemCount: pruneToolIds.length});
return pruneToolIds;
}
@@ -62,32 +62,36 @@ export async function requestPruneConfirmation(
const meta = toolMetadata.get(id);
let label = id;
if (meta) {
+ const toolName = meta.tool.charAt(0).toUpperCase() + meta.tool.slice(1);
const paramKey = extractParameterKey(meta.tool, meta.parameters);
if (paramKey) {
- label = `${meta.tool}: ${truncate(shortenPath(paramKey, workingDirectory), 50)}`;
+ label = `${toolName} ${truncate(
+ shortenPath(paramKey, workingDirectory),
+ 50
+ )}`;
} else {
- label = meta.tool;
+ label = `${toolName}`;
}
}
- return { id, label, checked: true };
+ return {id, label, checked: true};
});
- logger.info("Requesting prune confirmation", { itemCount: items.length });
+ logger.info("Requesting prune confirmation", {itemCount: items.length});
// Create the promise that will be resolved by UI events
return new Promise((resolve) => {
- setPendingPrune({ resolve, items });
+ setPendingPrune({resolve, items});
const agent = params.agent || undefined;
const model =
params.providerId && params.modelId
- ? { providerID: params.providerId, modelID: params.modelId }
+ ? {providerID: params.providerId, modelID: params.modelId}
: undefined;
// Send the confirmation UI message
client.session
.prompt({
- path: { id: sessionId },
+ path: {id: sessionId},
body: {
noReply: true,
agent,
@@ -97,7 +101,7 @@ export async function requestPruneConfirmation(
type: "text",
text: "dcp-confirm",
plugin: true,
- metadata: { items },
+ metadata: {items},
},
],
},
From 758357c739db26097a32c9c7ce0acce818d7ca79 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Mon, 22 Dec 2025 01:33:49 +0100
Subject: [PATCH 07/13] replacae input
---
index.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/index.ts b/index.ts
index 8e00574..6d15b98 100644
--- a/index.ts
+++ b/index.ts
@@ -39,6 +39,7 @@ const plugin: Plugin = (async (ctx) => {
ui: [
{
name: "dcp-confirm",
+ replaceInput: true,
template: {
type: "box",
direction: "column",
From ad824e3855fa6cee0fa662f09068e73b3e41ab87 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Mon, 22 Dec 2025 02:11:17 +0100
Subject: [PATCH 08/13] idk
---
index.ts | 49 +++++++++++++++++
lib/ui/notification.ts | 119 ++++++++++-------------------------------
2 files changed, 78 insertions(+), 90 deletions(-)
diff --git a/index.ts b/index.ts
index 6d15b98..d190250 100644
--- a/index.ts
+++ b/index.ts
@@ -37,6 +37,55 @@ const plugin: Plugin = (async (ctx) => {
return {
ui: [
+ {
+ name: "dcp-prune-summary",
+ template: {
+ type: "box",
+ direction: "column",
+ gap: 0,
+ children: [
+ {
+ type: "box",
+ direction: "row",
+ gap: 1,
+ children: [
+ {
+ type: "text",
+ content: "▣",
+ fg: "textMuted",
+ },
+ {
+ type: "text",
+ content: "DCP",
+ fg: "text",
+ bold: true,
+ },
+ {
+ type: "text",
+ content: "{{saved}} saved",
+ fg: "textMuted",
+ },
+ ],
+ },
+ {
+ type: "collapsible",
+ title: "{{count}} items pruned",
+ expanded: false,
+ fg: "textMuted",
+ fgExpanded: "text",
+ icon: "▶",
+ iconExpanded: "▼",
+ children: [
+ {
+ type: "text",
+ content: "{{itemsList}}",
+ fg: "textMuted",
+ },
+ ],
+ },
+ ],
+ },
+ },
{
name: "dcp-confirm",
replaceInput: true,
diff --git a/lib/ui/notification.ts b/lib/ui/notification.ts
index f9327ca..1680c36 100644
--- a/lib/ui/notification.ts
+++ b/lib/ui/notification.ts
@@ -1,64 +1,10 @@
import type { Logger } from "../logger";
import type { SessionState } from "../state";
-import { formatPrunedItemsList, formatTokenCount } from "./utils";
+import { formatPrunedItemsList } from "./utils";
import { ToolParameterEntry } from "../state";
import { PluginConfig } from "../config";
export type PruneReason = "completion" | "noise" | "consolidation";
-export const PRUNE_REASON_LABELS: Record = {
- completion: "Task Complete",
- noise: "Noise Removal",
- consolidation: "Consolidation",
-};
-
-function formatStatsHeader(
- totalTokensSaved: number,
- pruneTokenCounter: number,
-): string {
- const totalTokensSavedStr = `~${formatTokenCount(totalTokensSaved + pruneTokenCounter)}`;
- return [`▣ DCP | ${totalTokensSavedStr} saved total`].join("\n");
-}
-
-function buildMinimalMessage(
- state: SessionState,
- reason: PruneReason | undefined,
-): string {
- const reasonSuffix = reason ? ` [${PRUNE_REASON_LABELS[reason]}]` : "";
- return (
- formatStatsHeader(
- state.stats.totalPruneTokens,
- state.stats.pruneTokenCounter,
- ) + reasonSuffix
- );
-}
-
-function buildDetailedMessage(
- state: SessionState,
- reason: PruneReason | undefined,
- pruneToolIds: string[],
- toolMetadata: Map,
- workingDirectory?: string,
-): string {
- let message = formatStatsHeader(
- state.stats.totalPruneTokens,
- state.stats.pruneTokenCounter,
- );
-
- if (pruneToolIds.length > 0) {
- const pruneTokenCounterStr = `~${formatTokenCount(state.stats.pruneTokenCounter)}`;
- const reasonLabel = reason ? ` — ${PRUNE_REASON_LABELS[reason]}` : "";
- message += `\n\n▣ Pruning (${pruneTokenCounterStr})${reasonLabel}`;
-
- const itemLines = formatPrunedItemsList(
- pruneToolIds,
- toolMetadata,
- workingDirectory,
- );
- message += "\n" + itemLines.join("\n");
- }
-
- return message.trim();
-}
export async function sendUnifiedNotification(
client: any,
@@ -81,35 +27,34 @@ export async function sendUnifiedNotification(
return false;
}
- const message =
- config.pruningSummary === "minimal"
- ? buildMinimalMessage(state, reason)
- : buildDetailedMessage(
- state,
- reason,
- pruneToolIds,
- toolMetadata,
- workingDirectory,
- );
+ const totalSaved =
+ state.stats.totalPruneTokens + state.stats.pruneTokenCounter;
+ const itemLines = formatPrunedItemsList(
+ pruneToolIds,
+ toolMetadata,
+ workingDirectory,
+ );
- await sendIgnoredMessage(
+ await sendPruneSummary(
client,
sessionId,
- message,
params,
logger,
- state.stats.totalPruneTokens + state.stats.pruneTokenCounter,
+ totalSaved,
+ pruneToolIds.length,
+ itemLines,
);
return true;
}
-export async function sendIgnoredMessage(
+export async function sendPruneSummary(
client: any,
- sessionID: string,
- text: string,
+ sessionId: string,
params: any,
logger: Logger,
- totalSaved?: number,
+ totalSaved: number,
+ count: number,
+ itemLines: string[],
): Promise {
const agent = params.agent || undefined;
const model =
@@ -120,34 +65,28 @@ export async function sendIgnoredMessage(
}
: undefined;
+ const formatted =
+ totalSaved >= 1000
+ ? `~${(totalSaved / 1000).toFixed(1)}K`
+ : `${totalSaved}`;
+
const parts: any[] = [
{
type: "text",
- text: text,
- ignored: true,
- },
- ];
-
- // Add plugin UI part if we have saved tokens
- if (totalSaved && totalSaved > 0) {
- const formatted =
- totalSaved >= 1000
- ? `~${(totalSaved / 1000).toFixed(1)}K`
- : `${totalSaved}`;
- parts.push({
- type: "text",
- text: "dcp-status",
+ text: "dcp-prune-summary",
plugin: true,
metadata: {
saved: formatted,
+ count: String(count),
+ itemsList: itemLines.join("\n"),
},
- });
- }
+ },
+ ];
try {
await client.session.prompt({
path: {
- id: sessionID,
+ id: sessionId,
},
body: {
noReply: true,
@@ -157,6 +96,6 @@ export async function sendIgnoredMessage(
},
});
} catch (error: any) {
- logger.error("Failed to send notification", { error: error.message });
+ logger.error("Failed to send prune summary", { error: error.message });
}
}
From 74fb7debd0fe252f912ffbb5960c62e6ab4b8fec Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Mon, 22 Dec 2025 02:50:01 +0100
Subject: [PATCH 09/13] inline summary
---
index.ts | 29 +++++------
lib/ui/utils.ts | 129 ++++++++++++++++++++++++++----------------------
2 files changed, 84 insertions(+), 74 deletions(-)
diff --git a/index.ts b/index.ts
index d190250..ec6d868 100644
--- a/index.ts
+++ b/index.ts
@@ -1,9 +1,9 @@
-import type { Plugin } from "@opencode-ai/plugin";
-import { getConfig } from "./lib/config";
-import { Logger } from "./lib/logger";
-import { loadPrompt } from "./lib/prompt";
-import { createSessionState } from "./lib/state";
-import { createPruneTool } from "./lib/strategies";
+import type {Plugin} from "@opencode-ai/plugin";
+import {getConfig} from "./lib/config";
+import {Logger} from "./lib/logger";
+import {loadPrompt} from "./lib/prompt";
+import {createSessionState} from "./lib/state";
+import {createPruneTool} from "./lib/strategies";
import {
createChatMessageTransformHandler,
createEventHandler,
@@ -39,6 +39,7 @@ const plugin: Plugin = (async (ctx) => {
ui: [
{
name: "dcp-prune-summary",
+ inline: true,
template: {
type: "box",
direction: "column",
@@ -203,8 +204,8 @@ const plugin: Plugin = (async (ctx) => {
pending.items = event.data.items;
} else if (event.event === "confirm-prune" && pending) {
const confirmed = pending.items
- .filter((i: { checked: boolean }) => i.checked)
- .map((i: { id: string }) => i.id);
+ .filter((i: {checked: boolean}) => i.checked)
+ .map((i: {id: string}) => i.id);
pending.resolve(confirmed);
setPendingPrune(null);
} else if (event.event === "cancel-prune" && pending) {
@@ -214,8 +215,8 @@ const plugin: Plugin = (async (ctx) => {
// Enable auto-confirm and confirm this one
setAutoConfirm(true);
const confirmed = pending.items
- .filter((i: { checked: boolean }) => i.checked)
- .map((i: { id: string }) => i.id);
+ .filter((i: {checked: boolean}) => i.checked)
+ .map((i: {id: string}) => i.id);
pending.resolve(confirmed);
setPendingPrune(null);
}
@@ -223,7 +224,7 @@ const plugin: Plugin = (async (ctx) => {
},
"experimental.chat.system.transform": async (
_input: unknown,
- output: { system: string[] },
+ output: {system: string[]}
) => {
const syntheticPrompt = loadPrompt("prune-system-prompt");
output.system.push(syntheticPrompt);
@@ -232,7 +233,7 @@ const plugin: Plugin = (async (ctx) => {
ctx.client,
state,
logger,
- config,
+ config
),
tool: config.strategies.pruneTool.enabled
? {
@@ -256,7 +257,7 @@ const plugin: Plugin = (async (ctx) => {
primary_tools: [...existingPrimaryTools, "prune"],
};
logger.info(
- "Added 'prune' to experimental.primary_tools via config mutation",
+ "Added 'prune' to experimental.primary_tools via config mutation"
);
}
},
@@ -266,7 +267,7 @@ const plugin: Plugin = (async (ctx) => {
state,
logger,
ctx.directory,
- () => setAutoConfirm(false),
+ () => setAutoConfirm(false)
),
};
}) satisfies Plugin;
diff --git a/lib/ui/utils.ts b/lib/ui/utils.ts
index 11335fa..aac213d 100644
--- a/lib/ui/utils.ts
+++ b/lib/ui/utils.ts
@@ -1,96 +1,105 @@
-import { ToolParameterEntry } from "../state"
-import { extractParameterKey } from "../messages/utils"
+import {ToolParameterEntry} from "../state";
+import {extractParameterKey} from "../messages/utils";
export function formatTokenCount(tokens: number): string {
- if (tokens >= 1000) {
- return `${(tokens / 1000).toFixed(1)}K`.replace('.0K', 'K') + ' tokens'
- }
- return tokens.toString() + ' tokens'
+ if (tokens >= 1000) {
+ return `${(tokens / 1000).toFixed(1)}K`.replace(".0K", "K") + " tokens";
+ }
+ return tokens.toString() + " tokens";
}
export function truncate(str: string, maxLen: number = 60): string {
- if (str.length <= maxLen) return str
- return str.slice(0, maxLen - 3) + '...'
+ if (str.length <= maxLen) return str;
+ return str.slice(0, maxLen - 3) + "...";
}
export function shortenPath(input: string, workingDirectory?: string): string {
- const inPathMatch = input.match(/^(.+) in (.+)$/)
- if (inPathMatch) {
- const prefix = inPathMatch[1]
- const pathPart = inPathMatch[2]
- const shortenedPath = shortenSinglePath(pathPart, workingDirectory)
- return `${prefix} in ${shortenedPath}`
- }
+ const inPathMatch = input.match(/^(.+) in (.+)$/);
+ if (inPathMatch) {
+ const prefix = inPathMatch[1];
+ const pathPart = inPathMatch[2];
+ const shortenedPath = shortenSinglePath(pathPart, workingDirectory);
+ return `${prefix} in ${shortenedPath}`;
+ }
- return shortenSinglePath(input, workingDirectory)
+ return shortenSinglePath(input, workingDirectory);
}
function shortenSinglePath(path: string, workingDirectory?: string): string {
- if (workingDirectory) {
- if (path.startsWith(workingDirectory + '/')) {
- return path.slice(workingDirectory.length + 1)
- }
- if (path === workingDirectory) {
- return '.'
- }
+ if (workingDirectory) {
+ if (path.startsWith(workingDirectory + "/")) {
+ return path.slice(workingDirectory.length + 1);
+ }
+ if (path === workingDirectory) {
+ return ".";
}
+ }
- return path
+ return path;
}
/**
- * Formats a list of pruned items in the style: "→ tool: parameter"
+ * Formats a list of pruned items in the style: "tool: parameter"
*/
export function formatPrunedItemsList(
- pruneToolIds: string[],
- toolMetadata: Map,
- workingDirectory?: string
+ pruneToolIds: string[],
+ toolMetadata: Map,
+ workingDirectory?: string
): string[] {
- const lines: string[] = []
+ const lines: string[] = [];
- for (const id of pruneToolIds) {
- const metadata = toolMetadata.get(id)
+ for (const id of pruneToolIds) {
+ const metadata = toolMetadata.get(id);
- if (metadata) {
- const paramKey = extractParameterKey(metadata.tool, metadata.parameters)
- if (paramKey) {
- // Use 60 char limit to match notification style
- const displayKey = truncate(shortenPath(paramKey, workingDirectory), 60)
- lines.push(`→ ${metadata.tool}: ${displayKey}`)
- } else {
- lines.push(`→ ${metadata.tool}`)
- }
- }
+ if (metadata) {
+ const paramKey = extractParameterKey(metadata.tool, metadata.parameters);
+ if (paramKey) {
+ // Use 60 char limit to match notification style
+ const displayKey = truncate(
+ shortenPath(paramKey, workingDirectory),
+ 60
+ );
+ lines.push(`${metadata.tool}: ${displayKey}`);
+ } else {
+ lines.push(`${metadata.tool}`);
+ }
}
+ }
- const knownCount = pruneToolIds.filter(id =>
- toolMetadata.has(id)
- ).length
- const unknownCount = pruneToolIds.length - knownCount
+ const knownCount = pruneToolIds.filter((id) => toolMetadata.has(id)).length;
+ const unknownCount = pruneToolIds.length - knownCount;
- if (unknownCount > 0) {
- lines.push(`→ (${unknownCount} tool${unknownCount > 1 ? 's' : ''} with unknown metadata)`)
- }
+ if (unknownCount > 0) {
+ lines.push(
+ `(${unknownCount} tool${
+ unknownCount > 1 ? "s" : ""
+ } with unknown metadata)`
+ );
+ }
- return lines
+ return lines;
}
/**
* Formats a PruningResult into a human-readable string for the prune tool output.
*/
export function formatPruningResultForTool(
- prunedIds: string[],
- toolMetadata: Map,
- workingDirectory?: string
+ prunedIds: string[],
+ toolMetadata: Map,
+ workingDirectory?: string
): string {
- const lines: string[] = []
- lines.push(`Context pruning complete. Pruned ${prunedIds.length} tool outputs.`)
- lines.push('')
+ const lines: string[] = [];
+ lines.push(
+ `Context pruning complete. Pruned ${prunedIds.length} tool outputs.`
+ );
+ lines.push("");
- if (prunedIds.length > 0) {
- lines.push(`Semantically pruned (${prunedIds.length}):`)
- lines.push(...formatPrunedItemsList(prunedIds, toolMetadata, workingDirectory))
- }
+ if (prunedIds.length > 0) {
+ lines.push(`Semantically pruned (${prunedIds.length}):`);
+ lines.push(
+ ...formatPrunedItemsList(prunedIds, toolMetadata, workingDirectory)
+ );
+ }
- return lines.join('\n').trim()
+ return lines.join("\n").trim();
}
From 88d0af10358527b283138a1b8d6ba0ad5a999090 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Mon, 22 Dec 2025 02:58:57 +0100
Subject: [PATCH 10/13] toolname
---
lib/ui/utils.ts | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/lib/ui/utils.ts b/lib/ui/utils.ts
index aac213d..cb99c6a 100644
--- a/lib/ui/utils.ts
+++ b/lib/ui/utils.ts
@@ -59,9 +59,13 @@ export function formatPrunedItemsList(
shortenPath(paramKey, workingDirectory),
60
);
- lines.push(`${metadata.tool}: ${displayKey}`);
+ const toolName =
+ metadata.tool.charAt(0).toUpperCase() + metadata.tool.slice(1);
+ lines.push(`${toolName}: ${displayKey}`);
} else {
- lines.push(`${metadata.tool}`);
+ const toolName =
+ metadata.tool.charAt(0).toUpperCase() + metadata.tool.slice(1);
+ lines.push(`${toolName}`);
}
}
}
From 5a597033a1608888b02356bc542388533aa131b0 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Mon, 22 Dec 2025 03:41:09 +0100
Subject: [PATCH 11/13] modal
---
index.ts | 59 ++++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 45 insertions(+), 14 deletions(-)
diff --git a/index.ts b/index.ts
index ec6d868..7e3faa6 100644
--- a/index.ts
+++ b/index.ts
@@ -1,9 +1,9 @@
-import type {Plugin} from "@opencode-ai/plugin";
-import {getConfig} from "./lib/config";
-import {Logger} from "./lib/logger";
-import {loadPrompt} from "./lib/prompt";
-import {createSessionState} from "./lib/state";
-import {createPruneTool} from "./lib/strategies";
+import type { Plugin } from "@opencode-ai/plugin";
+import { getConfig } from "./lib/config";
+import { Logger } from "./lib/logger";
+import { loadPrompt } from "./lib/prompt";
+import { createSessionState } from "./lib/state";
+import { createPruneTool } from "./lib/strategies";
import {
createChatMessageTransformHandler,
createEventHandler,
@@ -90,6 +90,29 @@ const plugin: Plugin = (async (ctx) => {
{
name: "dcp-confirm",
replaceInput: true,
+ modals: [
+ {
+ name: "dcp-settings",
+ template: {
+ type: "box",
+ direction: "column",
+ gap: 1,
+ children: [
+ {
+ type: "text",
+ content: "DCP Settings",
+ fg: "text",
+ bold: true,
+ },
+ {
+ type: "text",
+ content: "Configure pruning behavior",
+ fg: "textMuted",
+ },
+ ],
+ },
+ },
+ ],
template: {
type: "box",
direction: "column",
@@ -103,6 +126,7 @@ const plugin: Plugin = (async (ctx) => {
gap: 1,
paddingX: 2,
paddingY: 1,
+ justifyContent: "space-between",
children: [
{
type: "text",
@@ -110,6 +134,13 @@ const plugin: Plugin = (async (ctx) => {
fg: "text",
bold: true,
},
+ {
+ type: "icon",
+ icon: "⚙",
+ fg: "textMuted",
+ fgHover: "warning",
+ onModal: "dcp-settings",
+ },
],
},
{
@@ -204,8 +235,8 @@ const plugin: Plugin = (async (ctx) => {
pending.items = event.data.items;
} else if (event.event === "confirm-prune" && pending) {
const confirmed = pending.items
- .filter((i: {checked: boolean}) => i.checked)
- .map((i: {id: string}) => i.id);
+ .filter((i: { checked: boolean }) => i.checked)
+ .map((i: { id: string }) => i.id);
pending.resolve(confirmed);
setPendingPrune(null);
} else if (event.event === "cancel-prune" && pending) {
@@ -215,8 +246,8 @@ const plugin: Plugin = (async (ctx) => {
// Enable auto-confirm and confirm this one
setAutoConfirm(true);
const confirmed = pending.items
- .filter((i: {checked: boolean}) => i.checked)
- .map((i: {id: string}) => i.id);
+ .filter((i: { checked: boolean }) => i.checked)
+ .map((i: { id: string }) => i.id);
pending.resolve(confirmed);
setPendingPrune(null);
}
@@ -224,7 +255,7 @@ const plugin: Plugin = (async (ctx) => {
},
"experimental.chat.system.transform": async (
_input: unknown,
- output: {system: string[]}
+ output: { system: string[] },
) => {
const syntheticPrompt = loadPrompt("prune-system-prompt");
output.system.push(syntheticPrompt);
@@ -233,7 +264,7 @@ const plugin: Plugin = (async (ctx) => {
ctx.client,
state,
logger,
- config
+ config,
),
tool: config.strategies.pruneTool.enabled
? {
@@ -257,7 +288,7 @@ const plugin: Plugin = (async (ctx) => {
primary_tools: [...existingPrimaryTools, "prune"],
};
logger.info(
- "Added 'prune' to experimental.primary_tools via config mutation"
+ "Added 'prune' to experimental.primary_tools via config mutation",
);
}
},
@@ -267,7 +298,7 @@ const plugin: Plugin = (async (ctx) => {
state,
logger,
ctx.directory,
- () => setAutoConfirm(false)
+ () => setAutoConfirm(false),
),
};
}) satisfies Plugin;
From d2b6d21a5870cd01738424ce2dae24954bdbba97 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Mon, 22 Dec 2025 03:42:11 +0100
Subject: [PATCH 12/13] prettier
---
.prettierrc | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 .prettierrc
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..3325b6e
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,10 @@
+{
+ "semi": false,
+ "singleQuote": false,
+ "tabWidth": 4,
+ "useTabs": false,
+ "trailingComma": "all",
+ "printWidth": 100,
+ "bracketSpacing": true,
+ "arrowParens": "always"
+}
From f3f789e72d8500051928365de9c549883c6f2203 Mon Sep 17 00:00:00 2001
From: spoons-and-mirrors
<212802214+spoons-and-mirrors@users.noreply.github.com>
Date: Mon, 22 Dec 2025 03:42:41 +0100
Subject: [PATCH 13/13] prettier
---
.github/workflows/pr-checks.yml | 56 +-
README.md | 94 +-
index.ts | 557 ++--
lib/config.ts | 377 ++-
lib/hooks.ts | 114 +-
lib/logger.ts | 21 +-
lib/messages/prune.ts | 54 +-
lib/messages/utils.ts | 16 +-
lib/model-selector.ts | 164 +-
lib/prompt.ts | 201 +-
lib/shared-utils.ts | 13 +-
lib/state/persistence.ts | 82 +-
lib/state/state.ts | 21 +-
lib/state/tool-cache.ts | 35 +-
lib/state/types.ts | 4 +-
lib/strategies/deduplication.ts | 8 +-
lib/strategies/on-idle.ts | 76 +-
lib/strategies/prune-tool.ts | 393 ++-
lib/strategies/supersede-writes.ts | 12 +-
lib/strategies/utils.ts | 37 +-
lib/ui/confirmation.ts | 159 +-
lib/ui/notification.ts | 164 +-
lib/ui/utils.ts | 131 +-
package-lock.json | 4610 ++++++++++++++--------------
package.json | 116 +-
tsconfig.json | 51 +-
26 files changed, 3810 insertions(+), 3756 deletions(-)
diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml
index e665cfc..83ffc5a 100644
--- a/.github/workflows/pr-checks.yml
+++ b/.github/workflows/pr-checks.yml
@@ -1,33 +1,33 @@
name: PR Checks
on:
- pull_request:
- branches: [master, dev]
+ pull_request:
+ branches: [master, dev]
jobs:
- validate:
- name: Type Check, Build & Audit
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: '20'
- cache: 'npm'
-
- - name: Install dependencies
- run: npm ci
-
- - name: Type check
- run: npm run typecheck
-
- - name: Build
- run: npm run build
-
- - name: Security audit
- run: npm audit --audit-level=high
- continue-on-error: false
+ validate:
+ name: Type Check, Build & Audit
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "20"
+ cache: "npm"
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Type check
+ run: npm run typecheck
+
+ - name: Build
+ run: npm run build
+
+ - name: Security audit
+ run: npm audit --audit-level=high
+ continue-on-error: false
diff --git a/README.md b/README.md
index 9d15e73..f0af36f 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ Add to your OpenCode config:
```jsonc
// opencode.jsonc
{
- "plugin": ["@tarquinen/opencode-dcp@latest"]
+ "plugin": ["@tarquinen/opencode-dcp@latest"],
}
```
@@ -54,53 +54,53 @@ DCP uses its own config file:
```jsonc
{
- // Enable or disable the plugin
- "enabled": true,
- // Enable debug logging to ~/.config/opencode/logs/dcp/
- "debug": false,
- // Summary display: "off", "minimal", or "detailed"
- "pruningSummary": "detailed",
- // Strategies for pruning tokens from chat history
- "strategies": {
- // Remove duplicate tool calls (same tool with same arguments)
- "deduplication": {
- "enabled": true,
- // Additional tools to protect from pruning
- "protectedTools": []
+ // Enable or disable the plugin
+ "enabled": true,
+ // Enable debug logging to ~/.config/opencode/logs/dcp/
+ "debug": false,
+ // Summary display: "off", "minimal", or "detailed"
+ "pruningSummary": "detailed",
+ // Strategies for pruning tokens from chat history
+ "strategies": {
+ // Remove duplicate tool calls (same tool with same arguments)
+ "deduplication": {
+ "enabled": true,
+ // Additional tools to protect from pruning
+ "protectedTools": [],
+ },
+ // Prune write tool inputs when the file has been subsequently read
+ "supersedeWrites": {
+ "enabled": true,
+ },
+ // Exposes a prune tool to your LLM to call when it determines pruning is necessary
+ "pruneTool": {
+ "enabled": true,
+ // Additional tools to protect from pruning
+ "protectedTools": [],
+ // Protect from pruning for message turns
+ "turnProtection": {
+ "enabled": false,
+ "turns": 4,
+ },
+ // Nudge the LLM to use the prune tool (every tool results)
+ "nudge": {
+ "enabled": true,
+ "frequency": 10,
+ },
+ },
+ // (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
+ "onIdle": {
+ "enabled": false,
+ // Additional tools to protect from pruning
+ "protectedTools": [],
+ // Override model for analysis (format: "provider/model")
+ // "model": "anthropic/claude-haiku-4-5",
+ // Show toast notifications when model selection fails
+ "showModelErrorToasts": true,
+ // When true, fallback models are not permitted
+ "strictModelSelection": false,
+ },
},
- // Prune write tool inputs when the file has been subsequently read
- "supersedeWrites": {
- "enabled": true
- },
- // Exposes a prune tool to your LLM to call when it determines pruning is necessary
- "pruneTool": {
- "enabled": true,
- // Additional tools to protect from pruning
- "protectedTools": [],
- // Protect from pruning for message turns
- "turnProtection": {
- "enabled": false,
- "turns": 4
- },
- // Nudge the LLM to use the prune tool (every tool results)
- "nudge": {
- "enabled": true,
- "frequency": 10
- }
- },
- // (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
- "onIdle": {
- "enabled": false,
- // Additional tools to protect from pruning
- "protectedTools": [],
- // Override model for analysis (format: "provider/model")
- // "model": "anthropic/claude-haiku-4-5",
- // Show toast notifications when model selection fails
- "showModelErrorToasts": true,
- // When true, fallback models are not permitted
- "strictModelSelection": false
- }
- }
}
```
diff --git a/index.ts b/index.ts
index 7e3faa6..224b7cd 100644
--- a/index.ts
+++ b/index.ts
@@ -1,306 +1,291 @@
-import type { Plugin } from "@opencode-ai/plugin";
-import { getConfig } from "./lib/config";
-import { Logger } from "./lib/logger";
-import { loadPrompt } from "./lib/prompt";
-import { createSessionState } from "./lib/state";
-import { createPruneTool } from "./lib/strategies";
-import {
- createChatMessageTransformHandler,
- createEventHandler,
-} from "./lib/hooks";
-import {
- getPendingPrune,
- setPendingPrune,
- setAutoConfirm,
-} from "./lib/ui/confirmation";
+import type { Plugin } from "@opencode-ai/plugin"
+import { getConfig } from "./lib/config"
+import { Logger } from "./lib/logger"
+import { loadPrompt } from "./lib/prompt"
+import { createSessionState } from "./lib/state"
+import { createPruneTool } from "./lib/strategies"
+import { createChatMessageTransformHandler, createEventHandler } from "./lib/hooks"
+import { getPendingPrune, setPendingPrune, setAutoConfirm } from "./lib/ui/confirmation"
const plugin: Plugin = (async (ctx) => {
- const config = getConfig(ctx);
+ const config = getConfig(ctx)
- if (!config.enabled) {
- return {};
- }
+ if (!config.enabled) {
+ return {}
+ }
- // Suppress AI SDK warnings
- if (typeof globalThis !== "undefined") {
- (globalThis as any).AI_SDK_LOG_WARNINGS = false;
- }
+ // Suppress AI SDK warnings
+ if (typeof globalThis !== "undefined") {
+ ;(globalThis as any).AI_SDK_LOG_WARNINGS = false
+ }
- // Initialize core components
- const logger = new Logger(config.debug);
- const state = createSessionState();
+ // Initialize core components
+ const logger = new Logger(config.debug)
+ const state = createSessionState()
- // Log initialization
- logger.info("DCP initialized", {
- strategies: config.strategies,
- });
+ // Log initialization
+ logger.info("DCP initialized", {
+ strategies: config.strategies,
+ })
- return {
- ui: [
- {
- name: "dcp-prune-summary",
- inline: true,
- template: {
- type: "box",
- direction: "column",
- gap: 0,
- children: [
+ return {
+ ui: [
{
- type: "box",
- direction: "row",
- gap: 1,
- children: [
- {
- type: "text",
- content: "▣",
- fg: "textMuted",
+ name: "dcp-prune-summary",
+ inline: true,
+ template: {
+ type: "box",
+ direction: "column",
+ gap: 0,
+ children: [
+ {
+ type: "box",
+ direction: "row",
+ gap: 1,
+ children: [
+ {
+ type: "text",
+ content: "▣",
+ fg: "textMuted",
+ },
+ {
+ type: "text",
+ content: "DCP",
+ fg: "text",
+ bold: true,
+ },
+ {
+ type: "text",
+ content: "{{saved}} saved",
+ fg: "textMuted",
+ },
+ ],
+ },
+ {
+ type: "collapsible",
+ title: "{{count}} items pruned",
+ expanded: false,
+ fg: "textMuted",
+ fgExpanded: "text",
+ icon: "▶",
+ iconExpanded: "▼",
+ children: [
+ {
+ type: "text",
+ content: "{{itemsList}}",
+ fg: "textMuted",
+ },
+ ],
+ },
+ ],
},
- {
- type: "text",
- content: "DCP",
- fg: "text",
- bold: true,
- },
- {
- type: "text",
- content: "{{saved}} saved",
- fg: "textMuted",
- },
- ],
},
{
- type: "collapsible",
- title: "{{count}} items pruned",
- expanded: false,
- fg: "textMuted",
- fgExpanded: "text",
- icon: "▶",
- iconExpanded: "▼",
- children: [
- {
- type: "text",
- content: "{{itemsList}}",
- fg: "textMuted",
- },
- ],
- },
- ],
- },
- },
- {
- name: "dcp-confirm",
- replaceInput: true,
- modals: [
- {
- name: "dcp-settings",
- template: {
- type: "box",
- direction: "column",
- gap: 1,
- children: [
- {
- type: "text",
- content: "DCP Settings",
- fg: "text",
- bold: true,
- },
- {
- type: "text",
- content: "Configure pruning behavior",
- fg: "textMuted",
- },
- ],
- },
- },
- ],
- template: {
- type: "box",
- direction: "column",
- bg: "backgroundPanel",
- paddingY: 0,
- gap: 0,
- children: [
- {
- type: "box",
- direction: "row",
- gap: 1,
- paddingX: 2,
- paddingY: 1,
- justifyContent: "space-between",
- children: [
- {
- type: "text",
- content: "Select files to prune",
- fg: "text",
- bold: true,
- },
- {
- type: "icon",
- icon: "⚙",
- fg: "textMuted",
- fgHover: "warning",
- onModal: "dcp-settings",
- },
- ],
- },
- {
- type: "box",
- direction: "column",
- paddingX: 2,
- paddingBottom: 1,
- children: [
- {
- type: "checklist",
- items: "{{items}}",
- fg: "textMuted",
- fgChecked: "text",
- borderColorChecked: "warning",
- onToggle: "item-toggled",
- },
- ],
- },
- {
- type: "box",
- direction: "row",
- gap: 0,
- justifyContent: "space-between",
- bg: "backgroundElement",
- paddingY: 1,
- paddingX: 2,
- children: [
- {
- type: "box",
- direction: "row",
- gap: 0,
- children: [
- {
- type: "text",
- content: "click items to select",
- fg: "textMuted",
- },
- ],
- },
- {
- type: "button-group",
- gap: 1,
- defaultIndex: 2,
- bgColor: "backgroundElement",
- children: [
- {
- type: "confirm-button",
- label: "Reject",
- fg: "textMuted",
- bg: "backgroundPanel",
- fgHover: "warning",
- borderColorHover: "warning",
- onConfirm: "cancel-prune",
- },
+ name: "dcp-confirm",
+ replaceInput: true,
+ modals: [
{
- type: "confirm-button",
- label: "Auto",
- fg: "textMuted",
- bg: "backgroundPanel",
- fgHover: "warning",
- borderColorHover: "warning",
- onConfirm: "auto-prune",
+ name: "dcp-settings",
+ template: {
+ type: "box",
+ direction: "column",
+ gap: 1,
+ children: [
+ {
+ type: "text",
+ content: "DCP Settings",
+ fg: "text",
+ bold: true,
+ },
+ {
+ type: "text",
+ content: "Configure pruning behavior",
+ fg: "textMuted",
+ },
+ ],
+ },
},
- {
- type: "confirm-button",
- label: "Confirm",
- fg: "textMuted",
- bg: "backgroundPanel",
- fgHover: "warning",
- borderColorHover: "warning",
- onConfirm: "confirm-prune",
- },
- ],
+ ],
+ template: {
+ type: "box",
+ direction: "column",
+ bg: "backgroundPanel",
+ paddingY: 0,
+ gap: 0,
+ children: [
+ {
+ type: "box",
+ direction: "row",
+ gap: 1,
+ paddingX: 2,
+ paddingY: 1,
+ justifyContent: "space-between",
+ children: [
+ {
+ type: "text",
+ content: "Select files to prune",
+ fg: "text",
+ bold: true,
+ },
+ {
+ type: "icon",
+ icon: "⚙",
+ fg: "textMuted",
+ fgHover: "warning",
+ onModal: "dcp-settings",
+ },
+ ],
+ },
+ {
+ type: "box",
+ direction: "column",
+ paddingX: 2,
+ paddingBottom: 1,
+ children: [
+ {
+ type: "checklist",
+ items: "{{items}}",
+ fg: "textMuted",
+ fgChecked: "text",
+ borderColorChecked: "warning",
+ onToggle: "item-toggled",
+ },
+ ],
+ },
+ {
+ type: "box",
+ direction: "row",
+ gap: 0,
+ justifyContent: "space-between",
+ bg: "backgroundElement",
+ paddingY: 1,
+ paddingX: 2,
+ children: [
+ {
+ type: "box",
+ direction: "row",
+ gap: 0,
+ children: [
+ {
+ type: "text",
+ content: "click items to select",
+ fg: "textMuted",
+ },
+ ],
+ },
+ {
+ type: "button-group",
+ gap: 1,
+ defaultIndex: 2,
+ bgColor: "backgroundElement",
+ children: [
+ {
+ type: "confirm-button",
+ label: "Reject",
+ fg: "textMuted",
+ bg: "backgroundPanel",
+ fgHover: "warning",
+ borderColorHover: "warning",
+ onConfirm: "cancel-prune",
+ },
+ {
+ type: "confirm-button",
+ label: "Auto",
+ fg: "textMuted",
+ bg: "backgroundPanel",
+ fgHover: "warning",
+ borderColorHover: "warning",
+ onConfirm: "auto-prune",
+ },
+ {
+ type: "confirm-button",
+ label: "Confirm",
+ fg: "textMuted",
+ bg: "backgroundPanel",
+ fgHover: "warning",
+ borderColorHover: "warning",
+ onConfirm: "confirm-prune",
+ },
+ ],
+ },
+ ],
+ },
+ ],
},
- ],
},
- ],
- },
- },
- ],
- "ui.event": async (event: {
- component: string;
- event: string;
- data: Record;
- }) => {
- logger.info("UI Event received", event);
+ ],
+ "ui.event": async (event: {
+ component: string
+ event: string
+ data: Record
+ }) => {
+ logger.info("UI Event received", event)
- if (event.component === "dcp-confirm") {
- const pending = getPendingPrune();
- if (event.event === "item-toggled" && event.data.items && pending) {
- // Update the pending items state
- pending.items = event.data.items;
- } else if (event.event === "confirm-prune" && pending) {
- const confirmed = pending.items
- .filter((i: { checked: boolean }) => i.checked)
- .map((i: { id: string }) => i.id);
- pending.resolve(confirmed);
- setPendingPrune(null);
- } else if (event.event === "cancel-prune" && pending) {
- pending.resolve([]);
- setPendingPrune(null);
- } else if (event.event === "auto-prune" && pending) {
- // Enable auto-confirm and confirm this one
- setAutoConfirm(true);
- const confirmed = pending.items
- .filter((i: { checked: boolean }) => i.checked)
- .map((i: { id: string }) => i.id);
- pending.resolve(confirmed);
- setPendingPrune(null);
- }
- }
- },
- "experimental.chat.system.transform": async (
- _input: unknown,
- output: { system: string[] },
- ) => {
- const syntheticPrompt = loadPrompt("prune-system-prompt");
- output.system.push(syntheticPrompt);
- },
- "experimental.chat.messages.transform": createChatMessageTransformHandler(
- ctx.client,
- state,
- logger,
- config,
- ),
- tool: config.strategies.pruneTool.enabled
- ? {
- prune: createPruneTool({
- client: ctx.client,
+ if (event.component === "dcp-confirm") {
+ const pending = getPendingPrune()
+ if (event.event === "item-toggled" && event.data.items && pending) {
+ // Update the pending items state
+ pending.items = event.data.items
+ } else if (event.event === "confirm-prune" && pending) {
+ const confirmed = pending.items
+ .filter((i: { checked: boolean }) => i.checked)
+ .map((i: { id: string }) => i.id)
+ pending.resolve(confirmed)
+ setPendingPrune(null)
+ } else if (event.event === "cancel-prune" && pending) {
+ pending.resolve([])
+ setPendingPrune(null)
+ } else if (event.event === "auto-prune" && pending) {
+ // Enable auto-confirm and confirm this one
+ setAutoConfirm(true)
+ const confirmed = pending.items
+ .filter((i: { checked: boolean }) => i.checked)
+ .map((i: { id: string }) => i.id)
+ pending.resolve(confirmed)
+ setPendingPrune(null)
+ }
+ }
+ },
+ "experimental.chat.system.transform": async (
+ _input: unknown,
+ output: { system: string[] },
+ ) => {
+ const syntheticPrompt = loadPrompt("prune-system-prompt")
+ output.system.push(syntheticPrompt)
+ },
+ "experimental.chat.messages.transform": createChatMessageTransformHandler(
+ ctx.client,
state,
logger,
config,
- workingDirectory: ctx.directory,
- }),
- }
- : undefined,
- config: async (opencodeConfig) => {
- // Add prune to primary_tools by mutating the opencode config
- // This works because config is cached and passed by reference
- if (config.strategies.pruneTool.enabled) {
- const existingPrimaryTools =
- opencodeConfig.experimental?.primary_tools ?? [];
- opencodeConfig.experimental = {
- ...opencodeConfig.experimental,
- primary_tools: [...existingPrimaryTools, "prune"],
- };
- logger.info(
- "Added 'prune' to experimental.primary_tools via config mutation",
- );
- }
- },
- event: createEventHandler(
- ctx.client,
- config,
- state,
- logger,
- ctx.directory,
- () => setAutoConfirm(false),
- ),
- };
-}) satisfies Plugin;
+ ),
+ tool: config.strategies.pruneTool.enabled
+ ? {
+ prune: createPruneTool({
+ client: ctx.client,
+ state,
+ logger,
+ config,
+ workingDirectory: ctx.directory,
+ }),
+ }
+ : undefined,
+ config: async (opencodeConfig) => {
+ // Add prune to primary_tools by mutating the opencode config
+ // This works because config is cached and passed by reference
+ if (config.strategies.pruneTool.enabled) {
+ const existingPrimaryTools = opencodeConfig.experimental?.primary_tools ?? []
+ opencodeConfig.experimental = {
+ ...opencodeConfig.experimental,
+ primary_tools: [...existingPrimaryTools, "prune"],
+ }
+ logger.info("Added 'prune' to experimental.primary_tools via config mutation")
+ }
+ },
+ event: createEventHandler(ctx.client, config, state, logger, ctx.directory, () =>
+ setAutoConfirm(false),
+ ),
+ }
+}) satisfies Plugin
-export default plugin;
+export default plugin
diff --git a/lib/config.ts b/lib/config.ts
index 9a670fe..8e30119 100644
--- a/lib/config.ts
+++ b/lib/config.ts
@@ -1,8 +1,8 @@
-import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from 'fs'
-import { join, dirname } from 'path'
-import { homedir } from 'os'
-import { parse } from 'jsonc-parser'
-import type { PluginInput } from '@opencode-ai/plugin'
+import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from "fs"
+import { join, dirname } from "path"
+import { homedir } from "os"
+import { parse } from "jsonc-parser"
+import type { PluginInput } from "@opencode-ai/plugin"
export interface Deduplication {
enabled: boolean
@@ -50,49 +50,49 @@ export interface PluginConfig {
}
}
-const DEFAULT_PROTECTED_TOOLS = ['task', 'todowrite', 'todoread', 'prune', 'batch']
+const DEFAULT_PROTECTED_TOOLS = ["task", "todowrite", "todoread", "prune", "batch"]
// Valid config keys for validation against user config
export const VALID_CONFIG_KEYS = new Set([
// Top-level keys
- 'enabled',
- 'debug',
- 'showUpdateToasts', // Deprecated but kept for backwards compatibility
- 'pruningSummary',
- 'strategies',
+ "enabled",
+ "debug",
+ "showUpdateToasts", // Deprecated but kept for backwards compatibility
+ "pruningSummary",
+ "strategies",
// strategies.deduplication
- 'strategies.deduplication',
- 'strategies.deduplication.enabled',
- 'strategies.deduplication.protectedTools',
+ "strategies.deduplication",
+ "strategies.deduplication.enabled",
+ "strategies.deduplication.protectedTools",
// strategies.supersedeWrites
- 'strategies.supersedeWrites',
- 'strategies.supersedeWrites.enabled',
+ "strategies.supersedeWrites",
+ "strategies.supersedeWrites.enabled",
// strategies.onIdle
- 'strategies.onIdle',
- 'strategies.onIdle.enabled',
- 'strategies.onIdle.model',
- 'strategies.onIdle.showModelErrorToasts',
- 'strategies.onIdle.strictModelSelection',
- 'strategies.onIdle.protectedTools',
+ "strategies.onIdle",
+ "strategies.onIdle.enabled",
+ "strategies.onIdle.model",
+ "strategies.onIdle.showModelErrorToasts",
+ "strategies.onIdle.strictModelSelection",
+ "strategies.onIdle.protectedTools",
// strategies.pruneTool
- 'strategies.pruneTool',
- 'strategies.pruneTool.enabled',
- 'strategies.pruneTool.protectedTools',
- 'strategies.pruneTool.turnProtection',
- 'strategies.pruneTool.turnProtection.enabled',
- 'strategies.pruneTool.turnProtection.turns',
- 'strategies.pruneTool.nudge',
- 'strategies.pruneTool.nudge.enabled',
- 'strategies.pruneTool.nudge.frequency'
+ "strategies.pruneTool",
+ "strategies.pruneTool.enabled",
+ "strategies.pruneTool.protectedTools",
+ "strategies.pruneTool.turnProtection",
+ "strategies.pruneTool.turnProtection.enabled",
+ "strategies.pruneTool.turnProtection.turns",
+ "strategies.pruneTool.nudge",
+ "strategies.pruneTool.nudge.enabled",
+ "strategies.pruneTool.nudge.frequency",
])
// Extract all key paths from a config object for validation
-function getConfigKeyPaths(obj: Record, prefix = ''): string[] {
+function getConfigKeyPaths(obj: Record, prefix = ""): string[] {
const keys: string[] = []
for (const key of Object.keys(obj)) {
const fullKey = prefix ? `${prefix}.${key}` : key
keys.push(fullKey)
- if (obj[key] && typeof obj[key] === 'object' && !Array.isArray(obj[key])) {
+ if (obj[key] && typeof obj[key] === "object" && !Array.isArray(obj[key])) {
keys.push(...getConfigKeyPaths(obj[key], fullKey))
}
}
@@ -102,7 +102,7 @@ function getConfigKeyPaths(obj: Record, prefix = ''): string[] {
// Returns invalid keys found in user config
export function getInvalidConfigKeys(userConfig: Record): string[] {
const userKeys = getConfigKeyPaths(userConfig)
- return userKeys.filter(key => !VALID_CONFIG_KEYS.has(key))
+ return userKeys.filter((key) => !VALID_CONFIG_KEYS.has(key))
}
// Type validators for config values
@@ -116,16 +116,20 @@ function validateConfigTypes(config: Record): ValidationError[] {
const errors: ValidationError[] = []
// Top-level validators
- if (config.enabled !== undefined && typeof config.enabled !== 'boolean') {
- errors.push({ key: 'enabled', expected: 'boolean', actual: typeof config.enabled })
+ if (config.enabled !== undefined && typeof config.enabled !== "boolean") {
+ errors.push({ key: "enabled", expected: "boolean", actual: typeof config.enabled })
}
- if (config.debug !== undefined && typeof config.debug !== 'boolean') {
- errors.push({ key: 'debug', expected: 'boolean', actual: typeof config.debug })
+ if (config.debug !== undefined && typeof config.debug !== "boolean") {
+ errors.push({ key: "debug", expected: "boolean", actual: typeof config.debug })
}
if (config.pruningSummary !== undefined) {
- const validValues = ['off', 'minimal', 'detailed']
+ const validValues = ["off", "minimal", "detailed"]
if (!validValues.includes(config.pruningSummary)) {
- errors.push({ key: 'pruningSummary', expected: '"off" | "minimal" | "detailed"', actual: JSON.stringify(config.pruningSummary) })
+ errors.push({
+ key: "pruningSummary",
+ expected: '"off" | "minimal" | "detailed"',
+ actual: JSON.stringify(config.pruningSummary),
+ })
}
}
@@ -133,62 +137,160 @@ function validateConfigTypes(config: Record): ValidationError[] {
const strategies = config.strategies
if (strategies) {
// deduplication
- if (strategies.deduplication?.enabled !== undefined && typeof strategies.deduplication.enabled !== 'boolean') {
- errors.push({ key: 'strategies.deduplication.enabled', expected: 'boolean', actual: typeof strategies.deduplication.enabled })
+ if (
+ strategies.deduplication?.enabled !== undefined &&
+ typeof strategies.deduplication.enabled !== "boolean"
+ ) {
+ errors.push({
+ key: "strategies.deduplication.enabled",
+ expected: "boolean",
+ actual: typeof strategies.deduplication.enabled,
+ })
}
- if (strategies.deduplication?.protectedTools !== undefined && !Array.isArray(strategies.deduplication.protectedTools)) {
- errors.push({ key: 'strategies.deduplication.protectedTools', expected: 'string[]', actual: typeof strategies.deduplication.protectedTools })
+ if (
+ strategies.deduplication?.protectedTools !== undefined &&
+ !Array.isArray(strategies.deduplication.protectedTools)
+ ) {
+ errors.push({
+ key: "strategies.deduplication.protectedTools",
+ expected: "string[]",
+ actual: typeof strategies.deduplication.protectedTools,
+ })
}
// onIdle
if (strategies.onIdle) {
- if (strategies.onIdle.enabled !== undefined && typeof strategies.onIdle.enabled !== 'boolean') {
- errors.push({ key: 'strategies.onIdle.enabled', expected: 'boolean', actual: typeof strategies.onIdle.enabled })
+ if (
+ strategies.onIdle.enabled !== undefined &&
+ typeof strategies.onIdle.enabled !== "boolean"
+ ) {
+ errors.push({
+ key: "strategies.onIdle.enabled",
+ expected: "boolean",
+ actual: typeof strategies.onIdle.enabled,
+ })
}
- if (strategies.onIdle.model !== undefined && typeof strategies.onIdle.model !== 'string') {
- errors.push({ key: 'strategies.onIdle.model', expected: 'string', actual: typeof strategies.onIdle.model })
+ if (
+ strategies.onIdle.model !== undefined &&
+ typeof strategies.onIdle.model !== "string"
+ ) {
+ errors.push({
+ key: "strategies.onIdle.model",
+ expected: "string",
+ actual: typeof strategies.onIdle.model,
+ })
}
- if (strategies.onIdle.showModelErrorToasts !== undefined && typeof strategies.onIdle.showModelErrorToasts !== 'boolean') {
- errors.push({ key: 'strategies.onIdle.showModelErrorToasts', expected: 'boolean', actual: typeof strategies.onIdle.showModelErrorToasts })
+ if (
+ strategies.onIdle.showModelErrorToasts !== undefined &&
+ typeof strategies.onIdle.showModelErrorToasts !== "boolean"
+ ) {
+ errors.push({
+ key: "strategies.onIdle.showModelErrorToasts",
+ expected: "boolean",
+ actual: typeof strategies.onIdle.showModelErrorToasts,
+ })
}
- if (strategies.onIdle.strictModelSelection !== undefined && typeof strategies.onIdle.strictModelSelection !== 'boolean') {
- errors.push({ key: 'strategies.onIdle.strictModelSelection', expected: 'boolean', actual: typeof strategies.onIdle.strictModelSelection })
+ if (
+ strategies.onIdle.strictModelSelection !== undefined &&
+ typeof strategies.onIdle.strictModelSelection !== "boolean"
+ ) {
+ errors.push({
+ key: "strategies.onIdle.strictModelSelection",
+ expected: "boolean",
+ actual: typeof strategies.onIdle.strictModelSelection,
+ })
}
- if (strategies.onIdle.protectedTools !== undefined && !Array.isArray(strategies.onIdle.protectedTools)) {
- errors.push({ key: 'strategies.onIdle.protectedTools', expected: 'string[]', actual: typeof strategies.onIdle.protectedTools })
+ if (
+ strategies.onIdle.protectedTools !== undefined &&
+ !Array.isArray(strategies.onIdle.protectedTools)
+ ) {
+ errors.push({
+ key: "strategies.onIdle.protectedTools",
+ expected: "string[]",
+ actual: typeof strategies.onIdle.protectedTools,
+ })
}
}
// pruneTool
if (strategies.pruneTool) {
- if (strategies.pruneTool.enabled !== undefined && typeof strategies.pruneTool.enabled !== 'boolean') {
- errors.push({ key: 'strategies.pruneTool.enabled', expected: 'boolean', actual: typeof strategies.pruneTool.enabled })
+ if (
+ strategies.pruneTool.enabled !== undefined &&
+ typeof strategies.pruneTool.enabled !== "boolean"
+ ) {
+ errors.push({
+ key: "strategies.pruneTool.enabled",
+ expected: "boolean",
+ actual: typeof strategies.pruneTool.enabled,
+ })
}
- if (strategies.pruneTool.protectedTools !== undefined && !Array.isArray(strategies.pruneTool.protectedTools)) {
- errors.push({ key: 'strategies.pruneTool.protectedTools', expected: 'string[]', actual: typeof strategies.pruneTool.protectedTools })
+ if (
+ strategies.pruneTool.protectedTools !== undefined &&
+ !Array.isArray(strategies.pruneTool.protectedTools)
+ ) {
+ errors.push({
+ key: "strategies.pruneTool.protectedTools",
+ expected: "string[]",
+ actual: typeof strategies.pruneTool.protectedTools,
+ })
}
if (strategies.pruneTool.turnProtection) {
- if (strategies.pruneTool.turnProtection.enabled !== undefined && typeof strategies.pruneTool.turnProtection.enabled !== 'boolean') {
- errors.push({ key: 'strategies.pruneTool.turnProtection.enabled', expected: 'boolean', actual: typeof strategies.pruneTool.turnProtection.enabled })
+ if (
+ strategies.pruneTool.turnProtection.enabled !== undefined &&
+ typeof strategies.pruneTool.turnProtection.enabled !== "boolean"
+ ) {
+ errors.push({
+ key: "strategies.pruneTool.turnProtection.enabled",
+ expected: "boolean",
+ actual: typeof strategies.pruneTool.turnProtection.enabled,
+ })
}
- if (strategies.pruneTool.turnProtection.turns !== undefined && typeof strategies.pruneTool.turnProtection.turns !== 'number') {
- errors.push({ key: 'strategies.pruneTool.turnProtection.turns', expected: 'number', actual: typeof strategies.pruneTool.turnProtection.turns })
+ if (
+ strategies.pruneTool.turnProtection.turns !== undefined &&
+ typeof strategies.pruneTool.turnProtection.turns !== "number"
+ ) {
+ errors.push({
+ key: "strategies.pruneTool.turnProtection.turns",
+ expected: "number",
+ actual: typeof strategies.pruneTool.turnProtection.turns,
+ })
}
}
if (strategies.pruneTool.nudge) {
- if (strategies.pruneTool.nudge.enabled !== undefined && typeof strategies.pruneTool.nudge.enabled !== 'boolean') {
- errors.push({ key: 'strategies.pruneTool.nudge.enabled', expected: 'boolean', actual: typeof strategies.pruneTool.nudge.enabled })
+ if (
+ strategies.pruneTool.nudge.enabled !== undefined &&
+ typeof strategies.pruneTool.nudge.enabled !== "boolean"
+ ) {
+ errors.push({
+ key: "strategies.pruneTool.nudge.enabled",
+ expected: "boolean",
+ actual: typeof strategies.pruneTool.nudge.enabled,
+ })
}
- if (strategies.pruneTool.nudge.frequency !== undefined && typeof strategies.pruneTool.nudge.frequency !== 'number') {
- errors.push({ key: 'strategies.pruneTool.nudge.frequency', expected: 'number', actual: typeof strategies.pruneTool.nudge.frequency })
+ if (
+ strategies.pruneTool.nudge.frequency !== undefined &&
+ typeof strategies.pruneTool.nudge.frequency !== "number"
+ ) {
+ errors.push({
+ key: "strategies.pruneTool.nudge.frequency",
+ expected: "number",
+ actual: typeof strategies.pruneTool.nudge.frequency,
+ })
}
}
}
// supersedeWrites
if (strategies.supersedeWrites) {
- if (strategies.supersedeWrites.enabled !== undefined && typeof strategies.supersedeWrites.enabled !== 'boolean') {
- errors.push({ key: 'strategies.supersedeWrites.enabled', expected: 'boolean', actual: typeof strategies.supersedeWrites.enabled })
+ if (
+ strategies.supersedeWrites.enabled !== undefined &&
+ typeof strategies.supersedeWrites.enabled !== "boolean"
+ ) {
+ errors.push({
+ key: "strategies.supersedeWrites.enabled",
+ expected: "boolean",
+ actual: typeof strategies.supersedeWrites.enabled,
+ })
}
}
}
@@ -201,7 +303,7 @@ function showConfigValidationWarnings(
ctx: PluginInput,
configPath: string,
configData: Record,
- isProject: boolean
+ isProject: boolean,
): void {
const invalidKeys = getInvalidConfigKeys(configData)
const typeErrors = validateConfigTypes(configData)
@@ -210,12 +312,12 @@ function showConfigValidationWarnings(
return
}
- const configType = isProject ? 'project config' : 'config'
+ const configType = isProject ? "project config" : "config"
const messages: string[] = []
if (invalidKeys.length > 0) {
- const keyList = invalidKeys.slice(0, 3).join(', ')
- const suffix = invalidKeys.length > 3 ? ` (+${invalidKeys.length - 3} more)` : ''
+ const keyList = invalidKeys.slice(0, 3).join(", ")
+ const suffix = invalidKeys.length > 3 ? ` (+${invalidKeys.length - 3} more)` : ""
messages.push(`Unknown keys: ${keyList}${suffix}`)
}
@@ -233,10 +335,10 @@ function showConfigValidationWarnings(
ctx.client.tui.showToast({
body: {
title: `DCP: Invalid ${configType}`,
- message: `${configPath}\n${messages.join('\n')}`,
+ message: `${configPath}\n${messages.join("\n")}`,
variant: "warning",
- duration: 7000
- }
+ duration: 7000,
+ },
})
} catch {}
}, 7000)
@@ -245,44 +347,44 @@ function showConfigValidationWarnings(
const defaultConfig: PluginConfig = {
enabled: true,
debug: false,
- pruningSummary: 'detailed',
+ pruningSummary: "detailed",
strategies: {
deduplication: {
enabled: true,
- protectedTools: [...DEFAULT_PROTECTED_TOOLS]
+ protectedTools: [...DEFAULT_PROTECTED_TOOLS],
},
supersedeWrites: {
- enabled: true
+ enabled: true,
},
pruneTool: {
enabled: true,
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
turnProtection: {
enabled: false,
- turns: 4
+ turns: 4,
},
nudge: {
enabled: true,
- frequency: 10
- }
+ frequency: 10,
+ },
},
onIdle: {
enabled: false,
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
showModelErrorToasts: true,
- strictModelSelection: false
- }
- }
+ strictModelSelection: false,
+ },
+ },
}
-const GLOBAL_CONFIG_DIR = join(homedir(), '.config', 'opencode')
-const GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR, 'dcp.jsonc')
-const GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR, 'dcp.json')
+const GLOBAL_CONFIG_DIR = join(homedir(), ".config", "opencode")
+const GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR, "dcp.jsonc")
+const GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR, "dcp.json")
function findOpencodeDir(startDir: string): string | null {
let current = startDir
- while (current !== '/') {
- const candidate = join(current, '.opencode')
+ while (current !== "/") {
+ const candidate = join(current, ".opencode")
if (existsSync(candidate) && statSync(candidate).isDirectory()) {
return candidate
}
@@ -293,7 +395,11 @@ function findOpencodeDir(startDir: string): string | null {
return null
}
-function getConfigPaths(ctx?: PluginInput): { global: string | null, configDir: string | null, project: string | null} {
+function getConfigPaths(ctx?: PluginInput): {
+ global: string | null
+ configDir: string | null
+ project: string | null
+} {
// Global: ~/.config/opencode/dcp.jsonc|json
let globalPath: string | null = null
if (existsSync(GLOBAL_CONFIG_PATH_JSONC)) {
@@ -306,8 +412,8 @@ function getConfigPaths(ctx?: PluginInput): { global: string | null, configDir:
let configDirPath: string | null = null
const opencodeConfigDir = process.env.OPENCODE_CONFIG_DIR
if (opencodeConfigDir) {
- const configJsonc = join(opencodeConfigDir, 'dcp.jsonc')
- const configJson = join(opencodeConfigDir, 'dcp.json')
+ const configJsonc = join(opencodeConfigDir, "dcp.jsonc")
+ const configJson = join(opencodeConfigDir, "dcp.json")
if (existsSync(configJsonc)) {
configDirPath = configJsonc
} else if (existsSync(configJson)) {
@@ -320,8 +426,8 @@ function getConfigPaths(ctx?: PluginInput): { global: string | null, configDir:
if (ctx?.directory) {
const opencodeDir = findOpencodeDir(ctx.directory)
if (opencodeDir) {
- const projectJsonc = join(opencodeDir, 'dcp.jsonc')
- const projectJson = join(opencodeDir, 'dcp.json')
+ const projectJsonc = join(opencodeDir, "dcp.jsonc")
+ const projectJson = join(opencodeDir, "dcp.json")
if (existsSync(projectJsonc)) {
projectPath = projectJsonc
} else if (existsSync(projectJson)) {
@@ -388,7 +494,7 @@ function createDefaultConfig(): void {
}
}
`
- writeFileSync(GLOBAL_CONFIG_PATH_JSONC, configContent, 'utf-8')
+ writeFileSync(GLOBAL_CONFIG_PATH_JSONC, configContent, "utf-8")
}
interface ConfigLoadResult {
@@ -399,7 +505,7 @@ interface ConfigLoadResult {
function loadConfigFile(configPath: string): ConfigLoadResult {
let fileContent: string
try {
- fileContent = readFileSync(configPath, 'utf-8')
+ fileContent = readFileSync(configPath, "utf-8")
} catch {
// File doesn't exist or can't be read - not a parse error
return { data: null }
@@ -408,18 +514,18 @@ function loadConfigFile(configPath: string): ConfigLoadResult {
try {
const parsed = parse(fileContent)
if (parsed === undefined || parsed === null) {
- return { data: null, parseError: 'Config file is empty or invalid' }
+ return { data: null, parseError: "Config file is empty or invalid" }
}
return { data: parsed }
} catch (error: any) {
- return { data: null, parseError: error.message || 'Failed to parse config' }
+ return { data: null, parseError: error.message || "Failed to parse config" }
}
}
function mergeStrategies(
- base: PluginConfig['strategies'],
- override?: Partial
-): PluginConfig['strategies'] {
+ base: PluginConfig["strategies"],
+ override?: Partial,
+): PluginConfig["strategies"] {
if (!override) return base
return {
@@ -428,42 +534,48 @@ function mergeStrategies(
protectedTools: [
...new Set([
...base.deduplication.protectedTools,
- ...(override.deduplication?.protectedTools ?? [])
- ])
- ]
+ ...(override.deduplication?.protectedTools ?? []),
+ ]),
+ ],
},
onIdle: {
enabled: override.onIdle?.enabled ?? base.onIdle.enabled,
model: override.onIdle?.model ?? base.onIdle.model,
- showModelErrorToasts: override.onIdle?.showModelErrorToasts ?? base.onIdle.showModelErrorToasts,
- strictModelSelection: override.onIdle?.strictModelSelection ?? base.onIdle.strictModelSelection,
+ showModelErrorToasts:
+ override.onIdle?.showModelErrorToasts ?? base.onIdle.showModelErrorToasts,
+ strictModelSelection:
+ override.onIdle?.strictModelSelection ?? base.onIdle.strictModelSelection,
protectedTools: [
...new Set([
...base.onIdle.protectedTools,
- ...(override.onIdle?.protectedTools ?? [])
- ])
- ]
+ ...(override.onIdle?.protectedTools ?? []),
+ ]),
+ ],
},
pruneTool: {
enabled: override.pruneTool?.enabled ?? base.pruneTool.enabled,
protectedTools: [
...new Set([
...base.pruneTool.protectedTools,
- ...(override.pruneTool?.protectedTools ?? [])
- ])
+ ...(override.pruneTool?.protectedTools ?? []),
+ ]),
],
turnProtection: {
- enabled: override.pruneTool?.turnProtection?.enabled ?? base.pruneTool.turnProtection.enabled,
- turns: override.pruneTool?.turnProtection?.turns ?? base.pruneTool.turnProtection.turns
+ enabled:
+ override.pruneTool?.turnProtection?.enabled ??
+ base.pruneTool.turnProtection.enabled,
+ turns:
+ override.pruneTool?.turnProtection?.turns ??
+ base.pruneTool.turnProtection.turns,
},
nudge: {
enabled: override.pruneTool?.nudge?.enabled ?? base.pruneTool.nudge.enabled,
- frequency: override.pruneTool?.nudge?.frequency ?? base.pruneTool.nudge.frequency
- }
+ frequency: override.pruneTool?.nudge?.frequency ?? base.pruneTool.nudge.frequency,
+ },
},
supersedeWrites: {
- enabled: override.supersedeWrites?.enabled ?? base.supersedeWrites.enabled
- }
+ enabled: override.supersedeWrites?.enabled ?? base.supersedeWrites.enabled,
+ },
}
}
@@ -473,26 +585,25 @@ function deepCloneConfig(config: PluginConfig): PluginConfig {
strategies: {
deduplication: {
...config.strategies.deduplication,
- protectedTools: [...config.strategies.deduplication.protectedTools]
+ protectedTools: [...config.strategies.deduplication.protectedTools],
},
onIdle: {
...config.strategies.onIdle,
- protectedTools: [...config.strategies.onIdle.protectedTools]
+ protectedTools: [...config.strategies.onIdle.protectedTools],
},
pruneTool: {
...config.strategies.pruneTool,
protectedTools: [...config.strategies.pruneTool.protectedTools],
turnProtection: { ...config.strategies.pruneTool.turnProtection },
- nudge: { ...config.strategies.pruneTool.nudge }
+ nudge: { ...config.strategies.pruneTool.nudge },
},
supersedeWrites: {
- ...config.strategies.supersedeWrites
- }
- }
+ ...config.strategies.supersedeWrites,
+ },
+ },
}
}
-
export function getConfig(ctx: PluginInput): PluginConfig {
let config = deepCloneConfig(defaultConfig)
const configPaths = getConfigPaths(ctx)
@@ -508,8 +619,8 @@ export function getConfig(ctx: PluginInput): PluginConfig {
title: "DCP: Invalid config",
message: `${configPaths.global}\n${result.parseError}\nUsing default values`,
variant: "warning",
- duration: 7000
- }
+ duration: 7000,
+ },
})
} catch {}
}, 7000)
@@ -520,7 +631,7 @@ export function getConfig(ctx: PluginInput): PluginConfig {
enabled: result.data.enabled ?? config.enabled,
debug: result.data.debug ?? config.debug,
pruningSummary: result.data.pruningSummary ?? config.pruningSummary,
- strategies: mergeStrategies(config.strategies, result.data.strategies as any)
+ strategies: mergeStrategies(config.strategies, result.data.strategies as any),
}
}
} else {
@@ -539,8 +650,8 @@ export function getConfig(ctx: PluginInput): PluginConfig {
title: "DCP: Invalid configDir config",
message: `${configPaths.configDir}\n${result.parseError}\nUsing global/default values`,
variant: "warning",
- duration: 7000
- }
+ duration: 7000,
+ },
})
} catch {}
}, 7000)
@@ -551,7 +662,7 @@ export function getConfig(ctx: PluginInput): PluginConfig {
enabled: result.data.enabled ?? config.enabled,
debug: result.data.debug ?? config.debug,
pruningSummary: result.data.pruningSummary ?? config.pruningSummary,
- strategies: mergeStrategies(config.strategies, result.data.strategies as any)
+ strategies: mergeStrategies(config.strategies, result.data.strategies as any),
}
}
}
@@ -567,8 +678,8 @@ export function getConfig(ctx: PluginInput): PluginConfig {
title: "DCP: Invalid project config",
message: `${configPaths.project}\n${result.parseError}\nUsing global/default values`,
variant: "warning",
- duration: 7000
- }
+ duration: 7000,
+ },
})
} catch {}
}, 7000)
@@ -579,7 +690,7 @@ export function getConfig(ctx: PluginInput): PluginConfig {
enabled: result.data.enabled ?? config.enabled,
debug: result.data.debug ?? config.debug,
pruningSummary: result.data.pruningSummary ?? config.pruningSummary,
- strategies: mergeStrategies(config.strategies, result.data.strategies as any)
+ strategies: mergeStrategies(config.strategies, result.data.strategies as any),
}
}
}
diff --git a/lib/hooks.ts b/lib/hooks.ts
index 0209ddb..b163ad6 100644
--- a/lib/hooks.ts
+++ b/lib/hooks.ts
@@ -1,77 +1,71 @@
-import type { SessionState, WithParts } from "./state";
-import type { Logger } from "./logger";
-import type { PluginConfig } from "./config";
-import { syncToolCache } from "./state/tool-cache";
-import { deduplicate, supersedeWrites } from "./strategies";
-import { prune, insertPruneToolContext } from "./messages";
-import { checkSession } from "./state";
-import { runOnIdle } from "./strategies/on-idle";
+import type { SessionState, WithParts } from "./state"
+import type { Logger } from "./logger"
+import type { PluginConfig } from "./config"
+import { syncToolCache } from "./state/tool-cache"
+import { deduplicate, supersedeWrites } from "./strategies"
+import { prune, insertPruneToolContext } from "./messages"
+import { checkSession } from "./state"
+import { runOnIdle } from "./strategies/on-idle"
export function createChatMessageTransformHandler(
- client: any,
- state: SessionState,
- logger: Logger,
- config: PluginConfig,
+ client: any,
+ state: SessionState,
+ logger: Logger,
+ config: PluginConfig,
) {
- return async (input: {}, output: { messages: WithParts[] }) => {
- await checkSession(client, state, logger, output.messages);
+ return async (input: {}, output: { messages: WithParts[] }) => {
+ await checkSession(client, state, logger, output.messages)
- if (state.isSubAgent) {
- return;
- }
+ if (state.isSubAgent) {
+ return
+ }
- syncToolCache(state, config, logger, output.messages);
+ syncToolCache(state, config, logger, output.messages)
- deduplicate(state, logger, config, output.messages);
- supersedeWrites(state, logger, config, output.messages);
+ deduplicate(state, logger, config, output.messages)
+ supersedeWrites(state, logger, config, output.messages)
- prune(state, logger, config, output.messages);
+ prune(state, logger, config, output.messages)
- insertPruneToolContext(state, config, logger, output.messages);
- };
+ insertPruneToolContext(state, config, logger, output.messages)
+ }
}
export function createEventHandler(
- client: any,
- config: PluginConfig,
- state: SessionState,
- logger: Logger,
- workingDirectory?: string,
- onUserMessage?: () => void,
+ client: any,
+ config: PluginConfig,
+ state: SessionState,
+ logger: Logger,
+ workingDirectory?: string,
+ onUserMessage?: () => void,
) {
- return async ({ event }: { event: any }) => {
- if (state.sessionId === null || state.isSubAgent) {
- return;
- }
+ return async ({ event }: { event: any }) => {
+ if (state.sessionId === null || state.isSubAgent) {
+ return
+ }
- // Reset auto-confirm on user message
- if (
- event.type === "message.part.added" &&
- event.properties?.part?.type === "text"
- ) {
- const role = event.properties?.role;
- if (role === "user" && onUserMessage) {
- onUserMessage();
- }
- }
+ // Reset auto-confirm on user message
+ if (event.type === "message.part.added" && event.properties?.part?.type === "text") {
+ const role = event.properties?.role
+ if (role === "user" && onUserMessage) {
+ onUserMessage()
+ }
+ }
- if (
- event.type === "session.status" &&
- event.properties.status.type === "idle"
- ) {
- if (!config.strategies.onIdle.enabled) {
- return;
- }
- if (state.lastToolPrune) {
- logger.info("Skipping OnIdle pruning - last tool was prune");
- return;
- }
+ if (event.type === "session.status" && event.properties.status.type === "idle") {
+ if (!config.strategies.onIdle.enabled) {
+ return
+ }
+ if (state.lastToolPrune) {
+ logger.info("Skipping OnIdle pruning - last tool was prune")
+ return
+ }
- try {
- await runOnIdle(client, state, logger, config, workingDirectory);
- } catch (err: any) {
- logger.error("OnIdle pruning failed", { error: err.message });
- }
+ try {
+ await runOnIdle(client, state, logger, config, workingDirectory)
+ } catch (err: any) {
+ logger.error("OnIdle pruning failed", { error: err.message })
+ }
+ }
}
- };
}
diff --git a/lib/logger.ts b/lib/logger.ts
index 0081db1..d101c67 100644
--- a/lib/logger.ts
+++ b/lib/logger.ts
@@ -29,15 +29,15 @@ export class Logger {
// Format arrays compactly
if (Array.isArray(value)) {
if (value.length === 0) continue
- parts.push(`${key}=[${value.slice(0, 3).join(",")}${value.length > 3 ? `...+${value.length - 3}` : ""}]`)
- }
- else if (typeof value === 'object') {
+ parts.push(
+ `${key}=[${value.slice(0, 3).join(",")}${value.length > 3 ? `...+${value.length - 3}` : ""}]`,
+ )
+ } else if (typeof value === "object") {
const str = JSON.stringify(value)
if (str.length < 50) {
parts.push(`${key}=${str}`)
}
- }
- else {
+ } else {
parts.push(`${key}=${value}`)
}
}
@@ -55,15 +55,15 @@ export class Logger {
// Skip specified number of frames to get to actual caller
for (let i = skipFrames; i < stack.length; i++) {
const filename = stack[i]?.getFileName()
- if (filename && !filename.includes('/logger.')) {
+ if (filename && !filename.includes("/logger.")) {
// Extract just the filename without path and extension
const match = filename.match(/([^/\\]+)\.[tj]s$/)
return match ? match[1] : filename
}
}
- return 'unknown'
+ return "unknown"
} catch {
- return 'unknown'
+ return "unknown"
}
}
@@ -83,10 +83,9 @@ export class Logger {
await mkdir(dailyLogDir, { recursive: true })
}
- const logFile = join(dailyLogDir, `${new Date().toISOString().split('T')[0]}.log`)
+ const logFile = join(dailyLogDir, `${new Date().toISOString().split("T")[0]}.log`)
await writeFile(logFile, logLine, { flag: "a" })
- } catch (error) {
- }
+ } catch (error) {}
}
info(message: string, data?: any) {
diff --git a/lib/messages/prune.ts b/lib/messages/prune.ts
index f9348bf..2d52ebb 100644
--- a/lib/messages/prune.ts
+++ b/lib/messages/prune.ts
@@ -6,8 +6,9 @@ import { extractParameterKey, buildToolIdList } from "./utils"
import { getLastUserMessage, isMessageCompacted } from "../shared-utils"
import { UserMessage } from "@opencode-ai/sdk"
-const PRUNED_TOOL_INPUT_REPLACEMENT = '[Input removed to save context]'
-const PRUNED_TOOL_OUTPUT_REPLACEMENT = '[Output removed to save context - information superseded or no longer needed]'
+const PRUNED_TOOL_INPUT_REPLACEMENT = "[Input removed to save context]"
+const PRUNED_TOOL_OUTPUT_REPLACEMENT =
+ "[Output removed to save context - information superseded or no longer needed]"
const NUDGE_STRING = loadPrompt("prune-nudge")
const wrapPrunableTools = (content: string): string => `
@@ -39,27 +40,34 @@ const buildPrunableToolsList = (
}
const numericId = toolIdList.indexOf(toolCallId)
if (numericId === -1) {
- logger.warn(`Tool in cache but not in toolIdList - possible stale entry`, { toolCallId, tool: toolParameterEntry.tool })
+ logger.warn(`Tool in cache but not in toolIdList - possible stale entry`, {
+ toolCallId,
+ tool: toolParameterEntry.tool,
+ })
return
}
const paramKey = extractParameterKey(toolParameterEntry.tool, toolParameterEntry.parameters)
- const description = paramKey ? `${toolParameterEntry.tool}, ${paramKey}` : toolParameterEntry.tool
+ const description = paramKey
+ ? `${toolParameterEntry.tool}, ${paramKey}`
+ : toolParameterEntry.tool
lines.push(`${numericId}: ${description}`)
- logger.debug(`Prunable tool found - ID: ${numericId}, Tool: ${toolParameterEntry.tool}, Call ID: ${toolCallId}`)
+ logger.debug(
+ `Prunable tool found - ID: ${numericId}, Tool: ${toolParameterEntry.tool}, Call ID: ${toolCallId}`,
+ )
})
if (lines.length === 0) {
return ""
}
- return wrapPrunableTools(lines.join('\n'))
+ return wrapPrunableTools(lines.join("\n"))
}
export const insertPruneToolContext = (
state: SessionState,
config: PluginConfig,
logger: Logger,
- messages: WithParts[]
+ messages: WithParts[],
): void => {
if (!config.strategies.pruneTool.enabled) {
return
@@ -101,8 +109,8 @@ export const insertPruneToolContext = (
agent: (lastUserMessage.info as UserMessage).agent || "build",
model: {
providerID: (lastUserMessage.info as UserMessage).model.providerID,
- modelID: (lastUserMessage.info as UserMessage).model.modelID
- }
+ modelID: (lastUserMessage.info as UserMessage).model.modelID,
+ },
},
parts: [
{
@@ -111,8 +119,8 @@ export const insertPruneToolContext = (
messageID: SYNTHETIC_MESSAGE_ID,
type: "text",
text: prunableToolsContent,
- }
- ]
+ },
+ ],
}
messages.push(userMessage)
@@ -122,55 +130,47 @@ export const prune = (
state: SessionState,
logger: Logger,
config: PluginConfig,
- messages: WithParts[]
+ messages: WithParts[],
): void => {
pruneToolOutputs(state, logger, messages)
pruneToolInputs(state, logger, messages)
}
-const pruneToolOutputs = (
- state: SessionState,
- logger: Logger,
- messages: WithParts[]
-): void => {
+const pruneToolOutputs = (state: SessionState, logger: Logger, messages: WithParts[]): void => {
for (const msg of messages) {
if (isMessageCompacted(state, msg)) {
continue
}
for (const part of msg.parts) {
- if (part.type !== 'tool') {
+ if (part.type !== "tool") {
continue
}
if (!state.prune.toolIds.includes(part.callID)) {
continue
}
// Skip write and edit tools - their inputs are pruned instead
- if (part.tool === 'write' || part.tool === 'edit') {
+ if (part.tool === "write" || part.tool === "edit") {
continue
}
- if (part.state.status === 'completed') {
+ if (part.state.status === "completed") {
part.state.output = PRUNED_TOOL_OUTPUT_REPLACEMENT
}
}
}
}
-const pruneToolInputs = (
- state: SessionState,
- logger: Logger,
- messages: WithParts[]
-): void => {
+const pruneToolInputs = (state: SessionState, logger: Logger, messages: WithParts[]): void => {
for (const msg of messages) {
for (const part of msg.parts) {
- if (part.type !== 'tool') {
+ if (part.type !== "tool") {
continue
}
if (!state.prune.toolIds.includes(part.callID)) {
continue
}
// Only prune inputs for write and edit tools
- if (part.tool !== 'write' && part.tool !== 'edit') {
+ if (part.tool !== "write" && part.tool !== "edit") {
continue
}
// Don't prune yet if tool is still pending or running
diff --git a/lib/messages/utils.ts b/lib/messages/utils.ts
index 48f453c..cbf497d 100644
--- a/lib/messages/utils.ts
+++ b/lib/messages/utils.ts
@@ -6,7 +6,7 @@ import type { SessionState, WithParts } from "../state"
* Extracts a human-readable key from tool metadata for display purposes.
*/
export const extractParameterKey = (tool: string, parameters: any): string => {
- if (!parameters) return ''
+ if (!parameters) return ""
if (tool === "read" && parameters.filePath) {
return parameters.filePath
@@ -19,21 +19,21 @@ export const extractParameterKey = (tool: string, parameters: any): string => {
}
if (tool === "list") {
- return parameters.path || '(current directory)'
+ return parameters.path || "(current directory)"
}
if (tool === "glob") {
if (parameters.pattern) {
const pathInfo = parameters.path ? ` in ${parameters.path}` : ""
return `"${parameters.pattern}"${pathInfo}`
}
- return '(unknown pattern)'
+ return "(unknown pattern)"
}
if (tool === "grep") {
if (parameters.pattern) {
const pathInfo = parameters.path ? ` in ${parameters.path}` : ""
return `"${parameters.pattern}"${pathInfo}`
}
- return '(unknown pattern)'
+ return "(unknown pattern)"
}
if (tool === "bash") {
@@ -67,8 +67,8 @@ export const extractParameterKey = (tool: string, parameters: any): string => {
}
const paramStr = JSON.stringify(parameters)
- if (paramStr === '{}' || paramStr === '[]' || paramStr === 'null') {
- return ''
+ if (paramStr === "{}" || paramStr === "[]" || paramStr === "null") {
+ return ""
}
return paramStr.substring(0, 50)
}
@@ -76,7 +76,7 @@ export const extractParameterKey = (tool: string, parameters: any): string => {
export function buildToolIdList(
state: SessionState,
messages: WithParts[],
- logger: Logger
+ logger: Logger,
): string[] {
const toolIds: string[] = []
for (const msg of messages) {
@@ -85,7 +85,7 @@ export function buildToolIdList(
}
if (msg.parts) {
for (const part of msg.parts) {
- if (part.type === 'tool' && part.callID && part.tool) {
+ if (part.type === "tool" && part.callID && part.tool) {
toolIds.push(part.callID)
}
}
diff --git a/lib/model-selector.ts b/lib/model-selector.ts
index d1499eb..efa8e87 100644
--- a/lib/model-selector.ts
+++ b/lib/model-selector.ts
@@ -1,107 +1,112 @@
-import type { LanguageModel } from 'ai';
-import type { Logger } from './logger';
+import type { LanguageModel } from "ai"
+import type { Logger } from "./logger"
export interface ModelInfo {
- providerID: string;
- modelID: string;
+ providerID: string
+ modelID: string
}
export const FALLBACK_MODELS: Record = {
- openai: 'gpt-5-mini',
- anthropic: 'claude-haiku-4-5', //This model isn't broken in opencode-auth-provider
- google: 'gemini-2.5-flash',
- deepseek: 'deepseek-chat',
- xai: 'grok-4-fast',
- alibaba: 'qwen3-coder-flash',
- zai: 'glm-4.5-flash',
- opencode: 'big-pickle'
-};
+ openai: "gpt-5-mini",
+ anthropic: "claude-haiku-4-5", //This model isn't broken in opencode-auth-provider
+ google: "gemini-2.5-flash",
+ deepseek: "deepseek-chat",
+ xai: "grok-4-fast",
+ alibaba: "qwen3-coder-flash",
+ zai: "glm-4.5-flash",
+ opencode: "big-pickle",
+}
const PROVIDER_PRIORITY = [
- 'openai',
- 'anthropic',
- 'google',
- 'deepseek',
- 'xai',
- 'alibaba',
- 'zai',
- 'opencode'
-];
+ "openai",
+ "anthropic",
+ "google",
+ "deepseek",
+ "xai",
+ "alibaba",
+ "zai",
+ "opencode",
+]
// TODO: some anthropic provided models aren't supported by the opencode-auth-provider package, so this provides a temporary workaround
-const SKIP_PROVIDERS = ['github-copilot', 'anthropic'];
+const SKIP_PROVIDERS = ["github-copilot", "anthropic"]
export interface ModelSelectionResult {
- model: LanguageModel;
- modelInfo: ModelInfo;
- source: 'user-model' | 'config' | 'fallback';
- reason?: string;
- failedModel?: ModelInfo;
+ model: LanguageModel
+ modelInfo: ModelInfo
+ source: "user-model" | "config" | "fallback"
+ reason?: string
+ failedModel?: ModelInfo
}
function shouldSkipProvider(providerID: string): boolean {
- const normalized = providerID.toLowerCase().trim();
- return SKIP_PROVIDERS.some(skip => normalized.includes(skip.toLowerCase()));
+ const normalized = providerID.toLowerCase().trim()
+ return SKIP_PROVIDERS.some((skip) => normalized.includes(skip.toLowerCase()))
}
-async function importOpencodeAI(logger?: Logger, maxRetries: number = 3, delayMs: number = 100, workspaceDir?: string): Promise {
- let lastError: Error | undefined;
+async function importOpencodeAI(
+ logger?: Logger,
+ maxRetries: number = 3,
+ delayMs: number = 100,
+ workspaceDir?: string,
+): Promise {
+ let lastError: Error | undefined
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
- const { OpencodeAI } = await import('@tarquinen/opencode-auth-provider');
- return new OpencodeAI({ workspaceDir });
+ const { OpencodeAI } = await import("@tarquinen/opencode-auth-provider")
+ return new OpencodeAI({ workspaceDir })
} catch (error: any) {
- lastError = error;
+ lastError = error
- if (error.message?.includes('before initialization')) {
+ if (error.message?.includes("before initialization")) {
logger?.debug(`Import attempt ${attempt}/${maxRetries} failed, will retry`, {
- error: error.message
- });
+ error: error.message,
+ })
if (attempt < maxRetries) {
- await new Promise(resolve => setTimeout(resolve, delayMs * attempt));
- continue;
+ await new Promise((resolve) => setTimeout(resolve, delayMs * attempt))
+ continue
}
}
- throw error;
+ throw error
}
}
- throw lastError;
+ throw lastError
}
export async function selectModel(
currentModel?: ModelInfo,
logger?: Logger,
configModel?: string,
- workspaceDir?: string
+ workspaceDir?: string,
): Promise {
- const opencodeAI = await importOpencodeAI(logger, 3, 100, workspaceDir);
+ const opencodeAI = await importOpencodeAI(logger, 3, 100, workspaceDir)
- let failedModelInfo: ModelInfo | undefined;
+ let failedModelInfo: ModelInfo | undefined
if (configModel) {
- const parts = configModel.split('/');
+ const parts = configModel.split("/")
if (parts.length !== 2) {
- logger?.warn('Invalid config model format', { configModel });
+ logger?.warn("Invalid config model format", { configModel })
} else {
- const [providerID, modelID] = parts;
+ const [providerID, modelID] = parts
try {
- const model = await opencodeAI.getLanguageModel(providerID, modelID);
+ const model = await opencodeAI.getLanguageModel(providerID, modelID)
return {
model,
modelInfo: { providerID, modelID },
- source: 'config',
- reason: 'Using model specified in dcp.jsonc config'
- };
+ source: "config",
+ reason: "Using model specified in dcp.jsonc config",
+ }
} catch (error: any) {
logger?.warn(`Config model failed: ${providerID}/${modelID}`, {
- error: error.message
- });
- failedModelInfo = { providerID, modelID };
+ error: error.message,
+ })
+ failedModelInfo = { providerID, modelID }
}
}
}
@@ -109,67 +114,72 @@ export async function selectModel(
if (currentModel) {
if (shouldSkipProvider(currentModel.providerID)) {
if (!failedModelInfo) {
- failedModelInfo = currentModel;
+ failedModelInfo = currentModel
}
} else {
try {
- const model = await opencodeAI.getLanguageModel(currentModel.providerID, currentModel.modelID);
+ const model = await opencodeAI.getLanguageModel(
+ currentModel.providerID,
+ currentModel.modelID,
+ )
return {
model,
modelInfo: currentModel,
- source: 'user-model',
- reason: 'Using current session model'
- };
+ source: "user-model",
+ reason: "Using current session model",
+ }
} catch (error: any) {
if (!failedModelInfo) {
- failedModelInfo = currentModel;
+ failedModelInfo = currentModel
}
}
}
}
- const providers = await opencodeAI.listProviders();
+ const providers = await opencodeAI.listProviders()
for (const providerID of PROVIDER_PRIORITY) {
- if (!providers[providerID]) continue;
+ if (!providers[providerID]) continue
- const fallbackModelID = FALLBACK_MODELS[providerID];
- if (!fallbackModelID) continue;
+ const fallbackModelID = FALLBACK_MODELS[providerID]
+ if (!fallbackModelID) continue
try {
- const model = await opencodeAI.getLanguageModel(providerID, fallbackModelID);
+ const model = await opencodeAI.getLanguageModel(providerID, fallbackModelID)
return {
model,
modelInfo: { providerID, modelID: fallbackModelID },
- source: 'fallback',
+ source: "fallback",
reason: `Using ${providerID}/${fallbackModelID}`,
- failedModel: failedModelInfo
- };
+ failedModel: failedModelInfo,
+ }
} catch (error: any) {
- continue;
+ continue
}
}
- throw new Error('No available models for analysis. Please authenticate with at least one provider.');
+ throw new Error(
+ "No available models for analysis. Please authenticate with at least one provider.",
+ )
}
export function extractModelFromSession(sessionState: any, logger?: Logger): ModelInfo | undefined {
if (sessionState?.model?.providerID && sessionState?.model?.modelID) {
return {
providerID: sessionState.model.providerID,
- modelID: sessionState.model.modelID
- };
+ modelID: sessionState.model.modelID,
+ }
}
if (sessionState?.messages && Array.isArray(sessionState.messages)) {
- const lastMessage = sessionState.messages[sessionState.messages.length - 1];
+ const lastMessage = sessionState.messages[sessionState.messages.length - 1]
if (lastMessage?.model?.providerID && lastMessage?.model?.modelID) {
return {
providerID: lastMessage.model.providerID,
- modelID: lastMessage.model.modelID
- };
+ modelID: lastMessage.model.modelID,
+ }
}
}
- return undefined;
+ return undefined
}
diff --git a/lib/prompt.ts b/lib/prompt.ts
index 33a490f..0f2bad3 100644
--- a/lib/prompt.ts
+++ b/lib/prompt.ts
@@ -6,129 +6,144 @@ export function loadPrompt(name: string, vars?: Record): string
let content = readFileSync(filePath, "utf8").trim()
if (vars) {
for (const [key, value] of Object.entries(vars)) {
- content = content.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value)
+ content = content.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value)
}
}
return content
}
-function minimizeMessages(messages: any[], alreadyPrunedIds?: string[], protectedToolCallIds?: string[]): any[] {
- const prunedIdsSet = alreadyPrunedIds ? new Set(alreadyPrunedIds.map(id => id.toLowerCase())) : new Set()
- const protectedIdsSet = protectedToolCallIds ? new Set(protectedToolCallIds.map(id => id.toLowerCase())) : new Set()
-
- return messages.map(msg => {
- const minimized: any = {
- role: msg.info?.role
- }
-
- if (msg.parts) {
- minimized.parts = msg.parts
- .filter((part: any) => {
- if (part.type === 'step-start' || part.type === 'step-finish') {
- return false
- }
- return true
- })
- .map((part: any) => {
- if (part.type === 'text') {
- if (part.ignored) {
- return null
- }
- return {
- type: 'text',
- text: part.text
+function minimizeMessages(
+ messages: any[],
+ alreadyPrunedIds?: string[],
+ protectedToolCallIds?: string[],
+): any[] {
+ const prunedIdsSet = alreadyPrunedIds
+ ? new Set(alreadyPrunedIds.map((id) => id.toLowerCase()))
+ : new Set()
+ const protectedIdsSet = protectedToolCallIds
+ ? new Set(protectedToolCallIds.map((id) => id.toLowerCase()))
+ : new Set()
+
+ return messages
+ .map((msg) => {
+ const minimized: any = {
+ role: msg.info?.role,
+ }
+
+ if (msg.parts) {
+ minimized.parts = msg.parts
+ .filter((part: any) => {
+ if (part.type === "step-start" || part.type === "step-finish") {
+ return false
}
- }
-
- // TODO: This should use the opencode normalized system instead of per provider settings
- if (part.type === 'reasoning') {
- // Calculate encrypted content size if present
- let encryptedContentLength = 0
- if (part.metadata?.openai?.reasoningEncryptedContent) {
- encryptedContentLength = part.metadata.openai.reasoningEncryptedContent.length
- } else if (part.metadata?.anthropic?.signature) {
- encryptedContentLength = part.metadata.anthropic.signature.length
- } else if (part.metadata?.google?.thoughtSignature) {
- encryptedContentLength = part.metadata.google.thoughtSignature.length
+ return true
+ })
+ .map((part: any) => {
+ if (part.type === "text") {
+ if (part.ignored) {
+ return null
+ }
+ return {
+ type: "text",
+ text: part.text,
+ }
}
- return {
- type: 'reasoning',
- text: part.text,
- textLength: part.text?.length || 0,
- encryptedContentLength,
- ...(part.time && { time: part.time }),
- ...(part.metadata && { metadataKeys: Object.keys(part.metadata) })
- }
- }
-
- if (part.type === 'tool') {
- const callIDLower = part.callID?.toLowerCase()
- const isAlreadyPruned = prunedIdsSet.has(callIDLower)
- const isProtected = protectedIdsSet.has(callIDLower)
-
- let displayCallID = part.callID
- if (isAlreadyPruned) {
- displayCallID = ''
- } else if (isProtected) {
- displayCallID = ''
- }
+ // TODO: This should use the opencode normalized system instead of per provider settings
+ if (part.type === "reasoning") {
+ // Calculate encrypted content size if present
+ let encryptedContentLength = 0
+ if (part.metadata?.openai?.reasoningEncryptedContent) {
+ encryptedContentLength =
+ part.metadata.openai.reasoningEncryptedContent.length
+ } else if (part.metadata?.anthropic?.signature) {
+ encryptedContentLength = part.metadata.anthropic.signature.length
+ } else if (part.metadata?.google?.thoughtSignature) {
+ encryptedContentLength =
+ part.metadata.google.thoughtSignature.length
+ }
- const toolPart: any = {
- type: 'tool',
- toolCallID: displayCallID,
- tool: part.tool
+ return {
+ type: "reasoning",
+ text: part.text,
+ textLength: part.text?.length || 0,
+ encryptedContentLength,
+ ...(part.time && { time: part.time }),
+ ...(part.metadata && { metadataKeys: Object.keys(part.metadata) }),
+ }
}
- if (part.state?.output) {
- toolPart.output = part.state.output
- }
+ if (part.type === "tool") {
+ const callIDLower = part.callID?.toLowerCase()
+ const isAlreadyPruned = prunedIdsSet.has(callIDLower)
+ const isProtected = protectedIdsSet.has(callIDLower)
- if (part.state?.input) {
- const input = part.state.input
+ let displayCallID = part.callID
+ if (isAlreadyPruned) {
+ displayCallID = ""
+ } else if (isProtected) {
+ displayCallID = ""
+ }
- if (input.filePath && (part.tool === 'write' || part.tool === 'edit' || part.tool === 'multiedit' || part.tool === 'patch')) {
- toolPart.input = input
+ const toolPart: any = {
+ type: "tool",
+ toolCallID: displayCallID,
+ tool: part.tool,
}
- else if (input.filePath) {
- toolPart.input = { filePath: input.filePath }
+
+ if (part.state?.output) {
+ toolPart.output = part.state.output
}
- else if (input.tool_calls && Array.isArray(input.tool_calls)) {
- toolPart.input = {
- batch_summary: `${input.tool_calls.length} tool calls`,
- tools: input.tool_calls.map((tc: any) => tc.tool)
+
+ if (part.state?.input) {
+ const input = part.state.input
+
+ if (
+ input.filePath &&
+ (part.tool === "write" ||
+ part.tool === "edit" ||
+ part.tool === "multiedit" ||
+ part.tool === "patch")
+ ) {
+ toolPart.input = input
+ } else if (input.filePath) {
+ toolPart.input = { filePath: input.filePath }
+ } else if (input.tool_calls && Array.isArray(input.tool_calls)) {
+ toolPart.input = {
+ batch_summary: `${input.tool_calls.length} tool calls`,
+ tools: input.tool_calls.map((tc: any) => tc.tool),
+ }
+ } else {
+ toolPart.input = input
}
}
- else {
- toolPart.input = input
- }
+
+ return toolPart
}
- return toolPart
- }
+ return null
+ })
+ .filter(Boolean)
+ }
- return null
- })
- .filter(Boolean)
- }
-
- return minimized
- }).filter(msg => {
- return msg.parts && msg.parts.length > 0
- })
+ return minimized
+ })
+ .filter((msg) => {
+ return msg.parts && msg.parts.length > 0
+ })
}
export function buildAnalysisPrompt(
unprunedToolCallIds: string[],
messages: any[],
alreadyPrunedIds?: string[],
- protectedToolCallIds?: string[]
+ protectedToolCallIds?: string[],
): string {
const minimizedMessages = minimizeMessages(messages, alreadyPrunedIds, protectedToolCallIds)
- const messagesJson = JSON.stringify(minimizedMessages, null, 2).replace(/\\n/g, '\n')
+ const messagesJson = JSON.stringify(minimizedMessages, null, 2).replace(/\\n/g, "\n")
return loadPrompt("on-idle-analysis", {
available_tool_call_ids: unprunedToolCallIds.join(", "),
- session_history: messagesJson
+ session_history: messagesJson,
})
}
diff --git a/lib/shared-utils.ts b/lib/shared-utils.ts
index fe6fbd6..ce3be56 100644
--- a/lib/shared-utils.ts
+++ b/lib/shared-utils.ts
@@ -1,22 +1,15 @@
import { SessionState, WithParts } from "./state"
-export const isMessageCompacted = (
- state: SessionState,
- msg: WithParts
-): boolean => {
+export const isMessageCompacted = (state: SessionState, msg: WithParts): boolean => {
return msg.info.time.created < state.lastCompaction
}
-export const getLastUserMessage = (
- messages: WithParts[]
-): WithParts | null => {
+export const getLastUserMessage = (messages: WithParts[]): WithParts | null => {
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i]
- if (msg.info.role === 'user') {
+ if (msg.info.role === "user") {
return msg
}
}
return null
}
-
-
diff --git a/lib/state/persistence.ts b/lib/state/persistence.ts
index eb97551..ccd4859 100644
--- a/lib/state/persistence.ts
+++ b/lib/state/persistence.ts
@@ -4,110 +4,98 @@
* Storage location: ~/.local/share/opencode/storage/plugin/dcp/{sessionId}.json
*/
-import * as fs from "fs/promises";
-import { existsSync } from "fs";
-import { homedir } from "os";
-import { join } from "path";
+import * as fs from "fs/promises"
+import { existsSync } from "fs"
+import { homedir } from "os"
+import { join } from "path"
import type { SessionState, SessionStats, Prune } from "./types"
-import type { Logger } from "../logger";
+import type { Logger } from "../logger"
export interface PersistedSessionState {
- sessionName?: string;
+ sessionName?: string
prune: Prune
- stats: SessionStats;
- lastUpdated: string;
+ stats: SessionStats
+ lastUpdated: string
}
-const STORAGE_DIR = join(
- homedir(),
- ".local",
- "share",
- "opencode",
- "storage",
- "plugin",
- "dcp"
-);
+const STORAGE_DIR = join(homedir(), ".local", "share", "opencode", "storage", "plugin", "dcp")
async function ensureStorageDir(): Promise {
if (!existsSync(STORAGE_DIR)) {
- await fs.mkdir(STORAGE_DIR, { recursive: true });
+ await fs.mkdir(STORAGE_DIR, { recursive: true })
}
}
function getSessionFilePath(sessionId: string): string {
- return join(STORAGE_DIR, `${sessionId}.json`);
+ return join(STORAGE_DIR, `${sessionId}.json`)
}
export async function saveSessionState(
sessionState: SessionState,
logger: Logger,
- sessionName?: string
+ sessionName?: string,
): Promise {
try {
if (!sessionState.sessionId) {
- return;
+ return
}
- await ensureStorageDir();
+ await ensureStorageDir()
const state: PersistedSessionState = {
sessionName: sessionName,
prune: sessionState.prune,
stats: sessionState.stats,
- lastUpdated: new Date().toISOString()
- };
+ lastUpdated: new Date().toISOString(),
+ }
- const filePath = getSessionFilePath(sessionState.sessionId);
- const content = JSON.stringify(state, null, 2);
- await fs.writeFile(filePath, content, "utf-8");
+ const filePath = getSessionFilePath(sessionState.sessionId)
+ const content = JSON.stringify(state, null, 2)
+ await fs.writeFile(filePath, content, "utf-8")
logger.info("Saved session state to disk", {
sessionId: sessionState.sessionId,
- totalTokensSaved: state.stats.totalPruneTokens
- });
+ totalTokensSaved: state.stats.totalPruneTokens,
+ })
} catch (error: any) {
logger.error("Failed to save session state", {
sessionId: sessionState.sessionId,
error: error?.message,
- });
+ })
}
}
export async function loadSessionState(
sessionId: string,
- logger: Logger
+ logger: Logger,
): Promise {
try {
- const filePath = getSessionFilePath(sessionId);
+ const filePath = getSessionFilePath(sessionId)
if (!existsSync(filePath)) {
- return null;
+ return null
}
- const content = await fs.readFile(filePath, "utf-8");
- const state = JSON.parse(content) as PersistedSessionState;
+ const content = await fs.readFile(filePath, "utf-8")
+ const state = JSON.parse(content) as PersistedSessionState
- if (!state ||
- !state.prune ||
- !Array.isArray(state.prune.toolIds) ||
- !state.stats
- ) {
+ if (!state || !state.prune || !Array.isArray(state.prune.toolIds) || !state.stats) {
logger.warn("Invalid session state file, ignoring", {
sessionId: sessionId,
- });
- return null;
+ })
+ return null
}
logger.info("Loaded session state from disk", {
- sessionId: sessionId
- });
+ sessionId: sessionId,
+ })
- return state;
+ return state
} catch (error: any) {
logger.warn("Failed to load session state", {
sessionId: sessionId,
error: error?.message,
- });
- return null;
+ })
+ return null
}
}
diff --git a/lib/state/state.ts b/lib/state/state.ts
index e33f4c8..956ac9d 100644
--- a/lib/state/state.ts
+++ b/lib/state/state.ts
@@ -8,9 +8,8 @@ export const checkSession = async (
client: any,
state: SessionState,
logger: Logger,
- messages: WithParts[]
+ messages: WithParts[],
): Promise => {
-
const lastUserMessage = getLastUserMessage(messages)
if (!lastUserMessage) {
return
@@ -32,7 +31,9 @@ export const checkSession = async (
state.lastCompaction = lastCompactionTimestamp
state.toolParameters.clear()
state.prune.toolIds = []
- logger.info("Detected compaction from messages - cleared tool cache", { timestamp: lastCompactionTimestamp })
+ logger.info("Detected compaction from messages - cleared tool cache", {
+ timestamp: lastCompactionTimestamp,
+ })
}
state.currentTurn = countTurns(state, messages)
@@ -43,7 +44,7 @@ export function createSessionState(): SessionState {
sessionId: null,
isSubAgent: false,
prune: {
- toolIds: []
+ toolIds: [],
},
stats: {
pruneTokenCounter: 0,
@@ -53,7 +54,7 @@ export function createSessionState(): SessionState {
nudgeCounter: 0,
lastToolPrune: false,
lastCompaction: 0,
- currentTurn: 0
+ currentTurn: 0,
}
}
@@ -61,7 +62,7 @@ export function resetSessionState(state: SessionState): void {
state.sessionId = null
state.isSubAgent = false
state.prune = {
- toolIds: []
+ toolIds: [],
}
state.stats = {
pruneTokenCounter: 0,
@@ -79,10 +80,10 @@ export async function ensureSessionInitialized(
state: SessionState,
sessionId: string,
logger: Logger,
- messages: WithParts[]
+ messages: WithParts[],
): Promise {
if (state.sessionId === sessionId) {
- return;
+ return
}
logger.info("session ID = " + sessionId)
@@ -100,11 +101,11 @@ export async function ensureSessionInitialized(
const persisted = await loadSessionState(sessionId, logger)
if (persisted === null) {
- return;
+ return
}
state.prune = {
- toolIds: persisted.prune.toolIds || []
+ toolIds: persisted.prune.toolIds || [],
}
state.stats = {
pruneTokenCounter: persisted.stats?.pruneTokenCounter || 0,
diff --git a/lib/state/tool-cache.ts b/lib/state/tool-cache.ts
index f8ad2b3..55a94f4 100644
--- a/lib/state/tool-cache.ts
+++ b/lib/state/tool-cache.ts
@@ -35,9 +35,11 @@ export async function syncToolCache(
continue
}
- const isProtectedByTurn = config.strategies.pruneTool.turnProtection.enabled &&
+ const isProtectedByTurn =
+ config.strategies.pruneTool.turnProtection.enabled &&
config.strategies.pruneTool.turnProtection.turns > 0 &&
- (state.currentTurn - turnCounter) < config.strategies.pruneTool.turnProtection.turns
+ state.currentTurn - turnCounter <
+ config.strategies.pruneTool.turnProtection.turns
state.lastToolPrune = part.tool === "prune"
@@ -58,25 +60,24 @@ export async function syncToolCache(
continue
}
- state.toolParameters.set(
- part.callID,
- {
- tool: part.tool,
- parameters: part.state?.input ?? {},
- status: part.state.status as ToolStatus | undefined,
- error: part.state.status === "error" ? part.state.error : undefined,
- turn: turnCounter,
- }
- )
+ state.toolParameters.set(part.callID, {
+ tool: part.tool,
+ parameters: part.state?.input ?? {},
+ status: part.state.status as ToolStatus | undefined,
+ error: part.state.status === "error" ? part.state.error : undefined,
+ turn: turnCounter,
+ })
logger.info(`Cached tool id: ${part.callID} (created on turn ${turnCounter})`)
}
}
- logger.info(`Synced cache - size: ${state.toolParameters.size}, currentTurn: ${state.currentTurn}, nudgeCounter: ${state.nudgeCounter}`)
+ logger.info(
+ `Synced cache - size: ${state.toolParameters.size}, currentTurn: ${state.currentTurn}, nudgeCounter: ${state.nudgeCounter}`,
+ )
trimToolParametersCache(state)
} catch (error) {
logger.warn("Failed to sync tool parameters from OpenCode", {
- error: error instanceof Error ? error.message : String(error)
+ error: error instanceof Error ? error.message : String(error),
})
}
}
@@ -90,8 +91,10 @@ export function trimToolParametersCache(state: SessionState): void {
return
}
- const keysToRemove = Array.from(state.toolParameters.keys())
- .slice(0, state.toolParameters.size - MAX_TOOL_CACHE_SIZE)
+ const keysToRemove = Array.from(state.toolParameters.keys()).slice(
+ 0,
+ state.toolParameters.size - MAX_TOOL_CACHE_SIZE,
+ )
for (const key of keysToRemove) {
state.toolParameters.delete(key)
diff --git a/lib/state/types.ts b/lib/state/types.ts
index 04847d5..9a6de02 100644
--- a/lib/state/types.ts
+++ b/lib/state/types.ts
@@ -12,7 +12,7 @@ export interface ToolParameterEntry {
parameters: any
status?: ToolStatus
error?: string
- turn: number // Which turn (step-start count) this tool was called on
+ turn: number // Which turn (step-start count) this tool was called on
}
export interface SessionStats {
@@ -33,5 +33,5 @@ export interface SessionState {
nudgeCounter: number
lastToolPrune: boolean
lastCompaction: number
- currentTurn: number // Current turn count derived from step-start parts
+ currentTurn: number // Current turn count derived from step-start parts
}
diff --git a/lib/strategies/deduplication.ts b/lib/strategies/deduplication.ts
index 11462a8..101e664 100644
--- a/lib/strategies/deduplication.ts
+++ b/lib/strategies/deduplication.ts
@@ -13,7 +13,7 @@ export const deduplicate = (
state: SessionState,
logger: Logger,
config: PluginConfig,
- messages: WithParts[]
+ messages: WithParts[],
): void => {
if (!config.strategies.deduplication.enabled) {
return
@@ -27,7 +27,7 @@ export const deduplicate = (
// Filter out IDs already pruned
const alreadyPruned = new Set(state.prune.toolIds)
- const unprunedIds = allToolIds.filter(id => !alreadyPruned.has(id))
+ const unprunedIds = allToolIds.filter((id) => !alreadyPruned.has(id))
if (unprunedIds.length === 0) {
return
@@ -86,7 +86,7 @@ function createToolSignature(tool: string, parameters?: any): string {
}
function normalizeParameters(params: any): any {
- if (typeof params !== 'object' || params === null) return params
+ if (typeof params !== "object" || params === null) return params
if (Array.isArray(params)) return params
const normalized: any = {}
@@ -99,7 +99,7 @@ function normalizeParameters(params: any): any {
}
function sortObjectKeys(obj: any): any {
- if (typeof obj !== 'object' || obj === null) return obj
+ if (typeof obj !== "object" || obj === null) return obj
if (Array.isArray(obj)) return obj.map(sortObjectKeys)
const sorted: any = {}
diff --git a/lib/strategies/on-idle.ts b/lib/strategies/on-idle.ts
index 602298e..43f481c 100644
--- a/lib/strategies/on-idle.ts
+++ b/lib/strategies/on-idle.ts
@@ -21,7 +21,7 @@ export interface OnIdleResult {
function parseMessages(
state: SessionState,
messages: WithParts[],
- toolParametersCache: Map
+ toolParametersCache: Map,
): {
toolCallIds: string[]
toolMetadata: Map
@@ -46,7 +46,7 @@ function parseMessages(
parameters: parameters,
status: part.state?.status,
error: part.state?.status === "error" ? part.state.error : undefined,
- turn: cachedData?.turn ?? 0
+ turn: cachedData?.turn ?? 0,
})
}
}
@@ -64,26 +64,28 @@ function replacePrunedToolOutputs(messages: WithParts[], prunedIds: string[]): W
const prunedIdsSet = new Set(prunedIds)
- return messages.map(msg => {
+ return messages.map((msg) => {
if (!msg.parts) return msg
return {
...msg,
parts: msg.parts.map((part: any) => {
- if (part.type === 'tool' &&
+ if (
+ part.type === "tool" &&
part.callID &&
prunedIdsSet.has(part.callID) &&
- part.state?.output) {
+ part.state?.output
+ ) {
return {
...part,
state: {
...part.state,
- output: '[Output removed to save context - information superseded or no longer needed]'
- }
+ output: "[Output removed to save context - information superseded or no longer needed]",
+ },
}
}
return part
- })
+ }),
}
}) as WithParts[]
}
@@ -100,10 +102,10 @@ async function runLlmAnalysis(
unprunedToolCallIds: string[],
alreadyPrunedIds: string[],
toolMetadata: Map,
- workingDirectory?: string
+ workingDirectory?: string,
): Promise {
const protectedToolCallIds: string[] = []
- const prunableToolCallIds = unprunedToolCallIds.filter(id => {
+ const prunableToolCallIds = unprunedToolCallIds.filter((id) => {
const metadata = toolMetadata.get(id)
if (metadata && config.strategies.onIdle.protectedTools.includes(metadata.tool)) {
protectedToolCallIds.push(id)
@@ -124,7 +126,7 @@ async function runLlmAnalysis(
if (model?.providerID && model?.modelID) {
validModelInfo = {
providerID: model.providerID,
- modelID: model.modelID
+ modelID: model.modelID,
}
}
}
@@ -133,15 +135,19 @@ async function runLlmAnalysis(
validModelInfo,
logger,
config.strategies.onIdle.model,
- workingDirectory
+ workingDirectory,
)
- logger.info(`OnIdle Model: ${modelSelection.modelInfo.providerID}/${modelSelection.modelInfo.modelID}`, {
- source: modelSelection.source
- })
+ logger.info(
+ `OnIdle Model: ${modelSelection.modelInfo.providerID}/${modelSelection.modelInfo.modelID}`,
+ {
+ source: modelSelection.source,
+ },
+ )
if (modelSelection.failedModel && config.strategies.onIdle.showModelErrorToasts) {
- const skipAi = modelSelection.source === 'fallback' && config.strategies.onIdle.strictModelSelection
+ const skipAi =
+ modelSelection.source === "fallback" && config.strategies.onIdle.strictModelSelection
try {
await client.tui.showToast({
body: {
@@ -150,20 +156,20 @@ async function runLlmAnalysis(
? `${modelSelection.failedModel.providerID}/${modelSelection.failedModel.modelID} failed\nAI analysis skipped (strictModelSelection enabled)`
: `${modelSelection.failedModel.providerID}/${modelSelection.failedModel.modelID} failed\nUsing ${modelSelection.modelInfo.providerID}/${modelSelection.modelInfo.modelID}`,
variant: "info",
- duration: 5000
- }
+ duration: 5000,
+ },
})
} catch {
// Ignore toast errors
}
}
- if (modelSelection.source === 'fallback' && config.strategies.onIdle.strictModelSelection) {
+ if (modelSelection.source === "fallback" && config.strategies.onIdle.strictModelSelection) {
logger.info("Skipping AI analysis (fallback model, strictModelSelection enabled)")
return []
}
- const { generateObject } = await import('ai')
+ const { generateObject } = await import("ai")
const sanitizedMessages = replacePrunedToolOutputs(messages, alreadyPrunedIds)
@@ -171,7 +177,7 @@ async function runLlmAnalysis(
prunableToolCallIds,
sanitizedMessages,
alreadyPrunedIds,
- protectedToolCallIds
+ protectedToolCallIds,
)
const result = await generateObject({
@@ -180,19 +186,17 @@ async function runLlmAnalysis(
pruned_tool_call_ids: z.array(z.string()),
reasoning: z.string(),
}),
- prompt: analysisPrompt
+ prompt: analysisPrompt,
})
const rawLlmPrunedIds = result.object.pruned_tool_call_ids
- const llmPrunedIds = rawLlmPrunedIds.filter(id =>
- prunableToolCallIds.includes(id)
- )
+ const llmPrunedIds = rawLlmPrunedIds.filter((id) => prunableToolCallIds.includes(id))
// Always log LLM output as debug
- const reasoning = result.object.reasoning.replace(/\n+/g, ' ').replace(/\s+/g, ' ').trim()
+ const reasoning = result.object.reasoning.replace(/\n+/g, " ").replace(/\s+/g, " ").trim()
logger.debug(`OnIdle LLM output`, {
pruned_tool_call_ids: rawLlmPrunedIds,
- reasoning: reasoning
+ reasoning: reasoning,
})
return llmPrunedIds
@@ -207,7 +211,7 @@ export async function runOnIdle(
state: SessionState,
logger: Logger,
config: PluginConfig,
- workingDirectory?: string
+ workingDirectory?: string,
): Promise {
try {
if (!state.sessionId) {
@@ -219,7 +223,7 @@ export async function runOnIdle(
// Fetch session info and messages
const [sessionInfoResponse, messagesResponse] = await Promise.all([
client.session.get({ path: { id: sessionId } }),
- client.session.messages({ path: { id: sessionId }})
+ client.session.messages({ path: { id: sessionId } }),
])
const sessionInfo = sessionInfoResponse.data
@@ -233,14 +237,14 @@ export async function runOnIdle(
const { toolCallIds, toolMetadata } = parseMessages(state, messages, state.toolParameters)
const alreadyPrunedIds = state.prune.toolIds
- const unprunedToolCallIds = toolCallIds.filter(id => !alreadyPrunedIds.includes(id))
+ const unprunedToolCallIds = toolCallIds.filter((id) => !alreadyPrunedIds.includes(id))
if (unprunedToolCallIds.length === 0) {
return null
}
// Count prunable tools (excluding protected)
- const candidateCount = unprunedToolCallIds.filter(id => {
+ const candidateCount = unprunedToolCallIds.filter((id) => {
const metadata = toolMetadata.get(id)
return !metadata || !config.strategies.onIdle.protectedTools.includes(metadata.tool)
}).length
@@ -259,10 +263,10 @@ export async function runOnIdle(
unprunedToolCallIds,
alreadyPrunedIds,
toolMetadata,
- workingDirectory
+ workingDirectory,
)
- const newlyPrunedIds = llmPrunedIds.filter(id => !alreadyPrunedIds.includes(id))
+ const newlyPrunedIds = llmPrunedIds.filter((id) => !alreadyPrunedIds.includes(id))
if (newlyPrunedIds.length === 0) {
return null
@@ -271,7 +275,7 @@ export async function runOnIdle(
// Log the tool IDs being pruned with their tool names
for (const id of newlyPrunedIds) {
const metadata = toolMetadata.get(id)
- const toolName = metadata?.tool || 'unknown'
+ const toolName = metadata?.tool || "unknown"
logger.info(`OnIdle pruning tool: ${toolName}`, { callID: id })
}
@@ -301,7 +305,7 @@ export async function runOnIdle(
prunedToolMetadata,
undefined, // reason
currentParams,
- workingDirectory || ""
+ workingDirectory || "",
)
state.stats.totalPruneTokens += state.stats.pruneTokenCounter
@@ -311,7 +315,7 @@ export async function runOnIdle(
// Persist state
const sessionName = sessionInfo?.title
- saveSessionState(state, logger, sessionName).catch(err => {
+ saveSessionState(state, logger, sessionName).catch((err) => {
logger.error("Failed to persist state", { error: err.message })
})
diff --git a/lib/strategies/prune-tool.ts b/lib/strategies/prune-tool.ts
index e6fd7a1..94de456 100644
--- a/lib/strategies/prune-tool.ts
+++ b/lib/strategies/prune-tool.ts
@@ -1,218 +1,197 @@
-import { tool } from "@opencode-ai/plugin";
-import type { SessionState, ToolParameterEntry, WithParts } from "../state";
-import type { PluginConfig } from "../config";
-import { buildToolIdList } from "../messages/utils";
-import { PruneReason, sendUnifiedNotification } from "../ui/notification";
-import { formatPruningResultForTool } from "../ui/utils";
-import { ensureSessionInitialized } from "../state";
-import { saveSessionState } from "../state/persistence";
-import type { Logger } from "../logger";
-import { loadPrompt } from "../prompt";
-import { calculateTokensSaved, getCurrentParams } from "./utils";
-import { requestPruneConfirmation } from "../ui/confirmation";
+import { tool } from "@opencode-ai/plugin"
+import type { SessionState, ToolParameterEntry, WithParts } from "../state"
+import type { PluginConfig } from "../config"
+import { buildToolIdList } from "../messages/utils"
+import { PruneReason, sendUnifiedNotification } from "../ui/notification"
+import { formatPruningResultForTool } from "../ui/utils"
+import { ensureSessionInitialized } from "../state"
+import { saveSessionState } from "../state/persistence"
+import type { Logger } from "../logger"
+import { loadPrompt } from "../prompt"
+import { calculateTokensSaved, getCurrentParams } from "./utils"
+import { requestPruneConfirmation } from "../ui/confirmation"
/** Tool description loaded from prompts/prune-tool-spec.txt */
-const TOOL_DESCRIPTION = loadPrompt("prune-tool-spec");
+const TOOL_DESCRIPTION = loadPrompt("prune-tool-spec")
export interface PruneToolContext {
- client: any;
- state: SessionState;
- logger: Logger;
- config: PluginConfig;
- workingDirectory: string;
+ client: any
+ state: SessionState
+ logger: Logger
+ config: PluginConfig
+ workingDirectory: string
}
/**
* Creates the prune tool definition.
* Accepts numeric IDs from the list and prunes those tool outputs.
*/
-export function createPruneTool(
- ctx: PruneToolContext,
-): ReturnType {
- return tool({
- description: TOOL_DESCRIPTION,
- args: {
- ids: tool.schema
- .array(tool.schema.string())
- .describe(
- "Numeric IDs as strings to prune from the list",
- ),
- metadata: tool.schema
- .object({
- reason: tool.schema
- .enum(["completion", "noise", "consolidation"])
- .describe("The reason for pruning"),
- distillation: tool.schema
- .record(tool.schema.string(), tool.schema.any())
- .optional()
- .describe(
- "An object containing detailed summaries or extractions of the key findings from the tools being pruned. This is REQUIRED for 'consolidation'.",
- ),
- })
- .describe("Metadata about the pruning operation."),
- },
- async execute(args, toolCtx) {
- const { client, state, logger, config, workingDirectory } = ctx;
- const sessionId = toolCtx.sessionID;
-
- logger.info("Prune tool invoked");
- logger.info(JSON.stringify(args));
-
- if (!args.ids || args.ids.length === 0) {
- logger.debug(
- "Prune tool called but args.ids is empty or undefined: " +
- JSON.stringify(args),
- );
- return "No IDs provided. Check the list for available IDs to prune.";
- }
-
- if (!args.metadata || !args.metadata.reason) {
- logger.debug(
- "Prune tool called without metadata.reason: " + JSON.stringify(args),
- );
- return "Missing metadata.reason. Provide metadata: { reason: 'completion' | 'noise' | 'consolidation' }";
- }
-
- const { reason, distillation } = args.metadata;
-
- const numericToolIds: number[] = args.ids
- .map((id) => parseInt(id, 10))
- .filter((n): n is number => !isNaN(n));
-
- if (numericToolIds.length === 0) {
- logger.debug(
- "No numeric tool IDs provided for pruning, yet prune tool was called: " +
- JSON.stringify(args),
- );
- return "No numeric IDs provided. Format: ids: [id1, id2, ...]";
- }
-
- // Fetch messages to calculate tokens and find current agent
- const messagesResponse = await client.session.messages({
- path: { id: sessionId },
- });
- const messages: WithParts[] = messagesResponse.data || messagesResponse;
-
- await ensureSessionInitialized(
- ctx.client,
- state,
- sessionId,
- logger,
- messages,
- );
-
- const currentParams = getCurrentParams(messages, logger);
- const toolIdList: string[] = buildToolIdList(state, messages, logger);
-
- // Validate that all numeric IDs are within bounds
- if (numericToolIds.some((id) => id < 0 || id >= toolIdList.length)) {
- logger.debug("Invalid tool IDs provided: " + numericToolIds.join(", "));
- return "Invalid IDs provided. Only use numeric IDs from the list.";
- }
-
- // Validate that all IDs exist in cache and aren't protected
- // (rejects hallucinated IDs and turn-protected tools not shown in )
- for (const index of numericToolIds) {
- const id = toolIdList[index];
- const metadata = state.toolParameters.get(id);
- if (!metadata) {
- logger.debug(
- "Rejecting prune request - ID not in cache (turn-protected or hallucinated)",
- { index, id },
- );
- return "Invalid IDs provided. Only use numeric IDs from the list.";
- }
- if (
- config.strategies.pruneTool.protectedTools.includes(metadata.tool)
- ) {
- logger.debug("Rejecting prune request - protected tool", {
- index,
- id,
- tool: metadata.tool,
- });
- return "Invalid IDs provided. Only use numeric IDs from the list.";
- }
- }
-
- const pruneToolIds: string[] = numericToolIds.map(
- (index) => toolIdList[index],
- );
-
- const toolMetadata = new Map();
- for (const id of pruneToolIds) {
- const toolParameters = state.toolParameters.get(id);
- if (toolParameters) {
- toolMetadata.set(id, toolParameters);
- } else {
- logger.debug("No metadata found for ID", { id });
- }
- }
-
- // Request user confirmation before pruning
- const confirmedIds = await requestPruneConfirmation(
- client,
- sessionId,
- pruneToolIds,
- toolMetadata,
- currentParams,
- logger,
- workingDirectory,
- );
-
- if (confirmedIds.length === 0) {
- logger.info("Prune cancelled by user");
- return "Prune cancelled by user.";
- }
-
- // Use only the confirmed IDs
- const finalPruneIds = confirmedIds;
- state.prune.toolIds.push(...finalPruneIds);
-
- // Recalculate metadata for confirmed IDs only
- const finalMetadata = new Map();
- for (const id of finalPruneIds) {
- const entry = toolMetadata.get(id);
- if (entry) finalMetadata.set(id, entry);
- }
-
- state.stats.pruneTokenCounter += calculateTokensSaved(
- state,
- messages,
- finalPruneIds,
- );
-
- await sendUnifiedNotification(
- client,
- logger,
- config,
- state,
- sessionId,
- finalPruneIds,
- finalMetadata,
- reason as PruneReason,
- currentParams,
- workingDirectory,
- );
-
- state.stats.totalPruneTokens += state.stats.pruneTokenCounter;
- state.stats.pruneTokenCounter = 0;
- state.nudgeCounter = 0;
-
- saveSessionState(state, logger).catch((err) =>
- logger.error("Failed to persist state", { error: err.message }),
- );
-
- const result = formatPruningResultForTool(
- finalPruneIds,
- finalMetadata,
- workingDirectory,
- );
-
- if (distillation) {
- logger.info("Distillation data received:", distillation);
- }
-
- return result;
- },
- });
+export function createPruneTool(ctx: PruneToolContext): ReturnType {
+ return tool({
+ description: TOOL_DESCRIPTION,
+ args: {
+ ids: tool.schema
+ .array(tool.schema.string())
+ .describe("Numeric IDs as strings to prune from the list"),
+ metadata: tool.schema
+ .object({
+ reason: tool.schema
+ .enum(["completion", "noise", "consolidation"])
+ .describe("The reason for pruning"),
+ distillation: tool.schema
+ .record(tool.schema.string(), tool.schema.any())
+ .optional()
+ .describe(
+ "An object containing detailed summaries or extractions of the key findings from the tools being pruned. This is REQUIRED for 'consolidation'.",
+ ),
+ })
+ .describe("Metadata about the pruning operation."),
+ },
+ async execute(args, toolCtx) {
+ const { client, state, logger, config, workingDirectory } = ctx
+ const sessionId = toolCtx.sessionID
+
+ logger.info("Prune tool invoked")
+ logger.info(JSON.stringify(args))
+
+ if (!args.ids || args.ids.length === 0) {
+ logger.debug(
+ "Prune tool called but args.ids is empty or undefined: " + JSON.stringify(args),
+ )
+ return "No IDs provided. Check the list for available IDs to prune."
+ }
+
+ if (!args.metadata || !args.metadata.reason) {
+ logger.debug("Prune tool called without metadata.reason: " + JSON.stringify(args))
+ return "Missing metadata.reason. Provide metadata: { reason: 'completion' | 'noise' | 'consolidation' }"
+ }
+
+ const { reason, distillation } = args.metadata
+
+ const numericToolIds: number[] = args.ids
+ .map((id) => parseInt(id, 10))
+ .filter((n): n is number => !isNaN(n))
+
+ if (numericToolIds.length === 0) {
+ logger.debug(
+ "No numeric tool IDs provided for pruning, yet prune tool was called: " +
+ JSON.stringify(args),
+ )
+ return "No numeric IDs provided. Format: ids: [id1, id2, ...]"
+ }
+
+ // Fetch messages to calculate tokens and find current agent
+ const messagesResponse = await client.session.messages({
+ path: { id: sessionId },
+ })
+ const messages: WithParts[] = messagesResponse.data || messagesResponse
+
+ await ensureSessionInitialized(ctx.client, state, sessionId, logger, messages)
+
+ const currentParams = getCurrentParams(messages, logger)
+ const toolIdList: string[] = buildToolIdList(state, messages, logger)
+
+ // Validate that all numeric IDs are within bounds
+ if (numericToolIds.some((id) => id < 0 || id >= toolIdList.length)) {
+ logger.debug("Invalid tool IDs provided: " + numericToolIds.join(", "))
+ return "Invalid IDs provided. Only use numeric IDs from the list."
+ }
+
+ // Validate that all IDs exist in cache and aren't protected
+ // (rejects hallucinated IDs and turn-protected tools not shown in )
+ for (const index of numericToolIds) {
+ const id = toolIdList[index]
+ const metadata = state.toolParameters.get(id)
+ if (!metadata) {
+ logger.debug(
+ "Rejecting prune request - ID not in cache (turn-protected or hallucinated)",
+ { index, id },
+ )
+ return "Invalid IDs provided. Only use numeric IDs from the list."
+ }
+ if (config.strategies.pruneTool.protectedTools.includes(metadata.tool)) {
+ logger.debug("Rejecting prune request - protected tool", {
+ index,
+ id,
+ tool: metadata.tool,
+ })
+ return "Invalid IDs provided. Only use numeric IDs from the list."
+ }
+ }
+
+ const pruneToolIds: string[] = numericToolIds.map((index) => toolIdList[index])
+
+ const toolMetadata = new Map()
+ for (const id of pruneToolIds) {
+ const toolParameters = state.toolParameters.get(id)
+ if (toolParameters) {
+ toolMetadata.set(id, toolParameters)
+ } else {
+ logger.debug("No metadata found for ID", { id })
+ }
+ }
+
+ // Request user confirmation before pruning
+ const confirmedIds = await requestPruneConfirmation(
+ client,
+ sessionId,
+ pruneToolIds,
+ toolMetadata,
+ currentParams,
+ logger,
+ workingDirectory,
+ )
+
+ if (confirmedIds.length === 0) {
+ logger.info("Prune cancelled by user")
+ return "Prune cancelled by user."
+ }
+
+ // Use only the confirmed IDs
+ const finalPruneIds = confirmedIds
+ state.prune.toolIds.push(...finalPruneIds)
+
+ // Recalculate metadata for confirmed IDs only
+ const finalMetadata = new Map()
+ for (const id of finalPruneIds) {
+ const entry = toolMetadata.get(id)
+ if (entry) finalMetadata.set(id, entry)
+ }
+
+ state.stats.pruneTokenCounter += calculateTokensSaved(state, messages, finalPruneIds)
+
+ await sendUnifiedNotification(
+ client,
+ logger,
+ config,
+ state,
+ sessionId,
+ finalPruneIds,
+ finalMetadata,
+ reason as PruneReason,
+ currentParams,
+ workingDirectory,
+ )
+
+ state.stats.totalPruneTokens += state.stats.pruneTokenCounter
+ state.stats.pruneTokenCounter = 0
+ state.nudgeCounter = 0
+
+ saveSessionState(state, logger).catch((err) =>
+ logger.error("Failed to persist state", { error: err.message }),
+ )
+
+ const result = formatPruningResultForTool(
+ finalPruneIds,
+ finalMetadata,
+ workingDirectory,
+ )
+
+ if (distillation) {
+ logger.info("Distillation data received:", distillation)
+ }
+
+ return result
+ },
+ })
}
diff --git a/lib/strategies/supersede-writes.ts b/lib/strategies/supersede-writes.ts
index b8bb847..327cb58 100644
--- a/lib/strategies/supersede-writes.ts
+++ b/lib/strategies/supersede-writes.ts
@@ -16,7 +16,7 @@ export const supersedeWrites = (
state: SessionState,
logger: Logger,
config: PluginConfig,
- messages: WithParts[]
+ messages: WithParts[],
): void => {
if (!config.strategies.supersedeWrites.enabled) {
return
@@ -31,14 +31,14 @@ export const supersedeWrites = (
// Filter out IDs already pruned
const alreadyPruned = new Set(state.prune.toolIds)
- const unprunedIds = allToolIds.filter(id => !alreadyPruned.has(id))
+ const unprunedIds = allToolIds.filter((id) => !alreadyPruned.has(id))
if (unprunedIds.length === 0) {
return
}
// Track write tools by file path: filePath -> [{ id, index }]
// We track index to determine chronological order
- const writesByFile = new Map()
+ const writesByFile = new Map()
// Track read file paths with their index
const readsByFile = new Map()
@@ -55,12 +55,12 @@ export const supersedeWrites = (
continue
}
- if (metadata.tool === 'write') {
+ if (metadata.tool === "write") {
if (!writesByFile.has(filePath)) {
writesByFile.set(filePath, [])
}
writesByFile.get(filePath)!.push({ id, index: i })
- } else if (metadata.tool === 'read') {
+ } else if (metadata.tool === "read") {
if (!readsByFile.has(filePath)) {
readsByFile.set(filePath, [])
}
@@ -85,7 +85,7 @@ export const supersedeWrites = (
}
// Check if any read comes after this write
- const hasSubsequentRead = reads.some(readIndex => readIndex > write.index)
+ const hasSubsequentRead = reads.some((readIndex) => readIndex > write.index)
if (hasSubsequentRead) {
newPruneIds.push(write.id)
}
diff --git a/lib/strategies/utils.ts b/lib/strategies/utils.ts
index 3c6a1b1..85d750e 100644
--- a/lib/strategies/utils.ts
+++ b/lib/strategies/utils.ts
@@ -1,15 +1,15 @@
import { SessionState, WithParts } from "../state"
import { UserMessage } from "@opencode-ai/sdk"
import { Logger } from "../logger"
-import { encode } from 'gpt-tokenizer'
+import { encode } from "gpt-tokenizer"
import { getLastUserMessage, isMessageCompacted } from "../shared-utils"
export function getCurrentParams(
messages: WithParts[],
- logger: Logger
+ logger: Logger,
): {
- providerId: string | undefined,
- modelId: string | undefined,
+ providerId: string | undefined
+ modelId: string | undefined
agent: string | undefined
} {
const userMsg = getLastUserMessage(messages)
@@ -29,9 +29,9 @@ export function getCurrentParams(
*/
function estimateTokensBatch(texts: string[]): number[] {
try {
- return texts.map(text => encode(text).length)
+ return texts.map((text) => encode(text).length)
} catch {
- return texts.map(text => Math.round(text.length / 4))
+ return texts.map((text) => Math.round(text.length / 4))
}
}
@@ -42,7 +42,7 @@ function estimateTokensBatch(texts: string[]): number[] {
export const calculateTokensSaved = (
state: SessionState,
messages: WithParts[],
- pruneToolIds: string[]
+ pruneToolIds: string[],
): number => {
try {
const contents: string[] = []
@@ -51,29 +51,32 @@ export const calculateTokensSaved = (
continue
}
for (const part of msg.parts) {
- if (part.type !== 'tool' || !pruneToolIds.includes(part.callID)) {
+ if (part.type !== "tool" || !pruneToolIds.includes(part.callID)) {
continue
}
// For write and edit tools, count input content as that is all we prune for these tools
// (input is present in both completed and error states)
if (part.tool === "write" || part.tool === "edit") {
const inputContent = part.state.input?.content
- const content = typeof inputContent === 'string'
- ? inputContent
- : JSON.stringify(inputContent ?? '')
+ const content =
+ typeof inputContent === "string"
+ ? inputContent
+ : JSON.stringify(inputContent ?? "")
contents.push(content)
continue
}
// For other tools, count output or error based on status
if (part.state.status === "completed") {
- const content = typeof part.state.output === 'string'
- ? part.state.output
- : JSON.stringify(part.state.output)
+ const content =
+ typeof part.state.output === "string"
+ ? part.state.output
+ : JSON.stringify(part.state.output)
contents.push(content)
} else if (part.state.status === "error") {
- const content = typeof part.state.error === 'string'
- ? part.state.error
- : JSON.stringify(part.state.error)
+ const content =
+ typeof part.state.error === "string"
+ ? part.state.error
+ : JSON.stringify(part.state.error)
contents.push(content)
}
}
diff --git a/lib/ui/confirmation.ts b/lib/ui/confirmation.ts
index 0eeca2c..2e6d0f9 100644
--- a/lib/ui/confirmation.ts
+++ b/lib/ui/confirmation.ts
@@ -1,40 +1,40 @@
-import type {Logger} from "../logger";
-import type {ToolParameterEntry} from "../state";
-import {extractParameterKey} from "../messages/utils";
-import {shortenPath, truncate} from "./utils";
+import type { Logger } from "../logger"
+import type { ToolParameterEntry } from "../state"
+import { extractParameterKey } from "../messages/utils"
+import { shortenPath, truncate } from "./utils"
export interface PendingConfirmation {
- resolve: (confirmedIds: string[]) => void;
- items: Array<{id: string; label: string; checked: boolean}>;
+ resolve: (confirmedIds: string[]) => void
+ items: Array<{ id: string; label: string; checked: boolean }>
}
// Shared state for pending confirmations
-let pendingPrune: PendingConfirmation | null = null;
+let pendingPrune: PendingConfirmation | null = null
// Auto-confirm mode - when true, automatically confirms all prunes
-let autoConfirmEnabled = false;
+let autoConfirmEnabled = false
export function getPendingPrune(): PendingConfirmation | null {
- return pendingPrune;
+ return pendingPrune
}
export function setPendingPrune(pending: PendingConfirmation | null): void {
- pendingPrune = pending;
+ pendingPrune = pending
}
export function isAutoConfirmEnabled(): boolean {
- return autoConfirmEnabled;
+ return autoConfirmEnabled
}
export function setAutoConfirm(enabled: boolean): void {
- autoConfirmEnabled = enabled;
+ autoConfirmEnabled = enabled
}
export function resolvePendingPrune(confirmedIds: string[]): void {
- if (pendingPrune) {
- pendingPrune.resolve(confirmedIds);
- pendingPrune = null;
- }
+ if (pendingPrune) {
+ pendingPrune.resolve(confirmedIds)
+ pendingPrune = null
+ }
}
/**
@@ -43,75 +43,72 @@ export function resolvePendingPrune(confirmedIds: string[]): void {
* If auto-confirm is enabled, immediately returns all IDs without showing UI.
*/
export async function requestPruneConfirmation(
- client: any,
- sessionId: string,
- pruneToolIds: string[],
- toolMetadata: Map,
- params: any,
- logger: Logger,
- workingDirectory: string
+ client: any,
+ sessionId: string,
+ pruneToolIds: string[],
+ toolMetadata: Map,
+ params: any,
+ logger: Logger,
+ workingDirectory: string,
): Promise {
- // If auto-confirm is enabled, immediately return all IDs
- if (autoConfirmEnabled) {
- logger.info("Auto-confirming prune", {itemCount: pruneToolIds.length});
- return pruneToolIds;
- }
-
- // Build checklist items from the tool metadata
- const items = pruneToolIds.map((id) => {
- const meta = toolMetadata.get(id);
- let label = id;
- if (meta) {
- const toolName = meta.tool.charAt(0).toUpperCase() + meta.tool.slice(1);
- const paramKey = extractParameterKey(meta.tool, meta.parameters);
- if (paramKey) {
- label = `${toolName} ${truncate(
- shortenPath(paramKey, workingDirectory),
- 50
- )}`;
- } else {
- label = `${toolName}`;
- }
+ // If auto-confirm is enabled, immediately return all IDs
+ if (autoConfirmEnabled) {
+ logger.info("Auto-confirming prune", { itemCount: pruneToolIds.length })
+ return pruneToolIds
}
- return {id, label, checked: true};
- });
- logger.info("Requesting prune confirmation", {itemCount: items.length});
+ // Build checklist items from the tool metadata
+ const items = pruneToolIds.map((id) => {
+ const meta = toolMetadata.get(id)
+ let label = id
+ if (meta) {
+ const toolName = meta.tool.charAt(0).toUpperCase() + meta.tool.slice(1)
+ const paramKey = extractParameterKey(meta.tool, meta.parameters)
+ if (paramKey) {
+ label = `${toolName} ${truncate(shortenPath(paramKey, workingDirectory), 50)}`
+ } else {
+ label = `${toolName}`
+ }
+ }
+ return { id, label, checked: true }
+ })
+
+ logger.info("Requesting prune confirmation", { itemCount: items.length })
- // Create the promise that will be resolved by UI events
- return new Promise((resolve) => {
- setPendingPrune({resolve, items});
+ // Create the promise that will be resolved by UI events
+ return new Promise((resolve) => {
+ setPendingPrune({ resolve, items })
- const agent = params.agent || undefined;
- const model =
- params.providerId && params.modelId
- ? {providerID: params.providerId, modelID: params.modelId}
- : undefined;
+ const agent = params.agent || undefined
+ const model =
+ params.providerId && params.modelId
+ ? { providerID: params.providerId, modelID: params.modelId }
+ : undefined
- // Send the confirmation UI message
- client.session
- .prompt({
- path: {id: sessionId},
- body: {
- noReply: true,
- agent,
- model,
- parts: [
- {
- type: "text",
- text: "dcp-confirm",
- plugin: true,
- metadata: {items},
- },
- ],
- },
- })
- .catch((error: any) => {
- logger.error("Failed to send confirmation UI", {
- error: error.message,
- });
- resolve([]); // Resolve with empty on error
- setPendingPrune(null);
- });
- });
+ // Send the confirmation UI message
+ client.session
+ .prompt({
+ path: { id: sessionId },
+ body: {
+ noReply: true,
+ agent,
+ model,
+ parts: [
+ {
+ type: "text",
+ text: "dcp-confirm",
+ plugin: true,
+ metadata: { items },
+ },
+ ],
+ },
+ })
+ .catch((error: any) => {
+ logger.error("Failed to send confirmation UI", {
+ error: error.message,
+ })
+ resolve([]) // Resolve with empty on error
+ setPendingPrune(null)
+ })
+ })
}
diff --git a/lib/ui/notification.ts b/lib/ui/notification.ts
index 1680c36..e2b415b 100644
--- a/lib/ui/notification.ts
+++ b/lib/ui/notification.ts
@@ -1,101 +1,93 @@
-import type { Logger } from "../logger";
-import type { SessionState } from "../state";
-import { formatPrunedItemsList } from "./utils";
-import { ToolParameterEntry } from "../state";
-import { PluginConfig } from "../config";
+import type { Logger } from "../logger"
+import type { SessionState } from "../state"
+import { formatPrunedItemsList } from "./utils"
+import { ToolParameterEntry } from "../state"
+import { PluginConfig } from "../config"
-export type PruneReason = "completion" | "noise" | "consolidation";
+export type PruneReason = "completion" | "noise" | "consolidation"
export async function sendUnifiedNotification(
- client: any,
- logger: Logger,
- config: PluginConfig,
- state: SessionState,
- sessionId: string,
- pruneToolIds: string[],
- toolMetadata: Map,
- reason: PruneReason | undefined,
- params: any,
- workingDirectory: string,
+ client: any,
+ logger: Logger,
+ config: PluginConfig,
+ state: SessionState,
+ sessionId: string,
+ pruneToolIds: string[],
+ toolMetadata: Map,
+ reason: PruneReason | undefined,
+ params: any,
+ workingDirectory: string,
): Promise {
- const hasPruned = pruneToolIds.length > 0;
- if (!hasPruned) {
- return false;
- }
+ const hasPruned = pruneToolIds.length > 0
+ if (!hasPruned) {
+ return false
+ }
- if (config.pruningSummary === "off") {
- return false;
- }
+ if (config.pruningSummary === "off") {
+ return false
+ }
- const totalSaved =
- state.stats.totalPruneTokens + state.stats.pruneTokenCounter;
- const itemLines = formatPrunedItemsList(
- pruneToolIds,
- toolMetadata,
- workingDirectory,
- );
+ const totalSaved = state.stats.totalPruneTokens + state.stats.pruneTokenCounter
+ const itemLines = formatPrunedItemsList(pruneToolIds, toolMetadata, workingDirectory)
- await sendPruneSummary(
- client,
- sessionId,
- params,
- logger,
- totalSaved,
- pruneToolIds.length,
- itemLines,
- );
- return true;
+ await sendPruneSummary(
+ client,
+ sessionId,
+ params,
+ logger,
+ totalSaved,
+ pruneToolIds.length,
+ itemLines,
+ )
+ return true
}
export async function sendPruneSummary(
- client: any,
- sessionId: string,
- params: any,
- logger: Logger,
- totalSaved: number,
- count: number,
- itemLines: string[],
+ client: any,
+ sessionId: string,
+ params: any,
+ logger: Logger,
+ totalSaved: number,
+ count: number,
+ itemLines: string[],
): Promise {
- const agent = params.agent || undefined;
- const model =
- params.providerId && params.modelId
- ? {
- providerID: params.providerId,
- modelID: params.modelId,
- }
- : undefined;
+ const agent = params.agent || undefined
+ const model =
+ params.providerId && params.modelId
+ ? {
+ providerID: params.providerId,
+ modelID: params.modelId,
+ }
+ : undefined
- const formatted =
- totalSaved >= 1000
- ? `~${(totalSaved / 1000).toFixed(1)}K`
- : `${totalSaved}`;
+ const formatted = totalSaved >= 1000 ? `~${(totalSaved / 1000).toFixed(1)}K` : `${totalSaved}`
- const parts: any[] = [
- {
- type: "text",
- text: "dcp-prune-summary",
- plugin: true,
- metadata: {
- saved: formatted,
- count: String(count),
- itemsList: itemLines.join("\n"),
- },
- },
- ];
+ const parts: any[] = [
+ {
+ type: "text",
+ text: "dcp-prune-summary",
+ plugin: true,
+ metadata: {
+ saved: formatted,
+ count: String(count),
+ itemsList: itemLines.join("\n"),
+ },
+ },
+ ]
- try {
- await client.session.prompt({
- path: {
- id: sessionId,
- },
- body: {
- noReply: true,
- agent: agent,
- model: model,
- parts: parts,
- },
- });
- } catch (error: any) {
- logger.error("Failed to send prune summary", { error: error.message });
- }
+ try {
+ await client.session.prompt({
+ path: {
+ id: sessionId,
+ },
+ body: {
+ noReply: true,
+ agent: agent,
+ model: model,
+ parts: parts,
+ },
+ })
+ } catch (error: any) {
+ logger.error("Failed to send prune summary", { error: error.message })
+ }
}
diff --git a/lib/ui/utils.ts b/lib/ui/utils.ts
index cb99c6a..539bc77 100644
--- a/lib/ui/utils.ts
+++ b/lib/ui/utils.ts
@@ -1,109 +1,96 @@
-import {ToolParameterEntry} from "../state";
-import {extractParameterKey} from "../messages/utils";
+import { ToolParameterEntry } from "../state"
+import { extractParameterKey } from "../messages/utils"
export function formatTokenCount(tokens: number): string {
- if (tokens >= 1000) {
- return `${(tokens / 1000).toFixed(1)}K`.replace(".0K", "K") + " tokens";
- }
- return tokens.toString() + " tokens";
+ if (tokens >= 1000) {
+ return `${(tokens / 1000).toFixed(1)}K`.replace(".0K", "K") + " tokens"
+ }
+ return tokens.toString() + " tokens"
}
export function truncate(str: string, maxLen: number = 60): string {
- if (str.length <= maxLen) return str;
- return str.slice(0, maxLen - 3) + "...";
+ if (str.length <= maxLen) return str
+ return str.slice(0, maxLen - 3) + "..."
}
export function shortenPath(input: string, workingDirectory?: string): string {
- const inPathMatch = input.match(/^(.+) in (.+)$/);
- if (inPathMatch) {
- const prefix = inPathMatch[1];
- const pathPart = inPathMatch[2];
- const shortenedPath = shortenSinglePath(pathPart, workingDirectory);
- return `${prefix} in ${shortenedPath}`;
- }
+ const inPathMatch = input.match(/^(.+) in (.+)$/)
+ if (inPathMatch) {
+ const prefix = inPathMatch[1]
+ const pathPart = inPathMatch[2]
+ const shortenedPath = shortenSinglePath(pathPart, workingDirectory)
+ return `${prefix} in ${shortenedPath}`
+ }
- return shortenSinglePath(input, workingDirectory);
+ return shortenSinglePath(input, workingDirectory)
}
function shortenSinglePath(path: string, workingDirectory?: string): string {
- if (workingDirectory) {
- if (path.startsWith(workingDirectory + "/")) {
- return path.slice(workingDirectory.length + 1);
- }
- if (path === workingDirectory) {
- return ".";
+ if (workingDirectory) {
+ if (path.startsWith(workingDirectory + "/")) {
+ return path.slice(workingDirectory.length + 1)
+ }
+ if (path === workingDirectory) {
+ return "."
+ }
}
- }
- return path;
+ return path
}
/**
* Formats a list of pruned items in the style: "tool: parameter"
*/
export function formatPrunedItemsList(
- pruneToolIds: string[],
- toolMetadata: Map,
- workingDirectory?: string
+ pruneToolIds: string[],
+ toolMetadata: Map,
+ workingDirectory?: string,
): string[] {
- const lines: string[] = [];
+ const lines: string[] = []
- for (const id of pruneToolIds) {
- const metadata = toolMetadata.get(id);
+ for (const id of pruneToolIds) {
+ const metadata = toolMetadata.get(id)
- if (metadata) {
- const paramKey = extractParameterKey(metadata.tool, metadata.parameters);
- if (paramKey) {
- // Use 60 char limit to match notification style
- const displayKey = truncate(
- shortenPath(paramKey, workingDirectory),
- 60
- );
- const toolName =
- metadata.tool.charAt(0).toUpperCase() + metadata.tool.slice(1);
- lines.push(`${toolName}: ${displayKey}`);
- } else {
- const toolName =
- metadata.tool.charAt(0).toUpperCase() + metadata.tool.slice(1);
- lines.push(`${toolName}`);
- }
+ if (metadata) {
+ const paramKey = extractParameterKey(metadata.tool, metadata.parameters)
+ if (paramKey) {
+ // Use 60 char limit to match notification style
+ const displayKey = truncate(shortenPath(paramKey, workingDirectory), 60)
+ const toolName = metadata.tool.charAt(0).toUpperCase() + metadata.tool.slice(1)
+ lines.push(`${toolName}: ${displayKey}`)
+ } else {
+ const toolName = metadata.tool.charAt(0).toUpperCase() + metadata.tool.slice(1)
+ lines.push(`${toolName}`)
+ }
+ }
}
- }
- const knownCount = pruneToolIds.filter((id) => toolMetadata.has(id)).length;
- const unknownCount = pruneToolIds.length - knownCount;
+ const knownCount = pruneToolIds.filter((id) => toolMetadata.has(id)).length
+ const unknownCount = pruneToolIds.length - knownCount
- if (unknownCount > 0) {
- lines.push(
- `(${unknownCount} tool${
- unknownCount > 1 ? "s" : ""
- } with unknown metadata)`
- );
- }
+ if (unknownCount > 0) {
+ lines.push(`(${unknownCount} tool${unknownCount > 1 ? "s" : ""} with unknown metadata)`)
+ }
- return lines;
+ return lines
}
/**
* Formats a PruningResult into a human-readable string for the prune tool output.
*/
export function formatPruningResultForTool(
- prunedIds: string[],
- toolMetadata: Map,
- workingDirectory?: string
+ prunedIds: string[],
+ toolMetadata: Map,
+ workingDirectory?: string,
): string {
- const lines: string[] = [];
- lines.push(
- `Context pruning complete. Pruned ${prunedIds.length} tool outputs.`
- );
- lines.push("");
+ const lines: string[] = []
+ lines.push(`Context pruning complete. Pruned ${prunedIds.length} tool outputs.`)
+ lines.push("")
- if (prunedIds.length > 0) {
- lines.push(`Semantically pruned (${prunedIds.length}):`);
- lines.push(
- ...formatPrunedItemsList(prunedIds, toolMetadata, workingDirectory)
- );
- }
+ if (prunedIds.length > 0) {
+ lines.push(`Semantically pruned (${prunedIds.length}):`)
+ lines.push(...formatPrunedItemsList(prunedIds, toolMetadata, workingDirectory))
+ }
- return lines.join("\n").trim();
+ return lines.join("\n").trim()
}
diff --git a/package-lock.json b/package-lock.json
index 42b0ebd..d7e73c4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,2309 +1,2309 @@
{
- "name": "@tarquinen/opencode-dcp",
- "version": "1.0.4",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "@tarquinen/opencode-dcp",
- "version": "1.0.4",
- "license": "MIT",
- "dependencies": {
- "@ai-sdk/openai-compatible": "^1.0.28",
- "@opencode-ai/sdk": "latest",
- "@tarquinen/opencode-auth-provider": "^0.1.7",
- "ai": "^5.0.106",
- "gpt-tokenizer": "^3.4.0",
- "jsonc-parser": "^3.3.1",
- "zod": "^4.1.13"
- },
- "devDependencies": {
- "@opencode-ai/plugin": "^1.0.143",
- "@types/node": "^24.10.1",
- "tsx": "^4.21.0",
- "typescript": "^5.9.3"
- },
- "peerDependencies": {
- "@opencode-ai/plugin": ">=0.13.7"
- }
- },
- "node_modules/@ai-sdk/gateway": {
- "version": "2.0.18",
- "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.18.tgz",
- "integrity": "sha512-sDQcW+6ck2m0pTIHW6BPHD7S125WD3qNkx/B8sEzJp/hurocmJ5Cni0ybExg6sQMGo+fr/GWOwpHF1cmCdg5rQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@ai-sdk/provider": "2.0.0",
- "@ai-sdk/provider-utils": "3.0.18",
- "@vercel/oidc": "3.0.5"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "zod": "^3.25.76 || ^4.1.8"
- }
- },
- "node_modules/@ai-sdk/openai-compatible": {
- "version": "1.0.28",
- "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-1.0.28.tgz",
- "integrity": "sha512-yKubDxLYtXyGUzkr9lNStf/lE/I+Okc8tmotvyABhsQHHieLKk6oV5fJeRJxhr67Ejhg+FRnwUOxAmjRoFM4dA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@ai-sdk/provider": "2.0.0",
- "@ai-sdk/provider-utils": "3.0.18"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "zod": "^3.25.76 || ^4.1.8"
- }
- },
- "node_modules/@ai-sdk/provider": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz",
- "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==",
- "license": "Apache-2.0",
- "dependencies": {
- "json-schema": "^0.4.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@ai-sdk/provider-utils": {
- "version": "3.0.18",
- "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.18.tgz",
- "integrity": "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@ai-sdk/provider": "2.0.0",
- "@standard-schema/spec": "^1.0.0",
- "eventsource-parser": "^3.0.6"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "zod": "^3.25.76 || ^4.1.8"
- }
- },
- "node_modules/@aws-crypto/sha256-browser": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
- "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-crypto/sha256-js": "^5.2.0",
- "@aws-crypto/supports-web-crypto": "^5.2.0",
- "@aws-crypto/util": "^5.2.0",
- "@aws-sdk/types": "^3.222.0",
- "@aws-sdk/util-locate-window": "^3.0.0",
- "@smithy/util-utf8": "^2.0.0",
- "tslib": "^2.6.2"
- }
- },
- "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
- "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
- "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/is-array-buffer": "^2.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
- "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/util-buffer-from": "^2.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@aws-crypto/sha256-js": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz",
- "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-crypto/util": "^5.2.0",
- "@aws-sdk/types": "^3.222.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=16.0.0"
- }
- },
- "node_modules/@aws-crypto/supports-web-crypto": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz",
- "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- }
- },
- "node_modules/@aws-crypto/util": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz",
- "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/types": "^3.222.0",
- "@smithy/util-utf8": "^2.0.0",
- "tslib": "^2.6.2"
- }
- },
- "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
- "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
- "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/is-array-buffer": "^2.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
- "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/util-buffer-from": "^2.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/@aws-sdk/client-cognito-identity": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.943.0.tgz",
- "integrity": "sha512-XkuokRF2IQ+VLBn0AwrwfFOkZ2c1IXACwQdn3CDnpBZpT1s2hgH3MX0DoH9+41w4ar2QCSI09uAJiv9PX4DLoQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-crypto/sha256-browser": "5.2.0",
- "@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/credential-provider-node": "3.943.0",
- "@aws-sdk/middleware-host-header": "3.936.0",
- "@aws-sdk/middleware-logger": "3.936.0",
- "@aws-sdk/middleware-recursion-detection": "3.936.0",
- "@aws-sdk/middleware-user-agent": "3.943.0",
- "@aws-sdk/region-config-resolver": "3.936.0",
- "@aws-sdk/types": "3.936.0",
- "@aws-sdk/util-endpoints": "3.936.0",
- "@aws-sdk/util-user-agent-browser": "3.936.0",
- "@aws-sdk/util-user-agent-node": "3.943.0",
- "@smithy/config-resolver": "^4.4.3",
- "@smithy/core": "^3.18.5",
- "@smithy/fetch-http-handler": "^5.3.6",
- "@smithy/hash-node": "^4.2.5",
- "@smithy/invalid-dependency": "^4.2.5",
- "@smithy/middleware-content-length": "^4.2.5",
- "@smithy/middleware-endpoint": "^4.3.12",
- "@smithy/middleware-retry": "^4.4.12",
- "@smithy/middleware-serde": "^4.2.6",
- "@smithy/middleware-stack": "^4.2.5",
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/node-http-handler": "^4.4.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/smithy-client": "^4.9.8",
- "@smithy/types": "^4.9.0",
- "@smithy/url-parser": "^4.2.5",
- "@smithy/util-base64": "^4.3.0",
- "@smithy/util-body-length-browser": "^4.2.0",
- "@smithy/util-body-length-node": "^4.2.1",
- "@smithy/util-defaults-mode-browser": "^4.3.11",
- "@smithy/util-defaults-mode-node": "^4.2.14",
- "@smithy/util-endpoints": "^3.2.5",
- "@smithy/util-middleware": "^4.2.5",
- "@smithy/util-retry": "^4.2.5",
- "@smithy/util-utf8": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/client-sso": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.943.0.tgz",
- "integrity": "sha512-kOTO2B8Ks2qX73CyKY8PAajtf5n39aMe2spoiOF5EkgSzGV7hZ/HONRDyADlyxwfsX39Q2F2SpPUaXzon32IGw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-crypto/sha256-browser": "5.2.0",
- "@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/middleware-host-header": "3.936.0",
- "@aws-sdk/middleware-logger": "3.936.0",
- "@aws-sdk/middleware-recursion-detection": "3.936.0",
- "@aws-sdk/middleware-user-agent": "3.943.0",
- "@aws-sdk/region-config-resolver": "3.936.0",
- "@aws-sdk/types": "3.936.0",
- "@aws-sdk/util-endpoints": "3.936.0",
- "@aws-sdk/util-user-agent-browser": "3.936.0",
- "@aws-sdk/util-user-agent-node": "3.943.0",
- "@smithy/config-resolver": "^4.4.3",
- "@smithy/core": "^3.18.5",
- "@smithy/fetch-http-handler": "^5.3.6",
- "@smithy/hash-node": "^4.2.5",
- "@smithy/invalid-dependency": "^4.2.5",
- "@smithy/middleware-content-length": "^4.2.5",
- "@smithy/middleware-endpoint": "^4.3.12",
- "@smithy/middleware-retry": "^4.4.12",
- "@smithy/middleware-serde": "^4.2.6",
- "@smithy/middleware-stack": "^4.2.5",
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/node-http-handler": "^4.4.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/smithy-client": "^4.9.8",
- "@smithy/types": "^4.9.0",
- "@smithy/url-parser": "^4.2.5",
- "@smithy/util-base64": "^4.3.0",
- "@smithy/util-body-length-browser": "^4.2.0",
- "@smithy/util-body-length-node": "^4.2.1",
- "@smithy/util-defaults-mode-browser": "^4.3.11",
- "@smithy/util-defaults-mode-node": "^4.2.14",
- "@smithy/util-endpoints": "^3.2.5",
- "@smithy/util-middleware": "^4.2.5",
- "@smithy/util-retry": "^4.2.5",
- "@smithy/util-utf8": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/core": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.943.0.tgz",
- "integrity": "sha512-8CBy2hI9ABF7RBVQuY1bgf/ue+WPmM/hl0adrXFlhnhkaQP0tFY5zhiy1Y+n7V+5f3/ORoHBmCCQmcHDDYJqJQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/types": "3.936.0",
- "@aws-sdk/xml-builder": "3.930.0",
- "@smithy/core": "^3.18.5",
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/signature-v4": "^5.3.5",
- "@smithy/smithy-client": "^4.9.8",
- "@smithy/types": "^4.9.0",
- "@smithy/util-base64": "^4.3.0",
- "@smithy/util-middleware": "^4.2.5",
- "@smithy/util-utf8": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-provider-cognito-identity": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.943.0.tgz",
- "integrity": "sha512-jZJ0uHjNlhfjx2ZX7YVYnh1wfSkLAvQmecGCSl9C6LJRNXy4uWFPbGjPqcA0tWp0WWIsUYhqjasgvCOMZIY8nw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/client-cognito-identity": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-provider-env": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.943.0.tgz",
- "integrity": "sha512-WnS5w9fK9CTuoZRVSIHLOMcI63oODg9qd1vXMYb7QGLGlfwUm4aG3hdu7i9XvYrpkQfE3dzwWLtXF4ZBuL1Tew==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-provider-http": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.943.0.tgz",
- "integrity": "sha512-SA8bUcYDEACdhnhLpZNnWusBpdmj4Vl67Vxp3Zke7SvoWSYbuxa+tiDiC+c92Z4Yq6xNOuLPW912ZPb9/NsSkA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/fetch-http-handler": "^5.3.6",
- "@smithy/node-http-handler": "^4.4.5",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/smithy-client": "^4.9.8",
- "@smithy/types": "^4.9.0",
- "@smithy/util-stream": "^4.5.6",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-provider-ini": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.943.0.tgz",
- "integrity": "sha512-BcLDb8l4oVW+NkuqXMlO7TnM6lBOWW318ylf4FRED/ply5eaGxkQYqdGvHSqGSN5Rb3vr5Ek0xpzSjeYD7C8Kw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/credential-provider-env": "3.943.0",
- "@aws-sdk/credential-provider-http": "3.943.0",
- "@aws-sdk/credential-provider-login": "3.943.0",
- "@aws-sdk/credential-provider-process": "3.943.0",
- "@aws-sdk/credential-provider-sso": "3.943.0",
- "@aws-sdk/credential-provider-web-identity": "3.943.0",
- "@aws-sdk/nested-clients": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/credential-provider-imds": "^4.2.5",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/shared-ini-file-loader": "^4.4.0",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-provider-login": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.943.0.tgz",
- "integrity": "sha512-9iCOVkiRW+evxiJE94RqosCwRrzptAVPhRhGWv4osfYDhjNAvUMyrnZl3T1bjqCoKNcETRKEZIU3dqYHnUkcwQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/nested-clients": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/shared-ini-file-loader": "^4.4.0",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-provider-node": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.943.0.tgz",
- "integrity": "sha512-14eddaH/gjCWoLSAELVrFOQNyswUYwWphIt+PdsJ/FqVfP4ay2HsiZVEIYbQtmrKHaoLJhiZKwBQRjcqJDZG0w==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/credential-provider-env": "3.943.0",
- "@aws-sdk/credential-provider-http": "3.943.0",
- "@aws-sdk/credential-provider-ini": "3.943.0",
- "@aws-sdk/credential-provider-process": "3.943.0",
- "@aws-sdk/credential-provider-sso": "3.943.0",
- "@aws-sdk/credential-provider-web-identity": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/credential-provider-imds": "^4.2.5",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/shared-ini-file-loader": "^4.4.0",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-provider-process": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.943.0.tgz",
- "integrity": "sha512-GIY/vUkthL33AdjOJ8r9vOosKf/3X+X7LIiACzGxvZZrtoOiRq0LADppdiKIB48vTL63VvW+eRIOFAxE6UDekw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/shared-ini-file-loader": "^4.4.0",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-provider-sso": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.943.0.tgz",
- "integrity": "sha512-1c5G11syUrru3D9OO6Uk+ul5e2lX1adb+7zQNyluNaLPXP6Dina6Sy6DFGRLu7tM8+M7luYmbS3w63rpYpaL+A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/client-sso": "3.943.0",
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/token-providers": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/shared-ini-file-loader": "^4.4.0",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-provider-web-identity": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.943.0.tgz",
- "integrity": "sha512-VtyGKHxICSb4kKGuaqotxso8JVM8RjCS3UYdIMOxUt9TaFE/CZIfZKtjTr+IJ7M0P7t36wuSUb/jRLyNmGzUUA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/nested-clients": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/shared-ini-file-loader": "^4.4.0",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/credential-providers": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.943.0.tgz",
- "integrity": "sha512-uZurSNsS01ehhrSwEPwcKdqp9lmd/x9q++BYO351bXyjSj1LzA/2lfUIxI2tCz/wAjJWOdnnlUdJj6P9I1uNvw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/client-cognito-identity": "3.943.0",
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/credential-provider-cognito-identity": "3.943.0",
- "@aws-sdk/credential-provider-env": "3.943.0",
- "@aws-sdk/credential-provider-http": "3.943.0",
- "@aws-sdk/credential-provider-ini": "3.943.0",
- "@aws-sdk/credential-provider-login": "3.943.0",
- "@aws-sdk/credential-provider-node": "3.943.0",
- "@aws-sdk/credential-provider-process": "3.943.0",
- "@aws-sdk/credential-provider-sso": "3.943.0",
- "@aws-sdk/credential-provider-web-identity": "3.943.0",
- "@aws-sdk/nested-clients": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/config-resolver": "^4.4.3",
- "@smithy/core": "^3.18.5",
- "@smithy/credential-provider-imds": "^4.2.5",
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/middleware-host-header": {
- "version": "3.936.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz",
- "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/types": "3.936.0",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/middleware-logger": {
- "version": "3.936.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz",
- "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/types": "3.936.0",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/middleware-recursion-detection": {
- "version": "3.936.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz",
- "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/types": "3.936.0",
- "@aws/lambda-invoke-store": "^0.2.0",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/middleware-user-agent": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.943.0.tgz",
- "integrity": "sha512-956n4kVEwFNXndXfhSAN5wO+KRgqiWEEY+ECwLvxmmO8uQ0NWOa8l6l65nTtyuiWzMX81c9BvlyNR5EgUeeUvA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@aws-sdk/util-endpoints": "3.936.0",
- "@smithy/core": "^3.18.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/nested-clients": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.943.0.tgz",
- "integrity": "sha512-anFtB0p2FPuyUnbOULwGmKYqYKSq1M73c9uZ08jR/NCq6Trjq9cuF5TFTeHwjJyPRb4wMf2Qk859oiVfFqnQiw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-crypto/sha256-browser": "5.2.0",
- "@aws-crypto/sha256-js": "5.2.0",
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/middleware-host-header": "3.936.0",
- "@aws-sdk/middleware-logger": "3.936.0",
- "@aws-sdk/middleware-recursion-detection": "3.936.0",
- "@aws-sdk/middleware-user-agent": "3.943.0",
- "@aws-sdk/region-config-resolver": "3.936.0",
- "@aws-sdk/types": "3.936.0",
- "@aws-sdk/util-endpoints": "3.936.0",
- "@aws-sdk/util-user-agent-browser": "3.936.0",
- "@aws-sdk/util-user-agent-node": "3.943.0",
- "@smithy/config-resolver": "^4.4.3",
- "@smithy/core": "^3.18.5",
- "@smithy/fetch-http-handler": "^5.3.6",
- "@smithy/hash-node": "^4.2.5",
- "@smithy/invalid-dependency": "^4.2.5",
- "@smithy/middleware-content-length": "^4.2.5",
- "@smithy/middleware-endpoint": "^4.3.12",
- "@smithy/middleware-retry": "^4.4.12",
- "@smithy/middleware-serde": "^4.2.6",
- "@smithy/middleware-stack": "^4.2.5",
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/node-http-handler": "^4.4.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/smithy-client": "^4.9.8",
- "@smithy/types": "^4.9.0",
- "@smithy/url-parser": "^4.2.5",
- "@smithy/util-base64": "^4.3.0",
- "@smithy/util-body-length-browser": "^4.2.0",
- "@smithy/util-body-length-node": "^4.2.1",
- "@smithy/util-defaults-mode-browser": "^4.3.11",
- "@smithy/util-defaults-mode-node": "^4.2.14",
- "@smithy/util-endpoints": "^3.2.5",
- "@smithy/util-middleware": "^4.2.5",
- "@smithy/util-retry": "^4.2.5",
- "@smithy/util-utf8": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/region-config-resolver": {
- "version": "3.936.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz",
- "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/types": "3.936.0",
- "@smithy/config-resolver": "^4.4.3",
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/token-providers": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.943.0.tgz",
- "integrity": "sha512-cRKyIzwfkS+XztXIFPoWORuaxlIswP+a83BJzelX4S1gUZ7FcXB4+lj9Jxjn8SbQhR4TPU3Owbpu+S7pd6IRbQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "3.943.0",
- "@aws-sdk/nested-clients": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/shared-ini-file-loader": "^4.4.0",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/types": {
- "version": "3.936.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz",
- "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/util-endpoints": {
- "version": "3.936.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz",
- "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/types": "3.936.0",
- "@smithy/types": "^4.9.0",
- "@smithy/url-parser": "^4.2.5",
- "@smithy/util-endpoints": "^3.2.5",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/util-locate-window": {
- "version": "3.893.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz",
- "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws-sdk/util-user-agent-browser": {
- "version": "3.936.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz",
- "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/types": "3.936.0",
- "@smithy/types": "^4.9.0",
- "bowser": "^2.11.0",
- "tslib": "^2.6.2"
- }
- },
- "node_modules/@aws-sdk/util-user-agent-node": {
- "version": "3.943.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.943.0.tgz",
- "integrity": "sha512-gn+ILprVRrgAgTIBk2TDsJLRClzIOdStQFeFTcN0qpL8Z4GBCqMFhw7O7X+MM55Stt5s4jAauQ/VvoqmCADnQg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/middleware-user-agent": "3.943.0",
- "@aws-sdk/types": "3.936.0",
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "aws-crt": ">=1.0.0"
- },
- "peerDependenciesMeta": {
- "aws-crt": {
- "optional": true
- }
- }
- },
- "node_modules/@aws-sdk/xml-builder": {
- "version": "3.930.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz",
- "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "fast-xml-parser": "5.2.5",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@aws/lambda-invoke-store": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.1.tgz",
- "integrity": "sha512-sIyFcoPZkTtNu9xFeEoynMef3bPJIAbOfUh+ueYcfhVl6xm2VRtMcMclSxmZCMnHHd4hlYKJeq/aggmBEWynww==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz",
- "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz",
- "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz",
- "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz",
- "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz",
- "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz",
- "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz",
- "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz",
- "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz",
- "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz",
- "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz",
- "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz",
- "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz",
- "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz",
- "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz",
- "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz",
- "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz",
- "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz",
- "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz",
- "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz",
- "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz",
- "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz",
- "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz",
- "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz",
- "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz",
- "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz",
- "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@openauthjs/openauth": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/@openauthjs/openauth/-/openauth-0.4.3.tgz",
- "integrity": "sha512-RlnjqvHzqcbFVymEwhlUEuac4utA5h4nhSK/i2szZuQmxTIqbGUxZ+nM+avM+VV4Ing+/ZaNLKILoXS3yrkOOw==",
- "dependencies": {
- "@standard-schema/spec": "1.0.0-beta.3",
- "aws4fetch": "1.0.20",
- "jose": "5.9.6"
- },
- "peerDependencies": {
- "arctic": "^2.2.2",
- "hono": "^4.0.0"
- }
- },
- "node_modules/@openauthjs/openauth/node_modules/@standard-schema/spec": {
- "version": "1.0.0-beta.3",
- "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0-beta.3.tgz",
- "integrity": "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw==",
- "license": "MIT"
- },
- "node_modules/@opencode-ai/plugin": {
- "version": "1.0.143",
- "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.0.143.tgz",
- "integrity": "sha512-yzaCmdazVJMDADJLbMM8KGp1X+Hd/HVyIXMlNt9qcvz/fcs/ET4EwHJsJaQi/9m/jLJ+plwBJAeIW08BMrECPg==",
- "dev": true,
- "dependencies": {
- "@opencode-ai/sdk": "1.0.143",
- "zod": "4.1.8"
- }
- },
- "node_modules/@opencode-ai/plugin/node_modules/zod": {
- "version": "4.1.8",
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz",
- "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/colinhacks"
- }
- },
- "node_modules/@opencode-ai/sdk": {
- "version": "1.0.143",
- "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.0.143.tgz",
- "integrity": "sha512-dtmkBfJ7IIAHzL6KCzAlwc9GybfJONVeCsF6ePYySpkuhslDbRkZBJYb5vqGd1H5zdsgjc6JjuvmOf0rPWUL6A=="
- },
- "node_modules/@opentelemetry/api": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
- "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/@oslojs/asn1": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@oslojs/asn1/-/asn1-1.0.0.tgz",
- "integrity": "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@oslojs/binary": "1.0.0"
- }
- },
- "node_modules/@oslojs/binary": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@oslojs/binary/-/binary-1.0.0.tgz",
- "integrity": "sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/@oslojs/crypto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@oslojs/crypto/-/crypto-1.0.1.tgz",
- "integrity": "sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@oslojs/asn1": "1.0.0",
- "@oslojs/binary": "1.0.0"
- }
- },
- "node_modules/@oslojs/encoding": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
- "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/@oslojs/jwt": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@oslojs/jwt/-/jwt-0.2.0.tgz",
- "integrity": "sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@oslojs/encoding": "0.4.1"
- }
- },
- "node_modules/@oslojs/jwt/node_modules/@oslojs/encoding": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-0.4.1.tgz",
- "integrity": "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/@smithy/abort-controller": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz",
- "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/config-resolver": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz",
- "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/types": "^4.9.0",
- "@smithy/util-config-provider": "^4.2.0",
- "@smithy/util-endpoints": "^3.2.5",
- "@smithy/util-middleware": "^4.2.5",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/core": {
- "version": "3.18.6",
- "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.6.tgz",
- "integrity": "sha512-8Q/ugWqfDUEU1Exw71+DoOzlONJ2Cn9QA8VeeDzLLjzO/qruh9UKFzbszy4jXcIYgGofxYiT0t1TT6+CT/GupQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/middleware-serde": "^4.2.6",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/types": "^4.9.0",
- "@smithy/util-base64": "^4.3.0",
- "@smithy/util-body-length-browser": "^4.2.0",
- "@smithy/util-middleware": "^4.2.5",
- "@smithy/util-stream": "^4.5.6",
- "@smithy/util-utf8": "^4.2.0",
- "@smithy/uuid": "^1.1.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/credential-provider-imds": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz",
- "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/types": "^4.9.0",
- "@smithy/url-parser": "^4.2.5",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/fetch-http-handler": {
- "version": "5.3.6",
- "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz",
- "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/querystring-builder": "^4.2.5",
- "@smithy/types": "^4.9.0",
- "@smithy/util-base64": "^4.3.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/hash-node": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz",
- "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "@smithy/util-buffer-from": "^4.2.0",
- "@smithy/util-utf8": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/invalid-dependency": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz",
- "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/is-array-buffer": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz",
- "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/middleware-content-length": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz",
- "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/middleware-endpoint": {
- "version": "4.3.13",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.13.tgz",
- "integrity": "sha512-X4za1qCdyx1hEVVXuAWlZuK6wzLDv1uw1OY9VtaYy1lULl661+frY7FeuHdYdl7qAARUxH2yvNExU2/SmRFfcg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/core": "^3.18.6",
- "@smithy/middleware-serde": "^4.2.6",
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/shared-ini-file-loader": "^4.4.0",
- "@smithy/types": "^4.9.0",
- "@smithy/url-parser": "^4.2.5",
- "@smithy/util-middleware": "^4.2.5",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/middleware-retry": {
- "version": "4.4.13",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.13.tgz",
- "integrity": "sha512-RzIDF9OrSviXX7MQeKOm8r/372KTyY8Jmp6HNKOOYlrguHADuM3ED/f4aCyNhZZFLG55lv5beBin7nL0Nzy1Dw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/service-error-classification": "^4.2.5",
- "@smithy/smithy-client": "^4.9.9",
- "@smithy/types": "^4.9.0",
- "@smithy/util-middleware": "^4.2.5",
- "@smithy/util-retry": "^4.2.5",
- "@smithy/uuid": "^1.1.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/middleware-serde": {
- "version": "4.2.6",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz",
- "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/middleware-stack": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz",
- "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/node-config-provider": {
- "version": "4.3.5",
- "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz",
- "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/property-provider": "^4.2.5",
- "@smithy/shared-ini-file-loader": "^4.4.0",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/node-http-handler": {
- "version": "4.4.5",
- "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz",
- "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/abort-controller": "^4.2.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/querystring-builder": "^4.2.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/property-provider": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz",
- "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/protocol-http": {
- "version": "5.3.5",
- "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz",
- "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/querystring-builder": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz",
- "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "@smithy/util-uri-escape": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/querystring-parser": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz",
- "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/service-error-classification": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz",
- "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/shared-ini-file-loader": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz",
- "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/signature-v4": {
- "version": "5.3.5",
- "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz",
- "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/is-array-buffer": "^4.2.0",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/types": "^4.9.0",
- "@smithy/util-hex-encoding": "^4.2.0",
- "@smithy/util-middleware": "^4.2.5",
- "@smithy/util-uri-escape": "^4.2.0",
- "@smithy/util-utf8": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/smithy-client": {
- "version": "4.9.9",
- "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.9.tgz",
- "integrity": "sha512-SUnZJMMo5yCmgjopJbiNeo1vlr8KvdnEfIHV9rlD77QuOGdRotIVBcOrBuMr+sI9zrnhtDtLP054bZVbpZpiQA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/core": "^3.18.6",
- "@smithy/middleware-endpoint": "^4.3.13",
- "@smithy/middleware-stack": "^4.2.5",
- "@smithy/protocol-http": "^5.3.5",
- "@smithy/types": "^4.9.0",
- "@smithy/util-stream": "^4.5.6",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/types": {
- "version": "4.9.0",
- "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz",
- "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/url-parser": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz",
- "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/querystring-parser": "^4.2.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-base64": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz",
- "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/util-buffer-from": "^4.2.0",
- "@smithy/util-utf8": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-body-length-browser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz",
- "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-body-length-node": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz",
- "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-buffer-from": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz",
- "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/is-array-buffer": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-config-provider": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz",
- "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-defaults-mode-browser": {
- "version": "4.3.12",
- "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.12.tgz",
- "integrity": "sha512-TKc6FnOxFULKxLgTNHYjcFqdOYzXVPFFVm5JhI30F3RdhT7nYOtOsjgaOwfDRmA/3U66O9KaBQ3UHoXwayRhAg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/property-provider": "^4.2.5",
- "@smithy/smithy-client": "^4.9.9",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-defaults-mode-node": {
- "version": "4.2.15",
- "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.15.tgz",
- "integrity": "sha512-94NqfQVo+vGc5gsQ9SROZqOvBkGNMQu6pjXbnn8aQvBUhc31kx49gxlkBEqgmaZQHUUfdRUin5gK/HlHKmbAwg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/config-resolver": "^4.4.3",
- "@smithy/credential-provider-imds": "^4.2.5",
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/property-provider": "^4.2.5",
- "@smithy/smithy-client": "^4.9.9",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-endpoints": {
- "version": "3.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz",
- "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/node-config-provider": "^4.3.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-hex-encoding": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz",
- "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-middleware": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz",
- "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-retry": {
- "version": "4.2.5",
- "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz",
- "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/service-error-classification": "^4.2.5",
- "@smithy/types": "^4.9.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-stream": {
- "version": "4.5.6",
- "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz",
- "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/fetch-http-handler": "^5.3.6",
- "@smithy/node-http-handler": "^4.4.5",
- "@smithy/types": "^4.9.0",
- "@smithy/util-base64": "^4.3.0",
- "@smithy/util-buffer-from": "^4.2.0",
- "@smithy/util-hex-encoding": "^4.2.0",
- "@smithy/util-utf8": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-uri-escape": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz",
- "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/util-utf8": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz",
- "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@smithy/util-buffer-from": "^4.2.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@smithy/uuid": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz",
- "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@standard-schema/spec": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
- "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
- "license": "MIT"
- },
- "node_modules/@tarquinen/opencode-auth-provider": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/@tarquinen/opencode-auth-provider/-/opencode-auth-provider-0.1.7.tgz",
- "integrity": "sha512-FH1QEyoirr2e8b48Z6HrjioIZIZUIM9zOpYmku1ad+c4Nv70F37fSWhcObyIdZo4Ly3OntpKPWjadyRhd/kQcg==",
- "license": "MIT",
- "dependencies": {
- "@aws-sdk/credential-providers": "^3.936.0",
- "ai": "^5.0.98",
- "jsonc-parser": "^3.3.1",
- "opencode-anthropic-auth": "0.0.2",
- "opencode-copilot-auth": "0.0.5",
- "opencode-gemini-auth": "^1.1.4",
- "remeda": "^2.32.0",
- "xdg-basedir": "^5.1.0",
- "zod": "^4.1.12"
- },
- "peerDependencies": {
- "@ai-sdk/amazon-bedrock": ">=1.0.0",
- "@ai-sdk/anthropic": ">=1.0.0",
- "@ai-sdk/azure": ">=1.0.0",
- "@ai-sdk/google": ">=1.0.0",
- "@ai-sdk/google-vertex": ">=1.0.0",
- "@ai-sdk/openai": ">=1.0.0",
- "@ai-sdk/openai-compatible": ">=0.1.0",
- "@openrouter/ai-sdk-provider": ">=0.1.0"
- },
- "peerDependenciesMeta": {
- "@ai-sdk/amazon-bedrock": {
- "optional": true
- },
- "@ai-sdk/anthropic": {
- "optional": true
- },
- "@ai-sdk/azure": {
- "optional": true
- },
- "@ai-sdk/google": {
- "optional": true
- },
- "@ai-sdk/google-vertex": {
- "optional": true
- },
- "@ai-sdk/openai": {
- "optional": true
- },
- "@ai-sdk/openai-compatible": {
- "optional": true
- },
- "@openrouter/ai-sdk-provider": {
- "optional": true
- }
- }
- },
- "node_modules/@types/node": {
- "version": "24.10.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
- "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "undici-types": "~7.16.0"
- }
- },
- "node_modules/@vercel/oidc": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.0.5.tgz",
- "integrity": "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">= 20"
- }
- },
- "node_modules/ai": {
- "version": "5.0.106",
- "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.106.tgz",
- "integrity": "sha512-M5obwavxSJJ3tGlAFqI6eltYNJB0D20X6gIBCFx/KVorb/X1fxVVfiZZpZb+Gslu4340droSOjT0aKQFCarNVg==",
- "license": "Apache-2.0",
- "dependencies": {
- "@ai-sdk/gateway": "2.0.18",
- "@ai-sdk/provider": "2.0.0",
- "@ai-sdk/provider-utils": "3.0.18",
- "@opentelemetry/api": "1.9.0"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "zod": "^3.25.76 || ^4.1.8"
- }
- },
- "node_modules/arctic": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/arctic/-/arctic-2.3.4.tgz",
- "integrity": "sha512-+p30BOWsctZp+CVYCt7oAean/hWGW42sH5LAcRQX56ttEkFJWbzXBhmSpibbzwSJkRrotmsA+oAoJoVsU0f5xA==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@oslojs/crypto": "1.0.1",
- "@oslojs/encoding": "1.1.0",
- "@oslojs/jwt": "0.2.0"
- }
- },
- "node_modules/aws4fetch": {
- "version": "1.0.20",
- "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz",
- "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==",
- "license": "MIT"
- },
- "node_modules/bowser": {
- "version": "2.13.1",
- "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz",
- "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==",
- "license": "MIT"
- },
- "node_modules/esbuild": {
- "version": "0.27.0",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz",
- "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.0",
- "@esbuild/android-arm": "0.27.0",
- "@esbuild/android-arm64": "0.27.0",
- "@esbuild/android-x64": "0.27.0",
- "@esbuild/darwin-arm64": "0.27.0",
- "@esbuild/darwin-x64": "0.27.0",
- "@esbuild/freebsd-arm64": "0.27.0",
- "@esbuild/freebsd-x64": "0.27.0",
- "@esbuild/linux-arm": "0.27.0",
- "@esbuild/linux-arm64": "0.27.0",
- "@esbuild/linux-ia32": "0.27.0",
- "@esbuild/linux-loong64": "0.27.0",
- "@esbuild/linux-mips64el": "0.27.0",
- "@esbuild/linux-ppc64": "0.27.0",
- "@esbuild/linux-riscv64": "0.27.0",
- "@esbuild/linux-s390x": "0.27.0",
- "@esbuild/linux-x64": "0.27.0",
- "@esbuild/netbsd-arm64": "0.27.0",
- "@esbuild/netbsd-x64": "0.27.0",
- "@esbuild/openbsd-arm64": "0.27.0",
- "@esbuild/openbsd-x64": "0.27.0",
- "@esbuild/openharmony-arm64": "0.27.0",
- "@esbuild/sunos-x64": "0.27.0",
- "@esbuild/win32-arm64": "0.27.0",
- "@esbuild/win32-ia32": "0.27.0",
- "@esbuild/win32-x64": "0.27.0"
- }
- },
- "node_modules/eventsource-parser": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz",
- "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==",
- "license": "MIT",
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/fast-xml-parser": {
- "version": "5.2.5",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz",
- "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "strnum": "^2.1.0"
- },
- "bin": {
- "fxparser": "src/cli/cli.js"
- }
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/get-tsconfig": {
- "version": "4.13.0",
- "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
- "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "resolve-pkg-maps": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
- }
- },
- "node_modules/gpt-tokenizer": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/gpt-tokenizer/-/gpt-tokenizer-3.4.0.tgz",
- "integrity": "sha512-wxFLnhIXTDjYebd9A9pGl3e31ZpSypbpIJSOswbgop5jLte/AsZVDvjlbEuVFlsqZixVKqbcoNmRlFDf6pz/UQ==",
- "license": "MIT"
- },
- "node_modules/hono": {
- "version": "4.10.7",
- "resolved": "https://registry.npmjs.org/hono/-/hono-4.10.7.tgz",
- "integrity": "sha512-icXIITfw/07Q88nLSkB9aiUrd8rYzSweK681Kjo/TSggaGbOX4RRyxxm71v+3PC8C/j+4rlxGeoTRxQDkaJkUw==",
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=16.9.0"
- }
- },
- "node_modules/jose": {
- "version": "5.9.6",
- "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz",
- "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/panva"
- }
- },
- "node_modules/json-schema": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
- "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
- "license": "(AFL-2.1 OR BSD-3-Clause)"
- },
- "node_modules/jsonc-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
- "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
- "license": "MIT"
- },
- "node_modules/opencode-anthropic-auth": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/opencode-anthropic-auth/-/opencode-anthropic-auth-0.0.2.tgz",
- "integrity": "sha512-m8dcEKtq2ExGLV7n4BMr1H5UimDaABV6aG82IDMcp1xmXUaO1K20/hess0s8cwvv6MFmJk4//2wbWZkzoOtirA==",
- "dependencies": {
- "@openauthjs/openauth": "^0.4.3"
- }
- },
- "node_modules/opencode-copilot-auth": {
- "version": "0.0.5",
- "resolved": "https://registry.npmjs.org/opencode-copilot-auth/-/opencode-copilot-auth-0.0.5.tgz",
- "integrity": "sha512-aOna2jy3BnaEpVJkeF32joUzI8DcpbBMWjd7zW6sgX4t58AnxaEB5sDadLsxRfcxJdhmABd5k6QSww5LcJ4e9Q=="
- },
- "node_modules/opencode-gemini-auth": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/opencode-gemini-auth/-/opencode-gemini-auth-1.1.6.tgz",
- "integrity": "sha512-7WxOEwYMqXeCD2jf/Wj+8yBS3qwnRxHKt/sWhn2ZBDgz+dVwrC/SpTNvpva1fF8KSgVVG8tS9yvDQXM0JcVGoQ==",
- "license": "MIT",
- "dependencies": {
- "@openauthjs/openauth": "^0.4.3"
- },
- "peerDependencies": {
- "typescript": "^5"
- }
- },
- "node_modules/remeda": {
- "version": "2.32.0",
- "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.32.0.tgz",
- "integrity": "sha512-BZx9DsT4FAgXDTOdgJIc5eY6ECIXMwtlSPQoPglF20ycSWigttDDe88AozEsPPT4OWk5NujroGSBC1phw5uU+w==",
- "license": "MIT",
- "dependencies": {
- "type-fest": "^4.41.0"
- }
- },
- "node_modules/resolve-pkg-maps": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
- "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
- }
- },
- "node_modules/strnum": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz",
- "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
+ "name": "@tarquinen/opencode-dcp",
+ "version": "1.0.4",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@tarquinen/opencode-dcp",
+ "version": "1.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "@ai-sdk/openai-compatible": "^1.0.28",
+ "@opencode-ai/sdk": "latest",
+ "@tarquinen/opencode-auth-provider": "^0.1.7",
+ "ai": "^5.0.106",
+ "gpt-tokenizer": "^3.4.0",
+ "jsonc-parser": "^3.3.1",
+ "zod": "^4.1.13"
+ },
+ "devDependencies": {
+ "@opencode-ai/plugin": "^1.0.143",
+ "@types/node": "^24.10.1",
+ "tsx": "^4.21.0",
+ "typescript": "^5.9.3"
+ },
+ "peerDependencies": {
+ "@opencode-ai/plugin": ">=0.13.7"
+ }
+ },
+ "node_modules/@ai-sdk/gateway": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.18.tgz",
+ "integrity": "sha512-sDQcW+6ck2m0pTIHW6BPHD7S125WD3qNkx/B8sEzJp/hurocmJ5Cni0ybExg6sQMGo+fr/GWOwpHF1cmCdg5rQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "2.0.0",
+ "@ai-sdk/provider-utils": "3.0.18",
+ "@vercel/oidc": "3.0.5"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@ai-sdk/openai-compatible": {
+ "version": "1.0.28",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-1.0.28.tgz",
+ "integrity": "sha512-yKubDxLYtXyGUzkr9lNStf/lE/I+Okc8tmotvyABhsQHHieLKk6oV5fJeRJxhr67Ejhg+FRnwUOxAmjRoFM4dA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "2.0.0",
+ "@ai-sdk/provider-utils": "3.0.18"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@ai-sdk/provider": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz",
+ "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "json-schema": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@ai-sdk/provider-utils": {
+ "version": "3.0.18",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.18.tgz",
+ "integrity": "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "2.0.0",
+ "@standard-schema/spec": "^1.0.0",
+ "eventsource-parser": "^3.0.6"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
+ "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-js": "^5.2.0",
+ "@aws-crypto/supports-web-crypto": "^5.2.0",
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "@aws-sdk/util-locate-window": "^3.0.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
+ "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
+ "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
+ "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-js": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz",
+ "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/supports-web-crypto": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz",
+ "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/util": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz",
+ "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.222.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
+ "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
+ "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
+ "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-cognito-identity": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.943.0.tgz",
+ "integrity": "sha512-XkuokRF2IQ+VLBn0AwrwfFOkZ2c1IXACwQdn3CDnpBZpT1s2hgH3MX0DoH9+41w4ar2QCSI09uAJiv9PX4DLoQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/credential-provider-node": "3.943.0",
+ "@aws-sdk/middleware-host-header": "3.936.0",
+ "@aws-sdk/middleware-logger": "3.936.0",
+ "@aws-sdk/middleware-recursion-detection": "3.936.0",
+ "@aws-sdk/middleware-user-agent": "3.943.0",
+ "@aws-sdk/region-config-resolver": "3.936.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-endpoints": "3.936.0",
+ "@aws-sdk/util-user-agent-browser": "3.936.0",
+ "@aws-sdk/util-user-agent-node": "3.943.0",
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/core": "^3.18.5",
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/hash-node": "^4.2.5",
+ "@smithy/invalid-dependency": "^4.2.5",
+ "@smithy/middleware-content-length": "^4.2.5",
+ "@smithy/middleware-endpoint": "^4.3.12",
+ "@smithy/middleware-retry": "^4.4.12",
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/middleware-stack": "^4.2.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.11",
+ "@smithy/util-defaults-mode-node": "^4.2.14",
+ "@smithy/util-endpoints": "^3.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-retry": "^4.2.5",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-sso": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.943.0.tgz",
+ "integrity": "sha512-kOTO2B8Ks2qX73CyKY8PAajtf5n39aMe2spoiOF5EkgSzGV7hZ/HONRDyADlyxwfsX39Q2F2SpPUaXzon32IGw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/middleware-host-header": "3.936.0",
+ "@aws-sdk/middleware-logger": "3.936.0",
+ "@aws-sdk/middleware-recursion-detection": "3.936.0",
+ "@aws-sdk/middleware-user-agent": "3.943.0",
+ "@aws-sdk/region-config-resolver": "3.936.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-endpoints": "3.936.0",
+ "@aws-sdk/util-user-agent-browser": "3.936.0",
+ "@aws-sdk/util-user-agent-node": "3.943.0",
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/core": "^3.18.5",
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/hash-node": "^4.2.5",
+ "@smithy/invalid-dependency": "^4.2.5",
+ "@smithy/middleware-content-length": "^4.2.5",
+ "@smithy/middleware-endpoint": "^4.3.12",
+ "@smithy/middleware-retry": "^4.4.12",
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/middleware-stack": "^4.2.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.11",
+ "@smithy/util-defaults-mode-node": "^4.2.14",
+ "@smithy/util-endpoints": "^3.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-retry": "^4.2.5",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/core": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.943.0.tgz",
+ "integrity": "sha512-8CBy2hI9ABF7RBVQuY1bgf/ue+WPmM/hl0adrXFlhnhkaQP0tFY5zhiy1Y+n7V+5f3/ORoHBmCCQmcHDDYJqJQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/xml-builder": "3.930.0",
+ "@smithy/core": "^3.18.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/signature-v4": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-cognito-identity": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.943.0.tgz",
+ "integrity": "sha512-jZJ0uHjNlhfjx2ZX7YVYnh1wfSkLAvQmecGCSl9C6LJRNXy4uWFPbGjPqcA0tWp0WWIsUYhqjasgvCOMZIY8nw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/client-cognito-identity": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-env": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.943.0.tgz",
+ "integrity": "sha512-WnS5w9fK9CTuoZRVSIHLOMcI63oODg9qd1vXMYb7QGLGlfwUm4aG3hdu7i9XvYrpkQfE3dzwWLtXF4ZBuL1Tew==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-http": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.943.0.tgz",
+ "integrity": "sha512-SA8bUcYDEACdhnhLpZNnWusBpdmj4Vl67Vxp3Zke7SvoWSYbuxa+tiDiC+c92Z4Yq6xNOuLPW912ZPb9/NsSkA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-stream": "^4.5.6",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-ini": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.943.0.tgz",
+ "integrity": "sha512-BcLDb8l4oVW+NkuqXMlO7TnM6lBOWW318ylf4FRED/ply5eaGxkQYqdGvHSqGSN5Rb3vr5Ek0xpzSjeYD7C8Kw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/credential-provider-env": "3.943.0",
+ "@aws-sdk/credential-provider-http": "3.943.0",
+ "@aws-sdk/credential-provider-login": "3.943.0",
+ "@aws-sdk/credential-provider-process": "3.943.0",
+ "@aws-sdk/credential-provider-sso": "3.943.0",
+ "@aws-sdk/credential-provider-web-identity": "3.943.0",
+ "@aws-sdk/nested-clients": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/credential-provider-imds": "^4.2.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-login": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.943.0.tgz",
+ "integrity": "sha512-9iCOVkiRW+evxiJE94RqosCwRrzptAVPhRhGWv4osfYDhjNAvUMyrnZl3T1bjqCoKNcETRKEZIU3dqYHnUkcwQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/nested-clients": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-node": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.943.0.tgz",
+ "integrity": "sha512-14eddaH/gjCWoLSAELVrFOQNyswUYwWphIt+PdsJ/FqVfP4ay2HsiZVEIYbQtmrKHaoLJhiZKwBQRjcqJDZG0w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/credential-provider-env": "3.943.0",
+ "@aws-sdk/credential-provider-http": "3.943.0",
+ "@aws-sdk/credential-provider-ini": "3.943.0",
+ "@aws-sdk/credential-provider-process": "3.943.0",
+ "@aws-sdk/credential-provider-sso": "3.943.0",
+ "@aws-sdk/credential-provider-web-identity": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/credential-provider-imds": "^4.2.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-process": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.943.0.tgz",
+ "integrity": "sha512-GIY/vUkthL33AdjOJ8r9vOosKf/3X+X7LIiACzGxvZZrtoOiRq0LADppdiKIB48vTL63VvW+eRIOFAxE6UDekw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-sso": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.943.0.tgz",
+ "integrity": "sha512-1c5G11syUrru3D9OO6Uk+ul5e2lX1adb+7zQNyluNaLPXP6Dina6Sy6DFGRLu7tM8+M7luYmbS3w63rpYpaL+A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/client-sso": "3.943.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/token-providers": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-web-identity": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.943.0.tgz",
+ "integrity": "sha512-VtyGKHxICSb4kKGuaqotxso8JVM8RjCS3UYdIMOxUt9TaFE/CZIfZKtjTr+IJ7M0P7t36wuSUb/jRLyNmGzUUA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/nested-clients": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-providers": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.943.0.tgz",
+ "integrity": "sha512-uZurSNsS01ehhrSwEPwcKdqp9lmd/x9q++BYO351bXyjSj1LzA/2lfUIxI2tCz/wAjJWOdnnlUdJj6P9I1uNvw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/client-cognito-identity": "3.943.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/credential-provider-cognito-identity": "3.943.0",
+ "@aws-sdk/credential-provider-env": "3.943.0",
+ "@aws-sdk/credential-provider-http": "3.943.0",
+ "@aws-sdk/credential-provider-ini": "3.943.0",
+ "@aws-sdk/credential-provider-login": "3.943.0",
+ "@aws-sdk/credential-provider-node": "3.943.0",
+ "@aws-sdk/credential-provider-process": "3.943.0",
+ "@aws-sdk/credential-provider-sso": "3.943.0",
+ "@aws-sdk/credential-provider-web-identity": "3.943.0",
+ "@aws-sdk/nested-clients": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/core": "^3.18.5",
+ "@smithy/credential-provider-imds": "^4.2.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-host-header": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz",
+ "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-logger": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz",
+ "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-recursion-detection": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz",
+ "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@aws/lambda-invoke-store": "^0.2.0",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-user-agent": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.943.0.tgz",
+ "integrity": "sha512-956n4kVEwFNXndXfhSAN5wO+KRgqiWEEY+ECwLvxmmO8uQ0NWOa8l6l65nTtyuiWzMX81c9BvlyNR5EgUeeUvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-endpoints": "3.936.0",
+ "@smithy/core": "^3.18.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/nested-clients": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.943.0.tgz",
+ "integrity": "sha512-anFtB0p2FPuyUnbOULwGmKYqYKSq1M73c9uZ08jR/NCq6Trjq9cuF5TFTeHwjJyPRb4wMf2Qk859oiVfFqnQiw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/middleware-host-header": "3.936.0",
+ "@aws-sdk/middleware-logger": "3.936.0",
+ "@aws-sdk/middleware-recursion-detection": "3.936.0",
+ "@aws-sdk/middleware-user-agent": "3.943.0",
+ "@aws-sdk/region-config-resolver": "3.936.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-endpoints": "3.936.0",
+ "@aws-sdk/util-user-agent-browser": "3.936.0",
+ "@aws-sdk/util-user-agent-node": "3.943.0",
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/core": "^3.18.5",
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/hash-node": "^4.2.5",
+ "@smithy/invalid-dependency": "^4.2.5",
+ "@smithy/middleware-content-length": "^4.2.5",
+ "@smithy/middleware-endpoint": "^4.3.12",
+ "@smithy/middleware-retry": "^4.4.12",
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/middleware-stack": "^4.2.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.11",
+ "@smithy/util-defaults-mode-node": "^4.2.14",
+ "@smithy/util-endpoints": "^3.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-retry": "^4.2.5",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/region-config-resolver": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz",
+ "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/token-providers": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.943.0.tgz",
+ "integrity": "sha512-cRKyIzwfkS+XztXIFPoWORuaxlIswP+a83BJzelX4S1gUZ7FcXB4+lj9Jxjn8SbQhR4TPU3Owbpu+S7pd6IRbQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/nested-clients": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/types": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz",
+ "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-endpoints": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz",
+ "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-endpoints": "^3.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-locate-window": {
+ "version": "3.893.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz",
+ "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-user-agent-browser": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz",
+ "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/types": "^4.9.0",
+ "bowser": "^2.11.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-sdk/util-user-agent-node": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.943.0.tgz",
+ "integrity": "sha512-gn+ILprVRrgAgTIBk2TDsJLRClzIOdStQFeFTcN0qpL8Z4GBCqMFhw7O7X+MM55Stt5s4jAauQ/VvoqmCADnQg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-user-agent": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "aws-crt": ">=1.0.0"
+ },
+ "peerDependenciesMeta": {
+ "aws-crt": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@aws-sdk/xml-builder": {
+ "version": "3.930.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz",
+ "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "fast-xml-parser": "5.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws/lambda-invoke-store": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.1.tgz",
+ "integrity": "sha512-sIyFcoPZkTtNu9xFeEoynMef3bPJIAbOfUh+ueYcfhVl6xm2VRtMcMclSxmZCMnHHd4hlYKJeq/aggmBEWynww==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz",
+ "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz",
+ "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz",
+ "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz",
+ "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz",
+ "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz",
+ "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz",
+ "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz",
+ "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz",
+ "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz",
+ "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz",
+ "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz",
+ "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz",
+ "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz",
+ "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz",
+ "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz",
+ "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz",
+ "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz",
+ "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz",
+ "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz",
+ "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz",
+ "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz",
+ "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz",
+ "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz",
+ "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz",
+ "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz",
+ "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@openauthjs/openauth": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@openauthjs/openauth/-/openauth-0.4.3.tgz",
+ "integrity": "sha512-RlnjqvHzqcbFVymEwhlUEuac4utA5h4nhSK/i2szZuQmxTIqbGUxZ+nM+avM+VV4Ing+/ZaNLKILoXS3yrkOOw==",
+ "dependencies": {
+ "@standard-schema/spec": "1.0.0-beta.3",
+ "aws4fetch": "1.0.20",
+ "jose": "5.9.6"
+ },
+ "peerDependencies": {
+ "arctic": "^2.2.2",
+ "hono": "^4.0.0"
+ }
+ },
+ "node_modules/@openauthjs/openauth/node_modules/@standard-schema/spec": {
+ "version": "1.0.0-beta.3",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0-beta.3.tgz",
+ "integrity": "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw==",
+ "license": "MIT"
+ },
+ "node_modules/@opencode-ai/plugin": {
+ "version": "1.0.143",
+ "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.0.143.tgz",
+ "integrity": "sha512-yzaCmdazVJMDADJLbMM8KGp1X+Hd/HVyIXMlNt9qcvz/fcs/ET4EwHJsJaQi/9m/jLJ+plwBJAeIW08BMrECPg==",
+ "dev": true,
+ "dependencies": {
+ "@opencode-ai/sdk": "1.0.143",
+ "zod": "4.1.8"
+ }
+ },
+ "node_modules/@opencode-ai/plugin/node_modules/zod": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz",
+ "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/@opencode-ai/sdk": {
+ "version": "1.0.143",
+ "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.0.143.tgz",
+ "integrity": "sha512-dtmkBfJ7IIAHzL6KCzAlwc9GybfJONVeCsF6ePYySpkuhslDbRkZBJYb5vqGd1H5zdsgjc6JjuvmOf0rPWUL6A=="
+ },
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@oslojs/asn1": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@oslojs/asn1/-/asn1-1.0.0.tgz",
+ "integrity": "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@oslojs/binary": "1.0.0"
+ }
+ },
+ "node_modules/@oslojs/binary": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@oslojs/binary/-/binary-1.0.0.tgz",
+ "integrity": "sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@oslojs/crypto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@oslojs/crypto/-/crypto-1.0.1.tgz",
+ "integrity": "sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@oslojs/asn1": "1.0.0",
+ "@oslojs/binary": "1.0.0"
+ }
+ },
+ "node_modules/@oslojs/encoding": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
+ "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@oslojs/jwt": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@oslojs/jwt/-/jwt-0.2.0.tgz",
+ "integrity": "sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@oslojs/encoding": "0.4.1"
+ }
+ },
+ "node_modules/@oslojs/jwt/node_modules/@oslojs/encoding": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-0.4.1.tgz",
+ "integrity": "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@smithy/abort-controller": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz",
+ "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/config-resolver": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz",
+ "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-config-provider": "^4.2.0",
+ "@smithy/util-endpoints": "^3.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/core": {
+ "version": "3.18.6",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.6.tgz",
+ "integrity": "sha512-8Q/ugWqfDUEU1Exw71+DoOzlONJ2Cn9QA8VeeDzLLjzO/qruh9UKFzbszy4jXcIYgGofxYiT0t1TT6+CT/GupQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-stream": "^4.5.6",
+ "@smithy/util-utf8": "^4.2.0",
+ "@smithy/uuid": "^1.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/credential-provider-imds": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz",
+ "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/fetch-http-handler": {
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz",
+ "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/querystring-builder": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-base64": "^4.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/hash-node": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz",
+ "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-buffer-from": "^4.2.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/invalid-dependency": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz",
+ "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/is-array-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz",
+ "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-content-length": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz",
+ "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-endpoint": {
+ "version": "4.3.13",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.13.tgz",
+ "integrity": "sha512-X4za1qCdyx1hEVVXuAWlZuK6wzLDv1uw1OY9VtaYy1lULl661+frY7FeuHdYdl7qAARUxH2yvNExU2/SmRFfcg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.18.6",
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-retry": {
+ "version": "4.4.13",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.13.tgz",
+ "integrity": "sha512-RzIDF9OrSviXX7MQeKOm8r/372KTyY8Jmp6HNKOOYlrguHADuM3ED/f4aCyNhZZFLG55lv5beBin7nL0Nzy1Dw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/service-error-classification": "^4.2.5",
+ "@smithy/smithy-client": "^4.9.9",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-retry": "^4.2.5",
+ "@smithy/uuid": "^1.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-serde": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz",
+ "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-stack": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz",
+ "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/node-config-provider": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz",
+ "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/node-http-handler": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz",
+ "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/abort-controller": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/querystring-builder": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/property-provider": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz",
+ "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/protocol-http": {
+ "version": "5.3.5",
+ "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz",
+ "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/querystring-builder": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz",
+ "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-uri-escape": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/querystring-parser": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz",
+ "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/service-error-classification": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz",
+ "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/shared-ini-file-loader": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz",
+ "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/signature-v4": {
+ "version": "5.3.5",
+ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz",
+ "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^4.2.0",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-hex-encoding": "^4.2.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-uri-escape": "^4.2.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/smithy-client": {
+ "version": "4.9.9",
+ "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.9.tgz",
+ "integrity": "sha512-SUnZJMMo5yCmgjopJbiNeo1vlr8KvdnEfIHV9rlD77QuOGdRotIVBcOrBuMr+sI9zrnhtDtLP054bZVbpZpiQA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.18.6",
+ "@smithy/middleware-endpoint": "^4.3.13",
+ "@smithy/middleware-stack": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-stream": "^4.5.6",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/types": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz",
+ "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/url-parser": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz",
+ "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/querystring-parser": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-base64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz",
+ "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^4.2.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-body-length-browser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz",
+ "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-body-length-node": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz",
+ "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-buffer-from": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz",
+ "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-config-provider": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz",
+ "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-defaults-mode-browser": {
+ "version": "4.3.12",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.12.tgz",
+ "integrity": "sha512-TKc6FnOxFULKxLgTNHYjcFqdOYzXVPFFVm5JhI30F3RdhT7nYOtOsjgaOwfDRmA/3U66O9KaBQ3UHoXwayRhAg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/smithy-client": "^4.9.9",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-defaults-mode-node": {
+ "version": "4.2.15",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.15.tgz",
+ "integrity": "sha512-94NqfQVo+vGc5gsQ9SROZqOvBkGNMQu6pjXbnn8aQvBUhc31kx49gxlkBEqgmaZQHUUfdRUin5gK/HlHKmbAwg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/credential-provider-imds": "^4.2.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/smithy-client": "^4.9.9",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-endpoints": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz",
+ "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-hex-encoding": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz",
+ "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-middleware": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz",
+ "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-retry": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz",
+ "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/service-error-classification": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-stream": {
+ "version": "4.5.6",
+ "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz",
+ "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-buffer-from": "^4.2.0",
+ "@smithy/util-hex-encoding": "^4.2.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-uri-escape": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz",
+ "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-utf8": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz",
+ "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/uuid": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz",
+ "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@standard-schema/spec": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
+ "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
+ "license": "MIT"
+ },
+ "node_modules/@tarquinen/opencode-auth-provider": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/@tarquinen/opencode-auth-provider/-/opencode-auth-provider-0.1.7.tgz",
+ "integrity": "sha512-FH1QEyoirr2e8b48Z6HrjioIZIZUIM9zOpYmku1ad+c4Nv70F37fSWhcObyIdZo4Ly3OntpKPWjadyRhd/kQcg==",
+ "license": "MIT",
+ "dependencies": {
+ "@aws-sdk/credential-providers": "^3.936.0",
+ "ai": "^5.0.98",
+ "jsonc-parser": "^3.3.1",
+ "opencode-anthropic-auth": "0.0.2",
+ "opencode-copilot-auth": "0.0.5",
+ "opencode-gemini-auth": "^1.1.4",
+ "remeda": "^2.32.0",
+ "xdg-basedir": "^5.1.0",
+ "zod": "^4.1.12"
+ },
+ "peerDependencies": {
+ "@ai-sdk/amazon-bedrock": ">=1.0.0",
+ "@ai-sdk/anthropic": ">=1.0.0",
+ "@ai-sdk/azure": ">=1.0.0",
+ "@ai-sdk/google": ">=1.0.0",
+ "@ai-sdk/google-vertex": ">=1.0.0",
+ "@ai-sdk/openai": ">=1.0.0",
+ "@ai-sdk/openai-compatible": ">=0.1.0",
+ "@openrouter/ai-sdk-provider": ">=0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@ai-sdk/amazon-bedrock": {
+ "optional": true
+ },
+ "@ai-sdk/anthropic": {
+ "optional": true
+ },
+ "@ai-sdk/azure": {
+ "optional": true
+ },
+ "@ai-sdk/google": {
+ "optional": true
+ },
+ "@ai-sdk/google-vertex": {
+ "optional": true
+ },
+ "@ai-sdk/openai": {
+ "optional": true
+ },
+ "@ai-sdk/openai-compatible": {
+ "optional": true
+ },
+ "@openrouter/ai-sdk-provider": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "24.10.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
+ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@vercel/oidc": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.0.5.tgz",
+ "integrity": "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/ai": {
+ "version": "5.0.106",
+ "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.106.tgz",
+ "integrity": "sha512-M5obwavxSJJ3tGlAFqI6eltYNJB0D20X6gIBCFx/KVorb/X1fxVVfiZZpZb+Gslu4340droSOjT0aKQFCarNVg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/gateway": "2.0.18",
+ "@ai-sdk/provider": "2.0.0",
+ "@ai-sdk/provider-utils": "3.0.18",
+ "@opentelemetry/api": "1.9.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/arctic": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/arctic/-/arctic-2.3.4.tgz",
+ "integrity": "sha512-+p30BOWsctZp+CVYCt7oAean/hWGW42sH5LAcRQX56ttEkFJWbzXBhmSpibbzwSJkRrotmsA+oAoJoVsU0f5xA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@oslojs/crypto": "1.0.1",
+ "@oslojs/encoding": "1.1.0",
+ "@oslojs/jwt": "0.2.0"
+ }
+ },
+ "node_modules/aws4fetch": {
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/aws4fetch/-/aws4fetch-1.0.20.tgz",
+ "integrity": "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==",
+ "license": "MIT"
+ },
+ "node_modules/bowser": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz",
+ "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==",
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz",
+ "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.0",
+ "@esbuild/android-arm": "0.27.0",
+ "@esbuild/android-arm64": "0.27.0",
+ "@esbuild/android-x64": "0.27.0",
+ "@esbuild/darwin-arm64": "0.27.0",
+ "@esbuild/darwin-x64": "0.27.0",
+ "@esbuild/freebsd-arm64": "0.27.0",
+ "@esbuild/freebsd-x64": "0.27.0",
+ "@esbuild/linux-arm": "0.27.0",
+ "@esbuild/linux-arm64": "0.27.0",
+ "@esbuild/linux-ia32": "0.27.0",
+ "@esbuild/linux-loong64": "0.27.0",
+ "@esbuild/linux-mips64el": "0.27.0",
+ "@esbuild/linux-ppc64": "0.27.0",
+ "@esbuild/linux-riscv64": "0.27.0",
+ "@esbuild/linux-s390x": "0.27.0",
+ "@esbuild/linux-x64": "0.27.0",
+ "@esbuild/netbsd-arm64": "0.27.0",
+ "@esbuild/netbsd-x64": "0.27.0",
+ "@esbuild/openbsd-arm64": "0.27.0",
+ "@esbuild/openbsd-x64": "0.27.0",
+ "@esbuild/openharmony-arm64": "0.27.0",
+ "@esbuild/sunos-x64": "0.27.0",
+ "@esbuild/win32-arm64": "0.27.0",
+ "@esbuild/win32-ia32": "0.27.0",
+ "@esbuild/win32-x64": "0.27.0"
+ }
+ },
+ "node_modules/eventsource-parser": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz",
+ "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.2.5",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz",
+ "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "strnum": "^2.1.0"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/gpt-tokenizer": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/gpt-tokenizer/-/gpt-tokenizer-3.4.0.tgz",
+ "integrity": "sha512-wxFLnhIXTDjYebd9A9pGl3e31ZpSypbpIJSOswbgop5jLte/AsZVDvjlbEuVFlsqZixVKqbcoNmRlFDf6pz/UQ==",
+ "license": "MIT"
+ },
+ "node_modules/hono": {
+ "version": "4.10.7",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.10.7.tgz",
+ "integrity": "sha512-icXIITfw/07Q88nLSkB9aiUrd8rYzSweK681Kjo/TSggaGbOX4RRyxxm71v+3PC8C/j+4rlxGeoTRxQDkaJkUw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/jose": {
+ "version": "5.9.6",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz",
+ "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "license": "(AFL-2.1 OR BSD-3-Clause)"
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+ "license": "MIT"
+ },
+ "node_modules/opencode-anthropic-auth": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/opencode-anthropic-auth/-/opencode-anthropic-auth-0.0.2.tgz",
+ "integrity": "sha512-m8dcEKtq2ExGLV7n4BMr1H5UimDaABV6aG82IDMcp1xmXUaO1K20/hess0s8cwvv6MFmJk4//2wbWZkzoOtirA==",
+ "dependencies": {
+ "@openauthjs/openauth": "^0.4.3"
+ }
+ },
+ "node_modules/opencode-copilot-auth": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/opencode-copilot-auth/-/opencode-copilot-auth-0.0.5.tgz",
+ "integrity": "sha512-aOna2jy3BnaEpVJkeF32joUzI8DcpbBMWjd7zW6sgX4t58AnxaEB5sDadLsxRfcxJdhmABd5k6QSww5LcJ4e9Q=="
+ },
+ "node_modules/opencode-gemini-auth": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/opencode-gemini-auth/-/opencode-gemini-auth-1.1.6.tgz",
+ "integrity": "sha512-7WxOEwYMqXeCD2jf/Wj+8yBS3qwnRxHKt/sWhn2ZBDgz+dVwrC/SpTNvpva1fF8KSgVVG8tS9yvDQXM0JcVGoQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@openauthjs/openauth": "^0.4.3"
+ },
+ "peerDependencies": {
+ "typescript": "^5"
+ }
+ },
+ "node_modules/remeda": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.32.0.tgz",
+ "integrity": "sha512-BZx9DsT4FAgXDTOdgJIc5eY6ECIXMwtlSPQoPglF20ycSWigttDDe88AozEsPPT4OWk5NujroGSBC1phw5uU+w==",
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^4.41.0"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/strnum": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz",
+ "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tsx": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/xdg-basedir": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz",
+ "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
+ "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
}
- ],
- "license": "MIT"
- },
- "node_modules/tslib": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
- "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
- "license": "0BSD"
- },
- "node_modules/tsx": {
- "version": "4.21.0",
- "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
- "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "esbuild": "~0.27.0",
- "get-tsconfig": "^4.7.5"
- },
- "bin": {
- "tsx": "dist/cli.mjs"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- }
- },
- "node_modules/type-fest": {
- "version": "4.41.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
- "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/typescript": {
- "version": "5.9.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "license": "Apache-2.0",
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/undici-types": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
- "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/xdg-basedir": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz",
- "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/zod": {
- "version": "4.1.13",
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
- "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/colinhacks"
- }
}
- }
}
diff --git a/package.json b/package.json
index 3732f49..2527951 100644
--- a/package.json
+++ b/package.json
@@ -1,60 +1,60 @@
{
- "$schema": "https://json.schemastore.org/package.json",
- "name": "@tarquinen/opencode-dcp",
- "version": "1.0.4",
- "type": "module",
- "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context",
- "main": "./dist/index.js",
- "types": "./dist/index.d.ts",
- "scripts": {
- "clean": "rm -rf dist",
- "build": "npm run clean && tsc && cp -r lib/prompts dist/lib/prompts",
- "postbuild": "rm -rf dist/logs",
- "prepublishOnly": "npm run build",
- "dev": "opencode plugin dev",
- "typecheck": "tsc --noEmit",
- "test": "node --import tsx --test tests/*.test.ts"
- },
- "keywords": [
- "opencode",
- "opencode-plugin",
- "plugin",
- "context",
- "pruning",
- "optimization",
- "tokens"
- ],
- "repository": {
- "type": "git",
- "url": "git+https://github.com/Tarquinen/opencode-dynamic-context-pruning.git"
- },
- "bugs": {
- "url": "https://github.com/Tarquinen/opencode-dynamic-context-pruning/issues"
- },
- "homepage": "https://github.com/Tarquinen/opencode-dynamic-context-pruning#readme",
- "author": "tarquinen",
- "license": "MIT",
- "peerDependencies": {
- "@opencode-ai/plugin": ">=0.13.7"
- },
- "dependencies": {
- "@ai-sdk/openai-compatible": "^1.0.28",
- "@opencode-ai/sdk": "latest",
- "@tarquinen/opencode-auth-provider": "^0.1.7",
- "ai": "^5.0.106",
- "gpt-tokenizer": "^3.4.0",
- "jsonc-parser": "^3.3.1",
- "zod": "^4.1.13"
- },
- "devDependencies": {
- "@opencode-ai/plugin": "^1.0.143",
- "@types/node": "^24.10.1",
- "tsx": "^4.21.0",
- "typescript": "^5.9.3"
- },
- "files": [
- "dist/",
- "README.md",
- "LICENSE"
- ]
+ "$schema": "https://json.schemastore.org/package.json",
+ "name": "@tarquinen/opencode-dcp",
+ "version": "1.0.4",
+ "type": "module",
+ "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "scripts": {
+ "clean": "rm -rf dist",
+ "build": "npm run clean && tsc && cp -r lib/prompts dist/lib/prompts",
+ "postbuild": "rm -rf dist/logs",
+ "prepublishOnly": "npm run build",
+ "dev": "opencode plugin dev",
+ "typecheck": "tsc --noEmit",
+ "test": "node --import tsx --test tests/*.test.ts"
+ },
+ "keywords": [
+ "opencode",
+ "opencode-plugin",
+ "plugin",
+ "context",
+ "pruning",
+ "optimization",
+ "tokens"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/Tarquinen/opencode-dynamic-context-pruning.git"
+ },
+ "bugs": {
+ "url": "https://github.com/Tarquinen/opencode-dynamic-context-pruning/issues"
+ },
+ "homepage": "https://github.com/Tarquinen/opencode-dynamic-context-pruning#readme",
+ "author": "tarquinen",
+ "license": "MIT",
+ "peerDependencies": {
+ "@opencode-ai/plugin": ">=0.13.7"
+ },
+ "dependencies": {
+ "@ai-sdk/openai-compatible": "^1.0.28",
+ "@opencode-ai/sdk": "latest",
+ "@tarquinen/opencode-auth-provider": "^0.1.7",
+ "ai": "^5.0.106",
+ "gpt-tokenizer": "^3.4.0",
+ "jsonc-parser": "^3.3.1",
+ "zod": "^4.1.13"
+ },
+ "devDependencies": {
+ "@opencode-ai/plugin": "^1.0.143",
+ "@types/node": "^24.10.1",
+ "tsx": "^4.21.0",
+ "typescript": "^5.9.3"
+ },
+ "files": [
+ "dist/",
+ "README.md",
+ "LICENSE"
+ ]
}
diff --git a/tsconfig.json b/tsconfig.json
index e4fdca1..b30286c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,31 +1,24 @@
{
- "$schema": "https://json.schemastore.org/tsconfig",
- "compilerOptions": {
- "target": "ES2022",
- "module": "ESNext",
- "lib": ["ES2022"],
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "allowJs": true,
- "checkJs": false,
- "outDir": "./dist",
- "rootDir": ".",
- "strict": true,
- "esModuleInterop": true,
- "skipLibCheck": true,
- "forceConsistentCasingInFileNames": true,
- "declaration": true,
- "declarationMap": true,
- "sourceMap": true,
- "types": ["node"]
- },
- "include": [
- "index.ts",
- "lib/**/*"
- ],
- "exclude": [
- "node_modules",
- "dist",
- "logs"
- ]
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "lib": ["ES2022"],
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "allowJs": true,
+ "checkJs": false,
+ "outDir": "./dist",
+ "rootDir": ".",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "types": ["node"]
+ },
+ "include": ["index.ts", "lib/**/*"],
+ "exclude": ["node_modules", "dist", "logs"]
}