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,346 @@
|
|
|
1
|
+
// src/lsp/client.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import {
|
|
4
|
+
createMessageConnection,
|
|
5
|
+
StreamMessageReader,
|
|
6
|
+
StreamMessageWriter
|
|
7
|
+
} from "vscode-jsonrpc/node.js";
|
|
8
|
+
var LspClient = class {
|
|
9
|
+
process = null;
|
|
10
|
+
connection = null;
|
|
11
|
+
config;
|
|
12
|
+
_state = "stopped";
|
|
13
|
+
capabilities = {};
|
|
14
|
+
diagnosticHandler;
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
}
|
|
18
|
+
get state() {
|
|
19
|
+
return this._state;
|
|
20
|
+
}
|
|
21
|
+
onDiagnostics(handler) {
|
|
22
|
+
this.diagnosticHandler = handler;
|
|
23
|
+
}
|
|
24
|
+
async start(rootUri) {
|
|
25
|
+
if (this._state === "running") return;
|
|
26
|
+
this._state = "starting";
|
|
27
|
+
try {
|
|
28
|
+
this.process = spawn(this.config.command, this.config.args ?? [], {
|
|
29
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
30
|
+
env: { ...process.env, ...this.config.env }
|
|
31
|
+
});
|
|
32
|
+
if (!this.process.stdout || !this.process.stdin) {
|
|
33
|
+
throw new Error("Failed to get stdio streams from LSP server");
|
|
34
|
+
}
|
|
35
|
+
this.connection = createMessageConnection(
|
|
36
|
+
new StreamMessageReader(this.process.stdout),
|
|
37
|
+
new StreamMessageWriter(this.process.stdin)
|
|
38
|
+
);
|
|
39
|
+
this.connection.onNotification(
|
|
40
|
+
"textDocument/publishDiagnostics",
|
|
41
|
+
(params) => {
|
|
42
|
+
if (!this.diagnosticHandler) return;
|
|
43
|
+
const filePath = params.uri.startsWith("file://") ? params.uri.slice(7) : params.uri;
|
|
44
|
+
const mapped = params.diagnostics.map((d) => ({
|
|
45
|
+
filePath,
|
|
46
|
+
line: d.range.start.line + 1,
|
|
47
|
+
character: d.range.start.character + 1,
|
|
48
|
+
severity: mapSeverity(d.severity),
|
|
49
|
+
message: d.message,
|
|
50
|
+
source: d.source
|
|
51
|
+
}));
|
|
52
|
+
this.diagnosticHandler(mapped);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
this.connection.listen();
|
|
56
|
+
const initResult = await this.connection.sendRequest(
|
|
57
|
+
"initialize",
|
|
58
|
+
{
|
|
59
|
+
processId: process.pid,
|
|
60
|
+
capabilities: {
|
|
61
|
+
textDocument: {
|
|
62
|
+
synchronization: { didSave: true },
|
|
63
|
+
publishDiagnostics: {},
|
|
64
|
+
hover: { contentFormat: ["plaintext"] },
|
|
65
|
+
definition: { linkSupport: true },
|
|
66
|
+
references: {},
|
|
67
|
+
documentSymbol: { hierarchicalDocumentSymbolSupport: true }
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
rootUri,
|
|
71
|
+
workspaceFolders: [{ uri: rootUri, name: "workspace" }]
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
this.capabilities = initResult?.capabilities ?? {};
|
|
75
|
+
this.connection.sendNotification("initialized", {});
|
|
76
|
+
this._state = "running";
|
|
77
|
+
this.process.on("exit", () => {
|
|
78
|
+
this._state = "stopped";
|
|
79
|
+
this.connection = null;
|
|
80
|
+
this.process = null;
|
|
81
|
+
});
|
|
82
|
+
} catch (err) {
|
|
83
|
+
this._state = "error";
|
|
84
|
+
this.stop();
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async stop() {
|
|
89
|
+
if (this.connection) {
|
|
90
|
+
try {
|
|
91
|
+
await this.connection.sendRequest(
|
|
92
|
+
"shutdown",
|
|
93
|
+
null
|
|
94
|
+
);
|
|
95
|
+
this.connection.sendNotification("exit");
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
this.connection.dispose();
|
|
99
|
+
this.connection = null;
|
|
100
|
+
}
|
|
101
|
+
if (this.process) {
|
|
102
|
+
this.process.kill();
|
|
103
|
+
this.process = null;
|
|
104
|
+
}
|
|
105
|
+
this._state = "stopped";
|
|
106
|
+
}
|
|
107
|
+
async sendRequest(method, params) {
|
|
108
|
+
if (!this.connection || this._state !== "running") {
|
|
109
|
+
throw new Error(`LSP server not running (state: ${this._state})`);
|
|
110
|
+
}
|
|
111
|
+
return this.connection.sendRequest(
|
|
112
|
+
method,
|
|
113
|
+
params
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
sendNotification(method, params) {
|
|
117
|
+
if (!this.connection || this._state !== "running") return;
|
|
118
|
+
this.connection.sendNotification(method, params);
|
|
119
|
+
}
|
|
120
|
+
hasCapability(name) {
|
|
121
|
+
return !!this.capabilities[name];
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
function mapSeverity(severity) {
|
|
125
|
+
switch (severity) {
|
|
126
|
+
case 1:
|
|
127
|
+
return "error";
|
|
128
|
+
case 2:
|
|
129
|
+
return "warning";
|
|
130
|
+
case 3:
|
|
131
|
+
return "info";
|
|
132
|
+
case 4:
|
|
133
|
+
return "hint";
|
|
134
|
+
default:
|
|
135
|
+
return "info";
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/lsp/diagnostics.ts
|
|
140
|
+
var MAX_PENDING_PER_FILE = 50;
|
|
141
|
+
var DiagnosticRegistry = class {
|
|
142
|
+
pending = /* @__PURE__ */ new Map();
|
|
143
|
+
/**
|
|
144
|
+
* Register diagnostics from an LSP server's publishDiagnostics notification.
|
|
145
|
+
* Replaces all diagnostics for the given file.
|
|
146
|
+
*/
|
|
147
|
+
register(diagnostics) {
|
|
148
|
+
if (diagnostics.length === 0) return;
|
|
149
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
150
|
+
for (const d of diagnostics) {
|
|
151
|
+
const existing = byFile.get(d.filePath) ?? [];
|
|
152
|
+
existing.push(d);
|
|
153
|
+
byFile.set(d.filePath, existing);
|
|
154
|
+
}
|
|
155
|
+
for (const [file, diags] of byFile) {
|
|
156
|
+
this.pending.set(file, diags.slice(0, MAX_PENDING_PER_FILE));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Clear diagnostics for a specific file (e.g., after a write/edit).
|
|
161
|
+
*/
|
|
162
|
+
clearForFile(filePath) {
|
|
163
|
+
this.pending.delete(filePath);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get all pending diagnostics and clear them.
|
|
167
|
+
*/
|
|
168
|
+
flush() {
|
|
169
|
+
const all = [];
|
|
170
|
+
for (const diags of this.pending.values()) {
|
|
171
|
+
all.push(...diags);
|
|
172
|
+
}
|
|
173
|
+
this.pending.clear();
|
|
174
|
+
return all;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get pending diagnostics without clearing.
|
|
178
|
+
*/
|
|
179
|
+
peek() {
|
|
180
|
+
const all = [];
|
|
181
|
+
for (const diags of this.pending.values()) {
|
|
182
|
+
all.push(...diags);
|
|
183
|
+
}
|
|
184
|
+
return all;
|
|
185
|
+
}
|
|
186
|
+
hasPending() {
|
|
187
|
+
return this.pending.size > 0;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/lsp/manager.ts
|
|
192
|
+
var LspServerManager = class {
|
|
193
|
+
servers = /* @__PURE__ */ new Map();
|
|
194
|
+
extensionMap = /* @__PURE__ */ new Map();
|
|
195
|
+
diagnostics = new DiagnosticRegistry();
|
|
196
|
+
rootUri;
|
|
197
|
+
constructor(configs, rootUri) {
|
|
198
|
+
this.rootUri = rootUri.startsWith("file://") ? rootUri : `file://${rootUri}`;
|
|
199
|
+
for (const [name, config] of Object.entries(configs)) {
|
|
200
|
+
const client = new LspClient(config);
|
|
201
|
+
client.onDiagnostics((diags) => this.diagnostics.register(diags));
|
|
202
|
+
this.servers.set(name, {
|
|
203
|
+
config,
|
|
204
|
+
client,
|
|
205
|
+
openFiles: /* @__PURE__ */ new Set()
|
|
206
|
+
});
|
|
207
|
+
for (const ext of config.fileExtensions) {
|
|
208
|
+
this.extensionMap.set(ext, name);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get the server that handles a given file path.
|
|
214
|
+
*/
|
|
215
|
+
getServerForFile(filePath) {
|
|
216
|
+
const ext = filePath.includes(".") ? "." + filePath.split(".").pop() : "";
|
|
217
|
+
const serverName = this.extensionMap.get(ext);
|
|
218
|
+
if (!serverName) return void 0;
|
|
219
|
+
return this.servers.get(serverName);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Ensure a server is started, starting it lazily if needed.
|
|
223
|
+
*/
|
|
224
|
+
async ensureStarted(server) {
|
|
225
|
+
if (server.client.state === "running") return;
|
|
226
|
+
await server.client.start(this.rootUri);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Send a request to the appropriate server for a file.
|
|
230
|
+
*/
|
|
231
|
+
async sendRequest(filePath, method, params) {
|
|
232
|
+
const server = this.getServerForFile(filePath);
|
|
233
|
+
if (!server) return null;
|
|
234
|
+
await this.ensureStarted(server);
|
|
235
|
+
await this.ensureFileOpen(server, filePath);
|
|
236
|
+
return server.client.sendRequest(method, params);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Notify LSP servers that a file was opened or changed.
|
|
240
|
+
*/
|
|
241
|
+
async notifyFileChanged(filePath, content, version) {
|
|
242
|
+
const server = this.getServerForFile(filePath);
|
|
243
|
+
if (!server) return;
|
|
244
|
+
if (server.client.state !== "running") return;
|
|
245
|
+
const uri = `file://${filePath}`;
|
|
246
|
+
if (server.openFiles.has(filePath)) {
|
|
247
|
+
server.client.sendNotification("textDocument/didChange", {
|
|
248
|
+
textDocument: { uri, version },
|
|
249
|
+
contentChanges: [{ text: content }]
|
|
250
|
+
});
|
|
251
|
+
} else {
|
|
252
|
+
await this.ensureFileOpen(server, filePath, content);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Notify LSP servers that a file was saved.
|
|
257
|
+
*/
|
|
258
|
+
notifyFileSaved(filePath) {
|
|
259
|
+
const server = this.getServerForFile(filePath);
|
|
260
|
+
if (!server || server.client.state !== "running") return;
|
|
261
|
+
server.client.sendNotification("textDocument/didSave", {
|
|
262
|
+
textDocument: { uri: `file://${filePath}` }
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
async ensureFileOpen(server, filePath, content) {
|
|
266
|
+
if (server.openFiles.has(filePath)) return;
|
|
267
|
+
const uri = `file://${filePath}`;
|
|
268
|
+
const ext = filePath.includes(".") ? filePath.split(".").pop() : "plaintext";
|
|
269
|
+
server.client.sendNotification("textDocument/didOpen", {
|
|
270
|
+
textDocument: {
|
|
271
|
+
uri,
|
|
272
|
+
languageId: mapExtToLanguageId(ext),
|
|
273
|
+
version: 1,
|
|
274
|
+
text: content ?? ""
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
server.openFiles.add(filePath);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get the diagnostic registry for reading pending diagnostics.
|
|
281
|
+
*/
|
|
282
|
+
getDiagnostics() {
|
|
283
|
+
return this.diagnostics;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Check if any LSP server is connected and running.
|
|
287
|
+
*/
|
|
288
|
+
isConnected() {
|
|
289
|
+
for (const server of this.servers.values()) {
|
|
290
|
+
if (server.client.state === "running") return true;
|
|
291
|
+
}
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Return the name and state of every configured LSP server.
|
|
296
|
+
*/
|
|
297
|
+
getServerStatus() {
|
|
298
|
+
return Array.from(this.servers.entries()).map(([name, s]) => ({
|
|
299
|
+
name,
|
|
300
|
+
state: s.client.state
|
|
301
|
+
}));
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Shut down all LSP servers.
|
|
305
|
+
*/
|
|
306
|
+
async shutdown() {
|
|
307
|
+
const tasks = [];
|
|
308
|
+
for (const server of this.servers.values()) {
|
|
309
|
+
tasks.push(server.client.stop());
|
|
310
|
+
}
|
|
311
|
+
await Promise.all(tasks);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
function mapExtToLanguageId(ext) {
|
|
315
|
+
const map = {
|
|
316
|
+
ts: "typescript",
|
|
317
|
+
tsx: "typescriptreact",
|
|
318
|
+
js: "javascript",
|
|
319
|
+
jsx: "javascriptreact",
|
|
320
|
+
py: "python",
|
|
321
|
+
rs: "rust",
|
|
322
|
+
go: "go",
|
|
323
|
+
java: "java",
|
|
324
|
+
rb: "ruby",
|
|
325
|
+
c: "c",
|
|
326
|
+
cpp: "cpp",
|
|
327
|
+
cs: "csharp",
|
|
328
|
+
html: "html",
|
|
329
|
+
css: "css",
|
|
330
|
+
json: "json",
|
|
331
|
+
md: "markdown",
|
|
332
|
+
yaml: "yaml",
|
|
333
|
+
yml: "yaml",
|
|
334
|
+
toml: "toml",
|
|
335
|
+
sh: "shellscript",
|
|
336
|
+
bash: "shellscript"
|
|
337
|
+
};
|
|
338
|
+
return map[ext] ?? "plaintext";
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export {
|
|
342
|
+
LspClient,
|
|
343
|
+
DiagnosticRegistry,
|
|
344
|
+
LspServerManager
|
|
345
|
+
};
|
|
346
|
+
//# sourceMappingURL=chunk-D43BWEZA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lsp/client.ts","../src/lsp/diagnostics.ts","../src/lsp/manager.ts"],"sourcesContent":["import { spawn, type ChildProcess } from \"node:child_process\";\nimport {\n createMessageConnection,\n StreamMessageReader,\n StreamMessageWriter,\n type MessageConnection,\n type RequestType,\n} from \"vscode-jsonrpc/node.js\";\nimport type { LspServerConfig, LspServerState, LspDiagnostic } from \"./types.js\";\n\ninterface ServerCapabilities {\n definitionProvider?: boolean;\n referencesProvider?: boolean;\n hoverProvider?: boolean;\n documentSymbolProvider?: boolean;\n workspaceSymbolProvider?: boolean;\n [key: string]: unknown;\n}\n\n/**\n * Wraps a single LSP server process with JSON-RPC communication.\n */\nexport class LspClient {\n private process: ChildProcess | null = null;\n private connection: MessageConnection | null = null;\n private config: LspServerConfig;\n private _state: LspServerState = \"stopped\";\n private capabilities: ServerCapabilities = {};\n private diagnosticHandler?: (diagnostics: LspDiagnostic[]) => void;\n\n constructor(config: LspServerConfig) {\n this.config = config;\n }\n\n get state(): LspServerState {\n return this._state;\n }\n\n onDiagnostics(handler: (diagnostics: LspDiagnostic[]) => void): void {\n this.diagnosticHandler = handler;\n }\n\n async start(rootUri: string): Promise<void> {\n if (this._state === \"running\") return;\n this._state = \"starting\";\n\n try {\n this.process = spawn(this.config.command, this.config.args ?? [], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...this.config.env },\n });\n\n if (!this.process.stdout || !this.process.stdin) {\n throw new Error(\"Failed to get stdio streams from LSP server\");\n }\n\n this.connection = createMessageConnection(\n new StreamMessageReader(this.process.stdout),\n new StreamMessageWriter(this.process.stdin),\n );\n\n this.connection.onNotification(\n \"textDocument/publishDiagnostics\",\n (params: { uri: string; diagnostics: Array<{ range: { start: { line: number; character: number } }; severity?: number; message: string; source?: string }> }) => {\n if (!this.diagnosticHandler) return;\n const filePath = params.uri.startsWith(\"file://\")\n ? params.uri.slice(7)\n : params.uri;\n const mapped = params.diagnostics.map((d) => ({\n filePath,\n line: d.range.start.line + 1,\n character: d.range.start.character + 1,\n severity: mapSeverity(d.severity),\n message: d.message,\n source: d.source,\n }));\n this.diagnosticHandler(mapped);\n },\n );\n\n this.connection.listen();\n\n const initResult = await this.connection.sendRequest(\n \"initialize\" as unknown as RequestType<unknown, unknown, unknown>,\n {\n processId: process.pid,\n capabilities: {\n textDocument: {\n synchronization: { didSave: true },\n publishDiagnostics: {},\n hover: { contentFormat: [\"plaintext\"] },\n definition: { linkSupport: true },\n references: {},\n documentSymbol: { hierarchicalDocumentSymbolSupport: true },\n },\n },\n rootUri,\n workspaceFolders: [{ uri: rootUri, name: \"workspace\" }],\n },\n );\n\n this.capabilities =\n (initResult as { capabilities?: ServerCapabilities })?.capabilities ?? {};\n\n this.connection.sendNotification(\"initialized\", {});\n this._state = \"running\";\n\n this.process.on(\"exit\", () => {\n this._state = \"stopped\";\n this.connection = null;\n this.process = null;\n });\n } catch (err) {\n this._state = \"error\";\n this.stop();\n throw err;\n }\n }\n\n async stop(): Promise<void> {\n if (this.connection) {\n try {\n await this.connection.sendRequest(\n \"shutdown\" as unknown as RequestType<unknown, unknown, unknown>,\n null,\n );\n this.connection.sendNotification(\"exit\");\n } catch {\n // ignore errors during shutdown\n }\n this.connection.dispose();\n this.connection = null;\n }\n if (this.process) {\n this.process.kill();\n this.process = null;\n }\n this._state = \"stopped\";\n }\n\n async sendRequest<T>(method: string, params: unknown): Promise<T> {\n if (!this.connection || this._state !== \"running\") {\n throw new Error(`LSP server not running (state: ${this._state})`);\n }\n return this.connection.sendRequest(\n method as unknown as RequestType<unknown, T, unknown>,\n params,\n ) as Promise<T>;\n }\n\n sendNotification(method: string, params: unknown): void {\n if (!this.connection || this._state !== \"running\") return;\n this.connection.sendNotification(method, params);\n }\n\n hasCapability(name: keyof ServerCapabilities): boolean {\n return !!this.capabilities[name];\n }\n}\n\nfunction mapSeverity(\n severity: number | undefined,\n): \"error\" | \"warning\" | \"info\" | \"hint\" {\n switch (severity) {\n case 1: return \"error\";\n case 2: return \"warning\";\n case 3: return \"info\";\n case 4: return \"hint\";\n default: return \"info\";\n }\n}\n","import type { LspDiagnostic } from \"./types.js\";\n\nconst MAX_PENDING_PER_FILE = 50;\n\n/**\n * Accumulates LSP diagnostics and provides them for injection into the agent context.\n */\nexport class DiagnosticRegistry {\n private pending = new Map<string, LspDiagnostic[]>();\n\n /**\n * Register diagnostics from an LSP server's publishDiagnostics notification.\n * Replaces all diagnostics for the given file.\n */\n register(diagnostics: LspDiagnostic[]): void {\n if (diagnostics.length === 0) return;\n\n const byFile = new Map<string, LspDiagnostic[]>();\n for (const d of diagnostics) {\n const existing = byFile.get(d.filePath) ?? [];\n existing.push(d);\n byFile.set(d.filePath, existing);\n }\n\n for (const [file, diags] of byFile) {\n this.pending.set(file, diags.slice(0, MAX_PENDING_PER_FILE));\n }\n }\n\n /**\n * Clear diagnostics for a specific file (e.g., after a write/edit).\n */\n clearForFile(filePath: string): void {\n this.pending.delete(filePath);\n }\n\n /**\n * Get all pending diagnostics and clear them.\n */\n flush(): LspDiagnostic[] {\n const all: LspDiagnostic[] = [];\n for (const diags of this.pending.values()) {\n all.push(...diags);\n }\n this.pending.clear();\n return all;\n }\n\n /**\n * Get pending diagnostics without clearing.\n */\n peek(): LspDiagnostic[] {\n const all: LspDiagnostic[] = [];\n for (const diags of this.pending.values()) {\n all.push(...diags);\n }\n return all;\n }\n\n hasPending(): boolean {\n return this.pending.size > 0;\n }\n}\n","import { LspClient } from \"./client.js\";\nimport { DiagnosticRegistry } from \"./diagnostics.js\";\nimport type { LspServerConfig, LspDiagnostic } from \"./types.js\";\n\ninterface ManagedServer {\n config: LspServerConfig;\n client: LspClient;\n openFiles: Set<string>;\n}\n\n/**\n * Manages multiple LSP servers, maps file extensions to servers,\n * and handles lifecycle (lazy start, file sync, shutdown).\n */\nexport class LspServerManager {\n private servers = new Map<string, ManagedServer>();\n private extensionMap = new Map<string, string>();\n private diagnostics = new DiagnosticRegistry();\n private rootUri: string;\n\n constructor(\n configs: Record<string, LspServerConfig>,\n rootUri: string,\n ) {\n this.rootUri = rootUri.startsWith(\"file://\") ? rootUri : `file://${rootUri}`;\n\n for (const [name, config] of Object.entries(configs)) {\n const client = new LspClient(config);\n client.onDiagnostics((diags) => this.diagnostics.register(diags));\n this.servers.set(name, {\n config,\n client,\n openFiles: new Set(),\n });\n\n for (const ext of config.fileExtensions) {\n this.extensionMap.set(ext, name);\n }\n }\n }\n\n /**\n * Get the server that handles a given file path.\n */\n private getServerForFile(filePath: string): ManagedServer | undefined {\n const ext = filePath.includes(\".\")\n ? \".\" + filePath.split(\".\").pop()!\n : \"\";\n const serverName = this.extensionMap.get(ext);\n if (!serverName) return undefined;\n return this.servers.get(serverName);\n }\n\n /**\n * Ensure a server is started, starting it lazily if needed.\n */\n private async ensureStarted(server: ManagedServer): Promise<void> {\n if (server.client.state === \"running\") return;\n await server.client.start(this.rootUri);\n }\n\n /**\n * Send a request to the appropriate server for a file.\n */\n async sendRequest<T>(\n filePath: string,\n method: string,\n params: unknown,\n ): Promise<T | null> {\n const server = this.getServerForFile(filePath);\n if (!server) return null;\n\n await this.ensureStarted(server);\n await this.ensureFileOpen(server, filePath);\n\n return server.client.sendRequest<T>(method, params);\n }\n\n /**\n * Notify LSP servers that a file was opened or changed.\n */\n async notifyFileChanged(\n filePath: string,\n content: string,\n version: number,\n ): Promise<void> {\n const server = this.getServerForFile(filePath);\n if (!server) return;\n if (server.client.state !== \"running\") return;\n\n const uri = `file://${filePath}`;\n if (server.openFiles.has(filePath)) {\n server.client.sendNotification(\"textDocument/didChange\", {\n textDocument: { uri, version },\n contentChanges: [{ text: content }],\n });\n } else {\n await this.ensureFileOpen(server, filePath, content);\n }\n }\n\n /**\n * Notify LSP servers that a file was saved.\n */\n notifyFileSaved(filePath: string): void {\n const server = this.getServerForFile(filePath);\n if (!server || server.client.state !== \"running\") return;\n\n server.client.sendNotification(\"textDocument/didSave\", {\n textDocument: { uri: `file://${filePath}` },\n });\n }\n\n private async ensureFileOpen(\n server: ManagedServer,\n filePath: string,\n content?: string,\n ): Promise<void> {\n if (server.openFiles.has(filePath)) return;\n\n const uri = `file://${filePath}`;\n const ext = filePath.includes(\".\")\n ? filePath.split(\".\").pop()!\n : \"plaintext\";\n\n server.client.sendNotification(\"textDocument/didOpen\", {\n textDocument: {\n uri,\n languageId: mapExtToLanguageId(ext),\n version: 1,\n text: content ?? \"\",\n },\n });\n server.openFiles.add(filePath);\n }\n\n /**\n * Get the diagnostic registry for reading pending diagnostics.\n */\n getDiagnostics(): DiagnosticRegistry {\n return this.diagnostics;\n }\n\n /**\n * Check if any LSP server is connected and running.\n */\n isConnected(): boolean {\n for (const server of this.servers.values()) {\n if (server.client.state === \"running\") return true;\n }\n return false;\n }\n\n /**\n * Return the name and state of every configured LSP server.\n */\n getServerStatus(): Array<{ name: string; state: string }> {\n return Array.from(this.servers.entries()).map(([name, s]) => ({\n name,\n state: s.client.state,\n }));\n }\n\n /**\n * Shut down all LSP servers.\n */\n async shutdown(): Promise<void> {\n const tasks: Promise<void>[] = [];\n for (const server of this.servers.values()) {\n tasks.push(server.client.stop());\n }\n await Promise.all(tasks);\n }\n}\n\nfunction mapExtToLanguageId(ext: string): string {\n const map: Record<string, string> = {\n ts: \"typescript\",\n tsx: \"typescriptreact\",\n js: \"javascript\",\n jsx: \"javascriptreact\",\n py: \"python\",\n rs: \"rust\",\n go: \"go\",\n java: \"java\",\n rb: \"ruby\",\n c: \"c\",\n cpp: \"cpp\",\n cs: \"csharp\",\n html: \"html\",\n css: \"css\",\n json: \"json\",\n md: \"markdown\",\n yaml: \"yaml\",\n yml: \"yaml\",\n toml: \"toml\",\n sh: \"shellscript\",\n bash: \"shellscript\",\n };\n return map[ext] ?? \"plaintext\";\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=chunk-DGUM43GV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/utils/content.ts
|
|
2
|
+
function normalizeContent(content) {
|
|
3
|
+
if (typeof content === "string") {
|
|
4
|
+
return [{ type: "text", text: content }];
|
|
5
|
+
}
|
|
6
|
+
return content;
|
|
7
|
+
}
|
|
8
|
+
function contentToString(content) {
|
|
9
|
+
if (typeof content === "string") return content;
|
|
10
|
+
return content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
11
|
+
}
|
|
12
|
+
function hasImageContent(content) {
|
|
13
|
+
if (typeof content === "string") return false;
|
|
14
|
+
return content.some((p) => p.type === "image" || p.type === "image_url");
|
|
15
|
+
}
|
|
16
|
+
function stripImageContent(content, placeholder = "[image removed]") {
|
|
17
|
+
if (typeof content === "string") return content;
|
|
18
|
+
if (!hasImageContent(content)) return content;
|
|
19
|
+
const parts = [];
|
|
20
|
+
let imagePlaceholderAdded = false;
|
|
21
|
+
for (const part of content) {
|
|
22
|
+
if (part.type === "image" || part.type === "image_url") {
|
|
23
|
+
if (!imagePlaceholderAdded) {
|
|
24
|
+
parts.push({ type: "text", text: placeholder });
|
|
25
|
+
imagePlaceholderAdded = true;
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
parts.push(part);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (parts.length === 1 && parts[0].type === "text") {
|
|
32
|
+
return parts[0].text;
|
|
33
|
+
}
|
|
34
|
+
return parts;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
normalizeContent,
|
|
39
|
+
contentToString,
|
|
40
|
+
hasImageContent,
|
|
41
|
+
stripImageContent
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=chunk-JACGEMTF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/content.ts"],"sourcesContent":["import type { ContentPart } from \"../session/types.js\";\n\n/**\n * Normalize content to a uniform ContentPart[] representation.\n * Strings are wrapped as a single TextContent block.\n */\nexport function normalizeContent(content: string | ContentPart[]): ContentPart[] {\n if (typeof content === \"string\") {\n return [{ type: \"text\", text: content }];\n }\n return content;\n}\n\n/**\n * Extract the text representation of content. Image blocks are omitted;\n * only text parts are concatenated.\n */\nexport function contentToString(content: string | ContentPart[]): string {\n if (typeof content === \"string\") return content;\n return content\n .filter((p): p is Extract<ContentPart, { type: \"text\" }> => p.type === \"text\")\n .map((p) => p.text)\n .join(\"\");\n}\n\n/**\n * Returns true if the content contains at least one image block.\n */\nexport function hasImageContent(content: string | ContentPart[]): boolean {\n if (typeof content === \"string\") return false;\n return content.some((p) => p.type === \"image\" || p.type === \"image_url\");\n}\n\n/**\n * Strip image blocks from content, replacing them with a text placeholder.\n * Returns a plain string when the result contains only text.\n */\nexport function stripImageContent(\n content: string | ContentPart[],\n placeholder = \"[image removed]\",\n): string | ContentPart[] {\n if (typeof content === \"string\") return content;\n if (!hasImageContent(content)) return content;\n\n const parts: ContentPart[] = [];\n let imagePlaceholderAdded = false;\n\n for (const part of content) {\n if (part.type === \"image\" || part.type === \"image_url\") {\n if (!imagePlaceholderAdded) {\n parts.push({ type: \"text\", text: placeholder });\n imagePlaceholderAdded = true;\n }\n } else {\n parts.push(part);\n }\n }\n\n if (parts.length === 1 && parts[0].type === \"text\") {\n return parts[0].text;\n }\n return parts;\n}\n"],"mappings":";AAMO,SAAS,iBAAiB,SAAgD;AAC/E,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,EACzC;AACA,SAAO;AACT;AAMO,SAAS,gBAAgB,SAAyC;AACvE,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,SAAO,QACJ,OAAO,CAAC,MAAmD,EAAE,SAAS,MAAM,EAC5E,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,EAAE;AACZ;AAKO,SAAS,gBAAgB,SAA0C;AACxE,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,SAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,SAAS,WAAW;AACzE;AAMO,SAAS,kBACd,SACA,cAAc,mBACU;AACxB,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,gBAAgB,OAAO,EAAG,QAAO;AAEtC,QAAM,QAAuB,CAAC;AAC9B,MAAI,wBAAwB;AAE5B,aAAW,QAAQ,SAAS;AAC1B,QAAI,KAAK,SAAS,WAAW,KAAK,SAAS,aAAa;AACtD,UAAI,CAAC,uBAAuB;AAC1B,cAAM,KAAK,EAAE,MAAM,QAAQ,MAAM,YAAY,CAAC;AAC9C,gCAAwB;AAAA,MAC1B;AAAA,IACF,OAAO;AACL,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,KAAK,MAAM,CAAC,EAAE,SAAS,QAAQ;AAClD,WAAO,MAAM,CAAC,EAAE;AAAA,EAClB;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/cli/provider-factory.ts
|
|
2
|
+
async function isOllamaRunning() {
|
|
3
|
+
try {
|
|
4
|
+
const res = await fetch(`${ollamaBaseURL()}/api/tags`, {
|
|
5
|
+
signal: AbortSignal.timeout(500)
|
|
6
|
+
});
|
|
7
|
+
return res.ok;
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function ollamaBaseURL() {
|
|
13
|
+
const host = process.env.OLLAMA_HOST ?? "http://localhost:11434";
|
|
14
|
+
return host.replace(/\/+$/, "");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
isOllamaRunning,
|
|
19
|
+
ollamaBaseURL
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=chunk-JX7CLUCV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/provider-factory.ts"],"sourcesContent":["export {\n resolveProvider as createProvider,\n detectProvider,\n type ProviderName,\n SUPPORTED_PROVIDERS,\n DEFAULT_MODELS,\n} from \"../providers/resolve.js\";\n\nexport type { ResolveProviderOptions as ProviderOptions } from \"../providers/resolve.js\";\n\nexport async function isOllamaRunning(): Promise<boolean> {\n try {\n const res = await fetch(`${ollamaBaseURL()}/api/tags`, {\n signal: AbortSignal.timeout(500),\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\nexport function ollamaBaseURL(): string {\n const host = process.env.OLLAMA_HOST ?? \"http://localhost:11434\";\n return host.replace(/\\/+$/, \"\");\n}\n"],"mappings":";AAUA,eAAsB,kBAAoC;AACxD,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa;AAAA,MACrD,QAAQ,YAAY,QAAQ,GAAG;AAAA,IACjC,CAAC;AACD,WAAO,IAAI;AAAA,EACb,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,gBAAwB;AACtC,QAAM,OAAO,QAAQ,IAAI,eAAe;AACxC,SAAO,KAAK,QAAQ,QAAQ,EAAE;AAChC;","names":[]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/cli/spinner.ts
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
var CHARACTERS = process.env.TERM === "xterm-ghostty" ? ["\xB7", "\u2722", "\u2733", "\u2736", "\u273B", "*"] : process.platform === "darwin" ? ["\xB7", "\u2722", "\u2733", "\u2736", "\u273B", "\u273D"] : ["\xB7", "\u2722", "*", "\u2736", "\u273B", "\u273D"];
|
|
4
|
+
var FRAMES = [...CHARACTERS, ...CHARACTERS.slice().reverse()];
|
|
5
|
+
var FRAME_MS = 120;
|
|
6
|
+
function formatElapsed(ms) {
|
|
7
|
+
const s = Math.floor(ms / 1e3);
|
|
8
|
+
if (s < 60) return `${s}s`;
|
|
9
|
+
const m = Math.floor(s / 60);
|
|
10
|
+
return `${m}m ${s % 60}s`;
|
|
11
|
+
}
|
|
12
|
+
function startSpinner(label = "Thinking") {
|
|
13
|
+
const start = Date.now();
|
|
14
|
+
let frameIdx = 0;
|
|
15
|
+
let lastFrameTime = start;
|
|
16
|
+
const write = () => {
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
if (now - lastFrameTime >= FRAME_MS) {
|
|
19
|
+
frameIdx = (frameIdx + 1) % FRAMES.length;
|
|
20
|
+
lastFrameTime = now;
|
|
21
|
+
}
|
|
22
|
+
const glyph = chalk.cyan(FRAMES[frameIdx]);
|
|
23
|
+
const elapsed = chalk.dim(formatElapsed(now - start));
|
|
24
|
+
process.stderr.write(` ${glyph} ${chalk.dim(label)} ${elapsed}\r`);
|
|
25
|
+
};
|
|
26
|
+
write();
|
|
27
|
+
const interval = setInterval(write, 50);
|
|
28
|
+
return {
|
|
29
|
+
stop() {
|
|
30
|
+
clearInterval(interval);
|
|
31
|
+
process.stderr.write("\x1B[2K\r");
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
startSpinner
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=chunk-KXDB56YW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/spinner.ts"],"sourcesContent":["import chalk from \"chalk\";\n\nconst CHARACTERS =\n process.env.TERM === \"xterm-ghostty\"\n ? [\"·\", \"✢\", \"✳\", \"✶\", \"✻\", \"*\"]\n : process.platform === \"darwin\"\n ? [\"·\", \"✢\", \"✳\", \"✶\", \"✻\", \"✽\"]\n : [\"·\", \"✢\", \"*\", \"✶\", \"✻\", \"✽\"];\n\nconst FRAMES = [...CHARACTERS, ...CHARACTERS.slice().reverse()];\nconst FRAME_MS = 120;\n\nfunction formatElapsed(ms: number): string {\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n return `${m}m ${s % 60}s`;\n}\n\nexport interface Spinner {\n stop(): void;\n}\n\nexport function startSpinner(label = \"Thinking\"): Spinner {\n const start = Date.now();\n let frameIdx = 0;\n let lastFrameTime = start;\n\n const write = () => {\n const now = Date.now();\n if (now - lastFrameTime >= FRAME_MS) {\n frameIdx = (frameIdx + 1) % FRAMES.length;\n lastFrameTime = now;\n }\n const glyph = chalk.cyan(FRAMES[frameIdx]);\n const elapsed = chalk.dim(formatElapsed(now - start));\n process.stderr.write(` ${glyph} ${chalk.dim(label)} ${elapsed}\\r`);\n };\n\n write();\n const interval = setInterval(write, 50);\n\n return {\n stop() {\n clearInterval(interval);\n process.stderr.write(\"\\x1b[2K\\r\");\n },\n };\n}\n"],"mappings":";AAAA,OAAO,WAAW;AAElB,IAAM,aACJ,QAAQ,IAAI,SAAS,kBACjB,CAAC,QAAK,UAAK,UAAK,UAAK,UAAK,GAAG,IAC7B,QAAQ,aAAa,WACnB,CAAC,QAAK,UAAK,UAAK,UAAK,UAAK,QAAG,IAC7B,CAAC,QAAK,UAAK,KAAK,UAAK,UAAK,QAAG;AAErC,IAAM,SAAS,CAAC,GAAG,YAAY,GAAG,WAAW,MAAM,EAAE,QAAQ,CAAC;AAC9D,IAAM,WAAW;AAEjB,SAAS,cAAc,IAAoB;AACzC,QAAM,IAAI,KAAK,MAAM,KAAK,GAAI;AAC9B,MAAI,IAAI,GAAI,QAAO,GAAG,CAAC;AACvB,QAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,SAAO,GAAG,CAAC,KAAK,IAAI,EAAE;AACxB;AAMO,SAAS,aAAa,QAAQ,YAAqB;AACxD,QAAM,QAAQ,KAAK,IAAI;AACvB,MAAI,WAAW;AACf,MAAI,gBAAgB;AAEpB,QAAM,QAAQ,MAAM;AAClB,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,MAAM,iBAAiB,UAAU;AACnC,kBAAY,WAAW,KAAK,OAAO;AACnC,sBAAgB;AAAA,IAClB;AACA,UAAM,QAAQ,MAAM,KAAK,OAAO,QAAQ,CAAC;AACzC,UAAM,UAAU,MAAM,IAAI,cAAc,MAAM,KAAK,CAAC;AACpD,YAAQ,OAAO,MAAM,KAAK,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC,KAAK,OAAO,IAAI;AAAA,EACrE;AAEA,QAAM;AACN,QAAM,WAAW,YAAY,OAAO,EAAE;AAEtC,SAAO;AAAA,IACL,OAAO;AACL,oBAAc,QAAQ;AACtB,cAAQ,OAAO,MAAM,WAAW;AAAA,IAClC;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/providers/cache.ts
|
|
2
|
+
function sortToolDefinitionsForCache(tools, mcpToolNames) {
|
|
3
|
+
const byName = (a, b) => a.function.name.localeCompare(b.function.name);
|
|
4
|
+
if (!mcpToolNames || mcpToolNames.size === 0) {
|
|
5
|
+
return [...tools].sort(byName);
|
|
6
|
+
}
|
|
7
|
+
const builtIn = [];
|
|
8
|
+
const mcp = [];
|
|
9
|
+
for (const t of tools) {
|
|
10
|
+
if (mcpToolNames.has(t.function.name)) {
|
|
11
|
+
mcp.push(t);
|
|
12
|
+
} else {
|
|
13
|
+
builtIn.push(t);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return [...builtIn.sort(byName), ...mcp.sort(byName)];
|
|
17
|
+
}
|
|
18
|
+
function getMessageCacheBreakpointIndex(messages, skipCacheWrite) {
|
|
19
|
+
if (messages.length === 0) return -1;
|
|
20
|
+
return skipCacheWrite && messages.length >= 2 ? messages.length - 2 : messages.length - 1;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/utils/context.ts
|
|
24
|
+
var MODEL_CONTEXT_WINDOWS = {
|
|
25
|
+
// Anthropic (evergreen prefixes — also match dated variants via startsWith)
|
|
26
|
+
"claude-sonnet-4": 2e5,
|
|
27
|
+
"claude-opus-4": 2e5,
|
|
28
|
+
"claude-haiku-3-5": 2e5,
|
|
29
|
+
"claude-3-5-sonnet": 2e5,
|
|
30
|
+
"claude-3-5-haiku": 2e5,
|
|
31
|
+
// OpenAI
|
|
32
|
+
"gpt-4o": 128e3,
|
|
33
|
+
"gpt-4o-mini": 128e3,
|
|
34
|
+
"gpt-4-turbo": 128e3,
|
|
35
|
+
"gpt-4": 8192,
|
|
36
|
+
"o1": 2e5,
|
|
37
|
+
"o1-mini": 128e3,
|
|
38
|
+
"o1-preview": 128e3,
|
|
39
|
+
"o3": 2e5,
|
|
40
|
+
"o3-mini": 2e5,
|
|
41
|
+
"o4-mini": 2e5,
|
|
42
|
+
// Google
|
|
43
|
+
"gemini-2.5-pro": 1048576,
|
|
44
|
+
"gemini-2.5-flash": 1048576,
|
|
45
|
+
"gemini-2.0-flash": 1048576,
|
|
46
|
+
"gemini-1.5-pro": 2097152,
|
|
47
|
+
"gemini-1.5-flash": 1048576
|
|
48
|
+
};
|
|
49
|
+
var MODEL_MAX_OUTPUT_TOKENS = {
|
|
50
|
+
"claude-sonnet-4": 16384,
|
|
51
|
+
"claude-opus-4": 16384,
|
|
52
|
+
"claude-haiku-3-5": 8192,
|
|
53
|
+
"claude-3-5-sonnet": 8192,
|
|
54
|
+
"claude-3-5-haiku": 8192,
|
|
55
|
+
"gpt-4o": 16384,
|
|
56
|
+
"gpt-4o-mini": 16384,
|
|
57
|
+
"gpt-4-turbo": 4096,
|
|
58
|
+
"o1": 1e5,
|
|
59
|
+
"o3": 1e5,
|
|
60
|
+
"o3-mini": 1e5,
|
|
61
|
+
"o4-mini": 1e5,
|
|
62
|
+
"gemini-2.5-pro": 65536,
|
|
63
|
+
"gemini-2.5-flash": 65536,
|
|
64
|
+
"gemini-2.0-flash": 8192
|
|
65
|
+
};
|
|
66
|
+
var DEFAULT_MAX_OUTPUT_TOKENS = 16384;
|
|
67
|
+
var DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
68
|
+
var AUTOCOMPACT_BUFFER_TOKENS = 13e3;
|
|
69
|
+
var MAX_OUTPUT_RESERVE = 2e4;
|
|
70
|
+
var customWindows = {};
|
|
71
|
+
function registerContextWindows(windows) {
|
|
72
|
+
customWindows = { ...customWindows, ...windows };
|
|
73
|
+
}
|
|
74
|
+
function getContextWindowForModel(model) {
|
|
75
|
+
if (customWindows[model] !== void 0) return customWindows[model];
|
|
76
|
+
if (MODEL_CONTEXT_WINDOWS[model] !== void 0)
|
|
77
|
+
return MODEL_CONTEXT_WINDOWS[model];
|
|
78
|
+
for (const [prefix, size] of Object.entries(MODEL_CONTEXT_WINDOWS)) {
|
|
79
|
+
if (model.startsWith(prefix)) return size;
|
|
80
|
+
}
|
|
81
|
+
for (const [prefix, size] of Object.entries(customWindows)) {
|
|
82
|
+
if (model.startsWith(prefix)) return size;
|
|
83
|
+
}
|
|
84
|
+
return DEFAULT_CONTEXT_WINDOW;
|
|
85
|
+
}
|
|
86
|
+
function getEffectiveContextWindow(model, maxOutputTokens) {
|
|
87
|
+
const window = getContextWindowForModel(model);
|
|
88
|
+
const reserve = Math.min(maxOutputTokens ?? MAX_OUTPUT_RESERVE, MAX_OUTPUT_RESERVE);
|
|
89
|
+
return window - reserve;
|
|
90
|
+
}
|
|
91
|
+
function getAutoCompactThreshold(model, maxOutputTokens) {
|
|
92
|
+
return getEffectiveContextWindow(model, maxOutputTokens) - AUTOCOMPACT_BUFFER_TOKENS;
|
|
93
|
+
}
|
|
94
|
+
function getMaxOutputTokensForModel(model) {
|
|
95
|
+
if (MODEL_MAX_OUTPUT_TOKENS[model] !== void 0)
|
|
96
|
+
return MODEL_MAX_OUTPUT_TOKENS[model];
|
|
97
|
+
for (const [prefix, size] of Object.entries(MODEL_MAX_OUTPUT_TOKENS)) {
|
|
98
|
+
if (model.startsWith(prefix)) return size;
|
|
99
|
+
}
|
|
100
|
+
return DEFAULT_MAX_OUTPUT_TOKENS;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export {
|
|
104
|
+
sortToolDefinitionsForCache,
|
|
105
|
+
getMessageCacheBreakpointIndex,
|
|
106
|
+
registerContextWindows,
|
|
107
|
+
getContextWindowForModel,
|
|
108
|
+
getEffectiveContextWindow,
|
|
109
|
+
getAutoCompactThreshold,
|
|
110
|
+
getMaxOutputTokensForModel
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=chunk-KY6ZPWHO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/cache.ts","../src/utils/context.ts"],"sourcesContent":["/**\n * Provider-agnostic prompt caching utilities.\n *\n * Stable tool ordering prevents cache invalidation when the tool set is\n * unchanged. The breakpoint index helper determines which message gets a\n * single cache_control marker per request (matching claude-code's strategy).\n */\n\nimport type { ToolDefinition } from \"./types.js\";\nimport type { ChatMessage } from \"../session/types.js\";\n\nexport type CacheScope = \"global\" | \"org\";\n\nexport interface CacheControlConfig {\n enabled: boolean;\n /** TTL for cached content. When set, produces `ttl: '1h'` in cache_control. */\n ttl?: \"1h\";\n /** Scope for shared cache across sessions/orgs. */\n scope?: CacheScope;\n}\n\n/**\n * Sort tool definitions deterministically for prompt cache stability.\n *\n * Strategy (matching claude-code's assembleToolPool): built-in tools form a\n * contiguous prefix sorted by name, followed by MCP/external tools sorted by\n * name. Tools with `mcpInfo` on the original Tool object are treated as MCP;\n * everything else is built-in. Since ToolDefinition doesn't carry mcpInfo,\n * callers can pass an optional set of MCP tool names to partition correctly.\n */\nexport function sortToolDefinitionsForCache(\n tools: ToolDefinition[],\n mcpToolNames?: ReadonlySet<string>,\n): ToolDefinition[] {\n const byName = (a: ToolDefinition, b: ToolDefinition) =>\n a.function.name.localeCompare(b.function.name);\n\n if (!mcpToolNames || mcpToolNames.size === 0) {\n return [...tools].sort(byName);\n }\n\n const builtIn: ToolDefinition[] = [];\n const mcp: ToolDefinition[] = [];\n\n for (const t of tools) {\n if (mcpToolNames.has(t.function.name)) {\n mcp.push(t);\n } else {\n builtIn.push(t);\n }\n }\n\n return [...builtIn.sort(byName), ...mcp.sort(byName)];\n}\n\n/**\n * Determine which message index should receive the cache_control breakpoint.\n *\n * Exactly one message per request is marked. Normally the last message;\n * for forked agents with skipCacheWrite the second-to-last so the fork\n * doesn't write its own tail into the cache.\n */\nexport function getMessageCacheBreakpointIndex(\n messages: ChatMessage[],\n skipCacheWrite?: boolean,\n): number {\n if (messages.length === 0) return -1;\n return skipCacheWrite && messages.length >= 2\n ? messages.length - 2\n : messages.length - 1;\n}\n","/**\n * Model context window sizes and effective window calculations.\n */\n\nconst MODEL_CONTEXT_WINDOWS: Record<string, number> = {\n // Anthropic (evergreen prefixes — also match dated variants via startsWith)\n \"claude-sonnet-4\": 200_000,\n \"claude-opus-4\": 200_000,\n \"claude-haiku-3-5\": 200_000,\n \"claude-3-5-sonnet\": 200_000,\n \"claude-3-5-haiku\": 200_000,\n // OpenAI\n \"gpt-4o\": 128_000,\n \"gpt-4o-mini\": 128_000,\n \"gpt-4-turbo\": 128_000,\n \"gpt-4\": 8_192,\n \"o1\": 200_000,\n \"o1-mini\": 128_000,\n \"o1-preview\": 128_000,\n \"o3\": 200_000,\n \"o3-mini\": 200_000,\n \"o4-mini\": 200_000,\n // Google\n \"gemini-2.5-pro\": 1_048_576,\n \"gemini-2.5-flash\": 1_048_576,\n \"gemini-2.0-flash\": 1_048_576,\n \"gemini-1.5-pro\": 2_097_152,\n \"gemini-1.5-flash\": 1_048_576,\n};\n\nconst MODEL_MAX_OUTPUT_TOKENS: Record<string, number> = {\n \"claude-sonnet-4\": 16_384,\n \"claude-opus-4\": 16_384,\n \"claude-haiku-3-5\": 8_192,\n \"claude-3-5-sonnet\": 8_192,\n \"claude-3-5-haiku\": 8_192,\n \"gpt-4o\": 16_384,\n \"gpt-4o-mini\": 16_384,\n \"gpt-4-turbo\": 4_096,\n \"o1\": 100_000,\n \"o3\": 100_000,\n \"o3-mini\": 100_000,\n \"o4-mini\": 100_000,\n \"gemini-2.5-pro\": 65_536,\n \"gemini-2.5-flash\": 65_536,\n \"gemini-2.0-flash\": 8_192,\n};\n\nconst DEFAULT_MAX_OUTPUT_TOKENS = 16_384;\nconst DEFAULT_CONTEXT_WINDOW = 128_000;\nconst AUTOCOMPACT_BUFFER_TOKENS = 13_000;\nconst MAX_OUTPUT_RESERVE = 20_000;\n\nlet customWindows: Record<string, number> = {};\n\n/**\n * Register custom context window sizes for models not in the built-in table.\n */\nexport function registerContextWindows(\n windows: Record<string, number>,\n): void {\n customWindows = { ...customWindows, ...windows };\n}\n\n/**\n * Get the context window size for a model. Checks custom overrides first,\n * then built-in table, then prefix-matches, then falls back to default.\n */\nexport function getContextWindowForModel(model: string): number {\n if (customWindows[model] !== undefined) return customWindows[model];\n if (MODEL_CONTEXT_WINDOWS[model] !== undefined)\n return MODEL_CONTEXT_WINDOWS[model];\n\n for (const [prefix, size] of Object.entries(MODEL_CONTEXT_WINDOWS)) {\n if (model.startsWith(prefix)) return size;\n }\n for (const [prefix, size] of Object.entries(customWindows)) {\n if (model.startsWith(prefix)) return size;\n }\n\n return DEFAULT_CONTEXT_WINDOW;\n}\n\n/**\n * Effective context window = total window minus space reserved for the\n * model's output during a compaction/summary request.\n */\nexport function getEffectiveContextWindow(\n model: string,\n maxOutputTokens?: number,\n): number {\n const window = getContextWindowForModel(model);\n const reserve = Math.min(maxOutputTokens ?? MAX_OUTPUT_RESERVE, MAX_OUTPUT_RESERVE);\n return window - reserve;\n}\n\n/**\n * Auto-compact threshold: effective window minus a buffer to ensure we\n * compact before we're at the hard limit.\n */\nexport function getAutoCompactThreshold(\n model: string,\n maxOutputTokens?: number,\n): number {\n return getEffectiveContextWindow(model, maxOutputTokens) - AUTOCOMPACT_BUFFER_TOKENS;\n}\n\n/**\n * Get the maximum output tokens a model supports. Used to clamp\n * max_tokens when extended thinking budgets are added.\n */\nexport function getMaxOutputTokensForModel(model: string): number {\n if (MODEL_MAX_OUTPUT_TOKENS[model] !== undefined)\n return MODEL_MAX_OUTPUT_TOKENS[model];\n for (const [prefix, size] of Object.entries(MODEL_MAX_OUTPUT_TOKENS)) {\n if (model.startsWith(prefix)) return size;\n }\n return DEFAULT_MAX_OUTPUT_TOKENS;\n}\n"],"mappings":"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","names":[]}
|