( deps: CtxReduceToolDeps, )
| 63 | } |
| 64 | |
| 65 | export function createCtxReduceTool( |
| 66 | deps: CtxReduceToolDeps, |
| 67 | ): ToolDefinition<typeof ParamsSchema> { |
| 68 | return { |
| 69 | name: "ctx_reduce", |
| 70 | label: "Magic Context: Reduce", |
| 71 | description: CTX_REDUCE_DESCRIPTION, |
| 72 | parameters: ParamsSchema, |
| 73 | async execute( |
| 74 | _toolCallId, |
| 75 | params: CtxReduceParams, |
| 76 | _signal, |
| 77 | _onUpdate, |
| 78 | ctx, |
| 79 | ) { |
| 80 | const sessionId = ctx.sessionManager.getSessionId(); |
| 81 | |
| 82 | if (!params.drop) { |
| 83 | return err("Error: 'drop' must be provided."); |
| 84 | } |
| 85 | |
| 86 | let dropIds: number[] = []; |
| 87 | try { |
| 88 | dropIds = parseRangeString(params.drop); |
| 89 | } catch (e) { |
| 90 | return err(`Error: Invalid range syntax. ${(e as Error).message}`); |
| 91 | } |
| 92 | |
| 93 | const allIds = [...new Set(dropIds)]; |
| 94 | |
| 95 | const allTags = getTagsBySession(deps.db, sessionId); |
| 96 | const foundSet = new Set(allTags.map((tag) => tag.tagNumber)); |
| 97 | const unknownIds = allIds.filter((id) => !foundSet.has(id)); |
| 98 | if (unknownIds.length > 0) { |
| 99 | return err( |
| 100 | `Error: Unknown tag(s) ${formatIds(unknownIds)}. Check available tags in conversation.`, |
| 101 | ); |
| 102 | } |
| 103 | |
| 104 | const activeTags = allTags.filter((tag) => tag.status === "active"); |
| 105 | const protectedTagIds = activeTags |
| 106 | .map((tag) => tag.tagNumber) |
| 107 | .sort((left, right) => right - left) |
| 108 | .slice(0, deps.protectedTags); |
| 109 | const protectedSet = new Set(protectedTagIds); |
| 110 | |
| 111 | const tagStatusMap = new Map( |
| 112 | allTags.map((tag) => [tag.tagNumber, tag.status]), |
| 113 | ); |
| 114 | |
| 115 | const pendingOps = getPendingOps(deps.db, sessionId); |
| 116 | const pendingMap = new Map( |
| 117 | pendingOps.map((op) => [op.tagId, op.operation]), |
| 118 | ); |
| 119 | |
| 120 | // Reject drops on compaction-survivor tags. Mirrors OpenCode's |
| 121 | // `tagStatusMap.get(id) === "compacted"` guard — those tags are |
| 122 | // the synthesized survivors of an OpenCode compaction marker and |
no outgoing calls