noumen 0.1.0 → 0.2.0
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 +767 -51
- package/dist/a2a/index.d.ts +148 -0
- package/dist/a2a/index.js +579 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/acp/index.d.ts +129 -0
- package/dist/acp/index.js +498 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/agent-BrkbZyOT.d.ts +1028 -0
- package/dist/cache-DVqaCX8v.d.ts +38 -0
- package/dist/chunk-2ZTGQLYK.js +356 -0
- package/dist/chunk-2ZTGQLYK.js.map +1 -0
- package/dist/chunk-42PHHZUA.js +132 -0
- package/dist/chunk-42PHHZUA.js.map +1 -0
- package/dist/chunk-4SQA2UCV.js +26 -0
- package/dist/chunk-4SQA2UCV.js.map +1 -0
- package/dist/chunk-5GEX6ZSB.js +179 -0
- package/dist/chunk-5GEX6ZSB.js.map +1 -0
- package/dist/chunk-7ZMN7XJE.js +94 -0
- package/dist/chunk-7ZMN7XJE.js.map +1 -0
- package/dist/chunk-AMYIJSAZ.js +57 -0
- package/dist/chunk-AMYIJSAZ.js.map +1 -0
- package/dist/chunk-BGG2E6JD.js +10 -0
- package/dist/chunk-BGG2E6JD.js.map +1 -0
- package/dist/chunk-BZSFUEWM.js +43 -0
- package/dist/chunk-BZSFUEWM.js.map +1 -0
- package/dist/chunk-CPFHEPW4.js +139 -0
- package/dist/chunk-CPFHEPW4.js.map +1 -0
- package/dist/chunk-D43BWEZA.js +346 -0
- package/dist/chunk-D43BWEZA.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-JACGEMTF.js +43 -0
- package/dist/chunk-JACGEMTF.js.map +1 -0
- package/dist/chunk-JX7CLUCV.js +21 -0
- package/dist/chunk-JX7CLUCV.js.map +1 -0
- package/dist/chunk-KXDB56YW.js +39 -0
- package/dist/chunk-KXDB56YW.js.map +1 -0
- package/dist/chunk-KY6ZPWHO.js +112 -0
- package/dist/chunk-KY6ZPWHO.js.map +1 -0
- package/dist/chunk-NBDFQYUZ.js +7992 -0
- package/dist/chunk-NBDFQYUZ.js.map +1 -0
- package/dist/chunk-OGXNFXFA.js +196 -0
- package/dist/chunk-OGXNFXFA.js.map +1 -0
- package/dist/chunk-QTJ7VTJY.js +1994 -0
- package/dist/chunk-QTJ7VTJY.js.map +1 -0
- package/dist/chunk-UVSSQBDY.js +192 -0
- package/dist/chunk-UVSSQBDY.js.map +1 -0
- package/dist/chunk-Y45R3PQL.js +684 -0
- package/dist/chunk-Y45R3PQL.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +868 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/index.d.ts +64 -0
- package/dist/client/index.js +409 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client-CRRO2376.js +10 -0
- package/dist/client-CRRO2376.js.map +1 -0
- package/dist/headless-Q7XHHZIW.js +143 -0
- package/dist/headless-Q7XHHZIW.js.map +1 -0
- package/dist/history-snip-64GYP4ZL.js +12 -0
- package/dist/history-snip-64GYP4ZL.js.map +1 -0
- package/dist/index.d.ts +1305 -418
- package/dist/index.js +384 -1757
- package/dist/index.js.map +1 -1
- package/dist/jsonrpc/index.d.ts +54 -0
- package/dist/jsonrpc/index.js +34 -0
- package/dist/jsonrpc/index.js.map +1 -0
- package/dist/lsp/index.d.ts +36 -0
- package/dist/lsp/index.js +16 -0
- package/dist/lsp/index.js.map +1 -0
- package/dist/lsp-PS3BWIHC.js +8 -0
- package/dist/lsp-PS3BWIHC.js.map +1 -0
- package/dist/manager-DLXK63XC.js +8 -0
- package/dist/manager-DLXK63XC.js.map +1 -0
- package/dist/mcp/index.d.ts +111 -0
- package/dist/mcp/index.js +104 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp-auth-AEI2R4ZC.js +9 -0
- package/dist/mcp-auth-AEI2R4ZC.js.map +1 -0
- package/dist/ollama-YNXAYP3R.js +18 -0
- package/dist/ollama-YNXAYP3R.js.map +1 -0
- package/dist/provider-factory-34MSWJZ3.js +20 -0
- package/dist/provider-factory-34MSWJZ3.js.map +1 -0
- package/dist/providers/anthropic.d.ts +19 -0
- package/dist/providers/anthropic.js +33 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/bedrock.d.ts +39 -0
- package/dist/providers/bedrock.js +54 -0
- package/dist/providers/bedrock.js.map +1 -0
- package/dist/providers/gemini.d.ts +16 -0
- package/dist/providers/gemini.js +224 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/openai.d.ts +18 -0
- package/dist/providers/openai.js +8 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openrouter.d.ts +16 -0
- package/dist/providers/openrouter.js +23 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/vertex.d.ts +40 -0
- package/dist/providers/vertex.js +64 -0
- package/dist/providers/vertex.js.map +1 -0
- package/dist/render-GRN4ZSSW.js +14 -0
- package/dist/render-GRN4ZSSW.js.map +1 -0
- package/dist/resolve-XM52G7YE.js +14 -0
- package/dist/resolve-XM52G7YE.js.map +1 -0
- package/dist/server/index.d.ts +128 -0
- package/dist/server/index.js +626 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server-Cg1yWGaV.d.ts +96 -0
- package/dist/spinner-OJNR6NFO.js +8 -0
- package/dist/spinner-OJNR6NFO.js.map +1 -0
- package/dist/types-2kTLUCnD.d.ts +107 -0
- package/dist/types-3c88cRKH.d.ts +547 -0
- package/dist/types-CwKKucOF.d.ts +620 -0
- package/dist/types-DwdzmXfs.d.ts +107 -0
- package/dist/types-NIyVwQ4h.d.ts +109 -0
- package/dist/types-QwfylltH.d.ts +71 -0
- package/package.json +134 -6
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { b as ChatMessage, T as ToolDefinition } from './types-3c88cRKH.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Provider-agnostic prompt caching utilities.
|
|
5
|
+
*
|
|
6
|
+
* Stable tool ordering prevents cache invalidation when the tool set is
|
|
7
|
+
* unchanged. The breakpoint index helper determines which message gets a
|
|
8
|
+
* single cache_control marker per request (matching claude-code's strategy).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type CacheScope = "global" | "org";
|
|
12
|
+
interface CacheControlConfig {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
/** TTL for cached content. When set, produces `ttl: '1h'` in cache_control. */
|
|
15
|
+
ttl?: "1h";
|
|
16
|
+
/** Scope for shared cache across sessions/orgs. */
|
|
17
|
+
scope?: CacheScope;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Sort tool definitions deterministically for prompt cache stability.
|
|
21
|
+
*
|
|
22
|
+
* Strategy (matching claude-code's assembleToolPool): built-in tools form a
|
|
23
|
+
* contiguous prefix sorted by name, followed by MCP/external tools sorted by
|
|
24
|
+
* name. Tools with `mcpInfo` on the original Tool object are treated as MCP;
|
|
25
|
+
* everything else is built-in. Since ToolDefinition doesn't carry mcpInfo,
|
|
26
|
+
* callers can pass an optional set of MCP tool names to partition correctly.
|
|
27
|
+
*/
|
|
28
|
+
declare function sortToolDefinitionsForCache(tools: ToolDefinition[], mcpToolNames?: ReadonlySet<string>): ToolDefinition[];
|
|
29
|
+
/**
|
|
30
|
+
* Determine which message index should receive the cache_control breakpoint.
|
|
31
|
+
*
|
|
32
|
+
* Exactly one message per request is marked. Normally the last message;
|
|
33
|
+
* for forked agents with skipCacheWrite the second-to-last so the fork
|
|
34
|
+
* doesn't write its own tail into the cache.
|
|
35
|
+
*/
|
|
36
|
+
declare function getMessageCacheBreakpointIndex(messages: ChatMessage[], skipCacheWrite?: boolean): number;
|
|
37
|
+
|
|
38
|
+
export { type CacheControlConfig as C, type CacheScope as a, getMessageCacheBreakpointIndex as g, sortToolDefinitionsForCache as s };
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getMaxOutputTokensForModel,
|
|
3
|
+
getMessageCacheBreakpointIndex
|
|
4
|
+
} from "./chunk-KY6ZPWHO.js";
|
|
5
|
+
|
|
6
|
+
// src/providers/anthropic-shared.ts
|
|
7
|
+
function buildCacheControlBlock(config) {
|
|
8
|
+
const cc = { type: "ephemeral" };
|
|
9
|
+
if (config?.ttl) cc.ttl = config.ttl;
|
|
10
|
+
if (config?.scope) cc.scope = config.scope;
|
|
11
|
+
return cc;
|
|
12
|
+
}
|
|
13
|
+
function isCachingEnabled(config) {
|
|
14
|
+
return config?.enabled === true;
|
|
15
|
+
}
|
|
16
|
+
function contentPartsToAnthropic(parts) {
|
|
17
|
+
return parts.map((part) => {
|
|
18
|
+
if (part.type === "text") {
|
|
19
|
+
return { type: "text", text: part.text };
|
|
20
|
+
}
|
|
21
|
+
if (part.type === "image") {
|
|
22
|
+
return {
|
|
23
|
+
type: "image",
|
|
24
|
+
source: {
|
|
25
|
+
type: "base64",
|
|
26
|
+
media_type: part.media_type,
|
|
27
|
+
data: part.data
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
type: "image",
|
|
33
|
+
source: { type: "url", url: part.url }
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function buildAnthropicTools(params, cacheConfig) {
|
|
38
|
+
if (!params.tools) return void 0;
|
|
39
|
+
const tools = params.tools.map((t) => ({
|
|
40
|
+
name: t.function.name,
|
|
41
|
+
description: t.function.description,
|
|
42
|
+
input_schema: t.function.parameters
|
|
43
|
+
}));
|
|
44
|
+
if (isCachingEnabled(cacheConfig) && tools.length > 0) {
|
|
45
|
+
const lastTool = tools[tools.length - 1];
|
|
46
|
+
lastTool.cache_control = buildCacheControlBlock(cacheConfig);
|
|
47
|
+
}
|
|
48
|
+
return tools;
|
|
49
|
+
}
|
|
50
|
+
function buildAnthropicSystemBlocks(systemPrompt, cacheConfig) {
|
|
51
|
+
if (!systemPrompt) return void 0;
|
|
52
|
+
if (!isCachingEnabled(cacheConfig)) return systemPrompt;
|
|
53
|
+
return [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: systemPrompt,
|
|
57
|
+
cache_control: buildCacheControlBlock(cacheConfig)
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
function convertAnthropicMessages(systemPrompt, messages, cacheConfig, skipCacheWrite) {
|
|
62
|
+
const result = [];
|
|
63
|
+
const caching = isCachingEnabled(cacheConfig);
|
|
64
|
+
const cacheBreakpointIdx = caching ? getMessageCacheBreakpointIndex(messages, skipCacheWrite) : -1;
|
|
65
|
+
for (let mi = 0; mi < messages.length; mi++) {
|
|
66
|
+
const msg = messages[mi];
|
|
67
|
+
const addCache = mi === cacheBreakpointIdx;
|
|
68
|
+
if (msg.role === "system") continue;
|
|
69
|
+
if (msg.role === "user") {
|
|
70
|
+
const isMultipart = Array.isArray(msg.content);
|
|
71
|
+
if (addCache && caching) {
|
|
72
|
+
const blocks = isMultipart ? contentPartsToAnthropic(msg.content) : [{ type: "text", text: msg.content }];
|
|
73
|
+
const lastBlock = blocks[blocks.length - 1];
|
|
74
|
+
lastBlock.cache_control = buildCacheControlBlock(cacheConfig);
|
|
75
|
+
result.push({ role: "user", content: blocks });
|
|
76
|
+
} else if (isMultipart) {
|
|
77
|
+
result.push({
|
|
78
|
+
role: "user",
|
|
79
|
+
content: contentPartsToAnthropic(msg.content)
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
result.push({ role: "user", content: msg.content });
|
|
83
|
+
}
|
|
84
|
+
} else if (msg.role === "assistant") {
|
|
85
|
+
const content = [];
|
|
86
|
+
if (msg.content) {
|
|
87
|
+
content.push({ type: "text", text: msg.content });
|
|
88
|
+
}
|
|
89
|
+
if (msg.tool_calls) {
|
|
90
|
+
for (const tc of msg.tool_calls) {
|
|
91
|
+
let input = {};
|
|
92
|
+
try {
|
|
93
|
+
input = JSON.parse(tc.function.arguments);
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
content.push({
|
|
97
|
+
type: "tool_use",
|
|
98
|
+
id: tc.id,
|
|
99
|
+
name: tc.function.name,
|
|
100
|
+
input
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (content.length === 0) {
|
|
105
|
+
content.push({ type: "text", text: "" });
|
|
106
|
+
}
|
|
107
|
+
if (addCache && caching && content.length > 0) {
|
|
108
|
+
const lastBlock = content[content.length - 1];
|
|
109
|
+
lastBlock.cache_control = buildCacheControlBlock(cacheConfig);
|
|
110
|
+
}
|
|
111
|
+
result.push({ role: "assistant", content });
|
|
112
|
+
} else if (msg.role === "tool") {
|
|
113
|
+
const isMultipart = Array.isArray(msg.content);
|
|
114
|
+
const toolContent = isMultipart ? contentPartsToAnthropic(msg.content) : msg.content;
|
|
115
|
+
const toolResultBlock = {
|
|
116
|
+
type: "tool_result",
|
|
117
|
+
tool_use_id: msg.tool_call_id,
|
|
118
|
+
content: toolContent
|
|
119
|
+
};
|
|
120
|
+
if (addCache && caching) {
|
|
121
|
+
toolResultBlock.cache_control = buildCacheControlBlock(cacheConfig);
|
|
122
|
+
}
|
|
123
|
+
const prev = result[result.length - 1];
|
|
124
|
+
if (prev && prev.role === "user" && Array.isArray(prev.content)) {
|
|
125
|
+
const blocks = prev.content;
|
|
126
|
+
if (blocks.length > 0 && blocks[0].type === "tool_result") {
|
|
127
|
+
blocks.push(toolResultBlock);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
result.push({ role: "user", content: [toolResultBlock] });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
system: buildAnthropicSystemBlocks(systemPrompt, cacheConfig),
|
|
136
|
+
messages: result
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function makeChunk(id, model, delta) {
|
|
140
|
+
return {
|
|
141
|
+
id,
|
|
142
|
+
model,
|
|
143
|
+
choices: [
|
|
144
|
+
{
|
|
145
|
+
index: 0,
|
|
146
|
+
delta,
|
|
147
|
+
finish_reason: null
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
async function* streamAnthropicChat(client, params, defaultModel, cacheConfig) {
|
|
153
|
+
const { system, messages: inputMessages } = convertAnthropicMessages(
|
|
154
|
+
params.system,
|
|
155
|
+
params.messages,
|
|
156
|
+
cacheConfig,
|
|
157
|
+
params.skipCacheWrite
|
|
158
|
+
);
|
|
159
|
+
const tools = buildAnthropicTools(params, cacheConfig);
|
|
160
|
+
const thinkingEnabled = params.thinking?.type === "enabled" && params.thinking.budgetTokens > 0;
|
|
161
|
+
const budgetTokens = thinkingEnabled ? params.thinking.budgetTokens : 0;
|
|
162
|
+
const model = params.model ?? defaultModel;
|
|
163
|
+
const modelMaxOutput = getMaxOutputTokensForModel(model);
|
|
164
|
+
const maxOutputTokens = thinkingEnabled ? Math.min(budgetTokens + (params.max_tokens ?? 8192), modelMaxOutput) : params.max_tokens ?? 8192;
|
|
165
|
+
const clampedBudget = thinkingEnabled ? Math.min(budgetTokens, maxOutputTokens - 1) : 0;
|
|
166
|
+
const streamParams = {
|
|
167
|
+
model,
|
|
168
|
+
max_tokens: maxOutputTokens,
|
|
169
|
+
system,
|
|
170
|
+
messages: inputMessages,
|
|
171
|
+
tools
|
|
172
|
+
};
|
|
173
|
+
if (!thinkingEnabled && params.temperature !== void 0) {
|
|
174
|
+
streamParams.temperature = params.temperature;
|
|
175
|
+
}
|
|
176
|
+
if (thinkingEnabled) {
|
|
177
|
+
streamParams.thinking = {
|
|
178
|
+
type: "enabled",
|
|
179
|
+
budget_tokens: clampedBudget
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (params.outputFormat?.type === "json_schema") {
|
|
183
|
+
streamParams.output_config = {
|
|
184
|
+
format: {
|
|
185
|
+
type: "json_schema",
|
|
186
|
+
schema: params.outputFormat.schema
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const betas = streamParams.betas ?? [];
|
|
190
|
+
if (!betas.includes("structured-outputs-2025-12-15")) {
|
|
191
|
+
betas.push("structured-outputs-2025-12-15");
|
|
192
|
+
}
|
|
193
|
+
streamParams.betas = betas;
|
|
194
|
+
} else if (params.outputFormat?.type === "json_object") {
|
|
195
|
+
const hint = "\n\nYou MUST respond with valid JSON only. No markdown, no explanation \u2014 just a single JSON object.";
|
|
196
|
+
if (typeof streamParams.system === "string") {
|
|
197
|
+
streamParams.system = streamParams.system + hint;
|
|
198
|
+
} else if (Array.isArray(streamParams.system)) {
|
|
199
|
+
const blocks = streamParams.system;
|
|
200
|
+
if (blocks.length > 0) {
|
|
201
|
+
const last = blocks[blocks.length - 1];
|
|
202
|
+
if (last.type === "text" && typeof last.text === "string") {
|
|
203
|
+
last.text = last.text + hint;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
} else if (!streamParams.system) {
|
|
207
|
+
streamParams.system = hint.trim();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const stream = client.messages.stream(streamParams);
|
|
211
|
+
let chunkIndex = 0;
|
|
212
|
+
const toolIndexMap = /* @__PURE__ */ new Map();
|
|
213
|
+
const blockIndexToToolId = /* @__PURE__ */ new Map();
|
|
214
|
+
const blockIndexToType = /* @__PURE__ */ new Map();
|
|
215
|
+
let nextToolIndex = 0;
|
|
216
|
+
let inputTokens = 0;
|
|
217
|
+
let outputTokens = 0;
|
|
218
|
+
let cacheReadTokens = 0;
|
|
219
|
+
let cacheCreationTokens = 0;
|
|
220
|
+
let stopReason;
|
|
221
|
+
for await (const event of stream) {
|
|
222
|
+
const ev = event;
|
|
223
|
+
const chunkId = `chatcmpl-${chunkIndex++}`;
|
|
224
|
+
if (ev.type === "message_start") {
|
|
225
|
+
const msg = ev.message ?? {};
|
|
226
|
+
const usage = msg.usage;
|
|
227
|
+
if (usage) {
|
|
228
|
+
inputTokens = usage.input_tokens ?? 0;
|
|
229
|
+
outputTokens = usage.output_tokens ?? 0;
|
|
230
|
+
cacheReadTokens = usage.cache_read_input_tokens ?? 0;
|
|
231
|
+
cacheCreationTokens = usage.cache_creation_input_tokens ?? 0;
|
|
232
|
+
}
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (ev.type === "message_delta") {
|
|
236
|
+
const delta = ev.delta;
|
|
237
|
+
if (delta?.stop_reason) {
|
|
238
|
+
stopReason = delta.stop_reason;
|
|
239
|
+
}
|
|
240
|
+
const usage = ev.usage;
|
|
241
|
+
if (usage?.output_tokens) {
|
|
242
|
+
outputTokens = usage.output_tokens;
|
|
243
|
+
}
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (ev.type === "content_block_start") {
|
|
247
|
+
const block = ev.content_block ?? {};
|
|
248
|
+
const blockIndex = ev.index;
|
|
249
|
+
if (blockIndex !== void 0) {
|
|
250
|
+
blockIndexToType.set(blockIndex, block.type);
|
|
251
|
+
}
|
|
252
|
+
if (block.type === "thinking") {
|
|
253
|
+
yield makeChunk(chunkId, model, { thinking_content: "" });
|
|
254
|
+
} else if (block.type === "text") {
|
|
255
|
+
yield makeChunk(chunkId, model, { content: "" });
|
|
256
|
+
} else if (block.type === "tool_use") {
|
|
257
|
+
const toolBlock = block;
|
|
258
|
+
const idx = nextToolIndex++;
|
|
259
|
+
toolIndexMap.set(toolBlock.id, idx);
|
|
260
|
+
if (blockIndex !== void 0) {
|
|
261
|
+
blockIndexToToolId.set(blockIndex, toolBlock.id);
|
|
262
|
+
}
|
|
263
|
+
yield makeChunk(chunkId, model, {
|
|
264
|
+
tool_calls: [
|
|
265
|
+
{
|
|
266
|
+
index: idx,
|
|
267
|
+
id: toolBlock.id,
|
|
268
|
+
type: "function",
|
|
269
|
+
function: { name: toolBlock.name, arguments: "" }
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
} else if (ev.type === "content_block_delta") {
|
|
275
|
+
const delta = ev.delta;
|
|
276
|
+
const deltaType = delta.type;
|
|
277
|
+
const blockIndex = ev.index;
|
|
278
|
+
if (deltaType === "thinking_delta") {
|
|
279
|
+
yield makeChunk(chunkId, model, {
|
|
280
|
+
thinking_content: delta.thinking
|
|
281
|
+
});
|
|
282
|
+
} else if (deltaType === "text_delta") {
|
|
283
|
+
yield makeChunk(chunkId, model, {
|
|
284
|
+
content: delta.text
|
|
285
|
+
});
|
|
286
|
+
} else if (deltaType === "signature_delta") {
|
|
287
|
+
if (blockIndex !== void 0 && blockIndexToType.get(blockIndex) === "thinking") {
|
|
288
|
+
yield makeChunk(chunkId, model, {
|
|
289
|
+
thinking_signature: delta.signature
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
} else if (deltaType === "input_json_delta") {
|
|
293
|
+
let toolId;
|
|
294
|
+
if (blockIndex !== void 0) {
|
|
295
|
+
toolId = blockIndexToToolId.get(blockIndex);
|
|
296
|
+
}
|
|
297
|
+
if (!toolId) {
|
|
298
|
+
toolId = Array.from(toolIndexMap.keys()).pop();
|
|
299
|
+
}
|
|
300
|
+
if (toolId) {
|
|
301
|
+
const idx = toolIndexMap.get(toolId);
|
|
302
|
+
yield makeChunk(chunkId, model, {
|
|
303
|
+
tool_calls: [
|
|
304
|
+
{
|
|
305
|
+
index: idx,
|
|
306
|
+
function: { arguments: delta.partial_json }
|
|
307
|
+
}
|
|
308
|
+
]
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
} else if (ev.type === "message_stop") {
|
|
313
|
+
let finishReason;
|
|
314
|
+
switch (stopReason) {
|
|
315
|
+
case "end_turn":
|
|
316
|
+
finishReason = "stop";
|
|
317
|
+
break;
|
|
318
|
+
case "tool_use":
|
|
319
|
+
finishReason = "tool_calls";
|
|
320
|
+
break;
|
|
321
|
+
case "max_tokens":
|
|
322
|
+
finishReason = "length";
|
|
323
|
+
break;
|
|
324
|
+
case "stop_sequence":
|
|
325
|
+
finishReason = "stop";
|
|
326
|
+
break;
|
|
327
|
+
default:
|
|
328
|
+
finishReason = toolIndexMap.size > 0 ? "tool_calls" : "stop";
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
yield {
|
|
332
|
+
id: chunkId,
|
|
333
|
+
model,
|
|
334
|
+
choices: [
|
|
335
|
+
{
|
|
336
|
+
index: 0,
|
|
337
|
+
delta: {},
|
|
338
|
+
finish_reason: finishReason
|
|
339
|
+
}
|
|
340
|
+
],
|
|
341
|
+
usage: {
|
|
342
|
+
prompt_tokens: inputTokens,
|
|
343
|
+
completion_tokens: outputTokens,
|
|
344
|
+
total_tokens: inputTokens + outputTokens,
|
|
345
|
+
cache_read_tokens: cacheReadTokens || void 0,
|
|
346
|
+
cache_creation_tokens: cacheCreationTokens || void 0
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export {
|
|
354
|
+
streamAnthropicChat
|
|
355
|
+
};
|
|
356
|
+
//# sourceMappingURL=chunk-2ZTGQLYK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/anthropic-shared.ts"],"sourcesContent":["/**\n * Shared Anthropic streaming, message conversion, and tool mapping logic.\n *\n * Used by AnthropicProvider, BedrockAnthropicProvider, and VertexAnthropicProvider.\n * Accepts a generic client shape so it works with all three SDKs without\n * importing any of them directly.\n */\n\nimport type { ChatParams, ChatStreamChunk } from \"./types.js\";\nimport type { ChatMessage, ContentPart } from \"../session/types.js\";\nimport type { CacheControlConfig } from \"./cache.js\";\nimport { getMessageCacheBreakpointIndex } from \"./cache.js\";\nimport { getMaxOutputTokensForModel } from \"../utils/context.js\";\n\ninterface AnthropicToolUseBlock {\n type: \"tool_use\";\n id: string;\n name: string;\n input: Record<string, unknown>;\n}\n\ntype CacheControlBlock = {\n type: \"ephemeral\";\n ttl?: \"1h\";\n scope?: \"global\" | \"org\";\n};\n\nexport interface AnthropicStreamClient {\n messages: {\n stream(params: Record<string, unknown>): AsyncIterable<Record<string, unknown>>;\n };\n}\n\nexport function buildCacheControlBlock(\n config: CacheControlConfig | undefined,\n): CacheControlBlock {\n const cc: CacheControlBlock = { type: \"ephemeral\" };\n if (config?.ttl) cc.ttl = config.ttl;\n if (config?.scope) cc.scope = config.scope;\n return cc;\n}\n\nfunction isCachingEnabled(config: CacheControlConfig | undefined): boolean {\n return config?.enabled === true;\n}\n\nexport function contentPartsToAnthropic(\n parts: ContentPart[],\n): Record<string, unknown>[] {\n return parts.map((part) => {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n if (part.type === \"image\") {\n return {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: part.media_type,\n data: part.data,\n },\n };\n }\n return {\n type: \"image\",\n source: { type: \"url\", url: part.url },\n };\n });\n}\n\nexport function buildAnthropicTools(\n params: ChatParams,\n cacheConfig?: CacheControlConfig,\n): Record<string, unknown>[] | undefined {\n if (!params.tools) return undefined;\n\n const tools = params.tools.map((t) => ({\n name: t.function.name,\n description: t.function.description,\n input_schema: t.function.parameters,\n }));\n\n if (isCachingEnabled(cacheConfig) && tools.length > 0) {\n const lastTool = tools[tools.length - 1] as Record<string, unknown>;\n lastTool.cache_control = buildCacheControlBlock(cacheConfig);\n }\n\n return tools;\n}\n\nexport function buildAnthropicSystemBlocks(\n systemPrompt: string | undefined,\n cacheConfig?: CacheControlConfig,\n): unknown {\n if (!systemPrompt) return undefined;\n if (!isCachingEnabled(cacheConfig)) return systemPrompt;\n\n return [\n {\n type: \"text\",\n text: systemPrompt,\n cache_control: buildCacheControlBlock(cacheConfig),\n },\n ];\n}\n\nexport function convertAnthropicMessages(\n systemPrompt: string | undefined,\n messages: ChatMessage[],\n cacheConfig?: CacheControlConfig,\n skipCacheWrite?: boolean,\n): {\n system: unknown;\n messages: Record<string, unknown>[];\n} {\n const result: Record<string, unknown>[] = [];\n const caching = isCachingEnabled(cacheConfig);\n const cacheBreakpointIdx = caching\n ? getMessageCacheBreakpointIndex(messages, skipCacheWrite)\n : -1;\n\n for (let mi = 0; mi < messages.length; mi++) {\n const msg = messages[mi];\n const addCache = mi === cacheBreakpointIdx;\n\n if (msg.role === \"system\") continue;\n\n if (msg.role === \"user\") {\n const isMultipart = Array.isArray(msg.content);\n if (addCache && caching) {\n const blocks = isMultipart\n ? contentPartsToAnthropic(msg.content as ContentPart[])\n : [{ type: \"text\", text: msg.content as string }];\n const lastBlock = blocks[blocks.length - 1] as Record<string, unknown>;\n lastBlock.cache_control = buildCacheControlBlock(cacheConfig);\n result.push({ role: \"user\", content: blocks });\n } else if (isMultipart) {\n result.push({\n role: \"user\",\n content: contentPartsToAnthropic(msg.content as ContentPart[]),\n });\n } else {\n result.push({ role: \"user\", content: msg.content as string });\n }\n } else if (msg.role === \"assistant\") {\n const content: Record<string, unknown>[] = [];\n if (msg.content) {\n content.push({ type: \"text\", text: msg.content });\n }\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n let input: Record<string, unknown> = {};\n try {\n input = JSON.parse(tc.function.arguments);\n } catch {\n // malformed JSON from truncated stream — send empty input\n }\n content.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input,\n });\n }\n }\n if (content.length === 0) {\n content.push({ type: \"text\", text: \"\" });\n }\n if (addCache && caching && content.length > 0) {\n const lastBlock = content[content.length - 1] as Record<string, unknown>;\n lastBlock.cache_control = buildCacheControlBlock(cacheConfig);\n }\n result.push({ role: \"assistant\", content });\n } else if (msg.role === \"tool\") {\n const isMultipart = Array.isArray(msg.content);\n const toolContent = isMultipart\n ? contentPartsToAnthropic(msg.content as ContentPart[])\n : (msg.content as string);\n\n const toolResultBlock: Record<string, unknown> = {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id,\n content: toolContent,\n };\n if (addCache && caching) {\n toolResultBlock.cache_control = buildCacheControlBlock(cacheConfig);\n }\n\n const prev = result[result.length - 1];\n if (prev && prev.role === \"user\" && Array.isArray(prev.content)) {\n const blocks = prev.content as Record<string, unknown>[];\n if (blocks.length > 0 && blocks[0].type === \"tool_result\") {\n blocks.push(toolResultBlock);\n continue;\n }\n }\n result.push({ role: \"user\", content: [toolResultBlock] });\n }\n }\n\n return {\n system: buildAnthropicSystemBlocks(systemPrompt, cacheConfig),\n messages: result,\n };\n}\n\nfunction makeChunk(\n id: string,\n model: string,\n delta: Record<string, unknown>,\n): ChatStreamChunk {\n return {\n id,\n model,\n choices: [\n {\n index: 0,\n delta: delta as ChatStreamChunk[\"choices\"][0][\"delta\"],\n finish_reason: null,\n },\n ],\n };\n}\n\n/**\n * Stream an Anthropic-compatible chat call and yield OpenAI-shaped ChatStreamChunks.\n * Works with Anthropic, AnthropicBedrock, and AnthropicVertex clients.\n */\nexport async function* streamAnthropicChat(\n client: AnthropicStreamClient,\n params: ChatParams,\n defaultModel: string,\n cacheConfig?: CacheControlConfig,\n): AsyncIterable<ChatStreamChunk> {\n const { system, messages: inputMessages } = convertAnthropicMessages(\n params.system,\n params.messages,\n cacheConfig,\n params.skipCacheWrite,\n );\n\n const tools = buildAnthropicTools(params, cacheConfig);\n\n const thinkingEnabled =\n params.thinking?.type === \"enabled\" &&\n (params.thinking as { budgetTokens: number }).budgetTokens > 0;\n const budgetTokens = thinkingEnabled\n ? (params.thinking as { type: \"enabled\"; budgetTokens: number }).budgetTokens\n : 0;\n\n const model = params.model ?? defaultModel;\n\n const modelMaxOutput = getMaxOutputTokensForModel(model);\n const maxOutputTokens = thinkingEnabled\n ? Math.min(budgetTokens + (params.max_tokens ?? 8192), modelMaxOutput)\n : (params.max_tokens ?? 8192);\n const clampedBudget = thinkingEnabled\n ? Math.min(budgetTokens, maxOutputTokens - 1)\n : 0;\n\n const streamParams: Record<string, unknown> = {\n model,\n max_tokens: maxOutputTokens,\n system,\n messages: inputMessages,\n tools,\n };\n\n if (!thinkingEnabled && params.temperature !== undefined) {\n streamParams.temperature = params.temperature;\n }\n\n if (thinkingEnabled) {\n streamParams.thinking = {\n type: \"enabled\",\n budget_tokens: clampedBudget,\n };\n }\n\n if (params.outputFormat?.type === \"json_schema\") {\n streamParams.output_config = {\n format: {\n type: \"json_schema\",\n schema: params.outputFormat.schema,\n },\n };\n const betas: string[] = (streamParams.betas as string[] | undefined) ?? [];\n if (!betas.includes(\"structured-outputs-2025-12-15\")) {\n betas.push(\"structured-outputs-2025-12-15\");\n }\n streamParams.betas = betas;\n } else if (params.outputFormat?.type === \"json_object\") {\n // Anthropic has no native json_object mode. Prepend a system-level hint\n // so the model knows to produce valid JSON.\n const hint = \"\\n\\nYou MUST respond with valid JSON only. No markdown, no explanation — just a single JSON object.\";\n if (typeof streamParams.system === \"string\") {\n streamParams.system = streamParams.system + hint;\n } else if (Array.isArray(streamParams.system)) {\n const blocks = streamParams.system as Array<Record<string, unknown>>;\n if (blocks.length > 0) {\n const last = blocks[blocks.length - 1];\n if (last.type === \"text\" && typeof last.text === \"string\") {\n last.text = last.text + hint;\n }\n }\n } else if (!streamParams.system) {\n streamParams.system = hint.trim();\n }\n }\n\n const stream = client.messages.stream(streamParams);\n\n let chunkIndex = 0;\n const toolIndexMap = new Map<string, number>();\n const blockIndexToToolId = new Map<number, string>();\n const blockIndexToType = new Map<number, string>();\n let nextToolIndex = 0;\n let inputTokens = 0;\n let outputTokens = 0;\n let cacheReadTokens = 0;\n let cacheCreationTokens = 0;\n let stopReason: string | undefined;\n\n for await (const event of stream) {\n const ev = event as Record<string, unknown>;\n const chunkId = `chatcmpl-${chunkIndex++}`;\n\n if (ev.type === \"message_start\") {\n const msg = (ev.message as Record<string, unknown>) ?? {};\n const usage = msg.usage as Record<string, unknown> | undefined;\n if (usage) {\n inputTokens = (usage.input_tokens as number) ?? 0;\n outputTokens = (usage.output_tokens as number) ?? 0;\n cacheReadTokens = (usage.cache_read_input_tokens as number) ?? 0;\n cacheCreationTokens = (usage.cache_creation_input_tokens as number) ?? 0;\n }\n continue;\n }\n\n if (ev.type === \"message_delta\") {\n const delta = (ev as Record<string, unknown>).delta as Record<string, unknown> | undefined;\n if (delta?.stop_reason) {\n stopReason = delta.stop_reason as string;\n }\n const usage = (ev as Record<string, unknown>).usage as\n | Record<string, unknown>\n | undefined;\n if (usage?.output_tokens) {\n outputTokens = usage.output_tokens as number;\n }\n continue;\n }\n\n if (ev.type === \"content_block_start\") {\n const block = (ev.content_block as Record<string, unknown>) ?? {};\n const blockIndex = ev.index as number | undefined;\n if (blockIndex !== undefined) {\n blockIndexToType.set(blockIndex, block.type as string);\n }\n\n if (block.type === \"thinking\") {\n yield makeChunk(chunkId, model, { thinking_content: \"\" });\n } else if (block.type === \"text\") {\n yield makeChunk(chunkId, model, { content: \"\" });\n } else if (block.type === \"tool_use\") {\n const toolBlock = block as unknown as AnthropicToolUseBlock;\n const idx = nextToolIndex++;\n toolIndexMap.set(toolBlock.id, idx);\n if (blockIndex !== undefined) {\n blockIndexToToolId.set(blockIndex, toolBlock.id);\n }\n yield makeChunk(chunkId, model, {\n tool_calls: [\n {\n index: idx,\n id: toolBlock.id,\n type: \"function\",\n function: { name: toolBlock.name, arguments: \"\" },\n },\n ],\n });\n }\n } else if (ev.type === \"content_block_delta\") {\n const delta = ev.delta as Record<string, unknown>;\n const deltaType = delta.type;\n const blockIndex = ev.index as number | undefined;\n\n if (deltaType === \"thinking_delta\") {\n yield makeChunk(chunkId, model, {\n thinking_content: delta.thinking as string,\n });\n } else if (deltaType === \"text_delta\") {\n yield makeChunk(chunkId, model, {\n content: delta.text as string,\n });\n } else if (deltaType === \"signature_delta\") {\n if (blockIndex !== undefined && blockIndexToType.get(blockIndex) === \"thinking\") {\n yield makeChunk(chunkId, model, {\n thinking_signature: delta.signature as string,\n });\n }\n } else if (deltaType === \"input_json_delta\") {\n let toolId: string | undefined;\n if (blockIndex !== undefined) {\n toolId = blockIndexToToolId.get(blockIndex);\n }\n if (!toolId) {\n toolId = Array.from(toolIndexMap.keys()).pop();\n }\n if (toolId) {\n const idx = toolIndexMap.get(toolId)!;\n yield makeChunk(chunkId, model, {\n tool_calls: [\n {\n index: idx,\n function: { arguments: delta.partial_json as string },\n },\n ],\n });\n }\n }\n } else if (ev.type === \"message_stop\") {\n let finishReason: string;\n switch (stopReason) {\n case \"end_turn\": finishReason = \"stop\"; break;\n case \"tool_use\": finishReason = \"tool_calls\"; break;\n case \"max_tokens\": finishReason = \"length\"; break;\n case \"stop_sequence\": finishReason = \"stop\"; break;\n default: finishReason = toolIndexMap.size > 0 ? \"tool_calls\" : \"stop\"; break;\n }\n yield {\n id: chunkId,\n model,\n choices: [\n {\n index: 0,\n delta: {},\n finish_reason: finishReason,\n },\n ],\n usage: {\n prompt_tokens: inputTokens,\n completion_tokens: outputTokens,\n total_tokens: inputTokens + outputTokens,\n cache_read_tokens: cacheReadTokens || undefined,\n cache_creation_tokens: cacheCreationTokens || undefined,\n },\n };\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/compact/history-snip.ts
|
|
2
|
+
function applySnipRemovals(entries) {
|
|
3
|
+
const toDelete = /* @__PURE__ */ new Set();
|
|
4
|
+
for (const entry of entries) {
|
|
5
|
+
if (entry.type === "snip-boundary" && entry.snipMetadata?.removedUuids) {
|
|
6
|
+
for (const uuid of entry.snipMetadata.removedUuids) {
|
|
7
|
+
toDelete.add(uuid);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
if (toDelete.size === 0) {
|
|
12
|
+
const messages = [];
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
if (entry.type === "message" || entry.type === "summary") {
|
|
15
|
+
messages.push(entry.message);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return { messages, removedCount: 0, relinkedCount: 0 };
|
|
19
|
+
}
|
|
20
|
+
const messagesMap = /* @__PURE__ */ new Map();
|
|
21
|
+
const ordered = [];
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
if (entry.type === "message" || entry.type === "summary") {
|
|
24
|
+
const e = entry;
|
|
25
|
+
messagesMap.set(e.uuid, {
|
|
26
|
+
uuid: e.uuid,
|
|
27
|
+
parentUuid: e.parentUuid,
|
|
28
|
+
message: e.message
|
|
29
|
+
});
|
|
30
|
+
ordered.push(e.uuid);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const deletedParent = /* @__PURE__ */ new Map();
|
|
34
|
+
let removedCount = 0;
|
|
35
|
+
for (const uuid of toDelete) {
|
|
36
|
+
const entry = messagesMap.get(uuid);
|
|
37
|
+
if (!entry) continue;
|
|
38
|
+
deletedParent.set(uuid, entry.parentUuid);
|
|
39
|
+
messagesMap.delete(uuid);
|
|
40
|
+
removedCount++;
|
|
41
|
+
}
|
|
42
|
+
const resolve = (start) => {
|
|
43
|
+
const path = [];
|
|
44
|
+
let cur = start;
|
|
45
|
+
while (cur && toDelete.has(cur)) {
|
|
46
|
+
path.push(cur);
|
|
47
|
+
cur = deletedParent.get(cur);
|
|
48
|
+
if (cur === void 0) {
|
|
49
|
+
cur = null;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const p of path) {
|
|
54
|
+
deletedParent.set(p, cur);
|
|
55
|
+
}
|
|
56
|
+
return cur;
|
|
57
|
+
};
|
|
58
|
+
let relinkedCount = 0;
|
|
59
|
+
for (const [uuid, msg] of messagesMap) {
|
|
60
|
+
if (!msg.parentUuid || !toDelete.has(msg.parentUuid)) continue;
|
|
61
|
+
const newParent = resolve(msg.parentUuid);
|
|
62
|
+
messagesMap.set(uuid, { ...msg, parentUuid: newParent });
|
|
63
|
+
relinkedCount++;
|
|
64
|
+
}
|
|
65
|
+
const result = [];
|
|
66
|
+
for (const uuid of ordered) {
|
|
67
|
+
const msg = messagesMap.get(uuid);
|
|
68
|
+
if (msg) {
|
|
69
|
+
result.push(msg.message);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { messages: result, removedCount, relinkedCount };
|
|
73
|
+
}
|
|
74
|
+
function snipMessagesByUuids(entries, removedUuids) {
|
|
75
|
+
if (removedUuids.size === 0) {
|
|
76
|
+
return {
|
|
77
|
+
messages: entries.map((e) => e.message),
|
|
78
|
+
removedCount: 0,
|
|
79
|
+
relinkedCount: 0
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const deletedParent = /* @__PURE__ */ new Map();
|
|
83
|
+
const surviving = [];
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
if (removedUuids.has(entry.uuid)) {
|
|
86
|
+
deletedParent.set(entry.uuid, entry.parentUuid);
|
|
87
|
+
} else {
|
|
88
|
+
surviving.push({ ...entry });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const resolve = (start) => {
|
|
92
|
+
const path = [];
|
|
93
|
+
let cur = start;
|
|
94
|
+
while (cur && removedUuids.has(cur)) {
|
|
95
|
+
path.push(cur);
|
|
96
|
+
cur = deletedParent.get(cur);
|
|
97
|
+
if (cur === void 0) {
|
|
98
|
+
cur = null;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const p of path) {
|
|
103
|
+
deletedParent.set(p, cur);
|
|
104
|
+
}
|
|
105
|
+
return cur;
|
|
106
|
+
};
|
|
107
|
+
let relinkedCount = 0;
|
|
108
|
+
for (const entry of surviving) {
|
|
109
|
+
if (entry.parentUuid && removedUuids.has(entry.parentUuid)) {
|
|
110
|
+
entry.parentUuid = resolve(entry.parentUuid);
|
|
111
|
+
relinkedCount++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
messages: surviving.map((e) => e.message),
|
|
116
|
+
removedCount: removedUuids.size,
|
|
117
|
+
relinkedCount
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function projectSnippedView(messages, snippedIndices, opts) {
|
|
121
|
+
if (opts?.includeSnipped || snippedIndices.size === 0) {
|
|
122
|
+
return messages;
|
|
123
|
+
}
|
|
124
|
+
return messages.filter((_, i) => !snippedIndices.has(i));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
applySnipRemovals,
|
|
129
|
+
snipMessagesByUuids,
|
|
130
|
+
projectSnippedView
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=chunk-42PHHZUA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compact/history-snip.ts"],"sourcesContent":["/**\n * History snip: remove middle ranges of conversation history.\n *\n * Unlike prefix compaction (which summarizes and removes the oldest\n * messages), history snip removes specific message ranges from the middle\n * of the conversation. The JSONL transcript stays append-only — snipped\n * messages remain on disk but their UUIDs are recorded on a snip-boundary\n * entry so they're filtered out on resume.\n *\n * Parent pointers are relinked across gaps using path compression so the\n * conversation chain stays intact.\n */\n\nimport type { ChatMessage, Entry, MessageEntry, SummaryEntry } from \"../session/types.js\";\nimport type { UUID } from \"../utils/uuid.js\";\n\nexport interface SnipConfig {\n enabled: boolean;\n}\n\nexport interface SnipResult {\n messages: ChatMessage[];\n removedCount: number;\n relinkedCount: number;\n}\n\n/**\n * UUID-keyed transcript message used during snip processing.\n * Mirrors the shape we need from JSONL entries for relinking.\n */\ninterface TranscriptMessage {\n uuid: UUID;\n parentUuid: UUID | null;\n message: ChatMessage;\n}\n\n/**\n * Apply snip removals to a UUID-keyed message map.\n *\n * Ported from claude-code's `applySnipRemovals`. Walks entries looking\n * for snip-boundary entries with `snipMetadata.removedUuids`, deletes\n * those messages, and relinks parentUuid across gaps with path compression.\n */\nexport function applySnipRemovals(\n entries: Entry[],\n): { messages: ChatMessage[]; removedCount: number; relinkedCount: number } {\n // Collect all UUIDs to remove from snip-boundary entries\n const toDelete = new Set<UUID>();\n for (const entry of entries) {\n if (entry.type === \"snip-boundary\" && entry.snipMetadata?.removedUuids) {\n for (const uuid of entry.snipMetadata.removedUuids) {\n toDelete.add(uuid as UUID);\n }\n }\n }\n\n if (toDelete.size === 0) {\n // No snips — just extract messages in order\n const messages: ChatMessage[] = [];\n for (const entry of entries) {\n if (entry.type === \"message\" || entry.type === \"summary\") {\n messages.push(entry.message);\n }\n }\n return { messages, removedCount: 0, relinkedCount: 0 };\n }\n\n // Build a Map of UUID -> TranscriptMessage for relinking\n const messagesMap = new Map<UUID, TranscriptMessage>();\n const ordered: UUID[] = [];\n\n for (const entry of entries) {\n if (entry.type === \"message\" || entry.type === \"summary\") {\n const e = entry as MessageEntry | SummaryEntry;\n messagesMap.set(e.uuid, {\n uuid: e.uuid,\n parentUuid: e.parentUuid,\n message: e.message,\n });\n ordered.push(e.uuid);\n }\n }\n\n // Record parent pointers of deleted entries before removing them\n const deletedParent = new Map<UUID, UUID | null>();\n let removedCount = 0;\n for (const uuid of toDelete) {\n const entry = messagesMap.get(uuid);\n if (!entry) continue;\n deletedParent.set(uuid, entry.parentUuid);\n messagesMap.delete(uuid);\n removedCount++;\n }\n\n // Path-compression resolver: walk deleted parent chain to find a surviving ancestor\n const resolve = (start: UUID): UUID | null => {\n const path: UUID[] = [];\n let cur: UUID | null | undefined = start;\n while (cur && toDelete.has(cur)) {\n path.push(cur);\n cur = deletedParent.get(cur);\n if (cur === undefined) {\n cur = null;\n break;\n }\n }\n // Path compression: cache resolved parent for all nodes on the path\n for (const p of path) {\n deletedParent.set(p, cur);\n }\n return cur;\n };\n\n // Relink surviving messages whose parent was deleted\n let relinkedCount = 0;\n for (const [uuid, msg] of messagesMap) {\n if (!msg.parentUuid || !toDelete.has(msg.parentUuid)) continue;\n const newParent = resolve(msg.parentUuid);\n messagesMap.set(uuid, { ...msg, parentUuid: newParent });\n relinkedCount++;\n }\n\n // Rebuild ordered message array (preserving original insertion order)\n const result: ChatMessage[] = [];\n for (const uuid of ordered) {\n const msg = messagesMap.get(uuid);\n if (msg) {\n result.push(msg.message);\n }\n }\n\n return { messages: result, removedCount, relinkedCount };\n}\n\n/**\n * Snip specific messages from an in-memory message array by UUID.\n *\n * This is the in-memory operation — call this during a live thread to\n * remove messages before the next model call. The caller is responsible\n * for persisting a snip-boundary entry to the JSONL transcript.\n */\nexport function snipMessagesByUuids(\n entries: Array<{ uuid: UUID; parentUuid: UUID | null; message: ChatMessage }>,\n removedUuids: Set<UUID>,\n): SnipResult {\n if (removedUuids.size === 0) {\n return {\n messages: entries.map((e) => e.message),\n removedCount: 0,\n relinkedCount: 0,\n };\n }\n\n const deletedParent = new Map<UUID, UUID | null>();\n const surviving: Array<{ uuid: UUID; parentUuid: UUID | null; message: ChatMessage }> = [];\n\n for (const entry of entries) {\n if (removedUuids.has(entry.uuid)) {\n deletedParent.set(entry.uuid, entry.parentUuid);\n } else {\n surviving.push({ ...entry });\n }\n }\n\n const resolve = (start: UUID): UUID | null => {\n const path: UUID[] = [];\n let cur: UUID | null | undefined = start;\n while (cur && removedUuids.has(cur)) {\n path.push(cur);\n cur = deletedParent.get(cur);\n if (cur === undefined) {\n cur = null;\n break;\n }\n }\n for (const p of path) {\n deletedParent.set(p, cur);\n }\n return cur;\n };\n\n let relinkedCount = 0;\n for (const entry of surviving) {\n if (entry.parentUuid && removedUuids.has(entry.parentUuid)) {\n entry.parentUuid = resolve(entry.parentUuid);\n relinkedCount++;\n }\n }\n\n return {\n messages: surviving.map((e) => e.message),\n removedCount: removedUuids.size,\n relinkedCount,\n };\n}\n\n/**\n * Project a \"snipped view\" of messages for the model.\n *\n * Filters out messages marked as snipped. Use the `includeSnipped` option\n * to get the full scrollback for UI display.\n */\nexport function projectSnippedView(\n messages: ChatMessage[],\n snippedIndices: Set<number>,\n opts?: { includeSnipped?: boolean },\n): ChatMessage[] {\n if (opts?.includeSnipped || snippedIndices.size === 0) {\n return messages;\n }\n return messages.filter((_, i) => !snippedIndices.has(i));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/mcp/normalization.ts
|
|
2
|
+
function normalizeNameForMCP(name) {
|
|
3
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
4
|
+
}
|
|
5
|
+
function getMcpPrefix(serverName) {
|
|
6
|
+
return `mcp__${normalizeNameForMCP(serverName)}__`;
|
|
7
|
+
}
|
|
8
|
+
function buildMcpToolName(serverName, toolName) {
|
|
9
|
+
return `${getMcpPrefix(serverName)}${normalizeNameForMCP(toolName)}`;
|
|
10
|
+
}
|
|
11
|
+
function parseMcpToolName(fullName) {
|
|
12
|
+
const parts = fullName.split("__");
|
|
13
|
+
const [prefix, serverName, ...toolParts] = parts;
|
|
14
|
+
if (prefix !== "mcp" || !serverName) return null;
|
|
15
|
+
const toolName = toolParts.length > 0 ? toolParts.join("__") : void 0;
|
|
16
|
+
if (!toolName) return null;
|
|
17
|
+
return { serverName, toolName };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
normalizeNameForMCP,
|
|
22
|
+
getMcpPrefix,
|
|
23
|
+
buildMcpToolName,
|
|
24
|
+
parseMcpToolName
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=chunk-4SQA2UCV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp/normalization.ts"],"sourcesContent":["/**\n * Normalize a server or tool name to be compatible with the API pattern\n * ^[a-zA-Z0-9_-]{1,64}$. Replaces any invalid characters with underscores.\n */\nexport function normalizeNameForMCP(name: string): string {\n return name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport function getMcpPrefix(serverName: string): string {\n return `mcp__${normalizeNameForMCP(serverName)}__`;\n}\n\nexport function buildMcpToolName(\n serverName: string,\n toolName: string,\n): string {\n return `${getMcpPrefix(serverName)}${normalizeNameForMCP(toolName)}`;\n}\n\n/**\n * Parse a fully-qualified MCP tool name back into server + tool components.\n * Returns null if the string doesn't match the mcp__server__tool pattern.\n */\nexport function parseMcpToolName(\n fullName: string,\n): { serverName: string; toolName: string } | null {\n const parts = fullName.split(\"__\");\n const [prefix, serverName, ...toolParts] = parts;\n if (prefix !== \"mcp\" || !serverName) return null;\n const toolName = toolParts.length > 0 ? toolParts.join(\"__\") : undefined;\n if (!toolName) return null;\n return { serverName, toolName };\n}\n"],"mappings":";AAIO,SAAS,oBAAoB,MAAsB;AACxD,SAAO,KAAK,QAAQ,mBAAmB,GAAG;AAC5C;AAEO,SAAS,aAAa,YAA4B;AACvD,SAAO,QAAQ,oBAAoB,UAAU,CAAC;AAChD;AAEO,SAAS,iBACd,YACA,UACQ;AACR,SAAO,GAAG,aAAa,UAAU,CAAC,GAAG,oBAAoB,QAAQ,CAAC;AACpE;AAMO,SAAS,iBACd,UACiD;AACjD,QAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,QAAM,CAAC,QAAQ,YAAY,GAAG,SAAS,IAAI;AAC3C,MAAI,WAAW,SAAS,CAAC,WAAY,QAAO;AAC5C,QAAM,WAAW,UAAU,SAAS,IAAI,UAAU,KAAK,IAAI,IAAI;AAC/D,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,EAAE,YAAY,SAAS;AAChC;","names":[]}
|