luckerr 0.41.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 +267 -0
- package/README.zh-CN.md +237 -0
- package/dashboard/app.css +3022 -0
- package/dashboard/dist/app.js +30137 -0
- package/dashboard/dist/app.js.map +1 -0
- package/dashboard/dist/vendor-hljs.css +10 -0
- package/dashboard/dist/vendor-uplot.css +1 -0
- package/dashboard/index.html +19 -0
- package/data/deepseek-tokenizer.json.gz +0 -0
- package/dist/cli/acp-EOOAI4F5.js +712 -0
- package/dist/cli/acp-EOOAI4F5.js.map +1 -0
- package/dist/cli/chat-7J6GJXL2.js +51 -0
- package/dist/cli/chat-7J6GJXL2.js.map +1 -0
- package/dist/cli/chunk-2425HK6U.js +54 -0
- package/dist/cli/chunk-2425HK6U.js.map +1 -0
- package/dist/cli/chunk-25T6CVUP.js +172 -0
- package/dist/cli/chunk-25T6CVUP.js.map +1 -0
- package/dist/cli/chunk-2UQP6H6T.js +31 -0
- package/dist/cli/chunk-2UQP6H6T.js.map +1 -0
- package/dist/cli/chunk-56OAJILV.js +47 -0
- package/dist/cli/chunk-56OAJILV.js.map +1 -0
- package/dist/cli/chunk-5FTI4KXH.js +150 -0
- package/dist/cli/chunk-5FTI4KXH.js.map +1 -0
- package/dist/cli/chunk-5TWQD73O.js +2846 -0
- package/dist/cli/chunk-5TWQD73O.js.map +1 -0
- package/dist/cli/chunk-653BOCMK.js +40 -0
- package/dist/cli/chunk-653BOCMK.js.map +1 -0
- package/dist/cli/chunk-6ALJTWWQ.js +2663 -0
- package/dist/cli/chunk-6ALJTWWQ.js.map +1 -0
- package/dist/cli/chunk-6DRKA2IL.js +341 -0
- package/dist/cli/chunk-6DRKA2IL.js.map +1 -0
- package/dist/cli/chunk-6LV63NJV.js +634 -0
- package/dist/cli/chunk-6LV63NJV.js.map +1 -0
- package/dist/cli/chunk-74EX7SUH.js +25293 -0
- package/dist/cli/chunk-74EX7SUH.js.map +1 -0
- package/dist/cli/chunk-74U5RKTX.js +60611 -0
- package/dist/cli/chunk-74U5RKTX.js.map +1 -0
- package/dist/cli/chunk-ANJSUESV.js +143 -0
- package/dist/cli/chunk-ANJSUESV.js.map +1 -0
- package/dist/cli/chunk-DB2Z3DKZ.js +54 -0
- package/dist/cli/chunk-DB2Z3DKZ.js.map +1 -0
- package/dist/cli/chunk-DDIH3ZAA.js +400 -0
- package/dist/cli/chunk-DDIH3ZAA.js.map +1 -0
- package/dist/cli/chunk-ELN3Z3B2.js +621 -0
- package/dist/cli/chunk-ELN3Z3B2.js.map +1 -0
- package/dist/cli/chunk-F6BSQJGV.js +200 -0
- package/dist/cli/chunk-F6BSQJGV.js.map +1 -0
- package/dist/cli/chunk-FET2UAG5.js +246 -0
- package/dist/cli/chunk-FET2UAG5.js.map +1 -0
- package/dist/cli/chunk-FFJ342IJ.js +190 -0
- package/dist/cli/chunk-FFJ342IJ.js.map +1 -0
- package/dist/cli/chunk-GB3247B6.js +130 -0
- package/dist/cli/chunk-GB3247B6.js.map +1 -0
- package/dist/cli/chunk-HC2J4U3G.js +373 -0
- package/dist/cli/chunk-HC2J4U3G.js.map +1 -0
- package/dist/cli/chunk-HRUZAIHQ.js +42 -0
- package/dist/cli/chunk-HRUZAIHQ.js.map +1 -0
- package/dist/cli/chunk-J3ZJFUDL.js +308 -0
- package/dist/cli/chunk-J3ZJFUDL.js.map +1 -0
- package/dist/cli/chunk-J5XJHLWM.js +55 -0
- package/dist/cli/chunk-J5XJHLWM.js.map +1 -0
- package/dist/cli/chunk-JFGLMRZ6.js +160 -0
- package/dist/cli/chunk-JFGLMRZ6.js.map +1 -0
- package/dist/cli/chunk-JMBMLOBP.js +26 -0
- package/dist/cli/chunk-JMBMLOBP.js.map +1 -0
- package/dist/cli/chunk-JMWHXZEL.js +551 -0
- package/dist/cli/chunk-JMWHXZEL.js.map +1 -0
- package/dist/cli/chunk-KEQGPJBO.js +209 -0
- package/dist/cli/chunk-KEQGPJBO.js.map +1 -0
- package/dist/cli/chunk-M4K6U37F.js +232 -0
- package/dist/cli/chunk-M4K6U37F.js.map +1 -0
- package/dist/cli/chunk-MIJI2WMN.js +95 -0
- package/dist/cli/chunk-MIJI2WMN.js.map +1 -0
- package/dist/cli/chunk-MPAO3JNR.js +128 -0
- package/dist/cli/chunk-MPAO3JNR.js.map +1 -0
- package/dist/cli/chunk-PZOFBEDC.js +873 -0
- package/dist/cli/chunk-PZOFBEDC.js.map +1 -0
- package/dist/cli/chunk-RAILYQLN.js +46 -0
- package/dist/cli/chunk-RAILYQLN.js.map +1 -0
- package/dist/cli/chunk-RR35VQVT.js +90 -0
- package/dist/cli/chunk-RR35VQVT.js.map +1 -0
- package/dist/cli/chunk-RRA7VPW4.js +417 -0
- package/dist/cli/chunk-RRA7VPW4.js.map +1 -0
- package/dist/cli/chunk-RU36QVN3.js +452 -0
- package/dist/cli/chunk-RU36QVN3.js.map +1 -0
- package/dist/cli/chunk-RUBIINXR.js +1819 -0
- package/dist/cli/chunk-RUBIINXR.js.map +1 -0
- package/dist/cli/chunk-S4XVGLRW.js +499 -0
- package/dist/cli/chunk-S4XVGLRW.js.map +1 -0
- package/dist/cli/chunk-TUK7OWJA.js +51 -0
- package/dist/cli/chunk-TUK7OWJA.js.map +1 -0
- package/dist/cli/chunk-VALDDV76.js +580 -0
- package/dist/cli/chunk-VALDDV76.js.map +1 -0
- package/dist/cli/chunk-WQOGPYGN.js +11390 -0
- package/dist/cli/chunk-WQOGPYGN.js.map +1 -0
- package/dist/cli/chunk-WREKDFXT.js +34320 -0
- package/dist/cli/chunk-WREKDFXT.js.map +1 -0
- package/dist/cli/chunk-Y7XQU2EL.js +270 -0
- package/dist/cli/chunk-Y7XQU2EL.js.map +1 -0
- package/dist/cli/chunk-YBVCZJU4.js +54 -0
- package/dist/cli/chunk-YBVCZJU4.js.map +1 -0
- package/dist/cli/chunk-YLIHDXUQ.js +749 -0
- package/dist/cli/chunk-YLIHDXUQ.js.map +1 -0
- package/dist/cli/chunk-YV5XXFD7.js +767 -0
- package/dist/cli/chunk-YV5XXFD7.js.map +1 -0
- package/dist/cli/chunk-ZRCNIYRQ.js +101 -0
- package/dist/cli/chunk-ZRCNIYRQ.js.map +1 -0
- package/dist/cli/code-CRKVCMFZ.js +155 -0
- package/dist/cli/code-CRKVCMFZ.js.map +1 -0
- package/dist/cli/commands-QLMD3T7B.js +356 -0
- package/dist/cli/commands-QLMD3T7B.js.map +1 -0
- package/dist/cli/commit-53PP32NC.js +293 -0
- package/dist/cli/commit-53PP32NC.js.map +1 -0
- package/dist/cli/desktop-R6W5CLJ5.js +1046 -0
- package/dist/cli/desktop-R6W5CLJ5.js.map +1 -0
- package/dist/cli/devtools-YECO25QO.js +3719 -0
- package/dist/cli/devtools-YECO25QO.js.map +1 -0
- package/dist/cli/diff-LYNRCJZE.js +166 -0
- package/dist/cli/diff-LYNRCJZE.js.map +1 -0
- package/dist/cli/doctor-5IBP4R5J.js +28 -0
- package/dist/cli/doctor-5IBP4R5J.js.map +1 -0
- package/dist/cli/events-QN6KLN2V.js +340 -0
- package/dist/cli/events-QN6KLN2V.js.map +1 -0
- package/dist/cli/index.js +3500 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/mcp-FGKEH7RG.js +277 -0
- package/dist/cli/mcp-FGKEH7RG.js.map +1 -0
- package/dist/cli/mcp-browse-YCND4NWT.js +178 -0
- package/dist/cli/mcp-browse-YCND4NWT.js.map +1 -0
- package/dist/cli/mcp-inspect-V34J3VX5.js +143 -0
- package/dist/cli/mcp-inspect-V34J3VX5.js.map +1 -0
- package/dist/cli/package.json +3 -0
- package/dist/cli/prompt-I775PNKT.js +16 -0
- package/dist/cli/prompt-I775PNKT.js.map +1 -0
- package/dist/cli/prune-sessions-KGIIYD3P.js +44 -0
- package/dist/cli/prune-sessions-KGIIYD3P.js.map +1 -0
- package/dist/cli/replay-RDXLUAOE.js +292 -0
- package/dist/cli/replay-RDXLUAOE.js.map +1 -0
- package/dist/cli/run-RCAC2RYW.js +223 -0
- package/dist/cli/run-RCAC2RYW.js.map +1 -0
- package/dist/cli/server-FFU6TLYJ.js +3658 -0
- package/dist/cli/server-FFU6TLYJ.js.map +1 -0
- package/dist/cli/sessions-QT26MQAE.js +107 -0
- package/dist/cli/sessions-QT26MQAE.js.map +1 -0
- package/dist/cli/setup-VV4WKXHV.js +767 -0
- package/dist/cli/setup-VV4WKXHV.js.map +1 -0
- package/dist/cli/stats-JVZPQWAN.js +15 -0
- package/dist/cli/stats-JVZPQWAN.js.map +1 -0
- package/dist/cli/update-KYI3OVJP.js +15 -0
- package/dist/cli/update-KYI3OVJP.js.map +1 -0
- package/dist/cli/version-ANYORXTI.js +34 -0
- package/dist/cli/version-ANYORXTI.js.map +1 -0
- package/dist/index.d.ts +2557 -0
- package/dist/index.js +15000 -0
- package/dist/index.js.map +1 -0
- package/package.json +106 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2557 @@
|
|
|
1
|
+
import { SpawnOptions } from 'node:child_process';
|
|
2
|
+
import { WriteStream } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
/** No retry on aborts or mid-stream body errors — re-billing the user for desynced output is worse than failing. */
|
|
5
|
+
interface RetryOptions {
|
|
6
|
+
/** Maximum total attempts (including the first). Default 4. */
|
|
7
|
+
maxAttempts?: number;
|
|
8
|
+
/** Initial backoff in ms. Doubles each retry, with jitter. Default 500. */
|
|
9
|
+
initialBackoffMs?: number;
|
|
10
|
+
/** Upper bound on any single backoff delay. Default 10000 (10s). */
|
|
11
|
+
maxBackoffMs?: number;
|
|
12
|
+
/** HTTP statuses to treat as retryable. Default [408, 429, 500, 502, 503, 504]. */
|
|
13
|
+
retryableStatuses?: readonly number[];
|
|
14
|
+
/** Abort signal; we do NOT retry once aborted. */
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
/** Telemetry hook — called before each wait. */
|
|
17
|
+
onRetry?: (info: RetryInfo) => void;
|
|
18
|
+
}
|
|
19
|
+
interface RetryInfo {
|
|
20
|
+
attempt: number;
|
|
21
|
+
reason: string;
|
|
22
|
+
waitMs: number;
|
|
23
|
+
}
|
|
24
|
+
declare function fetchWithRetry(fetchFn: typeof fetch, url: string, init: RequestInit, opts?: RetryOptions): Promise<Response>;
|
|
25
|
+
|
|
26
|
+
interface JSONSchema {
|
|
27
|
+
type?: string;
|
|
28
|
+
properties?: Record<string, JSONSchema>;
|
|
29
|
+
items?: JSONSchema;
|
|
30
|
+
required?: string[];
|
|
31
|
+
description?: string;
|
|
32
|
+
enum?: unknown[];
|
|
33
|
+
[k: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
interface ToolFunctionSpec {
|
|
36
|
+
name: string;
|
|
37
|
+
description: string;
|
|
38
|
+
parameters: JSONSchema;
|
|
39
|
+
}
|
|
40
|
+
interface ToolSpec {
|
|
41
|
+
type: "function";
|
|
42
|
+
function: ToolFunctionSpec;
|
|
43
|
+
}
|
|
44
|
+
interface ToolCall {
|
|
45
|
+
id?: string;
|
|
46
|
+
type?: "function";
|
|
47
|
+
function: {
|
|
48
|
+
name: string;
|
|
49
|
+
arguments: string;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
type Role = "system" | "user" | "assistant" | "tool";
|
|
53
|
+
interface ChatMessage {
|
|
54
|
+
role: Role;
|
|
55
|
+
content?: string | null;
|
|
56
|
+
name?: string;
|
|
57
|
+
tool_call_id?: string;
|
|
58
|
+
tool_calls?: ToolCall[];
|
|
59
|
+
/** Must round-trip in tool-loop continuations — thinking mode 400s without it. */
|
|
60
|
+
reasoning_content?: string | null;
|
|
61
|
+
}
|
|
62
|
+
interface RawUsage {
|
|
63
|
+
prompt_tokens?: number;
|
|
64
|
+
completion_tokens?: number;
|
|
65
|
+
total_tokens?: number;
|
|
66
|
+
prompt_cache_hit_tokens?: number;
|
|
67
|
+
prompt_cache_miss_tokens?: number;
|
|
68
|
+
}
|
|
69
|
+
interface ChatRequestOptions {
|
|
70
|
+
model: string;
|
|
71
|
+
messages: ChatMessage[];
|
|
72
|
+
tools?: ToolSpec[];
|
|
73
|
+
temperature?: number;
|
|
74
|
+
maxTokens?: number;
|
|
75
|
+
stream?: boolean;
|
|
76
|
+
signal?: AbortSignal;
|
|
77
|
+
/** DeepSeek response_format — use { type: "json_object" } to force valid JSON. */
|
|
78
|
+
responseFormat?: {
|
|
79
|
+
type: "json_object" | "text";
|
|
80
|
+
};
|
|
81
|
+
thinking?: "enabled" | "disabled";
|
|
82
|
+
reasoningEffort?: "high" | "max";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Multi-provider model system. Provider profiles capture auth, URL, model list, pricing, and thinking-mode semantics. */
|
|
86
|
+
interface ProviderAuth {
|
|
87
|
+
/** Env var for the API key (e.g. "DEEPSEEK_API_KEY"). */
|
|
88
|
+
envKey: string;
|
|
89
|
+
/** Auth header name. Defaults to "Authorization". */
|
|
90
|
+
header?: string;
|
|
91
|
+
/** Prefix prepended to the key in the auth header (e.g. "Bearer "). Empty string for raw keys. */
|
|
92
|
+
prefix?: string;
|
|
93
|
+
}
|
|
94
|
+
/** USD per 1M tokens. */
|
|
95
|
+
interface ModelPricing {
|
|
96
|
+
inputCacheHit: number;
|
|
97
|
+
inputCacheMiss: number;
|
|
98
|
+
output: number;
|
|
99
|
+
}
|
|
100
|
+
interface ProviderPricing {
|
|
101
|
+
/** Per-model pricing. Falls back to `defaults` for unlisted models. */
|
|
102
|
+
models?: Record<string, ModelPricing>;
|
|
103
|
+
/** Pricing when a model id isn't in `models`. */
|
|
104
|
+
defaults: ModelPricing;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* How the provider exposes reasoning/thinking mode.
|
|
108
|
+
* - "extra_body" — DeepSeek-style: extra_body.thinking.type
|
|
109
|
+
* - "reasoning_effort" — OpenAI-style: top-level reasoning_effort
|
|
110
|
+
* - "include" — Anthropic-style: thinking.type + budget_tokens
|
|
111
|
+
* - "none" — Provider doesn't support thinking mode.
|
|
112
|
+
*/
|
|
113
|
+
type ThinkingTransport = "extra_body" | "reasoning_effort" | "include" | "none";
|
|
114
|
+
interface ThinkingConfig {
|
|
115
|
+
transport: ThinkingTransport;
|
|
116
|
+
/** Model ids that support thinking. Undefined → allow all. */
|
|
117
|
+
thinkingModels?: string[];
|
|
118
|
+
/** Whether thinking is on by default for supported models. */
|
|
119
|
+
defaultEnabled?: boolean;
|
|
120
|
+
}
|
|
121
|
+
interface ProviderEndpoints {
|
|
122
|
+
/** Base URL for chat completions. Client appends /chat/completions. */
|
|
123
|
+
chat: string;
|
|
124
|
+
/** Optional balance-check endpoint (null = not available). */
|
|
125
|
+
balance?: string | null;
|
|
126
|
+
/** Optional model-list endpoint (null = not available). */
|
|
127
|
+
models?: string | null;
|
|
128
|
+
}
|
|
129
|
+
interface ProviderProfile {
|
|
130
|
+
/** Machine-readable id. */
|
|
131
|
+
id: string;
|
|
132
|
+
/** Human-readable label. */
|
|
133
|
+
label: string;
|
|
134
|
+
auth: ProviderAuth;
|
|
135
|
+
endpoints: ProviderEndpoints;
|
|
136
|
+
/** Static model list shown when the API doesn't return one. */
|
|
137
|
+
models: string[];
|
|
138
|
+
/** Default model — used when the user hasn't picked one. */
|
|
139
|
+
defaultModel: string;
|
|
140
|
+
/** Pro-tier model for auto-escalation. Defaults to defaultModel. */
|
|
141
|
+
escalationModel?: string;
|
|
142
|
+
pricing: ProviderPricing;
|
|
143
|
+
thinking: ThinkingConfig;
|
|
144
|
+
/** Per-model context window size (tokens). Falls back to defaultContextTokens. */
|
|
145
|
+
contextTokens?: Record<string, number>;
|
|
146
|
+
defaultContextTokens: number;
|
|
147
|
+
/** Per-model output token caps. */
|
|
148
|
+
maxOutputTokens?: Record<string, number>;
|
|
149
|
+
/** Extra HTTP headers for every request (e.g. OpenRouter referer). */
|
|
150
|
+
extraHeaders?: Record<string, string>;
|
|
151
|
+
/** Extra JSON fields merged into every request body. */
|
|
152
|
+
extraBody?: Record<string, unknown>;
|
|
153
|
+
}
|
|
154
|
+
/** Minimal interface the loop expects from a model client. */
|
|
155
|
+
interface ModelClient {
|
|
156
|
+
readonly apiKey: string;
|
|
157
|
+
readonly baseUrl: string;
|
|
158
|
+
readonly timeoutMs: number;
|
|
159
|
+
chat(opts: ChatRequestOptions): Promise<ChatResponse>;
|
|
160
|
+
stream(opts: ChatRequestOptions): AsyncGenerator<StreamChunk>;
|
|
161
|
+
getBalance(opts?: {
|
|
162
|
+
signal?: AbortSignal;
|
|
163
|
+
}): Promise<UserBalance | null>;
|
|
164
|
+
listModels(opts?: {
|
|
165
|
+
signal?: AbortSignal;
|
|
166
|
+
}): Promise<ModelList | null>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
declare class Usage {
|
|
170
|
+
promptTokens: number;
|
|
171
|
+
completionTokens: number;
|
|
172
|
+
totalTokens: number;
|
|
173
|
+
promptCacheHitTokens: number;
|
|
174
|
+
promptCacheMissTokens: number;
|
|
175
|
+
constructor(promptTokens?: number, completionTokens?: number, totalTokens?: number, promptCacheHitTokens?: number, promptCacheMissTokens?: number);
|
|
176
|
+
get cacheHitRatio(): number;
|
|
177
|
+
static fromApi(raw: RawUsage | undefined | null): Usage;
|
|
178
|
+
}
|
|
179
|
+
interface ChatResponse {
|
|
180
|
+
content: string;
|
|
181
|
+
reasoningContent: string | null;
|
|
182
|
+
toolCalls: ToolCall[];
|
|
183
|
+
usage: Usage;
|
|
184
|
+
raw: unknown;
|
|
185
|
+
}
|
|
186
|
+
interface StreamChunk {
|
|
187
|
+
contentDelta?: string;
|
|
188
|
+
reasoningDelta?: string;
|
|
189
|
+
toolCallDelta?: {
|
|
190
|
+
index: number;
|
|
191
|
+
id?: string;
|
|
192
|
+
name?: string;
|
|
193
|
+
argumentsDelta?: string;
|
|
194
|
+
};
|
|
195
|
+
usage?: Usage;
|
|
196
|
+
finishReason?: string;
|
|
197
|
+
raw: any;
|
|
198
|
+
}
|
|
199
|
+
interface BalanceInfo {
|
|
200
|
+
currency: string;
|
|
201
|
+
total_balance: string;
|
|
202
|
+
granted_balance?: string;
|
|
203
|
+
topped_up_balance?: string;
|
|
204
|
+
}
|
|
205
|
+
interface UserBalance {
|
|
206
|
+
is_available: boolean;
|
|
207
|
+
balance_infos: BalanceInfo[];
|
|
208
|
+
}
|
|
209
|
+
interface ModelInfo {
|
|
210
|
+
id: string;
|
|
211
|
+
object: "model";
|
|
212
|
+
owned_by: string;
|
|
213
|
+
}
|
|
214
|
+
interface ModelList {
|
|
215
|
+
object: "list";
|
|
216
|
+
data: ModelInfo[];
|
|
217
|
+
}
|
|
218
|
+
interface OpenAICompatClientOptions {
|
|
219
|
+
/**
|
|
220
|
+
* API key. Falls back to the provider's env var, then to
|
|
221
|
+
* the legacy `DEEPSEEK_API_KEY` env var for backwards compat.
|
|
222
|
+
*/
|
|
223
|
+
apiKey?: string;
|
|
224
|
+
/**
|
|
225
|
+
* Base URL override. Falls back to the provider's endpoint,
|
|
226
|
+
* then to the legacy `DEEPSEEK_BASE_URL` env var.
|
|
227
|
+
*/
|
|
228
|
+
baseUrl?: string;
|
|
229
|
+
/** Request timeout in ms. Default: 660_000 (11 min). */
|
|
230
|
+
timeoutMs?: number;
|
|
231
|
+
/** Custom fetch implementation. */
|
|
232
|
+
fetch?: typeof fetch;
|
|
233
|
+
/** Retry configuration. `{ maxAttempts: 1 }` disables retries. */
|
|
234
|
+
retry?: RetryOptions;
|
|
235
|
+
/**
|
|
236
|
+
* Provider profile. If not given, we resolve it from the
|
|
237
|
+
* default model or fall back to the DeepSeek profile.
|
|
238
|
+
*/
|
|
239
|
+
provider?: ProviderProfile;
|
|
240
|
+
/**
|
|
241
|
+
* If you know the model id upfront, we'll auto-resolve the
|
|
242
|
+
* provider. Only used when `provider` is not passed.
|
|
243
|
+
*/
|
|
244
|
+
model?: string;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Generic OpenAI-compatible chat client.
|
|
248
|
+
*
|
|
249
|
+
* Works with any provider that speaks `/chat/completions` with
|
|
250
|
+
* Bearer-auth and SSE streaming (DeepSeek, SiliconFlow, ModelScope,
|
|
251
|
+
* Zhipu, Moonshot, Qwen, OpenAI, Groq, OpenRouter, etc.).
|
|
252
|
+
*
|
|
253
|
+
* For Anthropic's non-OpenAI-compatible Messages API, a separate
|
|
254
|
+
* adapter will be needed.
|
|
255
|
+
*/
|
|
256
|
+
declare class OpenAICompatClient {
|
|
257
|
+
readonly apiKey: string;
|
|
258
|
+
readonly baseUrl: string;
|
|
259
|
+
readonly timeoutMs: number;
|
|
260
|
+
readonly retry: RetryOptions;
|
|
261
|
+
readonly provider: ProviderProfile;
|
|
262
|
+
private readonly _fetch;
|
|
263
|
+
constructor(opts?: OpenAICompatClientOptions);
|
|
264
|
+
private authHeader;
|
|
265
|
+
private buildPayload;
|
|
266
|
+
/** Returns null on failure so callers can degrade — session must keep working without balance UI. */
|
|
267
|
+
getBalance(opts?: {
|
|
268
|
+
signal?: AbortSignal;
|
|
269
|
+
}): Promise<UserBalance | null>;
|
|
270
|
+
/** Returns null on failure — callers fall back to the provider's static model list. */
|
|
271
|
+
listModels(opts?: {
|
|
272
|
+
signal?: AbortSignal;
|
|
273
|
+
}): Promise<ModelList | null>;
|
|
274
|
+
chat(opts: ChatRequestOptions): Promise<ChatResponse>;
|
|
275
|
+
stream(opts: ChatRequestOptions): AsyncGenerator<StreamChunk>;
|
|
276
|
+
}
|
|
277
|
+
interface DeepSeekClientOptions {
|
|
278
|
+
apiKey?: string;
|
|
279
|
+
baseUrl?: string;
|
|
280
|
+
timeoutMs?: number;
|
|
281
|
+
fetch?: typeof fetch;
|
|
282
|
+
retry?: RetryOptions;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* @deprecated Use `OpenAICompatClient` with a provider profile instead.
|
|
286
|
+
* Kept for backwards compatibility — all existing code that constructs
|
|
287
|
+
* `new DeepSeekClient()` continues to work.
|
|
288
|
+
*/
|
|
289
|
+
declare class DeepSeekClient extends OpenAICompatClient {
|
|
290
|
+
constructor(opts?: DeepSeekClientOptions);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Provider registry — maps model ids to provider profiles. Call registerProvider() to add custom providers. */
|
|
294
|
+
|
|
295
|
+
/** Register a custom or third-party provider at runtime. */
|
|
296
|
+
declare function registerProvider(profile: ProviderProfile): void;
|
|
297
|
+
/** Unregister a provider by id. */
|
|
298
|
+
declare function unregisterProvider(id: string): boolean;
|
|
299
|
+
/** Get a provider profile by id. */
|
|
300
|
+
declare function getProvider(id: string): ProviderProfile | undefined;
|
|
301
|
+
/** List all registered provider profiles. */
|
|
302
|
+
declare function listProviders(): ProviderProfile[];
|
|
303
|
+
/**
|
|
304
|
+
* Resolve which provider owns a given model id.
|
|
305
|
+
* Returns undefined for unknown models — callers should fall back
|
|
306
|
+
* to the default provider ("deepseek").
|
|
307
|
+
*/
|
|
308
|
+
declare function resolveProvider(modelId: string): ProviderProfile | undefined;
|
|
309
|
+
/** The default provider (DeepSeek), used when no provider can be resolved. */
|
|
310
|
+
declare function defaultProvider(): ProviderProfile;
|
|
311
|
+
/**
|
|
312
|
+
* Pricing for a given model. Falls back: model-specific → provider
|
|
313
|
+
* defaults → all-zeros for completely unknown models.
|
|
314
|
+
*/
|
|
315
|
+
declare function pricingFor(modelId: string): {
|
|
316
|
+
inputCacheHit: number;
|
|
317
|
+
inputCacheMiss: number;
|
|
318
|
+
output: number;
|
|
319
|
+
};
|
|
320
|
+
/** Context window size for a given model, resolved through the registry. */
|
|
321
|
+
declare function contextTokensFor(modelId: string): number;
|
|
322
|
+
|
|
323
|
+
/** Generic pause gate — bridges tool functions and the App's modals via Promises. */
|
|
324
|
+
type ConfirmationChoice = {
|
|
325
|
+
type: "deny";
|
|
326
|
+
denyContext?: string;
|
|
327
|
+
} | {
|
|
328
|
+
type: "run_once";
|
|
329
|
+
} | {
|
|
330
|
+
type: "always_allow";
|
|
331
|
+
prefix: string;
|
|
332
|
+
};
|
|
333
|
+
type PlanVerdict = {
|
|
334
|
+
type: "approve";
|
|
335
|
+
feedback?: string;
|
|
336
|
+
} | {
|
|
337
|
+
type: "refine";
|
|
338
|
+
feedback?: string;
|
|
339
|
+
} | {
|
|
340
|
+
type: "cancel";
|
|
341
|
+
feedback?: string;
|
|
342
|
+
};
|
|
343
|
+
type CheckpointVerdict = {
|
|
344
|
+
type: "continue";
|
|
345
|
+
} | {
|
|
346
|
+
type: "revise";
|
|
347
|
+
feedback?: string;
|
|
348
|
+
} | {
|
|
349
|
+
type: "stop";
|
|
350
|
+
};
|
|
351
|
+
type RevisionVerdict = {
|
|
352
|
+
type: "accepted";
|
|
353
|
+
} | {
|
|
354
|
+
type: "rejected";
|
|
355
|
+
} | {
|
|
356
|
+
type: "cancelled";
|
|
357
|
+
};
|
|
358
|
+
type ChoiceVerdict = {
|
|
359
|
+
type: "pick";
|
|
360
|
+
optionId: string;
|
|
361
|
+
} | {
|
|
362
|
+
type: "text";
|
|
363
|
+
text: string;
|
|
364
|
+
} | {
|
|
365
|
+
type: "cancel";
|
|
366
|
+
};
|
|
367
|
+
type ToolConfirmationAuditEvent = {
|
|
368
|
+
type: "tool.confirm.allow";
|
|
369
|
+
kind: "run_command" | "run_background";
|
|
370
|
+
payload: {
|
|
371
|
+
command: string;
|
|
372
|
+
};
|
|
373
|
+
} | {
|
|
374
|
+
type: "tool.confirm.deny";
|
|
375
|
+
kind: "run_command" | "run_background";
|
|
376
|
+
payload: {
|
|
377
|
+
command: string;
|
|
378
|
+
};
|
|
379
|
+
denyContext?: string;
|
|
380
|
+
} | {
|
|
381
|
+
type: "tool.confirm.always_allow";
|
|
382
|
+
kind: "run_command" | "run_background";
|
|
383
|
+
payload: {
|
|
384
|
+
command: string;
|
|
385
|
+
};
|
|
386
|
+
prefix: string;
|
|
387
|
+
};
|
|
388
|
+
interface PauseResponseMap {
|
|
389
|
+
run_command: ConfirmationChoice;
|
|
390
|
+
run_background: ConfirmationChoice;
|
|
391
|
+
path_access: ConfirmationChoice;
|
|
392
|
+
plan_proposed: PlanVerdict;
|
|
393
|
+
plan_checkpoint: CheckpointVerdict;
|
|
394
|
+
plan_revision: RevisionVerdict;
|
|
395
|
+
choice: ChoiceVerdict;
|
|
396
|
+
}
|
|
397
|
+
type PauseKind = keyof PauseResponseMap;
|
|
398
|
+
interface PausePayloadMap {
|
|
399
|
+
run_command: {
|
|
400
|
+
command: string;
|
|
401
|
+
cwd?: string;
|
|
402
|
+
timeoutSec?: number;
|
|
403
|
+
};
|
|
404
|
+
run_background: {
|
|
405
|
+
command: string;
|
|
406
|
+
cwd?: string;
|
|
407
|
+
waitSec?: number;
|
|
408
|
+
};
|
|
409
|
+
path_access: {
|
|
410
|
+
/** Absolute path the tool wants to touch. */
|
|
411
|
+
path: string;
|
|
412
|
+
/** Why we're being asked — read leaks content, write mutates files. */
|
|
413
|
+
intent: "read" | "write";
|
|
414
|
+
/** The filesystem tool calling in — surfaced so users can see what's about to happen. */
|
|
415
|
+
toolName: string;
|
|
416
|
+
/** Sandbox root the path is escaping — surfaced for context. */
|
|
417
|
+
sandboxRoot: string;
|
|
418
|
+
/** Directory prefix that would be persisted if the user picks "always allow". */
|
|
419
|
+
allowPrefix: string;
|
|
420
|
+
};
|
|
421
|
+
plan_proposed: {
|
|
422
|
+
plan: string;
|
|
423
|
+
steps?: unknown[];
|
|
424
|
+
summary?: string;
|
|
425
|
+
};
|
|
426
|
+
plan_checkpoint: {
|
|
427
|
+
stepId: string;
|
|
428
|
+
title?: string;
|
|
429
|
+
result: string;
|
|
430
|
+
notes?: string;
|
|
431
|
+
};
|
|
432
|
+
plan_revision: {
|
|
433
|
+
reason: string;
|
|
434
|
+
remainingSteps: unknown[];
|
|
435
|
+
summary?: string;
|
|
436
|
+
};
|
|
437
|
+
choice: {
|
|
438
|
+
question: string;
|
|
439
|
+
options: unknown[];
|
|
440
|
+
allowCustom: boolean;
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
type PauseRequest = {
|
|
444
|
+
id: number;
|
|
445
|
+
kind: PauseKind;
|
|
446
|
+
payload: unknown;
|
|
447
|
+
};
|
|
448
|
+
type GateListener = (request: PauseRequest) => void;
|
|
449
|
+
type AuditListener = (event: ToolConfirmationAuditEvent) => void;
|
|
450
|
+
/** Named options for PauseGate.ask() — makes it obvious which field is kind vs payload. */
|
|
451
|
+
interface PauseAskOpts<K extends PauseKind = PauseKind> {
|
|
452
|
+
kind: K;
|
|
453
|
+
payload: PausePayloadMap[K];
|
|
454
|
+
}
|
|
455
|
+
declare class PauseGate {
|
|
456
|
+
private _nextId;
|
|
457
|
+
private _pending;
|
|
458
|
+
private _listeners;
|
|
459
|
+
private _auditListener;
|
|
460
|
+
/** Block until the user responds. Takes a named options object so the
|
|
461
|
+
* kind and payload fields don't get confused at the call site. */
|
|
462
|
+
ask<K extends PauseKind>(opts: PauseAskOpts<K>): Promise<PauseResponseMap[K]>;
|
|
463
|
+
/** Resolve a pending request. Called by the App's modal callback. */
|
|
464
|
+
resolve(id: number, data: unknown): void;
|
|
465
|
+
/** Safe-cancel every outstanding request — frees stranded tool fns on Esc / /new. */
|
|
466
|
+
cancelAll(): void;
|
|
467
|
+
/** Cancel one pending request — used by multi-tab hosts that need per-scope abort. */
|
|
468
|
+
cancel(id: number): boolean;
|
|
469
|
+
setAuditListener(fn: AuditListener | null): void;
|
|
470
|
+
/** Subscribe to new pause requests. Returns an unsubscribe function. */
|
|
471
|
+
on(fn: GateListener): () => void;
|
|
472
|
+
/** Current pending request, if any (polling fallback). */
|
|
473
|
+
get current(): PauseRequest | null;
|
|
474
|
+
private emitAuditEvent;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/** Shell-command hooks; project scope first, then global. Exit 0=pass, 2=block on Pre*, other=warn. */
|
|
478
|
+
type HookEvent = "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "Stop";
|
|
479
|
+
/** All four events as a const array — drives slash listing + validation. */
|
|
480
|
+
declare const HOOK_EVENTS: readonly HookEvent[];
|
|
481
|
+
type HookScope = "project" | "global";
|
|
482
|
+
interface HookConfig {
|
|
483
|
+
/** Anchored regex; `"*"` / omitted = every tool. Pre/PostToolUse only. */
|
|
484
|
+
match?: string;
|
|
485
|
+
/** Shell command to run. Spawned through the platform shell. */
|
|
486
|
+
command: string;
|
|
487
|
+
/** Optional human description — surfaced in `/hooks`. */
|
|
488
|
+
description?: string;
|
|
489
|
+
/** Per-hook timeout override in ms. */
|
|
490
|
+
timeout?: number;
|
|
491
|
+
/** Defaults: project scope → project root; global scope → process.cwd(). */
|
|
492
|
+
cwd?: string;
|
|
493
|
+
}
|
|
494
|
+
/** Shape of `<scope>/.luckerr/settings.json` — only `hooks` for now. */
|
|
495
|
+
interface HookSettings {
|
|
496
|
+
hooks?: Partial<Record<HookEvent, HookConfig[]>>;
|
|
497
|
+
}
|
|
498
|
+
/** A loaded hook with its origin scope baked in (used for ordering and `/hooks`). */
|
|
499
|
+
interface ResolvedHook extends HookConfig {
|
|
500
|
+
event: HookEvent;
|
|
501
|
+
scope: HookScope;
|
|
502
|
+
/** Absolute path to the settings.json the hook came from. */
|
|
503
|
+
source: string;
|
|
504
|
+
}
|
|
505
|
+
/** Outcome of a single hook invocation. */
|
|
506
|
+
interface HookOutcome {
|
|
507
|
+
/** Which hook fired. */
|
|
508
|
+
hook: ResolvedHook;
|
|
509
|
+
/** pass=exit 0; block=exit 2 on blocking event; warn=other non-zero; timeout=killed; error=spawn failed. */
|
|
510
|
+
decision: "pass" | "block" | "warn" | "timeout" | "error";
|
|
511
|
+
exitCode: number | null;
|
|
512
|
+
/** Captured stdout (trimmed). May be empty. */
|
|
513
|
+
stdout: string;
|
|
514
|
+
/** Captured stderr (trimmed). The block / warn message comes from here. */
|
|
515
|
+
stderr: string;
|
|
516
|
+
durationMs: number;
|
|
517
|
+
/** Output crossed the per-stream byte cap; surfaced so user knows we kept less than the script wrote. */
|
|
518
|
+
truncated?: boolean;
|
|
519
|
+
}
|
|
520
|
+
/** Aggregate report for `runHooks`. */
|
|
521
|
+
interface HookReport {
|
|
522
|
+
event: HookEvent;
|
|
523
|
+
outcomes: HookOutcome[];
|
|
524
|
+
/** True iff at least one outcome was a `block` — only meaningful for blocking events. */
|
|
525
|
+
blocked: boolean;
|
|
526
|
+
}
|
|
527
|
+
declare const HOOK_SETTINGS_FILENAME = "settings.json";
|
|
528
|
+
declare const HOOK_SETTINGS_DIRNAME = ".luckerr";
|
|
529
|
+
/** Where the global settings.json lives. Equivalent to `~/.luckerr/settings.json`. */
|
|
530
|
+
declare function globalSettingsPath(homeDirOverride?: string): string;
|
|
531
|
+
/** Where the project settings.json lives for a given root. */
|
|
532
|
+
declare function projectSettingsPath(projectRoot: string): string;
|
|
533
|
+
/** Project hooks fire before global; within a scope, array order. */
|
|
534
|
+
interface LoadHookSettingsOptions {
|
|
535
|
+
/** Absolute project root, if any. Without it, only global hooks load. */
|
|
536
|
+
projectRoot?: string;
|
|
537
|
+
/** Override `~` for tests. */
|
|
538
|
+
homeDir?: string;
|
|
539
|
+
}
|
|
540
|
+
declare function loadHooks(opts?: LoadHookSettingsOptions): ResolvedHook[];
|
|
541
|
+
/** Match field is an ANCHORED regex — `"file"` won't trigger on `read_file`; use `".*file"`. */
|
|
542
|
+
declare function matchesTool(hook: ResolvedHook, toolName: string): boolean;
|
|
543
|
+
/** Payload envelope passed to hook stdin. */
|
|
544
|
+
interface HookPayload {
|
|
545
|
+
event: HookEvent;
|
|
546
|
+
cwd: string;
|
|
547
|
+
toolName?: string;
|
|
548
|
+
toolArgs?: unknown;
|
|
549
|
+
toolResult?: string;
|
|
550
|
+
prompt?: string;
|
|
551
|
+
lastAssistantText?: string;
|
|
552
|
+
turn?: number;
|
|
553
|
+
}
|
|
554
|
+
/** Test seam — same shape as Node's spawn but returns a Promise of the raw outcome bits. */
|
|
555
|
+
interface HookSpawnInput {
|
|
556
|
+
command: string;
|
|
557
|
+
cwd: string;
|
|
558
|
+
stdin: string;
|
|
559
|
+
timeoutMs: number;
|
|
560
|
+
}
|
|
561
|
+
interface HookSpawnResult {
|
|
562
|
+
exitCode: number | null;
|
|
563
|
+
stdout: string;
|
|
564
|
+
stderr: string;
|
|
565
|
+
timedOut: boolean;
|
|
566
|
+
/** True iff spawn() itself failed (ENOENT, EACCES, …). */
|
|
567
|
+
spawnError?: Error;
|
|
568
|
+
/** Output capped at byte limit — hook ran to completion but consumers see clipped view. */
|
|
569
|
+
truncated?: boolean;
|
|
570
|
+
}
|
|
571
|
+
type HookSpawner = (input: HookSpawnInput) => Promise<HookSpawnResult>;
|
|
572
|
+
declare function formatHookOutcomeMessage(outcome: HookOutcome): string;
|
|
573
|
+
declare function decideOutcome(event: HookEvent, raw: HookSpawnResult): "pass" | "block" | "warn" | "timeout" | "error";
|
|
574
|
+
interface RunHooksOptions {
|
|
575
|
+
payload: HookPayload;
|
|
576
|
+
hooks: ResolvedHook[];
|
|
577
|
+
/** Test seam — defaults to a real `spawn`. */
|
|
578
|
+
spawner?: HookSpawner;
|
|
579
|
+
}
|
|
580
|
+
/** Stops at first `block` so a gating hook can prevent later hooks running against a phantom success. */
|
|
581
|
+
declare function runHooks(opts: RunHooksOptions): Promise<HookReport>;
|
|
582
|
+
|
|
583
|
+
/** Authoritative running-id set — cards derive `running` from `has(id)` instead of trusting end-event delivery. Loop adds on dispatch entry, deletes in `finally` so every exit path cleans up. */
|
|
584
|
+
type InflightSubscriber = () => void;
|
|
585
|
+
declare class InflightSet {
|
|
586
|
+
private readonly _set;
|
|
587
|
+
private readonly _listeners;
|
|
588
|
+
add(id: string): void;
|
|
589
|
+
delete(id: string): void;
|
|
590
|
+
has(id: string): boolean;
|
|
591
|
+
/** Snapshot for diagnostics / tests; live view, do not mutate. */
|
|
592
|
+
get size(): number;
|
|
593
|
+
/** Subscribe to add/delete; returns the unsubscribe function. */
|
|
594
|
+
subscribe(fn: InflightSubscriber): () => void;
|
|
595
|
+
/** Drop everything — only use at session reset. Notifies once. */
|
|
596
|
+
clear(): void;
|
|
597
|
+
private _notify;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
interface ProviderProbeResult {
|
|
601
|
+
reachable: boolean;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Format an API error for display to the user.
|
|
605
|
+
*
|
|
606
|
+
* Error messages from the client now use `ProviderLabel STATUS: body`
|
|
607
|
+
* format (e.g. "DeepSeek 401: ..." or "SiliconFlow 503: ...").
|
|
608
|
+
* `ChatRequestOptions` doesn't carry a provider label so we match
|
|
609
|
+
* the general `"<Label> NNN:"` pattern.
|
|
610
|
+
*/
|
|
611
|
+
declare function formatLoopError(err: Error, probe?: ProviderProbeResult): string;
|
|
612
|
+
|
|
613
|
+
/** Drops both unpaired assistant.tool_calls and stray tool messages — DeepSeek 400s on either. */
|
|
614
|
+
declare function fixToolCallPairing(messages: ChatMessage[]): {
|
|
615
|
+
messages: ChatMessage[];
|
|
616
|
+
droppedAssistantCalls: number;
|
|
617
|
+
droppedStrayTools: number;
|
|
618
|
+
};
|
|
619
|
+
declare function healLoadedMessages(messages: ChatMessage[], maxChars: number): {
|
|
620
|
+
messages: ChatMessage[];
|
|
621
|
+
healedCount: number;
|
|
622
|
+
healedFrom: number;
|
|
623
|
+
};
|
|
624
|
+
/** Token-cap variant — char cap would let CJK slip past at 2× the intended token cost. */
|
|
625
|
+
declare function healLoadedMessagesByTokens(messages: ChatMessage[], maxTokens: number): {
|
|
626
|
+
messages: ChatMessage[];
|
|
627
|
+
healedCount: number;
|
|
628
|
+
tokensSaved: number;
|
|
629
|
+
charsSaved: number;
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
/** Thinking / reasoning mode helpers — provider-aware. Each provider exposes reasoning differently; this centralises detection logic. */
|
|
633
|
+
|
|
634
|
+
/** Strip hallucinated tool-call envelopes — `tools: undefined` doesn't always force prose. */
|
|
635
|
+
declare function stripHallucinatedToolMarkup(s: string): string;
|
|
636
|
+
|
|
637
|
+
/** Mutating calls clear prior read-only entries so a post-edit re-read isn't flagged as repeat. */
|
|
638
|
+
type IsMutating = (call: ToolCall) => boolean;
|
|
639
|
+
type IsStormExempt = (call: ToolCall) => boolean;
|
|
640
|
+
/** Tracks (name, args) repeats; mutating calls clear prior read-only entries while still counting amongst themselves. */
|
|
641
|
+
declare class StormBreaker {
|
|
642
|
+
private readonly windowSize;
|
|
643
|
+
private readonly threshold;
|
|
644
|
+
private readonly isMutating;
|
|
645
|
+
private readonly isStormExempt;
|
|
646
|
+
private readonly recent;
|
|
647
|
+
constructor(windowSize?: number, threshold?: number, isMutating?: IsMutating, isStormExempt?: IsStormExempt);
|
|
648
|
+
inspect(call: ToolCall): {
|
|
649
|
+
suppress: boolean;
|
|
650
|
+
reason?: string;
|
|
651
|
+
};
|
|
652
|
+
reset(): void;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/** DeepSeek drops args on schemas >2 levels deep or >10 leaves; flatten to dot-paths and re-nest after dispatch. */
|
|
656
|
+
|
|
657
|
+
interface FlattenDecision {
|
|
658
|
+
shouldFlatten: boolean;
|
|
659
|
+
leafCount: number;
|
|
660
|
+
maxDepth: number;
|
|
661
|
+
}
|
|
662
|
+
declare function analyzeSchema(schema: JSONSchema | undefined): FlattenDecision;
|
|
663
|
+
declare function flattenSchema(schema: JSONSchema): JSONSchema;
|
|
664
|
+
declare function nestArguments(flatArgs: Record<string, unknown>): Record<string, unknown>;
|
|
665
|
+
|
|
666
|
+
/** Local-only repair (balance braces, close strings, fill nulls); continuation calls belong to the loop, which owns budgets. */
|
|
667
|
+
interface TruncationRepairResult {
|
|
668
|
+
repaired: string;
|
|
669
|
+
changed: boolean;
|
|
670
|
+
notes: string[];
|
|
671
|
+
}
|
|
672
|
+
declare function repairTruncatedJson(input: string): TruncationRepairResult;
|
|
673
|
+
|
|
674
|
+
/** R1 sometimes emits tool-call JSON inside reasoning_content and forgets `tool_calls`; recover those calls. */
|
|
675
|
+
|
|
676
|
+
interface ScavengeOptions {
|
|
677
|
+
/** Names of tools the model may legitimately call. Other names are ignored. */
|
|
678
|
+
allowedNames: ReadonlySet<string>;
|
|
679
|
+
/** Maximum number of calls to scavenge per pass (defence against runaway). */
|
|
680
|
+
maxCalls?: number;
|
|
681
|
+
}
|
|
682
|
+
interface ScavengeResult {
|
|
683
|
+
calls: ToolCall[];
|
|
684
|
+
notes: string[];
|
|
685
|
+
}
|
|
686
|
+
declare function scavengeToolCalls(reasoningContent: string | null | undefined, opts: ScavengeOptions): ScavengeResult;
|
|
687
|
+
|
|
688
|
+
/** Pass order: scavenge → truncation → storm. Schema flatten runs at loop construction, not per-turn. */
|
|
689
|
+
|
|
690
|
+
interface RepairReport {
|
|
691
|
+
scavenged: number;
|
|
692
|
+
truncationsFixed: number;
|
|
693
|
+
stormsBroken: number;
|
|
694
|
+
notes: string[];
|
|
695
|
+
}
|
|
696
|
+
interface ToolCallRepairOptions {
|
|
697
|
+
allowedToolNames: ReadonlySet<string>;
|
|
698
|
+
stormWindow?: number;
|
|
699
|
+
stormThreshold?: number;
|
|
700
|
+
maxScavenge?: number;
|
|
701
|
+
/** Mutating calls clear the storm window so a post-edit verify-read isn't seen as a repeat. */
|
|
702
|
+
isMutating?: IsMutating;
|
|
703
|
+
/** Cheap state-inspection calls that should never trip repeat-loop suppression. */
|
|
704
|
+
isStormExempt?: IsStormExempt;
|
|
705
|
+
}
|
|
706
|
+
declare class ToolCallRepair {
|
|
707
|
+
private readonly storm;
|
|
708
|
+
private readonly opts;
|
|
709
|
+
constructor(opts: ToolCallRepairOptions);
|
|
710
|
+
/** Called at start of every user turn — fresh intent shouldn't inherit old repetition state. */
|
|
711
|
+
resetStorm(): void;
|
|
712
|
+
process(declaredCalls: ToolCall[], reasoningContent: string | null, content?: string | null): {
|
|
713
|
+
calls: ToolCall[];
|
|
714
|
+
report: RepairReport;
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
declare function costUsd(model: string, usage: Usage): number;
|
|
719
|
+
/** Input-side cost only (prompt, cache hit + miss). Used for the panel breakdown. */
|
|
720
|
+
declare function inputCostUsd(model: string, usage: Usage): number;
|
|
721
|
+
/** Output-side cost only (completion tokens). Used for the panel breakdown. */
|
|
722
|
+
declare function outputCostUsd(model: string, usage: Usage): number;
|
|
723
|
+
declare function claudeEquivalentCost(usage: Usage): number;
|
|
724
|
+
interface TurnStats {
|
|
725
|
+
turn: number;
|
|
726
|
+
model: string;
|
|
727
|
+
usage: Usage;
|
|
728
|
+
cost: number;
|
|
729
|
+
cacheHitRatio: number;
|
|
730
|
+
}
|
|
731
|
+
interface SessionSummary {
|
|
732
|
+
turns: number;
|
|
733
|
+
totalCostUsd: number;
|
|
734
|
+
totalInputCostUsd: number;
|
|
735
|
+
/** Output-side (completion) cost aggregated across the session. */
|
|
736
|
+
totalOutputCostUsd: number;
|
|
737
|
+
/** @deprecated Claude reference; kept for benchmarks + replay compat, no longer surfaced in the TUI. */
|
|
738
|
+
claudeEquivalentUsd: number;
|
|
739
|
+
/** @deprecated. Same as claudeEquivalentUsd — synthetic ratio, not a real measurement. */
|
|
740
|
+
savingsVsClaudePct: number;
|
|
741
|
+
cacheHitRatio: number;
|
|
742
|
+
/** Floor estimate for next call — actual cost = this + user delta + new tool outputs. */
|
|
743
|
+
lastPromptTokens: number;
|
|
744
|
+
lastTurnCostUsd: number;
|
|
745
|
+
}
|
|
746
|
+
declare class SessionStats {
|
|
747
|
+
readonly turns: TurnStats[];
|
|
748
|
+
/** Cost from prior runs of a resumed session, restored from session meta. */
|
|
749
|
+
private _carryoverCost;
|
|
750
|
+
/** Turn count from prior runs of a resumed session. */
|
|
751
|
+
private _carryoverTurns;
|
|
752
|
+
private _carryoverCacheHit;
|
|
753
|
+
private _carryoverCacheMiss;
|
|
754
|
+
/** Last turn's promptTokens before exit — surfaced via summary() until the next live turn lands. */
|
|
755
|
+
private _carryoverLastPromptTokens;
|
|
756
|
+
/** Seed totals from a resumed session's persisted meta — only call once at construction. */
|
|
757
|
+
seedCarryover(opts: {
|
|
758
|
+
totalCostUsd?: number;
|
|
759
|
+
turnCount?: number;
|
|
760
|
+
cacheHitTokens?: number;
|
|
761
|
+
cacheMissTokens?: number;
|
|
762
|
+
lastPromptTokens?: number;
|
|
763
|
+
}): void;
|
|
764
|
+
record(turn: number, model: string, usage: Usage): TurnStats;
|
|
765
|
+
get totalCost(): number;
|
|
766
|
+
get totalClaudeEquivalent(): number;
|
|
767
|
+
get savingsVsClaude(): number;
|
|
768
|
+
get totalInputCost(): number;
|
|
769
|
+
get totalOutputCost(): number;
|
|
770
|
+
get aggregateCacheHitRatio(): number;
|
|
771
|
+
summary(): SessionSummary;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
type EventRole = "assistant_delta" | "assistant_final"
|
|
775
|
+
/** Only liveness signal during a large-args tool call (no content/reasoning bytes). */
|
|
776
|
+
| "tool_call_delta"
|
|
777
|
+
/** Pre-dispatch ping so the TUI can show a spinner during long tool awaits. */
|
|
778
|
+
| "tool_start" | "tool" | "done" | "error" | "warning"
|
|
779
|
+
/** Transient indicator for silent phases; UI clears on next primary event. */
|
|
780
|
+
| "status";
|
|
781
|
+
interface LoopEvent {
|
|
782
|
+
turn: number;
|
|
783
|
+
role: EventRole;
|
|
784
|
+
content: string;
|
|
785
|
+
reasoningDelta?: string;
|
|
786
|
+
toolName?: string;
|
|
787
|
+
/** Raw args JSON — needed by `luckerr diff` to explain why a tool was called. */
|
|
788
|
+
toolArgs?: string;
|
|
789
|
+
/** Cumulative arguments-string length for `role === "tool_call_delta"`. */
|
|
790
|
+
toolCallArgsChars?: number;
|
|
791
|
+
/** Zero-based index of the tool call this delta belongs to (multi-tool progress). */
|
|
792
|
+
toolCallIndex?: number;
|
|
793
|
+
/** Count of tool calls whose args have parsed as valid JSON (UI progress, not dispatch gate). */
|
|
794
|
+
toolCallReadyCount?: number;
|
|
795
|
+
/** Stable id for tool_start / tool pairs — also the inflight-set key. UI uses this as the card id so it can derive `running` from `loop.inflight.has(callId)` instead of trusting end-event delivery. */
|
|
796
|
+
callId?: string;
|
|
797
|
+
stats?: TurnStats;
|
|
798
|
+
repair?: RepairReport;
|
|
799
|
+
error?: string;
|
|
800
|
+
/** Display-only — code-mode applier MUST skip SEARCH/REPLACE in forced-summary text. */
|
|
801
|
+
forcedSummary?: boolean;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
interface ImmutablePrefixOptions {
|
|
805
|
+
system: string;
|
|
806
|
+
toolSpecs?: readonly ToolSpec[];
|
|
807
|
+
fewShots?: readonly ChatMessage[];
|
|
808
|
+
}
|
|
809
|
+
declare class ImmutablePrefix {
|
|
810
|
+
/** Stable across turns; rebuilt only on /new when LUCKERR.md changed on disk. */
|
|
811
|
+
system: string;
|
|
812
|
+
/** Each `addTool` costs one cache-miss turn — DeepSeek's prefix cache is keyed by full tool list. */
|
|
813
|
+
private _toolSpecs;
|
|
814
|
+
readonly fewShots: readonly ChatMessage[];
|
|
815
|
+
/** Invalidated by addTool / removeTool / replaceSystem; bypassing any of those leaves cache stale → fingerprint diverges from sent prefix. */
|
|
816
|
+
private _fingerprintCache;
|
|
817
|
+
constructor(opts: ImmutablePrefixOptions);
|
|
818
|
+
/** Replaces the system prompt; returns true iff the string actually changed. Caller must accept a cache miss on the next turn. */
|
|
819
|
+
replaceSystem(s: string): boolean;
|
|
820
|
+
get toolSpecs(): readonly ToolSpec[];
|
|
821
|
+
toMessages(): ChatMessage[];
|
|
822
|
+
tools(): ToolSpec[];
|
|
823
|
+
addTool(spec: ToolSpec): boolean;
|
|
824
|
+
/** Mirror of addTool for MCP hot-unbridge. Same cache-miss cost — prefix changes shape. */
|
|
825
|
+
removeTool(name: string): boolean;
|
|
826
|
+
get fingerprint(): string;
|
|
827
|
+
/** Dev/test only — throws on cache drift, which always means a non-`addTool` mutation slipped in. */
|
|
828
|
+
verifyFingerprint(): string;
|
|
829
|
+
private computeFingerprint;
|
|
830
|
+
}
|
|
831
|
+
declare class AppendOnlyLog {
|
|
832
|
+
private _entries;
|
|
833
|
+
append(message: ChatMessage): void;
|
|
834
|
+
extend(messages: ChatMessage[]): void;
|
|
835
|
+
/** The one append-only-breaking path — reserved for `/compact` + recovery. Use `append()` otherwise. */
|
|
836
|
+
compactInPlace(replacement: ChatMessage[]): void;
|
|
837
|
+
get entries(): readonly ChatMessage[];
|
|
838
|
+
toMessages(): ChatMessage[];
|
|
839
|
+
get length(): number;
|
|
840
|
+
}
|
|
841
|
+
declare class VolatileScratch {
|
|
842
|
+
reasoning: string | null;
|
|
843
|
+
planState: Record<string, unknown> | null;
|
|
844
|
+
notes: string[];
|
|
845
|
+
reset(): void;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
interface ToolCallContext {
|
|
849
|
+
signal?: AbortSignal;
|
|
850
|
+
/** Inject a mock PauseGate for tests. When absent, tools use the singleton. */
|
|
851
|
+
confirmationGate?: PauseGate;
|
|
852
|
+
}
|
|
853
|
+
interface ToolDefinition<A = any, R = any> {
|
|
854
|
+
name: string;
|
|
855
|
+
description?: string;
|
|
856
|
+
parameters?: JSONSchema;
|
|
857
|
+
/** Safe in plan mode — registry refuses non-readonly calls when `planMode` is on. */
|
|
858
|
+
readOnly?: boolean;
|
|
859
|
+
/** Per-args check; takes precedence over `readOnly`. e.g. `run_command` + allowlisted argv. */
|
|
860
|
+
readOnlyCheck?: (args: A) => boolean;
|
|
861
|
+
/** Safe to dispatch concurrently with other parallel-safe calls in the same turn. Default false — opt-in only. */
|
|
862
|
+
parallelSafe?: boolean;
|
|
863
|
+
/** Excluded from repeat-loop storm accounting; use only for cheap, state-inspection tools. */
|
|
864
|
+
stormExempt?: boolean;
|
|
865
|
+
fn: (args: A, ctx?: ToolCallContext) => R | Promise<R>;
|
|
866
|
+
}
|
|
867
|
+
interface ToolRegistryOptions {
|
|
868
|
+
/** Auto-flatten + re-nest at dispatch; default true. */
|
|
869
|
+
autoFlatten?: boolean;
|
|
870
|
+
}
|
|
871
|
+
type ToolCallAuditEvent = {
|
|
872
|
+
name: string;
|
|
873
|
+
args: Record<string, unknown>;
|
|
874
|
+
};
|
|
875
|
+
type ToolCallAuditListener = (event: ToolCallAuditEvent) => void;
|
|
876
|
+
/** String return short-circuits dispatch; null/undefined falls through to the tool fn. */
|
|
877
|
+
type ToolInterceptor = (name: string, args: Record<string, unknown>) => string | null | undefined | Promise<string | null | undefined>;
|
|
878
|
+
/** Final-stage post-processor — runs on every dispatch return (success and error paths) so callers can append context like a remaining-budget hint. Whatever it returns becomes the dispatch result. */
|
|
879
|
+
type ToolResultAugmenter = (name: string, args: Record<string, unknown>, result: string) => string;
|
|
880
|
+
declare class ToolRegistry {
|
|
881
|
+
private readonly _tools;
|
|
882
|
+
private readonly _autoFlatten;
|
|
883
|
+
private _planMode;
|
|
884
|
+
private _interceptor;
|
|
885
|
+
private _auditListener;
|
|
886
|
+
private _resultAugmenter;
|
|
887
|
+
/** Per-tool fingerprint of the last call that failed schema validation. Cleared by any successful validation for that tool. */
|
|
888
|
+
private readonly _lastMalformed;
|
|
889
|
+
constructor(opts?: ToolRegistryOptions);
|
|
890
|
+
/** Enable / disable plan-mode enforcement at dispatch. */
|
|
891
|
+
setPlanMode(on: boolean): void;
|
|
892
|
+
/** True when the registry is currently refusing non-readonly calls. */
|
|
893
|
+
get planMode(): boolean;
|
|
894
|
+
/** At most one interceptor active; calling twice replaces. */
|
|
895
|
+
setToolInterceptor(fn: ToolInterceptor | null): void;
|
|
896
|
+
setAuditListener(fn: ToolCallAuditListener | null): void;
|
|
897
|
+
/** Final-stage post-processor; replaces previous augmenter when called twice. Pass null to clear. */
|
|
898
|
+
setResultAugmenter(fn: ToolResultAugmenter | null): void;
|
|
899
|
+
/** True when an augmenter is already wired — lets late-installing callers skip clobbering an earlier one. */
|
|
900
|
+
get hasResultAugmenter(): boolean;
|
|
901
|
+
register<A, R>(def: ToolDefinition<A, R>): this;
|
|
902
|
+
/** Drop a registered tool. Returns true if the name was present. Used by MCP hot-unbridge. */
|
|
903
|
+
unregister(name: string): boolean;
|
|
904
|
+
has(name: string): boolean;
|
|
905
|
+
get(name: string): ToolDefinition | undefined;
|
|
906
|
+
get size(): number;
|
|
907
|
+
/** True if a registered tool's schema was flattened for the model. */
|
|
908
|
+
wasFlattened(name: string): boolean;
|
|
909
|
+
/** Unknown / unannotated tools default to false — third-party MCP tools must opt in. */
|
|
910
|
+
isParallelSafe(name: string): boolean;
|
|
911
|
+
specs(): ToolSpec[];
|
|
912
|
+
dispatch(name: string, argumentsRaw: string | Record<string, unknown>, opts?: {
|
|
913
|
+
signal?: AbortSignal;
|
|
914
|
+
maxResultChars?: number;
|
|
915
|
+
maxResultTokens?: number;
|
|
916
|
+
/** Inject a mock PauseGate for tests. */
|
|
917
|
+
confirmationGate?: PauseGate;
|
|
918
|
+
}): Promise<string>;
|
|
919
|
+
/** Records the failed call's fingerprint; on the 2nd consecutive identical malformed call to the same tool, returns a sharper error that tells the model to stop retrying. */
|
|
920
|
+
private _noteMalformed;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
interface CacheFirstLoopOptions {
|
|
924
|
+
client: OpenAICompatClient;
|
|
925
|
+
prefix: ImmutablePrefix;
|
|
926
|
+
tools?: ToolRegistry;
|
|
927
|
+
model?: string;
|
|
928
|
+
maxToolIters?: number;
|
|
929
|
+
stream?: boolean;
|
|
930
|
+
reasoningEffort?: "high" | "max";
|
|
931
|
+
autoEscalate?: boolean;
|
|
932
|
+
/** Soft USD cap — warns at 80%, refuses next turn at 100%. Opt-in (default no cap). */
|
|
933
|
+
budgetUsd?: number;
|
|
934
|
+
/** Per-turn repair/error signal count required to escalate flash→pro. Defaults to FAILURE_ESCALATION_THRESHOLD. Out-of-range values warn + fall back. */
|
|
935
|
+
failureThreshold?: number;
|
|
936
|
+
session?: string;
|
|
937
|
+
/** PreToolUse + PostToolUse only — UserPromptSubmit / Stop live at the App boundary. */
|
|
938
|
+
hooks?: ResolvedHook[];
|
|
939
|
+
/** `cwd` reported to hooks; `luckerr code` sets this to the sandbox root, not shell home. */
|
|
940
|
+
hookCwd?: string;
|
|
941
|
+
/** PauseGate bridge — defaults to singleton, injectable for tests. */
|
|
942
|
+
confirmationGate?: PauseGate;
|
|
943
|
+
/** Re-runs the prompt builder (applyMemoryStack / codeSystemPrompt) on /new so LUCKERR.md edits take effect without a restart. Accepting a cache miss is the price. */
|
|
944
|
+
rebuildSystem?: () => string;
|
|
945
|
+
}
|
|
946
|
+
interface ReconfigurableOptions {
|
|
947
|
+
model?: string;
|
|
948
|
+
stream?: boolean;
|
|
949
|
+
/** V4 thinking mode only; deepseek-chat ignores. */
|
|
950
|
+
reasoningEffort?: "high" | "max";
|
|
951
|
+
/** `false` pins to `model` — kills both NEEDS_PRO marker scavenge and failure-count threshold. */
|
|
952
|
+
autoEscalate?: boolean;
|
|
953
|
+
}
|
|
954
|
+
declare class CacheFirstLoop {
|
|
955
|
+
readonly client: OpenAICompatClient;
|
|
956
|
+
readonly prefix: ImmutablePrefix;
|
|
957
|
+
readonly tools: ToolRegistry;
|
|
958
|
+
readonly maxToolIters: number;
|
|
959
|
+
readonly log: AppendOnlyLog;
|
|
960
|
+
readonly scratch: VolatileScratch;
|
|
961
|
+
readonly stats: SessionStats;
|
|
962
|
+
readonly repair: ToolCallRepair;
|
|
963
|
+
model: string;
|
|
964
|
+
stream: boolean;
|
|
965
|
+
reasoningEffort: "high" | "max";
|
|
966
|
+
autoEscalate: boolean;
|
|
967
|
+
budgetUsd: number | null;
|
|
968
|
+
/** One-shot 80% warning latch — cleared by setBudget so a bump re-arms at the new boundary. */
|
|
969
|
+
private _budgetWarned;
|
|
970
|
+
sessionName: string | null;
|
|
971
|
+
hooks: ResolvedHook[];
|
|
972
|
+
hookCwd: string;
|
|
973
|
+
/** PauseGate bridge — defaults to singleton, injectable for tests. */
|
|
974
|
+
readonly confirmationGate: PauseGate;
|
|
975
|
+
/** Number of messages that were pre-loaded from the session file. */
|
|
976
|
+
readonly resumedMessageCount: number;
|
|
977
|
+
private readonly _rebuildSystem;
|
|
978
|
+
private _turn;
|
|
979
|
+
private _streamPreference;
|
|
980
|
+
/** Threaded through HTTP + every tool dispatch so Esc cancels in-flight work, not after. */
|
|
981
|
+
private _turnAbort;
|
|
982
|
+
/** Authoritative running-id set — UI cards consult this instead of trusting end-event delivery. Insert at dispatch entry, delete in finally. */
|
|
983
|
+
private readonly _inflight;
|
|
984
|
+
private _proArmedForNextTurn;
|
|
985
|
+
private _escalateThisTurn;
|
|
986
|
+
private readonly _turnFailures;
|
|
987
|
+
private readonly _readOnlyLoop;
|
|
988
|
+
private _turnSelfCorrected;
|
|
989
|
+
private _foldedThisTurn;
|
|
990
|
+
private _toolDispatchesThisStep;
|
|
991
|
+
private context;
|
|
992
|
+
/** Subscribe API so UI hooks can derive `running` from finally-guaranteed insertions. */
|
|
993
|
+
get inflight(): InflightSet;
|
|
994
|
+
get currentTurn(): number;
|
|
995
|
+
constructor(opts: CacheFirstLoopOptions);
|
|
996
|
+
/** Replace older turns with one summary message; keep tail within keepRecentTokens budget. */
|
|
997
|
+
compactHistory(opts?: {
|
|
998
|
+
keepRecentTokens?: number;
|
|
999
|
+
}): Promise<{
|
|
1000
|
+
folded: boolean;
|
|
1001
|
+
beforeMessages: number;
|
|
1002
|
+
afterMessages: number;
|
|
1003
|
+
summaryChars: number;
|
|
1004
|
+
}>;
|
|
1005
|
+
appendAndPersist(message: ChatMessage): void;
|
|
1006
|
+
/** Swap the just-appended assistant entry — used by self-correction to restore the original tool_calls without dropping reasoning_content. */
|
|
1007
|
+
private replaceTailAssistantMessage;
|
|
1008
|
+
/** "New chat" — drops in-memory messages, archives the on-disk transcript so it survives in Sessions, keeps sessionName so the prefix cache stays warm. Re-runs the system-prompt builder if one was wired (issue #778: LUCKERR.md edits otherwise need a restart). */
|
|
1009
|
+
clearLog(): {
|
|
1010
|
+
dropped: number;
|
|
1011
|
+
archived: string | null;
|
|
1012
|
+
systemRebuilt: boolean;
|
|
1013
|
+
};
|
|
1014
|
+
configure(opts: ReconfigurableOptions): void;
|
|
1015
|
+
/** `null` disables the cap; any change re-arms the 80% warning. */
|
|
1016
|
+
setBudget(usd: number | null): void;
|
|
1017
|
+
/** Single-turn upgrade consumed at next step() — distinct from `/preset max` (persistent). */
|
|
1018
|
+
armProForNextTurn(): void;
|
|
1019
|
+
/** Cancel `/pro` arming before the next turn starts. */
|
|
1020
|
+
disarmPro(): void;
|
|
1021
|
+
/** UI surface — true while `/pro` is queued but hasn't fired yet. */
|
|
1022
|
+
get proArmed(): boolean;
|
|
1023
|
+
/** UI surface — true while the current turn is running on pro (armed or auto-escalated). */
|
|
1024
|
+
get escalatedThisTurn(): boolean;
|
|
1025
|
+
/** UI surface — model id of the call about to run (or running) right now, including escalation. */
|
|
1026
|
+
get currentCallModel(): string;
|
|
1027
|
+
/** The escalation model for the current provider. */
|
|
1028
|
+
get escalationModel(): string;
|
|
1029
|
+
private modelForCurrentCall;
|
|
1030
|
+
/** Returns true ONLY on the tipping call — caller surfaces a one-shot warning. */
|
|
1031
|
+
private noteToolFailureSignal;
|
|
1032
|
+
/** Returns true ONLY on the call where the read-only streak crosses the threshold (#681). */
|
|
1033
|
+
private noteReadOnlyToolCall;
|
|
1034
|
+
/** A call counts as mutating when its definition reports `readOnly !== true` and any dynamic `readOnlyCheck` doesn't override that for these args. */
|
|
1035
|
+
private isMutating;
|
|
1036
|
+
private runOneToolCall;
|
|
1037
|
+
/** Stable per-call id used as the inflight key AND threaded into tool_start / tool events so the UI matches them up. */
|
|
1038
|
+
private inflightIdFor;
|
|
1039
|
+
private _inflightCounter;
|
|
1040
|
+
private buildMessages;
|
|
1041
|
+
abort(): void;
|
|
1042
|
+
/** Drop the last user message + everything after; caller re-sends. Persists to session file. */
|
|
1043
|
+
retryLastUser(): string | null;
|
|
1044
|
+
step(userInput: string): AsyncGenerator<LoopEvent>;
|
|
1045
|
+
private summaryContext;
|
|
1046
|
+
run(userInput: string, onEvent?: (ev: LoopEvent) => void): Promise<string>;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/** Expand `@path` mentions inline. Paths must resolve inside rootDir; escapes / oversize get a skip note, not content. */
|
|
1050
|
+
/** Caps match tool-result dispatch truncation (0.5.2). */
|
|
1051
|
+
declare const DEFAULT_AT_MENTION_MAX_BYTES: number;
|
|
1052
|
+
/** Cap on entries returned for a `@<dir>` listing. ~200 paths × ~50 chars ≈ 10 KB — fits inside DEFAULT_AT_MENTION_MAX_BYTES with room for the rest of the prompt. */
|
|
1053
|
+
declare const DEFAULT_AT_DIR_MAX_ENTRIES = 200;
|
|
1054
|
+
/** Universally-uninteresting build / VCS dirs. Framework-specific dirs (Pods, target, …) live in .gitignore. */
|
|
1055
|
+
declare const DEFAULT_PICKER_IGNORE_DIRS: readonly string[];
|
|
1056
|
+
interface ListFilesOptions {
|
|
1057
|
+
/** Cap the walk once we've collected this many entries. Default 2000. */
|
|
1058
|
+
maxResults?: number;
|
|
1059
|
+
/** Directory names to skip entirely. Defaults to {@link DEFAULT_PICKER_IGNORE_DIRS}. */
|
|
1060
|
+
ignoreDirs?: readonly string[];
|
|
1061
|
+
/** Walk nested .gitignores (root + every subdir). Default true. */
|
|
1062
|
+
respectGitignore?: boolean;
|
|
1063
|
+
}
|
|
1064
|
+
/** Sync on purpose — fits the TUI's single-turn-per-tick model. Skips dot-DIRS but keeps dotfiles. */
|
|
1065
|
+
declare function listFilesSync(root: string, opts?: ListFilesOptions): string[];
|
|
1066
|
+
interface FileWithStats {
|
|
1067
|
+
/** Relative path with forward-slash separator. */
|
|
1068
|
+
path: string;
|
|
1069
|
+
/** Modification time (Date.getTime() / ms since epoch). 0 when stat failed. */
|
|
1070
|
+
mtimeMs: number;
|
|
1071
|
+
}
|
|
1072
|
+
/** Stat failures kept as `mtimeMs: 0` — entry still appears, sinks to bottom of recency sort. */
|
|
1073
|
+
declare function listFilesWithStatsSync(root: string, opts?: ListFilesOptions): FileWithStats[];
|
|
1074
|
+
/** Parallel stat per directory — Windows stat syscalls are 3-5× slower than Linux. */
|
|
1075
|
+
declare function listFilesWithStatsAsync(root: string, opts?: ListFilesOptions): Promise<FileWithStats[]>;
|
|
1076
|
+
interface StreamWalkOptions {
|
|
1077
|
+
ignoreDirs?: readonly string[];
|
|
1078
|
+
respectGitignore?: boolean;
|
|
1079
|
+
signal?: AbortSignal;
|
|
1080
|
+
/** Called per file entry. Return false to halt the walk. */
|
|
1081
|
+
onEntry: (entry: FileWithStats) => boolean | undefined;
|
|
1082
|
+
/** Called periodically with the running file-count. */
|
|
1083
|
+
onProgress?: (scanned: number) => void;
|
|
1084
|
+
/** Default 100ms — minimum gap between onProgress calls. */
|
|
1085
|
+
progressIntervalMs?: number;
|
|
1086
|
+
}
|
|
1087
|
+
/** Cancelable, streaming walker. Drives `listFilesWithStatsAsync` and the picker's search-mode walk. */
|
|
1088
|
+
declare function walkFilesStream(root: string, opts: StreamWalkOptions): Promise<{
|
|
1089
|
+
scanned: number;
|
|
1090
|
+
cancelled: boolean;
|
|
1091
|
+
}>;
|
|
1092
|
+
interface DirEntry {
|
|
1093
|
+
name: string;
|
|
1094
|
+
/** Relative-to-root path (forward slashes). For dirs, no trailing slash. */
|
|
1095
|
+
path: string;
|
|
1096
|
+
isDir: boolean;
|
|
1097
|
+
/** 0 for directories (no stat), real mtime for files. */
|
|
1098
|
+
mtimeMs: number;
|
|
1099
|
+
}
|
|
1100
|
+
interface ListDirectoryOptions {
|
|
1101
|
+
ignoreDirs?: readonly string[];
|
|
1102
|
+
respectGitignore?: boolean;
|
|
1103
|
+
}
|
|
1104
|
+
/** One-level browse for the @-picker. Folders first then files, alpha within each group. Resolves outside-root to []. */
|
|
1105
|
+
declare function listDirectory(root: string, relDir: string, opts?: ListDirectoryOptions): Promise<DirEntry[]>;
|
|
1106
|
+
interface ParsedAtQuery {
|
|
1107
|
+
/** Directory portion (rel from root, no trailing slash). Empty = root. */
|
|
1108
|
+
dir: string;
|
|
1109
|
+
/** Filter portion — chars after the last slash. Empty if query ended in `/`. */
|
|
1110
|
+
filter: string;
|
|
1111
|
+
/** True if the query ended in `/` — caller knows to browse `dir`. */
|
|
1112
|
+
trailingSlash: boolean;
|
|
1113
|
+
}
|
|
1114
|
+
/** Split `src/auth/log` → `{dir: "src/auth", filter: "log"}`; trailing slash sets `trailingSlash` and clears filter. */
|
|
1115
|
+
declare function parseAtQuery(query: string): ParsedAtQuery;
|
|
1116
|
+
/** Trailing-token only, anchored at end-of-input — distinct from `AT_MENTION_PATTERN` which scans all. `\p{L}\p{N}` for CJK and other non-ASCII filenames. */
|
|
1117
|
+
declare const AT_PICKER_PREFIX: RegExp;
|
|
1118
|
+
declare function detectAtPicker(input: string): {
|
|
1119
|
+
query: string;
|
|
1120
|
+
atOffset: number;
|
|
1121
|
+
} | null;
|
|
1122
|
+
/** A candidate accepted by the picker ranker — either a bare path or a path with mtime. */
|
|
1123
|
+
type PickerCandidate = string | FileWithStats;
|
|
1124
|
+
interface RankPickerOptions {
|
|
1125
|
+
/** Upper bound on returned entries. Default 40. */
|
|
1126
|
+
limit?: number;
|
|
1127
|
+
recentlyUsed?: readonly string[];
|
|
1128
|
+
}
|
|
1129
|
+
declare function rankPickerCandidates(files: readonly PickerCandidate[], query: string, limitOrOpts?: number | RankPickerOptions): string[];
|
|
1130
|
+
/** Word-boundary anchor rejects `@` embedded in emails / social handles; trailing `.` stripped before lookup. */
|
|
1131
|
+
declare const AT_MENTION_PATTERN: RegExp;
|
|
1132
|
+
interface AtMentionExpansion {
|
|
1133
|
+
/** The raw `@path` token as it appeared in the text. */
|
|
1134
|
+
token: string;
|
|
1135
|
+
/** The relative path, as resolved against rootDir. */
|
|
1136
|
+
path: string;
|
|
1137
|
+
/** True if the content was inlined. False = skipped (reason in `skip`). */
|
|
1138
|
+
ok: boolean;
|
|
1139
|
+
/** Bytes read (only for ok=true and isDirectory=false). */
|
|
1140
|
+
bytes?: number;
|
|
1141
|
+
/** True when the mention resolved to a directory (ok=true). Block uses `<directory>` instead of `<file>`. */
|
|
1142
|
+
isDirectory?: boolean;
|
|
1143
|
+
/** Number of files listed when isDirectory=true. */
|
|
1144
|
+
entries?: number;
|
|
1145
|
+
/** True iff the directory listing was clipped at maxDirEntries. */
|
|
1146
|
+
truncated?: boolean;
|
|
1147
|
+
/** Why the mention was skipped. Set when ok=false. */
|
|
1148
|
+
skip?: "missing" | "not-file" | "too-large" | "escape" | "read-error";
|
|
1149
|
+
}
|
|
1150
|
+
interface AtMentionOptions {
|
|
1151
|
+
/** Max file size in bytes before a mention is skipped. */
|
|
1152
|
+
maxBytes?: number;
|
|
1153
|
+
/** Cap on entries returned for a `@<dir>` listing. Default {@link DEFAULT_AT_DIR_MAX_ENTRIES}. */
|
|
1154
|
+
maxDirEntries?: number;
|
|
1155
|
+
fs?: {
|
|
1156
|
+
exists: (path: string) => boolean;
|
|
1157
|
+
isFile: (path: string) => boolean;
|
|
1158
|
+
/** Optional — when omitted, directories are skipped as `not-file`. */
|
|
1159
|
+
isDir?: (path: string) => boolean;
|
|
1160
|
+
/** Optional — receives the directory's absolute path and the project root, returns relative paths and a truncated flag. */
|
|
1161
|
+
listDir?: (dirAbs: string, root: string, max: number) => {
|
|
1162
|
+
files: string[];
|
|
1163
|
+
truncated: boolean;
|
|
1164
|
+
};
|
|
1165
|
+
size: (path: string) => number;
|
|
1166
|
+
read: (path: string) => string;
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
declare function expandAtMentions(text: string, rootDir: string, opts?: AtMentionOptions): {
|
|
1170
|
+
text: string;
|
|
1171
|
+
expansions: AtMentionExpansion[];
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
/** Reads LUCKERR.md → AGENTS.md → AGENT.md (first that exists); writes prefer the file already on disk. */
|
|
1175
|
+
/** Default WRITE target — created when no candidate exists yet. */
|
|
1176
|
+
declare const PROJECT_MEMORY_FILE = "LUCKERR.md";
|
|
1177
|
+
/** READ candidates, in priority order. AGENTS.md is the open spec at agents.md (Linux Foundation). */
|
|
1178
|
+
declare const PROJECT_MEMORY_FILES: readonly ["LUCKERR.md", "AGENTS.md", "AGENT.md"];
|
|
1179
|
+
declare const PROJECT_MEMORY_MAX_CHARS = 8000;
|
|
1180
|
+
/** Absolute path of the first PROJECT_MEMORY_FILES candidate that exists at rootDir, or null. */
|
|
1181
|
+
declare function findProjectMemoryPath(rootDir: string): string | null;
|
|
1182
|
+
/** Path callers should write to: an existing candidate wins, otherwise rootDir/LUCKERR.md. */
|
|
1183
|
+
declare function resolveProjectMemoryWritePath(rootDir: string): string;
|
|
1184
|
+
interface ProjectMemory {
|
|
1185
|
+
/** Absolute path the memory was read from. */
|
|
1186
|
+
path: string;
|
|
1187
|
+
/** Post-truncation content (may include a "… (truncated N chars)" marker). */
|
|
1188
|
+
content: string;
|
|
1189
|
+
/** Original byte length before truncation. */
|
|
1190
|
+
originalChars: number;
|
|
1191
|
+
/** True iff `originalChars > PROJECT_MEMORY_MAX_CHARS`. */
|
|
1192
|
+
truncated: boolean;
|
|
1193
|
+
}
|
|
1194
|
+
/** Empty / whitespace-only files return null so they don't perturb the cache prefix. */
|
|
1195
|
+
declare function readProjectMemory(rootDir: string): ProjectMemory | null;
|
|
1196
|
+
declare function memoryEnabled(): boolean;
|
|
1197
|
+
/** Deterministic — same memory file always yields the same prefix hash. */
|
|
1198
|
+
declare function applyProjectMemory(basePrompt: string, rootDir: string): string;
|
|
1199
|
+
|
|
1200
|
+
type ThemeName = "default" | "dark" | "light" | "tokyo-night" | "github-dark" | "github-light" | "high-contrast";
|
|
1201
|
+
|
|
1202
|
+
type LanguageCode = "EN" | "zh-CN";
|
|
1203
|
+
|
|
1204
|
+
/** Shared exclude defaults + resolver — chunker, directory_tree, and dashboard read from here. */
|
|
1205
|
+
interface IndexUserConfig {
|
|
1206
|
+
excludeDirs?: string[];
|
|
1207
|
+
excludeFiles?: string[];
|
|
1208
|
+
excludeExts?: string[];
|
|
1209
|
+
excludePatterns?: string[];
|
|
1210
|
+
respectGitignore?: boolean;
|
|
1211
|
+
maxFileBytes?: number;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/** Library reads only DEEPSEEK_API_KEY from env; the CLI bridges config.json → env var. */
|
|
1215
|
+
|
|
1216
|
+
/** Legacy `fast|smart|max` kept for back-compat with existing config.json files. */
|
|
1217
|
+
type PresetName = "auto" | "flash" | "pro" | "fast" | "smart" | "max";
|
|
1218
|
+
/** Single trust dial: review queues edits + gates shell; auto applies + gates shell; yolo skips both gates. */
|
|
1219
|
+
type EditMode = "review" | "auto" | "yolo";
|
|
1220
|
+
type ReasoningEffort = "high" | "max";
|
|
1221
|
+
type EmbeddingProvider = "ollama" | "openai-compat";
|
|
1222
|
+
interface OllamaEmbeddingUserConfig {
|
|
1223
|
+
baseUrl?: string;
|
|
1224
|
+
model?: string;
|
|
1225
|
+
}
|
|
1226
|
+
interface OpenAICompatEmbeddingUserConfig {
|
|
1227
|
+
baseUrl?: string;
|
|
1228
|
+
apiKey?: string;
|
|
1229
|
+
model?: string;
|
|
1230
|
+
extraBody?: Record<string, unknown>;
|
|
1231
|
+
}
|
|
1232
|
+
interface SemanticEmbeddingUserConfig {
|
|
1233
|
+
provider?: EmbeddingProvider;
|
|
1234
|
+
ollama?: OllamaEmbeddingUserConfig;
|
|
1235
|
+
openaiCompat?: OpenAICompatEmbeddingUserConfig;
|
|
1236
|
+
}
|
|
1237
|
+
interface LuckerrConfig {
|
|
1238
|
+
apiKey?: string;
|
|
1239
|
+
baseUrl?: string;
|
|
1240
|
+
/**
|
|
1241
|
+
* Active AI provider id (e.g. "deepseek", "siliconflow", "zhipu").
|
|
1242
|
+
* If unset, the system auto-resolves from the model id or falls
|
|
1243
|
+
* back to "deepseek".
|
|
1244
|
+
*/
|
|
1245
|
+
provider?: string;
|
|
1246
|
+
/** Per-provider API keys, keyed by provider id. */
|
|
1247
|
+
providerKeys?: Record<string, string>;
|
|
1248
|
+
/** Per-provider base URL overrides, keyed by provider id. */
|
|
1249
|
+
providerBaseUrls?: Record<string, string>;
|
|
1250
|
+
lang?: LanguageCode;
|
|
1251
|
+
preset?: PresetName;
|
|
1252
|
+
editMode?: EditMode;
|
|
1253
|
+
editModeHintShown?: boolean;
|
|
1254
|
+
mouseClipboardHintShown?: boolean;
|
|
1255
|
+
reasoningEffort?: ReasoningEffort;
|
|
1256
|
+
/** Default workspace root for the desktop client. CLI uses cwd. */
|
|
1257
|
+
workspaceDir?: string;
|
|
1258
|
+
/** Last N workspace paths the desktop client has opened, most recent first. */
|
|
1259
|
+
recentWorkspaces?: string[];
|
|
1260
|
+
/** Desktop only — `openWith` value for clicking file links. Empty/undefined = OS default app. Examples: "code", "cursor", "C:\\path\\to\\editor.exe". */
|
|
1261
|
+
editor?: string;
|
|
1262
|
+
theme?: ThemeName | "auto";
|
|
1263
|
+
/** Stored as `--mcp`-format strings so one parser handles both flag and config. */
|
|
1264
|
+
mcp?: string[];
|
|
1265
|
+
/** Names of servers in `mcp` to skip on bridge — see `/mcp disable <name>`. */
|
|
1266
|
+
mcpDisabled?: string[];
|
|
1267
|
+
/** Env overlay per MCP server name (matches the `name=` prefix of the spec). Stdio transports merge this over process.env; SSE/HTTP ignore it. */
|
|
1268
|
+
mcpEnv?: Record<string, Record<string, string>>;
|
|
1269
|
+
session?: string | null;
|
|
1270
|
+
setupCompleted?: boolean;
|
|
1271
|
+
search?: boolean;
|
|
1272
|
+
/** Web search engine backend: "mojeek" (default, scrapes Mojeek) or "searxng" (self-hosted SearXNG). */
|
|
1273
|
+
webSearchEngine?: "mojeek" | "searxng";
|
|
1274
|
+
/** Base URL for SearXNG instance (default http://localhost:8080). */
|
|
1275
|
+
webSearchEndpoint?: string;
|
|
1276
|
+
dashboard?: {
|
|
1277
|
+
/** Pin the embedded dashboard to a fixed port — required for stable SSH tunnels. 0/absent → ephemeral. */
|
|
1278
|
+
port?: number;
|
|
1279
|
+
};
|
|
1280
|
+
escalation?: {
|
|
1281
|
+
/** Per-turn repair/error signal count required to escalate flash→pro. Defaults to 3. Out-of-range → default. */
|
|
1282
|
+
failureThreshold?: number;
|
|
1283
|
+
};
|
|
1284
|
+
/** Per-field visibility toggles for the bottom status row. All default to true (visible). */
|
|
1285
|
+
statusBar?: {
|
|
1286
|
+
showBalance?: boolean;
|
|
1287
|
+
showSessionCost?: boolean;
|
|
1288
|
+
showTurnCost?: boolean;
|
|
1289
|
+
showCacheHit?: boolean;
|
|
1290
|
+
showVersion?: boolean;
|
|
1291
|
+
showFeedbackHint?: boolean;
|
|
1292
|
+
};
|
|
1293
|
+
projects?: {
|
|
1294
|
+
[absoluteRootDir: string]: {
|
|
1295
|
+
shellAllowed?: string[];
|
|
1296
|
+
/** Absolute directory prefixes the user pre-approved for outside-sandbox file access (#684). */
|
|
1297
|
+
pathAllowed?: string[];
|
|
1298
|
+
};
|
|
1299
|
+
};
|
|
1300
|
+
index?: IndexUserConfig;
|
|
1301
|
+
semantic?: SemanticEmbeddingUserConfig;
|
|
1302
|
+
/** User-declared extensions to the built-in memory types (#709). Unknown types round-trip even without a declaration; declaring one lets you attach a default priority + lifecycle. */
|
|
1303
|
+
memory?: {
|
|
1304
|
+
customTypes?: CustomMemoryTypeConfig[];
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
interface CustomMemoryTypeConfig {
|
|
1308
|
+
name: string;
|
|
1309
|
+
description?: string;
|
|
1310
|
+
priority?: "low" | "medium" | "high";
|
|
1311
|
+
expires?: "project_end";
|
|
1312
|
+
}
|
|
1313
|
+
declare function defaultConfigPath(): string;
|
|
1314
|
+
declare function readConfig(path?: string): LuckerrConfig;
|
|
1315
|
+
declare function writeConfig(cfg: LuckerrConfig, path?: string): void;
|
|
1316
|
+
/** Resolve the API key from env var first, then the config file. */
|
|
1317
|
+
/**
|
|
1318
|
+
* Load the API key for the default provider (DeepSeek).
|
|
1319
|
+
* Checks DEEPSEEK_API_KEY env var first, then config.json apiKey field.
|
|
1320
|
+
*/
|
|
1321
|
+
declare function loadApiKey(path?: string): string | undefined;
|
|
1322
|
+
/** env > config > undefined. Client falls back to api.deepseek.com when undefined. */
|
|
1323
|
+
declare function loadBaseUrl(path?: string): string | undefined;
|
|
1324
|
+
declare function saveBaseUrl(url: string, path?: string): void;
|
|
1325
|
+
declare function saveApiKey(key: string, path?: string): void;
|
|
1326
|
+
/** Self-hosted DeepSeek-compatible endpoints may issue any token shape, so we only typo-guard here — the real auth check is the first API call against `baseUrl`. */
|
|
1327
|
+
declare function isPlausibleKey(key: string): boolean;
|
|
1328
|
+
/** Mask a key for display: `sk-abcd...wxyz`. */
|
|
1329
|
+
declare function redactKey(key: string): string;
|
|
1330
|
+
|
|
1331
|
+
/** User-private memory pinned into the immutable prefix; distinct from committable LUCKERR.md. */
|
|
1332
|
+
|
|
1333
|
+
declare const USER_MEMORY_DIR = "memory";
|
|
1334
|
+
declare const MEMORY_INDEX_FILE = "MEMORY.md";
|
|
1335
|
+
/** Cap on the index file content loaded into the prefix, per scope. */
|
|
1336
|
+
declare const MEMORY_INDEX_MAX_CHARS = 4000;
|
|
1337
|
+
declare const BUILTIN_MEMORY_TYPES: readonly ["user", "feedback", "project", "reference"];
|
|
1338
|
+
type BuiltinMemoryType = (typeof BUILTIN_MEMORY_TYPES)[number];
|
|
1339
|
+
/** Built-ins plus any string declared in `config.memory.customTypes`. Unknown values are accepted (round-tripped verbatim). */
|
|
1340
|
+
type MemoryType = BuiltinMemoryType | (string & {});
|
|
1341
|
+
type MemoryScope = "global" | "project";
|
|
1342
|
+
type MemoryPriority = "low" | "medium" | "high";
|
|
1343
|
+
type MemoryExpires = "project_end";
|
|
1344
|
+
interface MemoryEntry {
|
|
1345
|
+
name: string;
|
|
1346
|
+
type: MemoryType;
|
|
1347
|
+
scope: MemoryScope;
|
|
1348
|
+
description: string;
|
|
1349
|
+
body: string;
|
|
1350
|
+
/** ISO date string (YYYY-MM-DD). */
|
|
1351
|
+
createdAt: string;
|
|
1352
|
+
/** Explicit per-entry priority; absent → resolve from config default for `type`, else "medium". */
|
|
1353
|
+
priority?: MemoryPriority;
|
|
1354
|
+
/** Lifecycle hint. `project_end` → cleared by `/memory clear project`. */
|
|
1355
|
+
expires?: MemoryExpires;
|
|
1356
|
+
}
|
|
1357
|
+
interface MemoryStoreOptions {
|
|
1358
|
+
/** Override `~/.luckerr` — tests set this to a tmpdir. */
|
|
1359
|
+
homeDir?: string;
|
|
1360
|
+
/** Absolute sandbox root. Required to use `scope: "project"`. */
|
|
1361
|
+
projectRoot?: string;
|
|
1362
|
+
}
|
|
1363
|
+
interface WriteInput {
|
|
1364
|
+
name: string;
|
|
1365
|
+
type: MemoryType;
|
|
1366
|
+
scope: MemoryScope;
|
|
1367
|
+
description: string;
|
|
1368
|
+
body: string;
|
|
1369
|
+
priority?: MemoryPriority;
|
|
1370
|
+
expires?: MemoryExpires;
|
|
1371
|
+
}
|
|
1372
|
+
/** Throws on path-injection (../, /, leading dot). Allowed: 3-40 chars, alnum/_/-, interior `.`. */
|
|
1373
|
+
declare function sanitizeMemoryName(raw: string): string;
|
|
1374
|
+
/** Stable 16-hex-char hash of an absolute sandbox root path. */
|
|
1375
|
+
declare function projectHash(rootDir: string): string;
|
|
1376
|
+
declare class MemoryStore {
|
|
1377
|
+
private readonly homeDir;
|
|
1378
|
+
private readonly projectRoot;
|
|
1379
|
+
constructor(opts?: MemoryStoreOptions);
|
|
1380
|
+
/** Directory this store writes `scope` files into, creating it if needed. */
|
|
1381
|
+
dir(scope: MemoryScope): string;
|
|
1382
|
+
/** Absolute path to a memory file (no existence check). */
|
|
1383
|
+
pathFor(scope: MemoryScope, name: string): string;
|
|
1384
|
+
/** True iff this store is configured with a project scope available. */
|
|
1385
|
+
hasProjectScope(): boolean;
|
|
1386
|
+
loadIndex(scope: MemoryScope): {
|
|
1387
|
+
content: string;
|
|
1388
|
+
originalChars: number;
|
|
1389
|
+
truncated: boolean;
|
|
1390
|
+
} | null;
|
|
1391
|
+
/** Read one memory file's body (frontmatter stripped). Throws if missing. */
|
|
1392
|
+
read(scope: MemoryScope, name: string): MemoryEntry;
|
|
1393
|
+
/** Skips malformed files — index stays queryable even if one file is hand-edited into nonsense. */
|
|
1394
|
+
list(): MemoryEntry[];
|
|
1395
|
+
write(input: WriteInput): string;
|
|
1396
|
+
/** Delete one memory + its index line. No-op if the file is already gone. */
|
|
1397
|
+
delete(scope: MemoryScope, rawName: string): boolean;
|
|
1398
|
+
/** Sorted by name — same file set must produce byte-identical MEMORY.md for stable prefix hashing. */
|
|
1399
|
+
private regenerateIndex;
|
|
1400
|
+
}
|
|
1401
|
+
/** Empty index → omit the whole block (otherwise we'd add bytes to the prefix hash for nothing). */
|
|
1402
|
+
declare function applyUserMemory(basePrompt: string, opts?: {
|
|
1403
|
+
homeDir?: string;
|
|
1404
|
+
projectRoot?: string;
|
|
1405
|
+
cfg?: LuckerrConfig;
|
|
1406
|
+
}): string;
|
|
1407
|
+
declare function applyMemoryStack(basePrompt: string, rootDir: string): string;
|
|
1408
|
+
|
|
1409
|
+
/** Native FS tools — sandbox enforced here, not delegated. `edit_file` takes a single SEARCH/REPLACE string. */
|
|
1410
|
+
|
|
1411
|
+
interface FilesystemToolsOptions {
|
|
1412
|
+
/** Absolute directory the tools may read/write. Paths outside this are refused. */
|
|
1413
|
+
rootDir: string;
|
|
1414
|
+
/** false → register only read-side tools. Default true. */
|
|
1415
|
+
allowWriting?: boolean;
|
|
1416
|
+
/** Per-read byte cap; floor against OOM on a multi-GB blob. */
|
|
1417
|
+
maxReadBytes?: number;
|
|
1418
|
+
/** Cap on total bytes from listing/grep tools — bounds tree-as-one-string accidents. */
|
|
1419
|
+
maxListBytes?: number;
|
|
1420
|
+
}
|
|
1421
|
+
declare function registerFilesystemTools(registry: ToolRegistry, opts: FilesystemToolsOptions): ToolRegistry;
|
|
1422
|
+
|
|
1423
|
+
/** Writes are eager but the prefix is NOT re-loaded mid-session — keeps prompt-cache stable. */
|
|
1424
|
+
|
|
1425
|
+
interface MemoryToolsOptions {
|
|
1426
|
+
/** Sandbox root for the `project` scope. Omit for chat mode. */
|
|
1427
|
+
projectRoot?: string;
|
|
1428
|
+
/** Override `~/.luckerr` (tests). */
|
|
1429
|
+
homeDir?: string;
|
|
1430
|
+
}
|
|
1431
|
+
declare function registerMemoryTools(registry: ToolRegistry, opts?: MemoryToolsOptions): ToolRegistry;
|
|
1432
|
+
|
|
1433
|
+
/** Branching primitive separate from submit_plan; throws ChoiceRequestedError so the TUI can mount a picker and the model stops. */
|
|
1434
|
+
|
|
1435
|
+
interface ChoiceOption {
|
|
1436
|
+
id: string;
|
|
1437
|
+
title: string;
|
|
1438
|
+
summary?: string;
|
|
1439
|
+
}
|
|
1440
|
+
declare class ChoiceRequestedError extends Error {
|
|
1441
|
+
readonly question: string;
|
|
1442
|
+
readonly options: ChoiceOption[];
|
|
1443
|
+
readonly allowCustom: boolean;
|
|
1444
|
+
constructor(question: string, options: ChoiceOption[], allowCustom: boolean);
|
|
1445
|
+
toToolResult(): {
|
|
1446
|
+
error: string;
|
|
1447
|
+
question: string;
|
|
1448
|
+
options: ChoiceOption[];
|
|
1449
|
+
allowCustom: boolean;
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
interface ChoiceToolOptions {
|
|
1453
|
+
onChoiceRequested?: (question: string, options: ChoiceOption[]) => void;
|
|
1454
|
+
}
|
|
1455
|
+
declare function registerChoiceTool(registry: ToolRegistry, opts?: ChoiceToolOptions): ToolRegistry;
|
|
1456
|
+
|
|
1457
|
+
type PlanStepRisk = "low" | "med" | "high";
|
|
1458
|
+
interface PlanStep {
|
|
1459
|
+
id: string;
|
|
1460
|
+
title: string;
|
|
1461
|
+
action: string;
|
|
1462
|
+
risk?: PlanStepRisk;
|
|
1463
|
+
}
|
|
1464
|
+
interface StepCompletion {
|
|
1465
|
+
kind: "step_completed";
|
|
1466
|
+
stepId: string;
|
|
1467
|
+
title?: string;
|
|
1468
|
+
result: string;
|
|
1469
|
+
notes?: string;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/** Plan-mode errors carry `toToolResult` so dispatch serializes structured payloads the TUI parses to mount pickers. */
|
|
1473
|
+
|
|
1474
|
+
declare class PlanProposedError extends Error {
|
|
1475
|
+
readonly plan: string;
|
|
1476
|
+
readonly steps?: PlanStep[];
|
|
1477
|
+
readonly summary?: string;
|
|
1478
|
+
constructor(plan: string, steps?: PlanStep[], summary?: string);
|
|
1479
|
+
toToolResult(): {
|
|
1480
|
+
error: string;
|
|
1481
|
+
plan: string;
|
|
1482
|
+
steps?: PlanStep[];
|
|
1483
|
+
summary?: string;
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
/** Surgical replace of in-flight plan tail; submit_plan would reset done steps. */
|
|
1487
|
+
declare class PlanRevisionProposedError extends Error {
|
|
1488
|
+
readonly reason: string;
|
|
1489
|
+
readonly remainingSteps: PlanStep[];
|
|
1490
|
+
readonly summary?: string;
|
|
1491
|
+
constructor(reason: string, remainingSteps: PlanStep[], summary?: string);
|
|
1492
|
+
toToolResult(): {
|
|
1493
|
+
error: string;
|
|
1494
|
+
reason: string;
|
|
1495
|
+
remainingSteps: PlanStep[];
|
|
1496
|
+
summary?: string;
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
interface PlanToolOptions {
|
|
1501
|
+
onPlanSubmitted?: (plan: string, steps?: PlanStep[]) => void;
|
|
1502
|
+
onStepCompleted?: (update: StepCompletion) => void;
|
|
1503
|
+
onPlanRevisionProposed?: (reason: string, remainingSteps: PlanStep[], summary?: string) => void;
|
|
1504
|
+
}
|
|
1505
|
+
declare function registerPlanTool(registry: ToolRegistry, opts?: PlanToolOptions): ToolRegistry;
|
|
1506
|
+
|
|
1507
|
+
type TodoStatus = "pending" | "in_progress" | "completed";
|
|
1508
|
+
interface TodoItem {
|
|
1509
|
+
content: string;
|
|
1510
|
+
status: TodoStatus;
|
|
1511
|
+
activeForm: string;
|
|
1512
|
+
}
|
|
1513
|
+
interface TodoToolOptions {
|
|
1514
|
+
onTodosUpdated?: (todos: TodoItem[]) => void;
|
|
1515
|
+
}
|
|
1516
|
+
declare function registerTodoTool(registry: ToolRegistry, opts?: TodoToolOptions): ToolRegistry;
|
|
1517
|
+
|
|
1518
|
+
/** Side-channel — subagents run inside a tool-dispatch frame, can't go through parent's `LoopEvent` stream. */
|
|
1519
|
+
interface SubagentEvent {
|
|
1520
|
+
kind: "start" | "progress" | "end" | "inner" | "phase";
|
|
1521
|
+
/** Stable per-spawn id; lets the UI key parallel runs apart instead of overwriting one shared row. */
|
|
1522
|
+
runId: string;
|
|
1523
|
+
task: string;
|
|
1524
|
+
skillName?: string;
|
|
1525
|
+
model?: string;
|
|
1526
|
+
iter?: number;
|
|
1527
|
+
elapsedMs?: number;
|
|
1528
|
+
summary?: string;
|
|
1529
|
+
error?: string;
|
|
1530
|
+
turns?: number;
|
|
1531
|
+
costUsd?: number;
|
|
1532
|
+
usage?: Usage;
|
|
1533
|
+
/** When kind === "inner": the raw child loop event. Parent UI translates to a child summary. */
|
|
1534
|
+
inner?: LoopEvent;
|
|
1535
|
+
/** When kind === "phase": coarse status verb for the activity row. */
|
|
1536
|
+
phase?: "exploring" | "summarising";
|
|
1537
|
+
}
|
|
1538
|
+
interface SubagentSink {
|
|
1539
|
+
current: ((ev: SubagentEvent) => void) | null;
|
|
1540
|
+
}
|
|
1541
|
+
interface SubagentToolOptions {
|
|
1542
|
+
client: DeepSeekClient;
|
|
1543
|
+
defaultSystem?: string;
|
|
1544
|
+
projectRoot?: string;
|
|
1545
|
+
defaultModel?: string;
|
|
1546
|
+
maxToolIters?: number;
|
|
1547
|
+
maxResultChars?: number;
|
|
1548
|
+
sink?: SubagentSink;
|
|
1549
|
+
}
|
|
1550
|
+
/** Library surface only — `luckerr code` uses Skills `runAs: subagent` as the user-facing path. */
|
|
1551
|
+
declare function registerSubagentTool(parentRegistry: ToolRegistry, opts: SubagentToolOptions): ToolRegistry;
|
|
1552
|
+
/** Plan-mode state propagates — a subagent spawned under `/plan` MUST NOT escape it. */
|
|
1553
|
+
declare function forkRegistryExcluding(parent: ToolRegistry, exclude: ReadonlySet<string>): ToolRegistry;
|
|
1554
|
+
|
|
1555
|
+
/** Background process registry for never-exiting commands; ready-signal detection short-circuits the startup wait. */
|
|
1556
|
+
interface JobStartOptions {
|
|
1557
|
+
/** Absolute path to cwd for the spawned child. */
|
|
1558
|
+
cwd: string;
|
|
1559
|
+
/** Capped at 30; ready-signal match short-circuits. Default 3. */
|
|
1560
|
+
waitSec?: number;
|
|
1561
|
+
/** Signal plumbed through from the calling tool's AbortSignal. */
|
|
1562
|
+
signal?: AbortSignal;
|
|
1563
|
+
/** Total per-job output buffer cap (bytes). Default 64 KB. */
|
|
1564
|
+
maxBufferBytes?: number;
|
|
1565
|
+
}
|
|
1566
|
+
interface JobStartResult {
|
|
1567
|
+
jobId: number;
|
|
1568
|
+
pid: number | null;
|
|
1569
|
+
/** True iff the child was still running at the point we returned. */
|
|
1570
|
+
stillRunning: boolean;
|
|
1571
|
+
/** True iff a READY_SIGNALS pattern matched during the wait window. */
|
|
1572
|
+
readyMatched: boolean;
|
|
1573
|
+
/** Preview of combined stdout+stderr accumulated during the wait. */
|
|
1574
|
+
preview: string;
|
|
1575
|
+
/** If the child exited during the wait, its exit code; else null. */
|
|
1576
|
+
exitCode: number | null;
|
|
1577
|
+
}
|
|
1578
|
+
interface JobRecord {
|
|
1579
|
+
id: number;
|
|
1580
|
+
command: string;
|
|
1581
|
+
pid: number | null;
|
|
1582
|
+
startedAt: number;
|
|
1583
|
+
/** Exit code once the process terminates; null while running. */
|
|
1584
|
+
exitCode: number | null;
|
|
1585
|
+
/** Combined stdout+stderr, ring-trimmed. */
|
|
1586
|
+
output: string;
|
|
1587
|
+
/** Counts all bytes the child wrote, not just what's still buffered in `output`. */
|
|
1588
|
+
totalBytesWritten: number;
|
|
1589
|
+
/** True iff the child is still alive. */
|
|
1590
|
+
running: boolean;
|
|
1591
|
+
/** Error from spawn() itself (ENOENT, etc.) once surfaced. */
|
|
1592
|
+
spawnError?: string;
|
|
1593
|
+
}
|
|
1594
|
+
declare class JobRegistry {
|
|
1595
|
+
private readonly jobs;
|
|
1596
|
+
private nextId;
|
|
1597
|
+
/** Resolves on (a) ready signal, (b) early exit, or (c) waitSec deadline — child keeps running regardless. */
|
|
1598
|
+
start(command: string, opts: JobStartOptions): Promise<JobStartResult>;
|
|
1599
|
+
read(id: number, opts?: {
|
|
1600
|
+
since?: number;
|
|
1601
|
+
tailLines?: number;
|
|
1602
|
+
}): JobReadResult | null;
|
|
1603
|
+
waitForJob(id: number, opts?: {
|
|
1604
|
+
timeoutMs?: number;
|
|
1605
|
+
waitFor?: "exit" | "output-or-exit";
|
|
1606
|
+
}): Promise<JobWaitResult | null>;
|
|
1607
|
+
/** SIGTERM, wait graceMs, then SIGKILL. Idempotent on already-exited jobs. */
|
|
1608
|
+
stop(id: number, opts?: {
|
|
1609
|
+
graceMs?: number;
|
|
1610
|
+
}): Promise<JobRecord | null>;
|
|
1611
|
+
list(): JobRecord[];
|
|
1612
|
+
shutdown(deadlineMs?: number): Promise<void>;
|
|
1613
|
+
/** Count of still-running jobs — drives the TUI status-bar indicator. */
|
|
1614
|
+
runningCount(): number;
|
|
1615
|
+
}
|
|
1616
|
+
interface JobReadResult {
|
|
1617
|
+
output: string;
|
|
1618
|
+
/** Total bytes ever in the buffer (pre-slice). Caller passes back as `since`. */
|
|
1619
|
+
byteLength: number;
|
|
1620
|
+
running: boolean;
|
|
1621
|
+
exitCode: number | null;
|
|
1622
|
+
command: string;
|
|
1623
|
+
pid: number | null;
|
|
1624
|
+
spawnError?: string;
|
|
1625
|
+
}
|
|
1626
|
+
interface JobWaitResult {
|
|
1627
|
+
exited: boolean;
|
|
1628
|
+
exitCode: number | null;
|
|
1629
|
+
latestOutput: string;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
interface RunCommandResult {
|
|
1633
|
+
exitCode: number | null;
|
|
1634
|
+
/** Combined stdout+stderr, truncated to `maxOutputChars` with a marker. */
|
|
1635
|
+
output: string;
|
|
1636
|
+
/** True when the process was killed for exceeding `timeoutSec`. */
|
|
1637
|
+
timedOut: boolean;
|
|
1638
|
+
}
|
|
1639
|
+
declare function runCommand(cmd: string, opts: {
|
|
1640
|
+
cwd: string;
|
|
1641
|
+
timeoutSec?: number;
|
|
1642
|
+
maxOutputChars?: number;
|
|
1643
|
+
signal?: AbortSignal;
|
|
1644
|
+
}): Promise<RunCommandResult>;
|
|
1645
|
+
interface ResolveExecutableOptions {
|
|
1646
|
+
platform?: NodeJS.Platform;
|
|
1647
|
+
env?: {
|
|
1648
|
+
PATH?: string;
|
|
1649
|
+
PATHEXT?: string;
|
|
1650
|
+
};
|
|
1651
|
+
isFile?: (path: string) => boolean;
|
|
1652
|
+
pathDelimiter?: string;
|
|
1653
|
+
}
|
|
1654
|
+
/** CreateProcess ignores PATHEXT — bare `npm` fails ENOENT under `shell:false` without this resolver. */
|
|
1655
|
+
declare function resolveExecutable(cmd: string, opts?: ResolveExecutableOptions): string;
|
|
1656
|
+
/** Windows workarounds: PATHEXT lookup + CVE-2024-27980 prohibition on direct `.cmd`/`.bat` spawn. */
|
|
1657
|
+
declare function prepareSpawn(argv: readonly string[], opts?: ResolveExecutableOptions): {
|
|
1658
|
+
bin: string;
|
|
1659
|
+
args: string[];
|
|
1660
|
+
spawnOverrides: SpawnOptions;
|
|
1661
|
+
};
|
|
1662
|
+
/** Targets `-Command` only — PowerShell quoting is finicky enough that wrapping script-file mode could break it. */
|
|
1663
|
+
declare function injectPowerShellUtf8(args: readonly string[]): string[] | null;
|
|
1664
|
+
/** Single `&` (not `&&`) so the command still runs on Win7 where chcp can return non-zero. */
|
|
1665
|
+
declare function withUtf8Codepage(cmdline: string): string;
|
|
1666
|
+
/** Doubles embedded quotes per cmd.exe's `""` escape rule; bare alnum passes through unquoted. */
|
|
1667
|
+
declare function quoteForCmdExe(arg: string): string;
|
|
1668
|
+
|
|
1669
|
+
/** No env / glob / backtick / `$(…)` expansion — prevents bypass of allowlist via concatenation. */
|
|
1670
|
+
declare function tokenizeCommand(cmd: string): string[];
|
|
1671
|
+
/** Up-front detection — without it, `dir | findstr foo` quotes `|` literal and pipe silently fails. */
|
|
1672
|
+
declare function detectShellOperator(cmd: string): string | null;
|
|
1673
|
+
/** Allowlist match on leading argv tokens; demoted by `RISKY_ARGS` when a destructive flag appears in the tail. */
|
|
1674
|
+
declare function isAllowed(cmd: string, extra?: readonly string[]): boolean;
|
|
1675
|
+
|
|
1676
|
+
/** cwd pinned to root; non-allowlisted commands throw to a UI confirm gate; spawn is `shell: false`, tokenized argv only. */
|
|
1677
|
+
|
|
1678
|
+
interface ShellToolsOptions {
|
|
1679
|
+
/** Directory to run commands in. Must be an absolute path. */
|
|
1680
|
+
rootDir: string;
|
|
1681
|
+
/** Seconds before an individual command is killed. Default: 60. */
|
|
1682
|
+
timeoutSec?: number;
|
|
1683
|
+
maxOutputChars?: number;
|
|
1684
|
+
/** Getter form is load-bearing — newly-persisted "always allow" prefixes MUST take effect mid-session. */
|
|
1685
|
+
extraAllowed?: readonly string[] | (() => readonly string[]);
|
|
1686
|
+
/** Getter form lets `editMode === "yolo"` flip mid-session without re-registering tools. */
|
|
1687
|
+
allowAll?: boolean | (() => boolean);
|
|
1688
|
+
jobs?: JobRegistry;
|
|
1689
|
+
}
|
|
1690
|
+
/** Error thrown by `run_command` when the command isn't allowlisted. */
|
|
1691
|
+
declare class NeedsConfirmationError extends Error {
|
|
1692
|
+
readonly command: string;
|
|
1693
|
+
constructor(command: string);
|
|
1694
|
+
}
|
|
1695
|
+
declare function registerShellTools(registry: ToolRegistry, opts: ShellToolsOptions): ToolRegistry;
|
|
1696
|
+
declare function formatCommandResult(cmd: string, r: RunCommandResult): string;
|
|
1697
|
+
|
|
1698
|
+
/** web_search uses Mojeek (DDG returns anti-bot 202 to unauthenticated POSTs); web_fetch sniffs HTML to text. */
|
|
1699
|
+
|
|
1700
|
+
interface SearchResult {
|
|
1701
|
+
title: string;
|
|
1702
|
+
url: string;
|
|
1703
|
+
snippet: string;
|
|
1704
|
+
}
|
|
1705
|
+
interface PageContent {
|
|
1706
|
+
url: string;
|
|
1707
|
+
title?: string;
|
|
1708
|
+
text: string;
|
|
1709
|
+
/** True when the extracted text was clipped to fit the cap. */
|
|
1710
|
+
truncated: boolean;
|
|
1711
|
+
}
|
|
1712
|
+
interface WebFetchOptions {
|
|
1713
|
+
/** Max bytes of extracted text. Defaults to 32_000 to match tool-result cap. */
|
|
1714
|
+
maxChars?: number;
|
|
1715
|
+
/** Timeout in ms. Defaults to 15_000. */
|
|
1716
|
+
timeoutMs?: number;
|
|
1717
|
+
signal?: AbortSignal;
|
|
1718
|
+
}
|
|
1719
|
+
interface WebSearchOptions {
|
|
1720
|
+
topK?: number;
|
|
1721
|
+
signal?: AbortSignal;
|
|
1722
|
+
/** Backend engine: "mojeek" (scrapes Mojeek HTML) or "searxng" (self-hosted SearXNG JSON API). */
|
|
1723
|
+
engine?: "mojeek" | "searxng";
|
|
1724
|
+
/** Base URL for SearXNG. Default http://localhost:8080. */
|
|
1725
|
+
endpoint?: string;
|
|
1726
|
+
}
|
|
1727
|
+
/** Distinguishes "truly 0 results" from "layout changed / blocked" so callers can tell. */
|
|
1728
|
+
declare function webSearch(query: string, opts?: WebSearchOptions): Promise<SearchResult[]>;
|
|
1729
|
+
/** Parse SearXNG HTML search results using node-html-parser. */
|
|
1730
|
+
declare function parseSearxngHtmlResults(html: string): SearchResult[];
|
|
1731
|
+
/** Title-anchor + snippet-paragraph passes paired positionally — robust to attribute reorder. */
|
|
1732
|
+
declare function parseMojeekResults(html: string): SearchResult[];
|
|
1733
|
+
declare function webFetch(url: string, opts?: WebFetchOptions): Promise<PageContent>;
|
|
1734
|
+
declare function htmlToText(html: string): string;
|
|
1735
|
+
interface WebToolsOptions {
|
|
1736
|
+
/** Default top-K for `web_search` when the model doesn't specify. */
|
|
1737
|
+
defaultTopK?: number;
|
|
1738
|
+
/** Byte cap for `web_fetch` extracted text. */
|
|
1739
|
+
maxFetchChars?: number;
|
|
1740
|
+
/** Backend engine: "mojeek" (default, scrapes Mojeek) or "searxng" (self-hosted SearXNG). */
|
|
1741
|
+
webSearchEngine?: "mojeek" | "searxng";
|
|
1742
|
+
/** Base URL for SearXNG (default http://localhost:8080). */
|
|
1743
|
+
webSearchEndpoint?: string;
|
|
1744
|
+
}
|
|
1745
|
+
declare function registerWebTools(registry: ToolRegistry, opts?: WebToolsOptions): ToolRegistry;
|
|
1746
|
+
declare function formatSearchResults(query: string, results: SearchResult[]): string;
|
|
1747
|
+
|
|
1748
|
+
/** JSONL append-only message log under `~/.luckerr/sessions/`; concurrent-write safe. */
|
|
1749
|
+
|
|
1750
|
+
interface SessionInfo {
|
|
1751
|
+
name: string;
|
|
1752
|
+
path: string;
|
|
1753
|
+
size: number;
|
|
1754
|
+
messageCount: number;
|
|
1755
|
+
mtime: Date;
|
|
1756
|
+
meta: SessionMeta;
|
|
1757
|
+
}
|
|
1758
|
+
interface SessionMeta {
|
|
1759
|
+
branch?: string;
|
|
1760
|
+
summary?: string;
|
|
1761
|
+
totalCostUsd?: number;
|
|
1762
|
+
turnCount?: number;
|
|
1763
|
+
/** Absolute path of the workspace root the session was created/used in. */
|
|
1764
|
+
workspace?: string;
|
|
1765
|
+
/** Wallet currency at last save — used to format `totalCostUsd` in the picker without re-fetching balance. */
|
|
1766
|
+
balanceCurrency?: string;
|
|
1767
|
+
/** Cumulative cache hit / miss tokens across the session — survives resume so /status cache% isn't 0 on a fresh boot. */
|
|
1768
|
+
cacheHitTokens?: number;
|
|
1769
|
+
cacheMissTokens?: number;
|
|
1770
|
+
/** Last turn's promptTokens — lets /status render the context bar before the next turn fires. */
|
|
1771
|
+
lastPromptTokens?: number;
|
|
1772
|
+
}
|
|
1773
|
+
declare function sessionsDir(): string;
|
|
1774
|
+
declare function sessionPath(name: string): string;
|
|
1775
|
+
declare function sanitizeName(name: string): string;
|
|
1776
|
+
declare function loadSessionMessages(name: string): ChatMessage[];
|
|
1777
|
+
declare function appendSessionMessage(name: string, message: ChatMessage): void;
|
|
1778
|
+
declare function listSessions(): SessionInfo[];
|
|
1779
|
+
declare function deleteSession(name: string): boolean;
|
|
1780
|
+
|
|
1781
|
+
declare function loadDotenv(path?: string): void;
|
|
1782
|
+
|
|
1783
|
+
/** Transcripts are receipts (cost/usage/prefix); sessions are memory (ChatMessages). Don't conflate. */
|
|
1784
|
+
|
|
1785
|
+
interface TranscriptRecord {
|
|
1786
|
+
/** ISO-8601 timestamp at emit time. */
|
|
1787
|
+
ts: string;
|
|
1788
|
+
/** 1-based turn number within the session. */
|
|
1789
|
+
turn: number;
|
|
1790
|
+
/** LoopEvent role — "assistant_delta" | "assistant_final" | "tool" | "done" | ... */
|
|
1791
|
+
role: string;
|
|
1792
|
+
/** For assistant events, the final (or delta) text; for tool events, the tool result. */
|
|
1793
|
+
content: string;
|
|
1794
|
+
/** Tool name (role === "tool"). */
|
|
1795
|
+
tool?: string;
|
|
1796
|
+
/** JSON-string args the model sent for a tool call (role === "tool"). Persisted so diff can explain *why* two runs made different calls. */
|
|
1797
|
+
args?: string;
|
|
1798
|
+
/** DeepSeek token-usage snapshot (role === "assistant_final"). */
|
|
1799
|
+
usage?: RawUsage;
|
|
1800
|
+
/** USD cost of this turn (role === "assistant_final"). */
|
|
1801
|
+
cost?: number;
|
|
1802
|
+
/** Model id that produced this turn. */
|
|
1803
|
+
model?: string;
|
|
1804
|
+
/** Lets diff attribute cache-hit delta to log stability vs prompt change. */
|
|
1805
|
+
prefixHash?: string;
|
|
1806
|
+
/** Optional error message (role === "error"). */
|
|
1807
|
+
error?: string;
|
|
1808
|
+
}
|
|
1809
|
+
interface TranscriptMeta {
|
|
1810
|
+
version: 1;
|
|
1811
|
+
source: string;
|
|
1812
|
+
model?: string;
|
|
1813
|
+
task?: string;
|
|
1814
|
+
mode?: string;
|
|
1815
|
+
repeat?: number;
|
|
1816
|
+
startedAt: string;
|
|
1817
|
+
}
|
|
1818
|
+
interface ReadTranscriptResult {
|
|
1819
|
+
meta: TranscriptMeta | null;
|
|
1820
|
+
records: TranscriptRecord[];
|
|
1821
|
+
}
|
|
1822
|
+
declare function recordFromLoopEvent(ev: LoopEvent, extra: {
|
|
1823
|
+
model: string;
|
|
1824
|
+
prefixHash: string;
|
|
1825
|
+
}): TranscriptRecord;
|
|
1826
|
+
/**
|
|
1827
|
+
* Append a record to an open write stream. Caller owns the stream lifecycle.
|
|
1828
|
+
*/
|
|
1829
|
+
declare function writeRecord(stream: WriteStream, record: TranscriptRecord): void;
|
|
1830
|
+
/**
|
|
1831
|
+
* Write a _meta line to an open write stream. Call exactly once, at the top.
|
|
1832
|
+
*/
|
|
1833
|
+
declare function writeMeta(stream: WriteStream, meta: TranscriptMeta): void;
|
|
1834
|
+
/**
|
|
1835
|
+
* Convenience: open a stream, write meta, return stream.
|
|
1836
|
+
*/
|
|
1837
|
+
declare function openTranscriptFile(path: string, meta: TranscriptMeta): WriteStream;
|
|
1838
|
+
/** Tolerant: empty / malformed lines skipped, missing optionals OK — live chats may be mid-write. */
|
|
1839
|
+
declare function readTranscript(path: string): ReadTranscriptResult;
|
|
1840
|
+
declare function parseTranscript(raw: string): ReadTranscriptResult;
|
|
1841
|
+
|
|
1842
|
+
/** Reconstruct session economics from a transcript alone — offline audit, no API key. */
|
|
1843
|
+
|
|
1844
|
+
interface ReplayStats extends SessionSummary {
|
|
1845
|
+
/** Per-turn stats, in turn order. Only assistant_final records contribute. */
|
|
1846
|
+
perTurn: TurnStats[];
|
|
1847
|
+
/** Unique models that appeared in the transcript's assistant_final records. */
|
|
1848
|
+
models: string[];
|
|
1849
|
+
/** Unique prefix hashes that appeared. Length > 1 means the prefix churned (cache-hostile). */
|
|
1850
|
+
prefixHashes: string[];
|
|
1851
|
+
/** Count of user-role records (user turns issued). */
|
|
1852
|
+
userTurns: number;
|
|
1853
|
+
/** Count of tool-role records (tool calls executed). */
|
|
1854
|
+
toolCalls: number;
|
|
1855
|
+
}
|
|
1856
|
+
declare function replayFromFile(path: string): {
|
|
1857
|
+
parsed: ReadTranscriptResult;
|
|
1858
|
+
stats: ReplayStats;
|
|
1859
|
+
};
|
|
1860
|
+
declare function computeReplayStats(records: TranscriptRecord[]): ReplayStats;
|
|
1861
|
+
|
|
1862
|
+
/** Transcript diff — pairs assistant_final by turn number; unmatched extras become only_in_a / only_in_b. */
|
|
1863
|
+
|
|
1864
|
+
interface DiffSide {
|
|
1865
|
+
label: string;
|
|
1866
|
+
meta: ReadTranscriptResult["meta"];
|
|
1867
|
+
records: TranscriptRecord[];
|
|
1868
|
+
stats: ReplayStats;
|
|
1869
|
+
}
|
|
1870
|
+
interface TurnPair {
|
|
1871
|
+
turn: number;
|
|
1872
|
+
aAssistant?: TranscriptRecord;
|
|
1873
|
+
bAssistant?: TranscriptRecord;
|
|
1874
|
+
aTools: TranscriptRecord[];
|
|
1875
|
+
bTools: TranscriptRecord[];
|
|
1876
|
+
kind: "match" | "diverge" | "only_in_a" | "only_in_b";
|
|
1877
|
+
/** When kind === "diverge", a short one-liner pointing at what differs. */
|
|
1878
|
+
divergenceNote?: string;
|
|
1879
|
+
}
|
|
1880
|
+
interface DiffReport {
|
|
1881
|
+
a: DiffSide;
|
|
1882
|
+
b: DiffSide;
|
|
1883
|
+
pairs: TurnPair[];
|
|
1884
|
+
firstDivergenceTurn: number | null;
|
|
1885
|
+
}
|
|
1886
|
+
declare function diffTranscripts(a: {
|
|
1887
|
+
label: string;
|
|
1888
|
+
parsed: ReadTranscriptResult;
|
|
1889
|
+
}, b: {
|
|
1890
|
+
label: string;
|
|
1891
|
+
parsed: ReadTranscriptResult;
|
|
1892
|
+
}): DiffReport;
|
|
1893
|
+
/** Falls back to token-overlap above 2000 chars to keep diff fast on chatty transcripts. */
|
|
1894
|
+
declare function similarity(a: string, b: string): number;
|
|
1895
|
+
interface RenderOptions {
|
|
1896
|
+
/** Monochrome output (for file redirection or piping). Defaults to true. */
|
|
1897
|
+
monochrome?: boolean;
|
|
1898
|
+
}
|
|
1899
|
+
declare function renderSummaryTable(report: DiffReport, _opts?: RenderOptions): string;
|
|
1900
|
+
declare function renderMarkdown(report: DiffReport): string;
|
|
1901
|
+
|
|
1902
|
+
/** MCP types (spec 2024-11-05). Stdio wire format is NDJSON — one JSON-RPC message per line, no Content-Length framing. */
|
|
1903
|
+
type JsonRpcId = string | number;
|
|
1904
|
+
interface JsonRpcRequest<P = unknown> {
|
|
1905
|
+
jsonrpc: "2.0";
|
|
1906
|
+
id: JsonRpcId;
|
|
1907
|
+
method: string;
|
|
1908
|
+
params?: P;
|
|
1909
|
+
}
|
|
1910
|
+
interface JsonRpcNotification<P = unknown> {
|
|
1911
|
+
jsonrpc: "2.0";
|
|
1912
|
+
method: string;
|
|
1913
|
+
params?: P;
|
|
1914
|
+
}
|
|
1915
|
+
interface JsonRpcSuccess<R = unknown> {
|
|
1916
|
+
jsonrpc: "2.0";
|
|
1917
|
+
id: JsonRpcId;
|
|
1918
|
+
result: R;
|
|
1919
|
+
}
|
|
1920
|
+
interface JsonRpcError {
|
|
1921
|
+
jsonrpc: "2.0";
|
|
1922
|
+
id: JsonRpcId | null;
|
|
1923
|
+
error: {
|
|
1924
|
+
/** JSON-RPC standard codes: -32700 parse, -32600 invalid request, -32601 method not found, -32602 invalid params, -32603 internal. MCP also defines its own range. */
|
|
1925
|
+
code: number;
|
|
1926
|
+
message: string;
|
|
1927
|
+
data?: unknown;
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
type JsonRpcResponse<R = unknown> = JsonRpcSuccess<R> | JsonRpcError;
|
|
1931
|
+
type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcSuccess | JsonRpcError;
|
|
1932
|
+
interface McpClientInfo {
|
|
1933
|
+
name: string;
|
|
1934
|
+
version: string;
|
|
1935
|
+
}
|
|
1936
|
+
interface InitializeResult {
|
|
1937
|
+
protocolVersion: string;
|
|
1938
|
+
serverInfo: {
|
|
1939
|
+
name: string;
|
|
1940
|
+
version: string;
|
|
1941
|
+
};
|
|
1942
|
+
capabilities: {
|
|
1943
|
+
tools?: {
|
|
1944
|
+
listChanged?: boolean;
|
|
1945
|
+
};
|
|
1946
|
+
resources?: unknown;
|
|
1947
|
+
prompts?: unknown;
|
|
1948
|
+
};
|
|
1949
|
+
instructions?: string;
|
|
1950
|
+
}
|
|
1951
|
+
interface McpToolSchema {
|
|
1952
|
+
/** JSON Schema — compatible with Luckerr's tools.ts JSONSchema shape. */
|
|
1953
|
+
type?: string;
|
|
1954
|
+
properties?: Record<string, unknown>;
|
|
1955
|
+
required?: string[];
|
|
1956
|
+
[extra: string]: unknown;
|
|
1957
|
+
}
|
|
1958
|
+
interface McpTool {
|
|
1959
|
+
name: string;
|
|
1960
|
+
description?: string;
|
|
1961
|
+
/** MCP calls this `inputSchema`. Luckerr's `parameters` field is the same concept. */
|
|
1962
|
+
inputSchema: McpToolSchema;
|
|
1963
|
+
}
|
|
1964
|
+
interface ListToolsResult {
|
|
1965
|
+
tools: McpTool[];
|
|
1966
|
+
nextCursor?: string;
|
|
1967
|
+
}
|
|
1968
|
+
interface ProgressNotificationParams {
|
|
1969
|
+
progressToken: string | number;
|
|
1970
|
+
progress: number;
|
|
1971
|
+
total?: number;
|
|
1972
|
+
message?: string;
|
|
1973
|
+
}
|
|
1974
|
+
/** Values a `ProgressHandler` receives — `progressToken` is already matched away. */
|
|
1975
|
+
interface McpProgressInfo {
|
|
1976
|
+
progress: number;
|
|
1977
|
+
total?: number;
|
|
1978
|
+
message?: string;
|
|
1979
|
+
}
|
|
1980
|
+
type McpProgressHandler = (info: McpProgressInfo) => void;
|
|
1981
|
+
interface McpContentBlockText {
|
|
1982
|
+
type: "text";
|
|
1983
|
+
text: string;
|
|
1984
|
+
}
|
|
1985
|
+
interface McpContentBlockImage {
|
|
1986
|
+
type: "image";
|
|
1987
|
+
data: string;
|
|
1988
|
+
mimeType: string;
|
|
1989
|
+
}
|
|
1990
|
+
/** MCP result content is an array of typed blocks. Luckerr consumes only text for now — image blocks get stringified with a placeholder. */
|
|
1991
|
+
type McpContentBlock = McpContentBlockText | McpContentBlockImage;
|
|
1992
|
+
interface CallToolResult {
|
|
1993
|
+
content: McpContentBlock[];
|
|
1994
|
+
/** True = tool raised an error; the content describes it. */
|
|
1995
|
+
isError?: boolean;
|
|
1996
|
+
}
|
|
1997
|
+
interface McpResource {
|
|
1998
|
+
uri: string;
|
|
1999
|
+
name: string;
|
|
2000
|
+
description?: string;
|
|
2001
|
+
/** Hint for the content type (e.g. "text/markdown"). Purely informational. */
|
|
2002
|
+
mimeType?: string;
|
|
2003
|
+
}
|
|
2004
|
+
interface ListResourcesResult {
|
|
2005
|
+
resources: McpResource[];
|
|
2006
|
+
nextCursor?: string;
|
|
2007
|
+
}
|
|
2008
|
+
/** Server populates exactly one of `text` (UTF-8) or `blob` (base64) per entry. */
|
|
2009
|
+
interface McpResourceContentsText {
|
|
2010
|
+
uri: string;
|
|
2011
|
+
mimeType?: string;
|
|
2012
|
+
text: string;
|
|
2013
|
+
}
|
|
2014
|
+
interface McpResourceContentsBlob {
|
|
2015
|
+
uri: string;
|
|
2016
|
+
mimeType?: string;
|
|
2017
|
+
blob: string;
|
|
2018
|
+
}
|
|
2019
|
+
type McpResourceContents = McpResourceContentsText | McpResourceContentsBlob;
|
|
2020
|
+
interface ReadResourceResult {
|
|
2021
|
+
contents: McpResourceContents[];
|
|
2022
|
+
}
|
|
2023
|
+
interface McpPromptArgument {
|
|
2024
|
+
name: string;
|
|
2025
|
+
description?: string;
|
|
2026
|
+
required?: boolean;
|
|
2027
|
+
}
|
|
2028
|
+
interface McpPrompt {
|
|
2029
|
+
name: string;
|
|
2030
|
+
description?: string;
|
|
2031
|
+
arguments?: McpPromptArgument[];
|
|
2032
|
+
}
|
|
2033
|
+
interface ListPromptsResult {
|
|
2034
|
+
prompts: McpPrompt[];
|
|
2035
|
+
nextCursor?: string;
|
|
2036
|
+
}
|
|
2037
|
+
interface McpPromptMessage {
|
|
2038
|
+
role: "user" | "assistant";
|
|
2039
|
+
content: McpContentBlock | McpPromptResourceBlock;
|
|
2040
|
+
}
|
|
2041
|
+
interface McpPromptResourceBlock {
|
|
2042
|
+
type: "resource";
|
|
2043
|
+
resource: McpResourceContents;
|
|
2044
|
+
}
|
|
2045
|
+
interface GetPromptResult {
|
|
2046
|
+
description?: string;
|
|
2047
|
+
messages: McpPromptMessage[];
|
|
2048
|
+
}
|
|
2049
|
+
/** Current MCP protocol version Luckerr is coded against. */
|
|
2050
|
+
declare const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
2051
|
+
/** Type guard — success vs error response. */
|
|
2052
|
+
declare function isJsonRpcError(msg: JsonRpcResponse): msg is JsonRpcError;
|
|
2053
|
+
|
|
2054
|
+
/** MCP stdio = newline-delimited JSON-RPC; transport iface lets tests fake it without spawning. */
|
|
2055
|
+
|
|
2056
|
+
interface McpTransport {
|
|
2057
|
+
/** Send one JSON-RPC message. Resolves when the bytes are accepted. */
|
|
2058
|
+
send(message: JsonRpcMessage): Promise<void>;
|
|
2059
|
+
/** Async iterator over incoming messages. Ends when the connection closes. */
|
|
2060
|
+
messages(): AsyncIterableIterator<JsonRpcMessage>;
|
|
2061
|
+
/** Close the underlying resource (kill child process, close streams). */
|
|
2062
|
+
close(): Promise<void>;
|
|
2063
|
+
}
|
|
2064
|
+
interface StdioTransportOptions {
|
|
2065
|
+
/** Argv to spawn. First element is the command. */
|
|
2066
|
+
command: string;
|
|
2067
|
+
args?: string[];
|
|
2068
|
+
/** Env overlay — merged over process.env unless replaceEnv=true. */
|
|
2069
|
+
env?: Record<string, string>;
|
|
2070
|
+
/** When true, only the env above is visible to the child. Default false. */
|
|
2071
|
+
replaceEnv?: boolean;
|
|
2072
|
+
/** CWD for the child. Default: process.cwd(). */
|
|
2073
|
+
cwd?: string;
|
|
2074
|
+
/** Default true on win32 to resolve `.cmd`/`.bat` wrappers (npx.cmd etc.). */
|
|
2075
|
+
shell?: boolean;
|
|
2076
|
+
}
|
|
2077
|
+
declare class StdioTransport implements McpTransport {
|
|
2078
|
+
private readonly child;
|
|
2079
|
+
private readonly queue;
|
|
2080
|
+
private readonly waiters;
|
|
2081
|
+
private closed;
|
|
2082
|
+
private stdoutBuffer;
|
|
2083
|
+
constructor(opts: StdioTransportOptions);
|
|
2084
|
+
send(message: JsonRpcMessage): Promise<void>;
|
|
2085
|
+
messages(): AsyncIterableIterator<JsonRpcMessage>;
|
|
2086
|
+
close(): Promise<void>;
|
|
2087
|
+
/** Parse incoming stdout chunks into NDJSON messages. */
|
|
2088
|
+
private onStdout;
|
|
2089
|
+
private onClose;
|
|
2090
|
+
private push;
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
interface McpClientOptions {
|
|
2094
|
+
transport: McpTransport;
|
|
2095
|
+
clientInfo?: McpClientInfo;
|
|
2096
|
+
/** Per-request timeout. Default 60s. */
|
|
2097
|
+
requestTimeoutMs?: number;
|
|
2098
|
+
}
|
|
2099
|
+
declare class McpClient {
|
|
2100
|
+
private readonly transport;
|
|
2101
|
+
private readonly clientInfo;
|
|
2102
|
+
private readonly requestTimeoutMs;
|
|
2103
|
+
private readonly pending;
|
|
2104
|
+
private nextId;
|
|
2105
|
+
private readerStarted;
|
|
2106
|
+
private initialized;
|
|
2107
|
+
private _serverCapabilities;
|
|
2108
|
+
private _serverInfo;
|
|
2109
|
+
private _protocolVersion;
|
|
2110
|
+
private _instructions;
|
|
2111
|
+
private readonly progressHandlers;
|
|
2112
|
+
private nextProgressToken;
|
|
2113
|
+
constructor(opts: McpClientOptions);
|
|
2114
|
+
/** Server's advertised capabilities, available after initialize(). */
|
|
2115
|
+
get serverCapabilities(): InitializeResult["capabilities"];
|
|
2116
|
+
/** Server's self-reported name + version, available after initialize(). */
|
|
2117
|
+
get serverInfo(): InitializeResult["serverInfo"];
|
|
2118
|
+
/** Protocol version the server agreed to during the handshake. */
|
|
2119
|
+
get protocolVersion(): string;
|
|
2120
|
+
/** Optional free-form instructions the server provides at handshake. */
|
|
2121
|
+
get serverInstructions(): string | undefined;
|
|
2122
|
+
/** Compliant servers reject other methods until this completes. */
|
|
2123
|
+
initialize(): Promise<InitializeResult>;
|
|
2124
|
+
/** List tools the server exposes. */
|
|
2125
|
+
listTools(): Promise<ListToolsResult>;
|
|
2126
|
+
/** Abort sends `notifications/cancelled` and rejects immediately; late server responses are dropped. */
|
|
2127
|
+
callTool(name: string, args?: Record<string, unknown>, opts?: {
|
|
2128
|
+
onProgress?: McpProgressHandler;
|
|
2129
|
+
signal?: AbortSignal;
|
|
2130
|
+
}): Promise<CallToolResult>;
|
|
2131
|
+
/** Throws on method-not-found; callers should gate on `serverCapabilities.resources` first. */
|
|
2132
|
+
listResources(cursor?: string): Promise<ListResourcesResult>;
|
|
2133
|
+
/** Read the contents of a resource by URI. */
|
|
2134
|
+
readResource(uri: string): Promise<ReadResourceResult>;
|
|
2135
|
+
/** List prompt templates the server exposes. */
|
|
2136
|
+
listPrompts(cursor?: string): Promise<ListPromptsResult>;
|
|
2137
|
+
getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult>;
|
|
2138
|
+
/** Close the transport and reject any outstanding requests. */
|
|
2139
|
+
close(): Promise<void>;
|
|
2140
|
+
private assertInitialized;
|
|
2141
|
+
private request;
|
|
2142
|
+
private startReaderIfNeeded;
|
|
2143
|
+
private readLoop;
|
|
2144
|
+
private dispatch;
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
/** MCP HTTP+SSE transport (spec 2024-11-05) — POST endpoint URL arrives as the first `event: endpoint` SSE frame. */
|
|
2148
|
+
|
|
2149
|
+
interface SseTransportOptions {
|
|
2150
|
+
/** SSE endpoint URL, e.g. `https://mcp.example.com/sse`. */
|
|
2151
|
+
url: string;
|
|
2152
|
+
/** Extra headers sent on both the SSE GET and the JSON-RPC POSTs (e.g. `Authorization`). */
|
|
2153
|
+
headers?: Record<string, string>;
|
|
2154
|
+
}
|
|
2155
|
+
declare class SseTransport implements McpTransport {
|
|
2156
|
+
private readonly url;
|
|
2157
|
+
private readonly headers;
|
|
2158
|
+
private readonly queue;
|
|
2159
|
+
private readonly waiters;
|
|
2160
|
+
private readonly controller;
|
|
2161
|
+
private closed;
|
|
2162
|
+
private postUrl;
|
|
2163
|
+
private readonly endpointReady;
|
|
2164
|
+
private resolveEndpoint;
|
|
2165
|
+
private rejectEndpoint;
|
|
2166
|
+
constructor(opts: SseTransportOptions);
|
|
2167
|
+
send(message: JsonRpcMessage): Promise<void>;
|
|
2168
|
+
messages(): AsyncIterableIterator<JsonRpcMessage>;
|
|
2169
|
+
close(): Promise<void>;
|
|
2170
|
+
private runStream;
|
|
2171
|
+
private handleEvent;
|
|
2172
|
+
private failHandshake;
|
|
2173
|
+
private pushMessage;
|
|
2174
|
+
private pushError;
|
|
2175
|
+
private markClosed;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
/** MCP Streamable HTTP transport (2025-03-26) — POST-only; no long-lived GET stream, no Last-Event-ID resume. */
|
|
2179
|
+
|
|
2180
|
+
interface StreamableHttpTransportOptions {
|
|
2181
|
+
/** Streamable HTTP endpoint URL, e.g. `https://mcp.example.com/mcp`. */
|
|
2182
|
+
url: string;
|
|
2183
|
+
/** Extra headers sent on every request (e.g. `Authorization`). */
|
|
2184
|
+
headers?: Record<string, string>;
|
|
2185
|
+
}
|
|
2186
|
+
declare class StreamableHttpTransport implements McpTransport {
|
|
2187
|
+
private readonly url;
|
|
2188
|
+
private readonly extraHeaders;
|
|
2189
|
+
private readonly queue;
|
|
2190
|
+
private readonly waiters;
|
|
2191
|
+
private readonly controller;
|
|
2192
|
+
/** Session id minted by server on (typically) the initialize response. */
|
|
2193
|
+
private sessionId;
|
|
2194
|
+
private closed;
|
|
2195
|
+
/** Background SSE read-loops kicked off by send(); awaited on close(). */
|
|
2196
|
+
private readonly streams;
|
|
2197
|
+
constructor(opts: StreamableHttpTransportOptions);
|
|
2198
|
+
send(message: JsonRpcMessage): Promise<void>;
|
|
2199
|
+
messages(): AsyncIterableIterator<JsonRpcMessage>;
|
|
2200
|
+
close(): Promise<void>;
|
|
2201
|
+
/** Visible for tests — confirm session header round-trip. */
|
|
2202
|
+
getSessionId(): string | null;
|
|
2203
|
+
private consumeStream;
|
|
2204
|
+
private pushMessage;
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
/** Per-server ring-buffered latency tracker; emits a "slow" event on threshold cross only. */
|
|
2208
|
+
interface SlowEvent {
|
|
2209
|
+
serverName: string;
|
|
2210
|
+
p95Ms: number;
|
|
2211
|
+
sampleSize: number;
|
|
2212
|
+
}
|
|
2213
|
+
interface LatencyTrackerOptions {
|
|
2214
|
+
thresholdMs?: number;
|
|
2215
|
+
onSlow?: (ev: SlowEvent) => void;
|
|
2216
|
+
}
|
|
2217
|
+
declare class LatencyTracker {
|
|
2218
|
+
private readonly serverName;
|
|
2219
|
+
private samples;
|
|
2220
|
+
private wasOverThreshold;
|
|
2221
|
+
private readonly thresholdMs;
|
|
2222
|
+
private readonly onSlow?;
|
|
2223
|
+
constructor(serverName: string, opts?: LatencyTrackerOptions);
|
|
2224
|
+
record(elapsedMs: number): void;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
interface BridgeOptions {
|
|
2228
|
+
/** Prefix for tool names — disambiguates collisions when bridging multiple servers. */
|
|
2229
|
+
namePrefix?: string;
|
|
2230
|
+
/** Registry to populate. Creates a fresh one if omitted. */
|
|
2231
|
+
registry?: ToolRegistry;
|
|
2232
|
+
/** Auto-flatten deep schemas (Pillar 3). Defaults to the registry's own default (true). */
|
|
2233
|
+
autoFlatten?: boolean;
|
|
2234
|
+
/** Cap on tool result chars; head+tail truncation. Floor against context-poisoning oversized reads. */
|
|
2235
|
+
maxResultChars?: number;
|
|
2236
|
+
/** Absent → no `_meta.progressToken` sent and server won't emit progress. */
|
|
2237
|
+
onProgress?: (info: {
|
|
2238
|
+
toolName: string;
|
|
2239
|
+
progress: number;
|
|
2240
|
+
total?: number;
|
|
2241
|
+
message?: string;
|
|
2242
|
+
}) => void;
|
|
2243
|
+
/** Server name used to tag latency samples + slow events. Falls through to namePrefix without trailing `_`. */
|
|
2244
|
+
serverName?: string;
|
|
2245
|
+
/** p95 cutoff in ms before a slow event fires — defaults to 4000. */
|
|
2246
|
+
slowThresholdMs?: number;
|
|
2247
|
+
/** Fired exactly when the per-server p95 transitions over `slowThresholdMs`. */
|
|
2248
|
+
onSlow?: (ev: SlowEvent) => void;
|
|
2249
|
+
/** Indirection so reconnect can swap the underlying client without re-registering tools. */
|
|
2250
|
+
host?: McpClientHost;
|
|
2251
|
+
/** Awaited before each `callTool` — resolves on `connected`, rejects on `failed`, caps via `readyTimeoutMs`. */
|
|
2252
|
+
ready?: Promise<void>;
|
|
2253
|
+
/** How long to wait on `ready` before failing the dispatch. Default 30_000ms. */
|
|
2254
|
+
readyTimeoutMs?: number;
|
|
2255
|
+
}
|
|
2256
|
+
/** Mutable holder so `/mcp reconnect` can swap the underlying client without re-bridging tools. */
|
|
2257
|
+
interface McpClientHost {
|
|
2258
|
+
client: McpClient;
|
|
2259
|
+
}
|
|
2260
|
+
declare const DEFAULT_MAX_RESULT_CHARS = 32000;
|
|
2261
|
+
/** ~6% of DeepSeek V3 context. Char cap alone fails on CJK (~1 char/token). */
|
|
2262
|
+
declare const DEFAULT_MAX_RESULT_TOKENS = 8000;
|
|
2263
|
+
interface BridgeResult {
|
|
2264
|
+
registry: ToolRegistry;
|
|
2265
|
+
/** Names actually registered (may differ from MCP names when a prefix is applied). */
|
|
2266
|
+
registeredNames: string[];
|
|
2267
|
+
/** Names the server listed but the bridge skipped (e.g. invalid schemas). */
|
|
2268
|
+
skipped: Array<{
|
|
2269
|
+
name: string;
|
|
2270
|
+
reason: string;
|
|
2271
|
+
}>;
|
|
2272
|
+
}
|
|
2273
|
+
/** Resolved bridge environment that `registerSingleMcpTool` needs. Stored on summaries so reconnect can append new tools later. */
|
|
2274
|
+
interface BridgeEnv {
|
|
2275
|
+
registry: ToolRegistry;
|
|
2276
|
+
host: McpClientHost;
|
|
2277
|
+
prefix: string;
|
|
2278
|
+
maxResultChars: number;
|
|
2279
|
+
tracker: LatencyTracker | null;
|
|
2280
|
+
onProgress?: BridgeOptions["onProgress"];
|
|
2281
|
+
/** Optional readiness gate awaited before each `callTool` dispatch. */
|
|
2282
|
+
ready?: Promise<void>;
|
|
2283
|
+
/** Timeout for waiting on `ready` — milliseconds. Defaults to DEFAULT_READY_TIMEOUT_MS. */
|
|
2284
|
+
readyTimeoutMs?: number;
|
|
2285
|
+
/** Server name surfaced in timeout errors. Defaults to the prefix or "anon". */
|
|
2286
|
+
serverName?: string;
|
|
2287
|
+
}
|
|
2288
|
+
declare function bridgeMcpTools(client: McpClient, opts?: BridgeOptions): Promise<BridgeResult & {
|
|
2289
|
+
env: BridgeEnv;
|
|
2290
|
+
}>;
|
|
2291
|
+
interface FlattenOptions {
|
|
2292
|
+
/** Cap the flattened string at this many characters. Default: no cap. */
|
|
2293
|
+
maxChars?: number;
|
|
2294
|
+
}
|
|
2295
|
+
declare function flattenMcpResult(result: CallToolResult, opts?: FlattenOptions): string;
|
|
2296
|
+
/** Head + 1KB tail so error messages at end of stack traces aren't lost. */
|
|
2297
|
+
declare function truncateForModel(s: string, maxChars: number): string;
|
|
2298
|
+
/** Never tokenizes full input — pathological repetitive text (`AAAA…`) costs 30s+ on the pure-TS BPE port. */
|
|
2299
|
+
declare function truncateForModelByTokens(s: string, maxTokens: number): string;
|
|
2300
|
+
|
|
2301
|
+
/** Plain http:// stays HTTP+SSE for back-compat; Streamable HTTP is opt-in via the `streamable+` URL prefix. */
|
|
2302
|
+
interface StdioMcpSpec {
|
|
2303
|
+
transport: "stdio";
|
|
2304
|
+
/** Namespace prefix applied to each registered tool, or null if anonymous. */
|
|
2305
|
+
name: string | null;
|
|
2306
|
+
/** Argv[0]. */
|
|
2307
|
+
command: string;
|
|
2308
|
+
/** Remaining argv. */
|
|
2309
|
+
args: string[];
|
|
2310
|
+
}
|
|
2311
|
+
interface SseMcpSpec {
|
|
2312
|
+
transport: "sse";
|
|
2313
|
+
name: string | null;
|
|
2314
|
+
/** Fully qualified SSE endpoint URL. */
|
|
2315
|
+
url: string;
|
|
2316
|
+
}
|
|
2317
|
+
interface StreamableHttpMcpSpec {
|
|
2318
|
+
transport: "streamable-http";
|
|
2319
|
+
name: string | null;
|
|
2320
|
+
/** Fully qualified Streamable HTTP endpoint URL (no `streamable+` prefix). */
|
|
2321
|
+
url: string;
|
|
2322
|
+
}
|
|
2323
|
+
type McpSpec = StdioMcpSpec | SseMcpSpec | StreamableHttpMcpSpec;
|
|
2324
|
+
declare function parseMcpSpec(input: string): McpSpec;
|
|
2325
|
+
|
|
2326
|
+
/** Unsupported list methods surface as `{supported:false}` instead of throwing — minimal servers still get a clean report. */
|
|
2327
|
+
|
|
2328
|
+
interface InspectionReport {
|
|
2329
|
+
protocolVersion: string;
|
|
2330
|
+
serverInfo: {
|
|
2331
|
+
name: string;
|
|
2332
|
+
version: string;
|
|
2333
|
+
};
|
|
2334
|
+
capabilities: Record<string, unknown>;
|
|
2335
|
+
instructions?: string;
|
|
2336
|
+
tools: SectionResult<McpTool>;
|
|
2337
|
+
resources: SectionResult<McpResource>;
|
|
2338
|
+
prompts: SectionResult<McpPrompt>;
|
|
2339
|
+
/** Wall-clock for the three list calls combined; surfaced as the server's "p95-ish" latency in the browser. */
|
|
2340
|
+
elapsedMs: number;
|
|
2341
|
+
}
|
|
2342
|
+
type SectionResult<T> = {
|
|
2343
|
+
supported: true;
|
|
2344
|
+
items: T[];
|
|
2345
|
+
} | {
|
|
2346
|
+
supported: false;
|
|
2347
|
+
reason: string;
|
|
2348
|
+
};
|
|
2349
|
+
/** Caller owns initialize() / close() — keeps this pure so tests can feed a FakeMcpTransport. */
|
|
2350
|
+
declare function inspectMcpServer(client: McpClient): Promise<InspectionReport>;
|
|
2351
|
+
|
|
2352
|
+
/** SEARCH must match byte-for-byte; empty SEARCH = create new file. No fuzzy match — silent wrong edit beats a missing one. */
|
|
2353
|
+
interface EditBlock {
|
|
2354
|
+
/** Path as written by the model — relative to rootDir, or absolute. */
|
|
2355
|
+
path: string;
|
|
2356
|
+
/** Literal text to match in the target file. Empty → create new file. */
|
|
2357
|
+
search: string;
|
|
2358
|
+
/** Replacement text to write in place of `search`. */
|
|
2359
|
+
replace: string;
|
|
2360
|
+
/** Char offset in the source message where this block started. */
|
|
2361
|
+
offset: number;
|
|
2362
|
+
}
|
|
2363
|
+
type ApplyStatus =
|
|
2364
|
+
/** Edit landed on disk. */
|
|
2365
|
+
"applied"
|
|
2366
|
+
/** New file created (SEARCH was empty and file didn't exist). */
|
|
2367
|
+
| "created"
|
|
2368
|
+
/** File exists but SEARCH block wasn't found in its content. */
|
|
2369
|
+
| "not-found"
|
|
2370
|
+
/** File doesn't exist and SEARCH was non-empty (can't create without content). */
|
|
2371
|
+
| "file-missing"
|
|
2372
|
+
/** Path escapes rootDir — refused on safety grounds. */
|
|
2373
|
+
| "path-escape"
|
|
2374
|
+
/** fs write / read threw. */
|
|
2375
|
+
| "error";
|
|
2376
|
+
interface ApplyResult {
|
|
2377
|
+
path: string;
|
|
2378
|
+
status: ApplyStatus;
|
|
2379
|
+
/** Extra detail (e.g. error message) for logs. */
|
|
2380
|
+
message?: string;
|
|
2381
|
+
}
|
|
2382
|
+
declare function parseEditBlocks(text: string): EditBlock[];
|
|
2383
|
+
declare function applyEditBlock(block: EditBlock, rootDir: string): ApplyResult;
|
|
2384
|
+
declare function applyEditBlocks(blocks: EditBlock[], rootDir: string): ApplyResult[];
|
|
2385
|
+
interface EditSnapshot {
|
|
2386
|
+
/** Path relative to rootDir, as the block named it. */
|
|
2387
|
+
path: string;
|
|
2388
|
+
/** `null` = file didn't exist; restore means delete. */
|
|
2389
|
+
prevContent: string | null;
|
|
2390
|
+
}
|
|
2391
|
+
/** De-duped by path — one "before" snapshot per file even with multiple blocks. */
|
|
2392
|
+
declare function snapshotBeforeEdits(blocks: EditBlock[], rootDir: string): EditSnapshot[];
|
|
2393
|
+
declare function restoreSnapshots(snapshots: EditSnapshot[], rootDir: string): ApplyResult[];
|
|
2394
|
+
|
|
2395
|
+
/** Backward-compat — public-API const, frozen at the historical flash phrasing. Internal callers use codeSystemPrompt(rootDir, { modelId }) so the contract names the real tier (#582). */
|
|
2396
|
+
declare const CODE_SYSTEM_PROMPT: string;
|
|
2397
|
+
interface CodeSystemPromptOptions {
|
|
2398
|
+
/** True when semantic_search is registered for this run. Adds an
|
|
2399
|
+
* explicit routing fragment so the model picks it for intent-style
|
|
2400
|
+
* queries instead of defaulting to grep. */
|
|
2401
|
+
hasSemanticSearch?: boolean;
|
|
2402
|
+
/** Inline string appended after the generated code system prompt.
|
|
2403
|
+
* Preserves the default prompt — this is append-only, not a replacement. */
|
|
2404
|
+
systemAppend?: string;
|
|
2405
|
+
/** UTF-8 file contents appended after the generated code system prompt.
|
|
2406
|
+
* Preserves the default prompt — this is append-only, not a replacement. */
|
|
2407
|
+
systemAppendFile?: string;
|
|
2408
|
+
/** Model the loop will run on — interpolated into the escalation contract so the model can name itself correctly when asked (#582). */
|
|
2409
|
+
modelId?: string;
|
|
2410
|
+
}
|
|
2411
|
+
declare function codeSystemPrompt(rootDir: string, opts?: CodeSystemPromptOptions): string;
|
|
2412
|
+
|
|
2413
|
+
/** VERSION sourced from package.json so it never drifts from npm; latest-check returns null on any failure. */
|
|
2414
|
+
/** TTL for the on-disk cache entry. 24h keeps noise low; users who
|
|
2415
|
+
* want a fresh check can run `luckerr update` which passes
|
|
2416
|
+
* `force: true`. */
|
|
2417
|
+
declare const LATEST_CACHE_TTL_MS: number;
|
|
2418
|
+
/** Network timeout. Short — we never block the UI waiting on this. */
|
|
2419
|
+
declare const LATEST_FETCH_TIMEOUT_MS = 2000;
|
|
2420
|
+
declare const VERSION: string;
|
|
2421
|
+
interface GetLatestVersionOptions {
|
|
2422
|
+
/** Ignore the cached entry and always fetch fresh. Used by `luckerr update`. */
|
|
2423
|
+
force?: boolean;
|
|
2424
|
+
/** Registry URL override (tests). */
|
|
2425
|
+
registryUrl?: string;
|
|
2426
|
+
/** Home-directory override (tests). */
|
|
2427
|
+
homeDir?: string;
|
|
2428
|
+
/** Fetch implementation override (tests). Defaults to `globalThis.fetch`. */
|
|
2429
|
+
fetchImpl?: typeof fetch;
|
|
2430
|
+
/** TTL override (tests). */
|
|
2431
|
+
ttlMs?: number;
|
|
2432
|
+
/** Network timeout override (tests). */
|
|
2433
|
+
timeoutMs?: number;
|
|
2434
|
+
}
|
|
2435
|
+
/** Returns null on failure; cache only writes on success so bad responses can't poison it. */
|
|
2436
|
+
declare function getLatestVersion(opts?: GetLatestVersionOptions): Promise<string | null>;
|
|
2437
|
+
/** Pre-release with same core sorts BELOW the bare version — matches npm `latest` dist-tag semantics. */
|
|
2438
|
+
declare function compareVersions(a: string, b: string): number;
|
|
2439
|
+
type InstallSource = "npm" | "bun" | "pnpm" | "yarn" | "npx" | "unknown";
|
|
2440
|
+
/** Each manager owns a unique global path segment, so argv[1] tells us who installed us. */
|
|
2441
|
+
declare function detectInstallSource(bin?: string): InstallSource;
|
|
2442
|
+
/** Returns null when no path is given. Callers must check installSource first. */
|
|
2443
|
+
declare function isNpxInstall(): boolean;
|
|
2444
|
+
/** Pin npm to the install location via --prefix so `nvm use` doesn't redirect the install elsewhere. */
|
|
2445
|
+
declare function detectNpmInstallPrefix(bin?: string): string | null;
|
|
2446
|
+
|
|
2447
|
+
/** Append-only JSONL of per-turn tokens + cost; best-effort writes, never blocks the turn. No prompts/completions logged. */
|
|
2448
|
+
|
|
2449
|
+
/** One turn's snapshot — serialized verbatim as a JSONL line. */
|
|
2450
|
+
interface UsageRecord {
|
|
2451
|
+
/** Epoch millis when the record was written. */
|
|
2452
|
+
ts: number;
|
|
2453
|
+
/** Session name if the turn ran inside a persisted session, `null` for ephemeral. */
|
|
2454
|
+
session: string | null;
|
|
2455
|
+
/** Model id the turn ran against (drives the pricing lookup). */
|
|
2456
|
+
model: string;
|
|
2457
|
+
promptTokens: number;
|
|
2458
|
+
completionTokens: number;
|
|
2459
|
+
cacheHitTokens: number;
|
|
2460
|
+
cacheMissTokens: number;
|
|
2461
|
+
/** Total cost of the turn in USD. */
|
|
2462
|
+
costUsd: number;
|
|
2463
|
+
/** What the same turn would have cost at Claude Sonnet 4.6 rates. */
|
|
2464
|
+
claudeEquivUsd: number;
|
|
2465
|
+
/** Absent on legacy records — treat as "turn" when missing. */
|
|
2466
|
+
kind?: "turn" | "subagent";
|
|
2467
|
+
/** Present when `kind === "subagent"`. Attribution metadata for the /stats roll-up. */
|
|
2468
|
+
subagent?: {
|
|
2469
|
+
/** Skill that spawned it, when the spawn came from a `runAs: subagent` skill. */
|
|
2470
|
+
skillName?: string;
|
|
2471
|
+
/** First ~60 chars of the task prompt — enough context to recognize a run, never the full text. */
|
|
2472
|
+
taskPreview: string;
|
|
2473
|
+
/** Tool calls the child loop dispatched before returning. */
|
|
2474
|
+
toolIters: number;
|
|
2475
|
+
/** Wall-clock ms. */
|
|
2476
|
+
durationMs: number;
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
/** Where the log lives. Tests override via `opts.path`. */
|
|
2480
|
+
declare function defaultUsageLogPath(homeDirOverride?: string): string;
|
|
2481
|
+
interface AppendUsageInput {
|
|
2482
|
+
session: string | null;
|
|
2483
|
+
model: string;
|
|
2484
|
+
usage: Usage;
|
|
2485
|
+
/** Override the timestamp (tests). */
|
|
2486
|
+
now?: number;
|
|
2487
|
+
/** Override the log path (tests). */
|
|
2488
|
+
path?: string;
|
|
2489
|
+
/** When appending a subagent summary row, set `kind: "subagent"` and populate `subagent`. */
|
|
2490
|
+
kind?: "turn" | "subagent";
|
|
2491
|
+
subagent?: UsageRecord["subagent"];
|
|
2492
|
+
}
|
|
2493
|
+
/** Returns the record so tests can assert cost fields without re-reading the log. */
|
|
2494
|
+
declare function appendUsage(input: AppendUsageInput): UsageRecord;
|
|
2495
|
+
declare function readUsageLog(path?: string): UsageRecord[];
|
|
2496
|
+
/** One row of the `luckerr stats` dashboard — a rolled-up window. */
|
|
2497
|
+
interface UsageBucket {
|
|
2498
|
+
label: string;
|
|
2499
|
+
/** Start of the window as epoch millis. `0` = unbounded (all-time). */
|
|
2500
|
+
since: number;
|
|
2501
|
+
turns: number;
|
|
2502
|
+
promptTokens: number;
|
|
2503
|
+
completionTokens: number;
|
|
2504
|
+
cacheHitTokens: number;
|
|
2505
|
+
cacheMissTokens: number;
|
|
2506
|
+
costUsd: number;
|
|
2507
|
+
claudeEquivUsd: number;
|
|
2508
|
+
/** Recomputed from current pricing each aggregate — intentionally NOT frozen with `costUsd`. */
|
|
2509
|
+
cacheSavingsUsd: number;
|
|
2510
|
+
}
|
|
2511
|
+
/** Cache hit ratio for a bucket — zero denominator returns 0. */
|
|
2512
|
+
declare function bucketCacheHitRatio(b: UsageBucket): number;
|
|
2513
|
+
/** Savings vs Claude as a fraction (0.94 = 94% savings). 0 if Claude cost is 0. */
|
|
2514
|
+
declare function bucketSavingsFraction(b: UsageBucket): number;
|
|
2515
|
+
interface AggregateOptions {
|
|
2516
|
+
/** Override `Date.now()` for deterministic tests. */
|
|
2517
|
+
now?: number;
|
|
2518
|
+
}
|
|
2519
|
+
interface UsageAggregate {
|
|
2520
|
+
/** Fixed-order rolling windows: today, week, month, all-time. */
|
|
2521
|
+
buckets: UsageBucket[];
|
|
2522
|
+
/** Model id → turn count. Sorted descending; top entry is the "most used." */
|
|
2523
|
+
byModel: Array<{
|
|
2524
|
+
model: string;
|
|
2525
|
+
turns: number;
|
|
2526
|
+
}>;
|
|
2527
|
+
/** Session name → turn count. Sorted descending. Null sessions are grouped under `"(ephemeral)"`. */
|
|
2528
|
+
bySession: Array<{
|
|
2529
|
+
session: string;
|
|
2530
|
+
turns: number;
|
|
2531
|
+
}>;
|
|
2532
|
+
/** Earliest record's ts, or `null` when the log is empty. Drives "saved $X since <date>". */
|
|
2533
|
+
firstSeen: number | null;
|
|
2534
|
+
/** Latest record's ts, or `null` when the log is empty. */
|
|
2535
|
+
lastSeen: number | null;
|
|
2536
|
+
/** Undefined when no subagent records exist; counts spawns, not internal child-loop turns. */
|
|
2537
|
+
subagents?: SubagentAggregate;
|
|
2538
|
+
}
|
|
2539
|
+
/** Rolled-up view of all `kind: "subagent"` records. */
|
|
2540
|
+
interface SubagentAggregate {
|
|
2541
|
+
total: number;
|
|
2542
|
+
costUsd: number;
|
|
2543
|
+
totalDurationMs: number;
|
|
2544
|
+
/** Per-skill breakdown. Records without `skillName` (raw spawn_subagent calls) group under `"(adhoc)"`. */
|
|
2545
|
+
bySkill: Array<{
|
|
2546
|
+
skillName: string;
|
|
2547
|
+
count: number;
|
|
2548
|
+
costUsd: number;
|
|
2549
|
+
durationMs: number;
|
|
2550
|
+
}>;
|
|
2551
|
+
}
|
|
2552
|
+
/** Rolling 24h/7d/30d windows — avoids "it's 00:03, 'today' is empty" surprises. */
|
|
2553
|
+
declare function aggregateUsage(records: UsageRecord[], opts?: AggregateOptions): UsageAggregate;
|
|
2554
|
+
/** File-size helper for the stats header — "1.2 MB" etc. Returns "" if missing. */
|
|
2555
|
+
declare function formatLogSize(path?: string): string;
|
|
2556
|
+
|
|
2557
|
+
export { AT_MENTION_PATTERN, AT_PICKER_PREFIX, type AggregateOptions, AppendOnlyLog, type AppendUsageInput, type ApplyResult, type ApplyStatus, type AtMentionExpansion, type AtMentionOptions, type BalanceInfo, type BridgeOptions, type BridgeResult, CODE_SYSTEM_PROMPT, CacheFirstLoop, type CacheFirstLoopOptions, type CallToolResult, type ChatMessage, type ChatResponse, type ChoiceOption, ChoiceRequestedError, type ChoiceToolOptions, type CodeSystemPromptOptions, DEFAULT_AT_DIR_MAX_ENTRIES, DEFAULT_AT_MENTION_MAX_BYTES, DEFAULT_MAX_RESULT_CHARS, DEFAULT_MAX_RESULT_TOKENS, DEFAULT_PICKER_IGNORE_DIRS, DeepSeekClient, type DeepSeekClientOptions, type RenderOptions as DiffRenderOptions, type DiffReport, type DiffSide, type DirEntry, type EditBlock, type EditSnapshot, type EventRole, type FileWithStats, type FilesystemToolsOptions, type FlattenDecision, type FlattenOptions, type GetLatestVersionOptions, type GetPromptResult, HOOK_EVENTS, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME, type HookConfig, type HookEvent, type HookOutcome, type HookPayload, type HookReport, type HookScope, type HookSettings, type HookSpawnInput, type HookSpawnResult, type HookSpawner, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type InspectionReport, type InstallSource, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, LATEST_CACHE_TTL_MS, LATEST_FETCH_TIMEOUT_MS, type ListDirectoryOptions, type ListFilesOptions, type ListPromptsResult, type ListResourcesResult, type ListToolsResult, type LoadHookSettingsOptions, type LoopEvent, type LuckerrConfig, MCP_PROTOCOL_VERSION, MEMORY_INDEX_FILE, MEMORY_INDEX_MAX_CHARS, McpClient, type McpClientOptions, type McpContentBlock, type McpProgressHandler, type McpProgressInfo, type McpPrompt, type McpPromptArgument, type McpPromptMessage, type McpPromptResourceBlock, type McpResource, type McpResourceContents, type McpResourceContentsBlob, type McpResourceContentsText, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type MemoryEntry, type MemoryScope, MemoryStore, type MemoryStoreOptions, type MemoryToolsOptions, type MemoryType, type WriteInput as MemoryWriteInput, type ModelClient, type ModelInfo, type ModelList, type ModelPricing, NeedsConfirmationError, OpenAICompatClient, type OpenAICompatClientOptions, PROJECT_MEMORY_FILE, PROJECT_MEMORY_FILES, PROJECT_MEMORY_MAX_CHARS, type PageContent, type ParsedAtQuery, type PickerCandidate, PlanProposedError, PlanRevisionProposedError, type PlanStep, type PlanStepRisk, type PlanToolOptions, type ProgressNotificationParams, type ProjectMemory, type ProviderAuth, type ProviderEndpoints, type ProviderPricing, type ProviderProfile, type RankPickerOptions, type ReadResourceResult, type ReadTranscriptResult, type ReconfigurableOptions, type RepairReport, type ReplayStats, type ResolvedHook, type RetryInfo, type RetryOptions, type Role, type RunCommandResult, type RunHooksOptions, type ScavengeOptions, type ScavengeResult, type SearchResult, type SectionResult, type SessionInfo, SessionStats, type SessionSummary, type ShellToolsOptions, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, type StepCompletion, StormBreaker, type StreamChunk, type StreamWalkOptions, type StreamableHttpMcpSpec, StreamableHttpTransport, type StreamableHttpTransportOptions, type SubagentEvent, type SubagentSink, type SubagentToolOptions, type ThinkingConfig, type ThinkingTransport, type TodoItem, type TodoStatus, type TodoToolOptions, type ToolCall, type ToolCallContext, ToolCallRepair, type ToolCallRepairOptions, type ToolDefinition, type ToolFunctionSpec, ToolRegistry, type ToolSpec, type TranscriptMeta, type TranscriptRecord, type TruncationRepairResult, type TurnPair, type TurnStats, USER_MEMORY_DIR, Usage, type UsageAggregate, type UsageBucket, type UsageRecord, type UserBalance, VERSION, VolatileScratch, type WebFetchOptions, type WebSearchOptions, type WebToolsOptions, aggregateUsage, analyzeSchema, appendSessionMessage, appendUsage, applyEditBlock, applyEditBlocks, applyMemoryStack, applyProjectMemory, applyUserMemory, bridgeMcpTools, bucketCacheHitRatio, bucketSavingsFraction, claudeEquivalentCost, codeSystemPrompt, compareVersions, computeReplayStats, contextTokensFor, costUsd, decideOutcome, defaultConfigPath, defaultProvider, defaultUsageLogPath, deleteSession, detectAtPicker, detectInstallSource, detectNpmInstallPrefix, detectShellOperator, diffTranscripts, expandAtMentions, fetchWithRetry, findProjectMemoryPath, fixToolCallPairing, flattenMcpResult, flattenSchema, forkRegistryExcluding, formatCommandResult, formatHookOutcomeMessage, formatLogSize, formatLoopError, formatSearchResults, getLatestVersion, getProvider, globalSettingsPath, healLoadedMessages, healLoadedMessagesByTokens, htmlToText, injectPowerShellUtf8, inputCostUsd, inspectMcpServer, isAllowed, isJsonRpcError, isNpxInstall, isPlausibleKey, listDirectory, listFilesSync, listFilesWithStatsAsync, listFilesWithStatsSync, listProviders, listSessions, loadApiKey, loadBaseUrl, loadDotenv, loadHooks, loadSessionMessages, matchesTool, memoryEnabled, nestArguments, openTranscriptFile, outputCostUsd, parseAtQuery, parseEditBlocks, parseMcpSpec, parseMojeekResults, parseSearxngHtmlResults, parseTranscript, prepareSpawn, pricingFor, projectHash, projectSettingsPath, quoteForCmdExe, rankPickerCandidates, readConfig, readProjectMemory, readTranscript, readUsageLog, recordFromLoopEvent, redactKey, registerChoiceTool, registerFilesystemTools, registerMemoryTools, registerPlanTool, registerProvider, registerShellTools, registerSubagentTool, registerTodoTool, registerWebTools, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, resolveExecutable, resolveProjectMemoryWritePath, resolveProvider, restoreSnapshots, runCommand, runHooks, sanitizeMemoryName, sanitizeName as sanitizeSessionName, saveApiKey, saveBaseUrl, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, stripHallucinatedToolMarkup, tokenizeCommand, truncateForModel, truncateForModelByTokens, unregisterProvider, walkFilesStream, webFetch, webSearch, withUtf8Codepage, writeConfig, writeMeta, writeRecord };
|