// src/tools/memory/pin-remove.ts // Copyright (C) 2025 DTP Technologies, LLC // All Rights Reserved import type { ToolDefinition } from "../../lib/ai-client.js"; import { DtpTool, type ToolArguments, type ToolContext, } from "../../lib/tool.js"; import { ChatSession, ChatSessionMode } from "../../models/chat-session.js"; export class PinRemoveTool extends DtpTool { get name(): string { return "PinRemoveTool"; } get slug(): string { return "pin-remove"; } get metadata() { return { name: this.definition.function.name || "pin_remove", category: "memory", tags: ["pin", "pinboard", "note", "memory", "context", "remove"], modes: [ ChatSessionMode.Plan, ChatSessionMode.Build, ChatSessionMode.Test, ChatSessionMode.Ship, ChatSessionMode.Develop, ], }; } public definition: ToolDefinition = { type: "function", function: { name: "pin_remove", description: "Remove a pin from the session pinboard by its ID. Use this to free up space on the pinboard or remove information that is no longer relevant.", parameters: { type: "object", properties: { pin_id: { type: "string", description: "The _id of the pin to remove. This is returned when the pin was added via pin_add, and is also visible in the PINBOARD section of the system prompt.", }, }, required: ["pin_id"], }, }, }; public async execute( context: ToolContext, args: ToolArguments, ): Promise { const pinId = args.pin_id as string | undefined; if (!pinId || pinId.trim().length === 0) { return this.error("INVALID_PARAMETER", "pin_id must not be empty.", { parameter: "pin_id", recoveryHint: "Provide the _id of the pin you want to remove.", }); } const sessionId = context.session._id.toHexString(); try { const session = await ChatSession.findById(sessionId); if (!session) { return this.error("NOT_FOUND", `Session not found: ${sessionId}`); } const pinIndex = session.pins.findIndex( (pin) => pin._id?.toString() === pinId, ); if (pinIndex === -1) { return this.error( "NOT_FOUND", `Pin not found: ${pinId}. Check the pin ID and try again.`, { recoveryHint: "Pin IDs are shown in the PINBOARD section of the system prompt and returned when pins are added.", }, ); } const removedContent = session.pins[pinIndex]!.content; session.pins.splice(pinIndex, 1); await session.save(); const totalChars = session.pins.reduce( (sum, pin) => sum + pin.content.length, 0, ); return this.success( { pinId, content: removedContent, totalPins: session.pins.length, totalChars, maxChars: 8192, }, `Pin removed from pinboard (${session.pins.length} pins remaining, ${totalChars}/8192 characters used).`, ); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.log.error("Failed to remove pin", { sessionId, error: errorMessage, }); return this.error( "OPERATION_FAILED", `Failed to remove pin: ${errorMessage}`, ); } } } export default new PinRemoveTool();