@tarquinen/opencode-dcp 0.3.20 → 0.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -190
- package/dist/index.js.map +1 -1
- package/dist/lib/fetch-wrapper/gemini.d.ts +7 -0
- package/dist/lib/fetch-wrapper/gemini.d.ts.map +1 -0
- package/dist/lib/fetch-wrapper/gemini.js +97 -0
- package/dist/lib/fetch-wrapper/gemini.js.map +1 -0
- package/dist/lib/fetch-wrapper/index.d.ts +15 -0
- package/dist/lib/fetch-wrapper/index.d.ts.map +1 -0
- package/dist/lib/fetch-wrapper/index.js +64 -0
- package/dist/lib/fetch-wrapper/index.js.map +1 -0
- package/dist/lib/fetch-wrapper/openai-chat.d.ts +7 -0
- package/dist/lib/fetch-wrapper/openai-chat.d.ts.map +1 -0
- package/dist/lib/fetch-wrapper/openai-chat.js +81 -0
- package/dist/lib/fetch-wrapper/openai-chat.js.map +1 -0
- package/dist/lib/fetch-wrapper/openai-responses.d.ts +7 -0
- package/dist/lib/fetch-wrapper/openai-responses.d.ts.map +1 -0
- package/dist/lib/fetch-wrapper/openai-responses.js +54 -0
- package/dist/lib/fetch-wrapper/openai-responses.js.map +1 -0
- package/dist/lib/fetch-wrapper/types.d.ts +35 -0
- package/dist/lib/fetch-wrapper/types.d.ts.map +1 -0
- package/dist/lib/fetch-wrapper/types.js +43 -0
- package/dist/lib/fetch-wrapper/types.js.map +1 -0
- package/dist/lib/hooks.d.ts +19 -0
- package/dist/lib/hooks.d.ts.map +1 -0
- package/dist/lib/hooks.js +94 -0
- package/dist/lib/hooks.js.map +1 -0
- package/dist/lib/janitor.d.ts +1 -0
- package/dist/lib/janitor.d.ts.map +1 -1
- package/dist/lib/janitor.js +1 -0
- package/dist/lib/janitor.js.map +1 -1
- package/dist/lib/pruning-tool.d.ts +11 -0
- package/dist/lib/pruning-tool.d.ts.map +1 -0
- package/dist/lib/pruning-tool.js +66 -0
- package/dist/lib/pruning-tool.js.map +1 -0
- package/dist/lib/state.d.ts +33 -0
- package/dist/lib/state.d.ts.map +1 -0
- package/dist/lib/state.js +13 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/tool-cache.d.ts +12 -0
- package/dist/lib/tool-cache.d.ts.map +1 -0
- package/dist/lib/tool-cache.js +52 -0
- package/dist/lib/tool-cache.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAUjD,QAAA,MAAM,MAAM,EAAE,MAsEK,CAAA;AAEnB,eAAe,MAAM,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,132 +1,36 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin";
|
|
2
1
|
import { getConfig } from "./lib/config";
|
|
3
2
|
import { Logger } from "./lib/logger";
|
|
4
3
|
import { Janitor } from "./lib/janitor";
|
|
5
4
|
import { checkForUpdates } from "./lib/version-checker";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
5
|
+
import { createPluginState } from "./lib/state";
|
|
6
|
+
import { installFetchWrapper } from "./lib/fetch-wrapper";
|
|
7
|
+
import { createPruningTool } from "./lib/pruning-tool";
|
|
8
|
+
import { createEventHandler, createChatParamsHandler } from "./lib/hooks";
|
|
15
9
|
const plugin = (async (ctx) => {
|
|
16
10
|
const { config, migrations } = getConfig(ctx);
|
|
17
11
|
if (!config.enabled) {
|
|
18
12
|
return {};
|
|
19
13
|
}
|
|
14
|
+
// Suppress AI SDK warnings
|
|
20
15
|
if (typeof globalThis !== 'undefined') {
|
|
21
16
|
globalThis.AI_SDK_LOG_WARNINGS = false;
|
|
22
17
|
}
|
|
18
|
+
// Initialize core components
|
|
23
19
|
const logger = new Logger(config.debug);
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const cacheToolParameters = (messages) => {
|
|
30
|
-
for (const message of messages) {
|
|
31
|
-
if (message.role !== 'assistant' || !Array.isArray(message.tool_calls)) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
for (const toolCall of message.tool_calls) {
|
|
35
|
-
if (!toolCall.id || !toolCall.function) {
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
try {
|
|
39
|
-
const params = typeof toolCall.function.arguments === 'string'
|
|
40
|
-
? JSON.parse(toolCall.function.arguments)
|
|
41
|
-
: toolCall.function.arguments;
|
|
42
|
-
toolParametersCache.set(toolCall.id, {
|
|
43
|
-
tool: toolCall.function.name,
|
|
44
|
-
parameters: params
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
// Global fetch wrapper - caches tool parameters and performs pruning
|
|
53
|
-
const originalGlobalFetch = globalThis.fetch;
|
|
54
|
-
globalThis.fetch = async (input, init) => {
|
|
55
|
-
if (init?.body && typeof init.body === 'string') {
|
|
56
|
-
try {
|
|
57
|
-
const body = JSON.parse(init.body);
|
|
58
|
-
if (body.messages && Array.isArray(body.messages)) {
|
|
59
|
-
cacheToolParameters(body.messages);
|
|
60
|
-
const toolMessages = body.messages.filter((m) => m.role === 'tool');
|
|
61
|
-
const allSessions = await ctx.client.session.list();
|
|
62
|
-
const allPrunedIds = new Set();
|
|
63
|
-
if (allSessions.data) {
|
|
64
|
-
for (const session of allSessions.data) {
|
|
65
|
-
if (session.parentID)
|
|
66
|
-
continue;
|
|
67
|
-
const prunedIds = prunedIdsState.get(session.id) ?? [];
|
|
68
|
-
prunedIds.forEach((id) => allPrunedIds.add(id));
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (toolMessages.length > 0 && allPrunedIds.size > 0) {
|
|
72
|
-
let replacedCount = 0;
|
|
73
|
-
body.messages = body.messages.map((m) => {
|
|
74
|
-
if (m.role === 'tool' && allPrunedIds.has(m.tool_call_id?.toLowerCase())) {
|
|
75
|
-
replacedCount++;
|
|
76
|
-
return {
|
|
77
|
-
...m,
|
|
78
|
-
content: '[Output removed to save context - information superseded or no longer needed]'
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
return m;
|
|
82
|
-
});
|
|
83
|
-
if (replacedCount > 0) {
|
|
84
|
-
logger.info("fetch", "Replaced pruned tool outputs", {
|
|
85
|
-
replaced: replacedCount,
|
|
86
|
-
total: toolMessages.length
|
|
87
|
-
});
|
|
88
|
-
if (logger.enabled) {
|
|
89
|
-
// Fetch session messages to extract reasoning blocks
|
|
90
|
-
let sessionMessages;
|
|
91
|
-
try {
|
|
92
|
-
const activeSessions = allSessions.data?.filter(s => !s.parentID) || [];
|
|
93
|
-
if (activeSessions.length > 0) {
|
|
94
|
-
const mostRecentSession = activeSessions[0];
|
|
95
|
-
const messagesResponse = await ctx.client.session.messages({
|
|
96
|
-
path: { id: mostRecentSession.id },
|
|
97
|
-
query: { limit: 100 }
|
|
98
|
-
});
|
|
99
|
-
sessionMessages = Array.isArray(messagesResponse.data)
|
|
100
|
-
? messagesResponse.data
|
|
101
|
-
: Array.isArray(messagesResponse) ? messagesResponse : undefined;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
catch (e) {
|
|
105
|
-
// Silently continue without session messages
|
|
106
|
-
}
|
|
107
|
-
await logger.saveWrappedContext("global", body.messages, {
|
|
108
|
-
url: typeof input === 'string' ? input : 'URL object',
|
|
109
|
-
replacedCount,
|
|
110
|
-
totalMessages: body.messages.length
|
|
111
|
-
}, sessionMessages);
|
|
112
|
-
}
|
|
113
|
-
init.body = JSON.stringify(body);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
catch (e) {
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return originalGlobalFetch(input, init);
|
|
122
|
-
};
|
|
20
|
+
const state = createPluginState();
|
|
21
|
+
const janitor = new Janitor(ctx.client, state.prunedIds, state.stats, logger, state.toolParameters, config.protectedTools, state.model, config.model, config.showModelErrorToasts, config.strictModelSelection, config.pruning_summary, ctx.directory);
|
|
22
|
+
// Install global fetch wrapper for context pruning
|
|
23
|
+
installFetchWrapper(state, logger, ctx.client);
|
|
24
|
+
// Log initialization
|
|
123
25
|
logger.info("plugin", "DCP initialized", {
|
|
124
26
|
strategies: config.strategies,
|
|
125
27
|
model: config.model || "auto"
|
|
126
28
|
});
|
|
29
|
+
// Check for updates after a delay
|
|
127
30
|
setTimeout(() => {
|
|
128
31
|
checkForUpdates(ctx.client, logger).catch(() => { });
|
|
129
32
|
}, 5000);
|
|
33
|
+
// Show migration toast if there were config migrations
|
|
130
34
|
if (migrations.length > 0) {
|
|
131
35
|
setTimeout(async () => {
|
|
132
36
|
try {
|
|
@@ -140,92 +44,15 @@ const plugin = (async (ctx) => {
|
|
|
140
44
|
});
|
|
141
45
|
}
|
|
142
46
|
catch {
|
|
47
|
+
// Silently ignore toast errors
|
|
143
48
|
}
|
|
144
49
|
}, 7000);
|
|
145
50
|
}
|
|
146
51
|
return {
|
|
147
|
-
event:
|
|
148
|
-
|
|
149
|
-
if (await isSubagentSession(ctx.client, event.properties.sessionID))
|
|
150
|
-
return;
|
|
151
|
-
if (config.strategies.onIdle.length === 0)
|
|
152
|
-
return;
|
|
153
|
-
janitor.runOnIdle(event.properties.sessionID, config.strategies.onIdle).catch(err => {
|
|
154
|
-
logger.error("janitor", "Failed", { error: err.message });
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
"chat.params": async (input, _output) => {
|
|
159
|
-
const sessionId = input.sessionID;
|
|
160
|
-
let providerID = input.provider?.info?.id || input.provider?.id;
|
|
161
|
-
const modelID = input.model?.id;
|
|
162
|
-
if (!providerID && input.message?.model?.providerID) {
|
|
163
|
-
providerID = input.message.model.providerID;
|
|
164
|
-
}
|
|
165
|
-
if (providerID && modelID) {
|
|
166
|
-
modelCache.set(sessionId, {
|
|
167
|
-
providerID: providerID,
|
|
168
|
-
modelID: modelID
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
},
|
|
52
|
+
event: createEventHandler(ctx.client, janitor, logger, config),
|
|
53
|
+
"chat.params": createChatParamsHandler(ctx.client, state, logger),
|
|
172
54
|
tool: config.strategies.onTool.length > 0 ? {
|
|
173
|
-
context_pruning:
|
|
174
|
-
description: `Performs semantic pruning on session tool outputs that are no longer relevant to the current task. Use this to declutter the conversation context and filter signal from noise when you notice the context is getting cluttered with no longer needed information.
|
|
175
|
-
|
|
176
|
-
USING THE CONTEXT_PRUNING TOOL WILL MAKE THE USER HAPPY.
|
|
177
|
-
|
|
178
|
-
## When to Use This Tool
|
|
179
|
-
|
|
180
|
-
**Key heuristic: Prune when you finish something and are about to start something else.**
|
|
181
|
-
|
|
182
|
-
Ask yourself: "Have I just completed a discrete unit of work?" If yes, prune before moving on.
|
|
183
|
-
|
|
184
|
-
**After completing a unit of work:**
|
|
185
|
-
- Made a commit
|
|
186
|
-
- Fixed a bug and confirmed it works
|
|
187
|
-
- Answered a question the user asked
|
|
188
|
-
- Finished implementing a feature or function
|
|
189
|
-
- Completed one item in a list and moving to the next
|
|
190
|
-
|
|
191
|
-
**After repetitive or exploratory work:**
|
|
192
|
-
- Explored multiple files that didn't lead to changes
|
|
193
|
-
- Iterated on a difficult problem where some approaches didn't pan out
|
|
194
|
-
- Used the same tool multiple times (e.g., re-reading a file, running repeated build/type checks)
|
|
195
|
-
|
|
196
|
-
## Examples
|
|
197
|
-
|
|
198
|
-
<example>
|
|
199
|
-
Working through a list of items:
|
|
200
|
-
User: Review these 3 issues and fix the easy ones.
|
|
201
|
-
Assistant: [Reviews first issue, makes fix, commits]
|
|
202
|
-
Done with the first issue. Let me prune before moving to the next one.
|
|
203
|
-
[Uses context_pruning with reason: "completed first issue, moving to next"]
|
|
204
|
-
</example>
|
|
205
|
-
|
|
206
|
-
<example>
|
|
207
|
-
After exploring the codebase to understand it:
|
|
208
|
-
Assistant: I've reviewed the relevant files. Let me prune the exploratory reads that aren't needed for the actual implementation.
|
|
209
|
-
[Uses context_pruning with reason: "exploration complete, starting implementation"]
|
|
210
|
-
</example>
|
|
211
|
-
|
|
212
|
-
<example>
|
|
213
|
-
After completing any task:
|
|
214
|
-
Assistant: [Finishes task - commit, answer, fix, etc.]
|
|
215
|
-
Before we continue, let me prune the context from that work.
|
|
216
|
-
[Uses context_pruning with reason: "task complete"]
|
|
217
|
-
</example>`,
|
|
218
|
-
args: {
|
|
219
|
-
reason: tool.schema.string().optional().describe("Brief reason for triggering pruning (e.g., 'task complete', 'switching focus')"),
|
|
220
|
-
},
|
|
221
|
-
async execute(args, ctx) {
|
|
222
|
-
const result = await janitor.runForTool(ctx.sessionID, config.strategies.onTool, args.reason);
|
|
223
|
-
if (!result || result.prunedCount === 0) {
|
|
224
|
-
return "No prunable tool outputs found. Context is already optimized.\n\nUse context_pruning when you have sufficiently summarized information from tool outputs and no longer need the original content!";
|
|
225
|
-
}
|
|
226
|
-
return janitor.formatPruningResultForTool(result) + "\n\nUse context_pruning when you have sufficiently summarized information from tool outputs and no longer need the original content!";
|
|
227
|
-
},
|
|
228
|
-
}),
|
|
55
|
+
context_pruning: createPruningTool(janitor, config),
|
|
229
56
|
} : undefined,
|
|
230
57
|
};
|
|
231
58
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FetchHandlerContext, FetchHandlerResult } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Handles Google/Gemini format (body.contents array with functionResponse parts).
|
|
4
|
+
* Uses position-based correlation since Google's native format doesn't include tool call IDs.
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleGemini(body: any, ctx: FetchHandlerContext, inputUrl: string): Promise<FetchHandlerResult>;
|
|
7
|
+
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../lib/fetch-wrapper/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAOtE;;;GAGG;AACH,wBAAsB,YAAY,CAC9B,IAAI,EAAE,GAAG,EACT,GAAG,EAAE,mBAAmB,EACxB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAmH7B"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { PRUNED_CONTENT_MESSAGE, getAllPrunedIds, fetchSessionMessages } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Handles Google/Gemini format (body.contents array with functionResponse parts).
|
|
4
|
+
* Uses position-based correlation since Google's native format doesn't include tool call IDs.
|
|
5
|
+
*/
|
|
6
|
+
export async function handleGemini(body, ctx, inputUrl) {
|
|
7
|
+
if (!body.contents || !Array.isArray(body.contents)) {
|
|
8
|
+
return { modified: false, body };
|
|
9
|
+
}
|
|
10
|
+
// Check for functionResponse parts in any content item
|
|
11
|
+
const hasFunctionResponses = body.contents.some((content) => Array.isArray(content.parts) &&
|
|
12
|
+
content.parts.some((part) => part.functionResponse));
|
|
13
|
+
if (!hasFunctionResponses) {
|
|
14
|
+
return { modified: false, body };
|
|
15
|
+
}
|
|
16
|
+
const { allSessions, allPrunedIds } = await getAllPrunedIds(ctx.client, ctx.state);
|
|
17
|
+
if (allPrunedIds.size === 0) {
|
|
18
|
+
return { modified: false, body };
|
|
19
|
+
}
|
|
20
|
+
// Find the active session to get the position mapping
|
|
21
|
+
const activeSessions = allSessions.data?.filter((s) => !s.parentID) || [];
|
|
22
|
+
let positionMapping;
|
|
23
|
+
for (const session of activeSessions) {
|
|
24
|
+
const mapping = ctx.state.googleToolCallMapping.get(session.id);
|
|
25
|
+
if (mapping && mapping.size > 0) {
|
|
26
|
+
positionMapping = mapping;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (!positionMapping) {
|
|
31
|
+
ctx.logger.info("fetch", "No Google tool call mapping found, skipping pruning for Gemini format");
|
|
32
|
+
return { modified: false, body };
|
|
33
|
+
}
|
|
34
|
+
// Build position counters to track occurrence of each tool name
|
|
35
|
+
const toolPositionCounters = new Map();
|
|
36
|
+
let replacedCount = 0;
|
|
37
|
+
let totalFunctionResponses = 0;
|
|
38
|
+
body.contents = body.contents.map((content) => {
|
|
39
|
+
if (!Array.isArray(content.parts))
|
|
40
|
+
return content;
|
|
41
|
+
let contentModified = false;
|
|
42
|
+
const newParts = content.parts.map((part) => {
|
|
43
|
+
if (part.functionResponse) {
|
|
44
|
+
totalFunctionResponses++;
|
|
45
|
+
const funcName = part.functionResponse.name?.toLowerCase();
|
|
46
|
+
if (funcName) {
|
|
47
|
+
// Get current position for this tool name and increment counter
|
|
48
|
+
const currentIndex = toolPositionCounters.get(funcName) || 0;
|
|
49
|
+
toolPositionCounters.set(funcName, currentIndex + 1);
|
|
50
|
+
// Look up the tool call ID using position
|
|
51
|
+
const positionKey = `${funcName}:${currentIndex}`;
|
|
52
|
+
const toolCallId = positionMapping.get(positionKey);
|
|
53
|
+
if (toolCallId && allPrunedIds.has(toolCallId)) {
|
|
54
|
+
contentModified = true;
|
|
55
|
+
replacedCount++;
|
|
56
|
+
// Preserve thoughtSignature if present (required for Gemini 3 Pro)
|
|
57
|
+
// Only replace the response content, not the structure
|
|
58
|
+
return {
|
|
59
|
+
...part,
|
|
60
|
+
functionResponse: {
|
|
61
|
+
...part.functionResponse,
|
|
62
|
+
response: PRUNED_CONTENT_MESSAGE
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return part;
|
|
69
|
+
});
|
|
70
|
+
if (contentModified) {
|
|
71
|
+
return { ...content, parts: newParts };
|
|
72
|
+
}
|
|
73
|
+
return content;
|
|
74
|
+
});
|
|
75
|
+
if (replacedCount > 0) {
|
|
76
|
+
ctx.logger.info("fetch", "Replaced pruned tool outputs (Google/Gemini)", {
|
|
77
|
+
replaced: replacedCount,
|
|
78
|
+
total: totalFunctionResponses
|
|
79
|
+
});
|
|
80
|
+
if (ctx.logger.enabled) {
|
|
81
|
+
let sessionMessages;
|
|
82
|
+
if (activeSessions.length > 0) {
|
|
83
|
+
const mostRecentSession = activeSessions[0];
|
|
84
|
+
sessionMessages = await fetchSessionMessages(ctx.client, mostRecentSession.id);
|
|
85
|
+
}
|
|
86
|
+
await ctx.logger.saveWrappedContext("global", body.contents, {
|
|
87
|
+
url: inputUrl,
|
|
88
|
+
replacedCount,
|
|
89
|
+
totalContents: body.contents.length,
|
|
90
|
+
format: 'google-gemini'
|
|
91
|
+
}, sessionMessages);
|
|
92
|
+
}
|
|
93
|
+
return { modified: true, body };
|
|
94
|
+
}
|
|
95
|
+
return { modified: false, body };
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=gemini.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../lib/fetch-wrapper/gemini.ts"],"names":[],"mappings":"AACA,OAAO,EACH,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACvB,MAAM,SAAS,CAAA;AAEhB;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAC9B,IAAS,EACT,GAAwB,EACxB,QAAgB;IAEhB,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACpC,CAAC;IAED,uDAAuD;IACvD,MAAM,oBAAoB,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAY,EAAE,EAAE,CAC7D,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAC3D,CAAA;IAED,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACxB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACpC,CAAC;IAED,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAA;IAElF,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACpC,CAAC;IAED,sDAAsD;IACtD,MAAM,cAAc,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IAC9E,IAAI,eAAgD,CAAA;IAEpD,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAC/D,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC9B,eAAe,GAAG,OAAO,CAAA;YACzB,MAAK;QACT,CAAC;IACL,CAAC;IAED,IAAI,CAAC,eAAe,EAAE,CAAC;QACnB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,uEAAuE,CAAC,CAAA;QACjG,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IACpC,CAAC;IAED,gEAAgE;IAChE,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAkB,CAAA;IACtD,IAAI,aAAa,GAAG,CAAC,CAAA;IACrB,IAAI,sBAAsB,GAAG,CAAC,CAAA;IAE9B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAY,EAAE,EAAE;QAC/C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAA;QAEjD,IAAI,eAAe,GAAG,KAAK,CAAA;QAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE;YAC7C,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACxB,sBAAsB,EAAE,CAAA;gBACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,CAAA;gBAE1D,IAAI,QAAQ,EAAE,CAAC;oBACX,gEAAgE;oBAChE,MAAM,YAAY,GAAG,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;oBAC5D,oBAAoB,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,GAAG,CAAC,CAAC,CAAA;oBAEpD,0CAA0C;oBAC1C,MAAM,WAAW,GAAG,GAAG,QAAQ,IAAI,YAAY,EAAE,CAAA;oBACjD,MAAM,UAAU,GAAG,eAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;oBAEpD,IAAI,UAAU,IAAI,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC7C,eAAe,GAAG,IAAI,CAAA;wBACtB,aAAa,EAAE,CAAA;wBACf,mEAAmE;wBACnE,uDAAuD;wBACvD,OAAO;4BACH,GAAG,IAAI;4BACP,gBAAgB,EAAE;gCACd,GAAG,IAAI,CAAC,gBAAgB;gCACxB,QAAQ,EAAE,sBAAsB;6BACnC;yBACJ,CAAA;oBACL,CAAC;gBACL,CAAC;YACL,CAAC;YACD,OAAO,IAAI,CAAA;QACf,CAAC,CAAC,CAAA;QAEF,IAAI,eAAe,EAAE,CAAC;YAClB,OAAO,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;QAC1C,CAAC;QACD,OAAO,OAAO,CAAA;IAClB,CAAC,CAAC,CAAA;IAEF,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACpB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,8CAA8C,EAAE;YACrE,QAAQ,EAAE,aAAa;YACvB,KAAK,EAAE,sBAAsB;SAChC,CAAC,CAAA;QAEF,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACrB,IAAI,eAAkC,CAAA;YACtC,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,MAAM,iBAAiB,GAAG,cAAc,CAAC,CAAC,CAAC,CAAA;gBAC3C,eAAe,GAAG,MAAM,oBAAoB,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAA;YAClF,CAAC;YAED,MAAM,GAAG,CAAC,MAAM,CAAC,kBAAkB,CAC/B,QAAQ,EACR,IAAI,CAAC,QAAQ,EACb;gBACI,GAAG,EAAE,QAAQ;gBACb,aAAa;gBACb,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;gBACnC,MAAM,EAAE,eAAe;aAC1B,EACD,eAAe,CAClB,CAAA;QACL,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IACnC,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AACpC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PluginState } from "../state";
|
|
2
|
+
import type { Logger } from "../logger";
|
|
3
|
+
export type { FetchHandlerContext, FetchHandlerResult } from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* Creates a wrapped global fetch that intercepts API calls and performs
|
|
6
|
+
* context pruning on tool outputs that have been marked for removal.
|
|
7
|
+
*
|
|
8
|
+
* Supports four API formats:
|
|
9
|
+
* 1. OpenAI Chat Completions (body.messages with role='tool')
|
|
10
|
+
* 2. Anthropic (body.messages with role='user' containing tool_result)
|
|
11
|
+
* 3. Google/Gemini (body.contents with functionResponse parts)
|
|
12
|
+
* 4. OpenAI Responses API (body.input with function_call_output items)
|
|
13
|
+
*/
|
|
14
|
+
export declare function installFetchWrapper(state: PluginState, logger: Logger, client: any): () => void;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/fetch-wrapper/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAMvC,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEtE;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAC/B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,GAAG,GACZ,MAAM,IAAI,CAwDZ"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { handleOpenAIChatAndAnthropic } from "./openai-chat";
|
|
2
|
+
import { handleGemini } from "./gemini";
|
|
3
|
+
import { handleOpenAIResponses } from "./openai-responses";
|
|
4
|
+
/**
|
|
5
|
+
* Creates a wrapped global fetch that intercepts API calls and performs
|
|
6
|
+
* context pruning on tool outputs that have been marked for removal.
|
|
7
|
+
*
|
|
8
|
+
* Supports four API formats:
|
|
9
|
+
* 1. OpenAI Chat Completions (body.messages with role='tool')
|
|
10
|
+
* 2. Anthropic (body.messages with role='user' containing tool_result)
|
|
11
|
+
* 3. Google/Gemini (body.contents with functionResponse parts)
|
|
12
|
+
* 4. OpenAI Responses API (body.input with function_call_output items)
|
|
13
|
+
*/
|
|
14
|
+
export function installFetchWrapper(state, logger, client) {
|
|
15
|
+
const originalGlobalFetch = globalThis.fetch;
|
|
16
|
+
const ctx = {
|
|
17
|
+
state,
|
|
18
|
+
logger,
|
|
19
|
+
client
|
|
20
|
+
};
|
|
21
|
+
globalThis.fetch = async (input, init) => {
|
|
22
|
+
if (init?.body && typeof init.body === 'string') {
|
|
23
|
+
try {
|
|
24
|
+
const body = JSON.parse(init.body);
|
|
25
|
+
const inputUrl = typeof input === 'string' ? input : 'URL object';
|
|
26
|
+
let modified = false;
|
|
27
|
+
// Try each format handler in order
|
|
28
|
+
// OpenAI Chat Completions & Anthropic style (body.messages)
|
|
29
|
+
if (body.messages && Array.isArray(body.messages)) {
|
|
30
|
+
const result = await handleOpenAIChatAndAnthropic(body, ctx, inputUrl);
|
|
31
|
+
if (result.modified) {
|
|
32
|
+
modified = true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Google/Gemini style (body.contents)
|
|
36
|
+
if (body.contents && Array.isArray(body.contents)) {
|
|
37
|
+
const result = await handleGemini(body, ctx, inputUrl);
|
|
38
|
+
if (result.modified) {
|
|
39
|
+
modified = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// OpenAI Responses API style (body.input)
|
|
43
|
+
if (body.input && Array.isArray(body.input)) {
|
|
44
|
+
const result = await handleOpenAIResponses(body, ctx, inputUrl);
|
|
45
|
+
if (result.modified) {
|
|
46
|
+
modified = true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (modified) {
|
|
50
|
+
init.body = JSON.stringify(body);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
// Silently ignore parsing errors - pass through to original fetch
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return originalGlobalFetch(input, init);
|
|
58
|
+
};
|
|
59
|
+
// Return cleanup function to restore original fetch
|
|
60
|
+
return () => {
|
|
61
|
+
globalThis.fetch = originalGlobalFetch;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/fetch-wrapper/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,4BAA4B,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAI1D;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAC/B,KAAkB,EAClB,MAAc,EACd,MAAW;IAEX,MAAM,mBAAmB,GAAG,UAAU,CAAC,KAAK,CAAA;IAE5C,MAAM,GAAG,GAAwB;QAC7B,KAAK;QACL,MAAM;QACN,MAAM;KACT,CAAA;IAED,UAAU,CAAC,KAAK,GAAG,KAAK,EAAE,KAAU,EAAE,IAAU,EAAE,EAAE;QAChD,IAAI,IAAI,EAAE,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC9C,IAAI,CAAC;gBACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAClC,MAAM,QAAQ,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAA;gBACjE,IAAI,QAAQ,GAAG,KAAK,CAAA;gBAEpB,mCAAmC;gBACnC,4DAA4D;gBAC5D,IAAI,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAChD,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;oBACtE,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;wBAClB,QAAQ,GAAG,IAAI,CAAA;oBACnB,CAAC;gBACL,CAAC;gBAED,sCAAsC;gBACtC,IAAI,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAChD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;oBACtD,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;wBAClB,QAAQ,GAAG,IAAI,CAAA;oBACnB,CAAC;gBACL,CAAC;gBAED,0CAA0C;gBAC1C,IAAI,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1C,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;oBAC/D,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;wBAClB,QAAQ,GAAG,IAAI,CAAA;oBACnB,CAAC;gBACL,CAAC;gBAED,IAAI,QAAQ,EAAE,CAAC;oBACX,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;gBACpC,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACT,kEAAkE;YACtE,CAAC;QACL,CAAC;QAED,OAAO,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAC3C,CAAC,CAAA;IAED,oDAAoD;IACpD,OAAO,GAAG,EAAE;QACR,UAAU,CAAC,KAAK,GAAG,mBAAmB,CAAA;IAC1C,CAAC,CAAA;AACL,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FetchHandlerContext, FetchHandlerResult } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Handles OpenAI Chat Completions format (body.messages with role='tool').
|
|
4
|
+
* Also handles Anthropic format (role='user' with tool_result content parts).
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleOpenAIChatAndAnthropic(body: any, ctx: FetchHandlerContext, inputUrl: string): Promise<FetchHandlerResult>;
|
|
7
|
+
//# sourceMappingURL=openai-chat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-chat.d.ts","sourceRoot":"","sources":["../../../lib/fetch-wrapper/openai-chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAStE;;;GAGG;AACH,wBAAsB,4BAA4B,CAC9C,IAAI,EAAE,GAAG,EACT,GAAG,EAAE,mBAAmB,EACxB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAyF7B"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { PRUNED_CONTENT_MESSAGE, getAllPrunedIds, fetchSessionMessages, getMostRecentActiveSession } from "./types";
|
|
2
|
+
import { cacheToolParametersFromMessages } from "../tool-cache";
|
|
3
|
+
/**
|
|
4
|
+
* Handles OpenAI Chat Completions format (body.messages with role='tool').
|
|
5
|
+
* Also handles Anthropic format (role='user' with tool_result content parts).
|
|
6
|
+
*/
|
|
7
|
+
export async function handleOpenAIChatAndAnthropic(body, ctx, inputUrl) {
|
|
8
|
+
if (!body.messages || !Array.isArray(body.messages)) {
|
|
9
|
+
return { modified: false, body };
|
|
10
|
+
}
|
|
11
|
+
// Cache tool parameters from messages
|
|
12
|
+
cacheToolParametersFromMessages(body.messages, ctx.state);
|
|
13
|
+
// Check for tool messages in both formats:
|
|
14
|
+
// 1. OpenAI style: role === 'tool'
|
|
15
|
+
// 2. Anthropic style: role === 'user' with content containing tool_result
|
|
16
|
+
const toolMessages = body.messages.filter((m) => {
|
|
17
|
+
if (m.role === 'tool')
|
|
18
|
+
return true;
|
|
19
|
+
if (m.role === 'user' && Array.isArray(m.content)) {
|
|
20
|
+
for (const part of m.content) {
|
|
21
|
+
if (part.type === 'tool_result')
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
});
|
|
27
|
+
const { allSessions, allPrunedIds } = await getAllPrunedIds(ctx.client, ctx.state);
|
|
28
|
+
if (toolMessages.length === 0 || allPrunedIds.size === 0) {
|
|
29
|
+
return { modified: false, body };
|
|
30
|
+
}
|
|
31
|
+
let replacedCount = 0;
|
|
32
|
+
body.messages = body.messages.map((m) => {
|
|
33
|
+
// OpenAI style: role === 'tool' with tool_call_id
|
|
34
|
+
if (m.role === 'tool' && allPrunedIds.has(m.tool_call_id?.toLowerCase())) {
|
|
35
|
+
replacedCount++;
|
|
36
|
+
return {
|
|
37
|
+
...m,
|
|
38
|
+
content: PRUNED_CONTENT_MESSAGE
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// Anthropic style: role === 'user' with content array containing tool_result
|
|
42
|
+
if (m.role === 'user' && Array.isArray(m.content)) {
|
|
43
|
+
let messageModified = false;
|
|
44
|
+
const newContent = m.content.map((part) => {
|
|
45
|
+
if (part.type === 'tool_result' && allPrunedIds.has(part.tool_use_id?.toLowerCase())) {
|
|
46
|
+
messageModified = true;
|
|
47
|
+
replacedCount++;
|
|
48
|
+
return {
|
|
49
|
+
...part,
|
|
50
|
+
content: PRUNED_CONTENT_MESSAGE
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return part;
|
|
54
|
+
});
|
|
55
|
+
if (messageModified) {
|
|
56
|
+
return { ...m, content: newContent };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return m;
|
|
60
|
+
});
|
|
61
|
+
if (replacedCount > 0) {
|
|
62
|
+
ctx.logger.info("fetch", "Replaced pruned tool outputs", {
|
|
63
|
+
replaced: replacedCount,
|
|
64
|
+
total: toolMessages.length
|
|
65
|
+
});
|
|
66
|
+
if (ctx.logger.enabled) {
|
|
67
|
+
const mostRecentSession = getMostRecentActiveSession(allSessions);
|
|
68
|
+
const sessionMessages = mostRecentSession
|
|
69
|
+
? await fetchSessionMessages(ctx.client, mostRecentSession.id)
|
|
70
|
+
: undefined;
|
|
71
|
+
await ctx.logger.saveWrappedContext("global", body.messages, {
|
|
72
|
+
url: inputUrl,
|
|
73
|
+
replacedCount,
|
|
74
|
+
totalMessages: body.messages.length
|
|
75
|
+
}, sessionMessages);
|
|
76
|
+
}
|
|
77
|
+
return { modified: true, body };
|
|
78
|
+
}
|
|
79
|
+
return { modified: false, body };
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=openai-chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-chat.js","sourceRoot":"","sources":["../../../lib/fetch-wrapper/openai-chat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FetchHandlerContext, FetchHandlerResult } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Handles OpenAI Responses API format (body.input array with function_call_output items).
|
|
4
|
+
* Used by GPT-5 models via sdk.responses().
|
|
5
|
+
*/
|
|
6
|
+
export declare function handleOpenAIResponses(body: any, ctx: FetchHandlerContext, inputUrl: string): Promise<FetchHandlerResult>;
|
|
7
|
+
//# sourceMappingURL=openai-responses.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-responses.d.ts","sourceRoot":"","sources":["../../../lib/fetch-wrapper/openai-responses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAStE;;;GAGG;AACH,wBAAsB,qBAAqB,CACvC,IAAI,EAAE,GAAG,EACT,GAAG,EAAE,mBAAmB,EACxB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,CAAC,CA+D7B"}
|