( deps: CtxExpandToolDeps, )
| 79 | } |
| 80 | |
| 81 | export function createCtxExpandTool( |
| 82 | deps: CtxExpandToolDeps, |
| 83 | ): ToolDefinition<typeof ParamsSchema> { |
| 84 | return { |
| 85 | name: "ctx_expand", |
| 86 | label: "Magic Context: Expand", |
| 87 | description: CTX_EXPAND_DESCRIPTION, |
| 88 | parameters: ParamsSchema, |
| 89 | async execute( |
| 90 | _toolCallId, |
| 91 | params: CtxExpandParams, |
| 92 | _signal, |
| 93 | _onUpdate, |
| 94 | ctx, |
| 95 | ) { |
| 96 | const sessionId = ctx.sessionManager.getSessionId(); |
| 97 | if (!sessionId) { |
| 98 | return err("Error: no active Pi session."); |
| 99 | } |
| 100 | |
| 101 | // All raw reads go through the shared provider-aware helpers, so |
| 102 | // register Pi's source for the duration of this single call. |
| 103 | // setRawMessageProvider returns an unregister fn so we don't leak the |
| 104 | // binding into concurrent transform passes. |
| 105 | const unregister = setRawMessageProvider(sessionId, { |
| 106 | readMessages: () => readPiSessionMessages(ctx), |
| 107 | }); |
| 108 | |
| 109 | try { |
| 110 | // By-ordinal mode: full recovery of a single message from JSONL. |
| 111 | if (typeof params.message === "number" && params.message >= 1) { |
| 112 | return ok(renderMessageByOrdinal(sessionId, params.message)); |
| 113 | } |
| 114 | |
| 115 | if ( |
| 116 | typeof params.start !== "number" || |
| 117 | typeof params.end !== "number" || |
| 118 | params.start < 1 || |
| 119 | params.end < params.start |
| 120 | ) { |
| 121 | return err( |
| 122 | "Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).", |
| 123 | ); |
| 124 | } |
| 125 | |
| 126 | // Clamp to the last compartment boundary (parity with OpenCode + |
| 127 | // ctx_search): messages after it are the live tail already visible |
| 128 | // to the agent, so re-expanding them wastes output tokens. -1 = no |
| 129 | // compartments yet → nothing compacted, so don't clamp. |
| 130 | const lastCompartmentEnd = getLastCompartmentEndMessage( |
| 131 | deps.db, |
| 132 | sessionId, |
| 133 | ); |
| 134 | if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) { |
| 135 | return ok( |
| 136 | `Range ${params.start}-${params.end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`, |
| 137 | ); |
| 138 | } |
no outgoing calls
no test coverage detected