@jun133/kitty 0.0.14 → 0.0.16
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 +25 -137
- package/dist/{App-CBTIS4IK.mjs → App-K7CBA2FU.mjs} +1 -1
- package/dist/{chunk-KROQCOWD.mjs → chunk-7FOTCUIH.mjs} +1486 -305
- package/dist/{chunk-3KMC6H5K.mjs → chunk-C3MFBHV3.mjs} +445 -1428
- package/dist/{chunk-4BN45TQG.mjs → chunk-FKBVCYPW.mjs} +1 -1
- package/dist/{chunk-NBKU7KA4.mjs → chunk-L27HOXD2.mjs} +192 -196
- package/dist/{chunk-KKGULDIF.mjs → chunk-LCTAPA2B.mjs} +113 -56
- package/dist/{chunk-VR3L2EPP.mjs → chunk-XY3SQDFZ.mjs} +1 -1
- package/dist/cli.js +7687 -6924
- package/dist/cli.js.map +1 -1
- package/dist/{interactive-KRRDJYBR.mjs → interactive-ZNDB2JQO.mjs} +5 -5
- package/dist/{oneShot-PHU3JOPP.mjs → oneShot-4SH2HITB.mjs} +3 -3
- package/dist/{session-XKWJHRVY.mjs → session-WJWPSYBD.mjs} +2 -2
- package/dist/tui.mjs +17 -7
- package/package.json +5 -2
|
@@ -1,1212 +1,237 @@
|
|
|
1
|
-
// src/provider/
|
|
2
|
-
var DEFAULT_PROVIDER = "openai-compatible";
|
|
1
|
+
// src/provider/catalog.ts
|
|
3
2
|
var DEFAULT_REQUEST_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
4
3
|
var DEFAULT_DOCTOR_PROBE_TIMEOUT_MS = 1e4;
|
|
5
4
|
var RELAY_REQUEST_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
6
5
|
var RELAY_DOCTOR_PROBE_TIMEOUT_MS = 45e3;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
6
|
+
var PROVIDER_CATALOG = [
|
|
7
|
+
{
|
|
8
|
+
id: "deepseek",
|
|
9
|
+
label: "DeepSeek official",
|
|
10
|
+
apiKind: "deepseek-openai-compatible",
|
|
11
|
+
transport: "standard",
|
|
12
|
+
defaultBaseUrl: "https://api.deepseek.com",
|
|
13
|
+
requestTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
|
|
14
|
+
doctorProbeTimeoutMs: DEFAULT_DOCTOR_PROBE_TIMEOUT_MS
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "yls",
|
|
18
|
+
label: "YLS Codex",
|
|
19
|
+
apiKind: "openai-sdk",
|
|
20
|
+
transport: "relay",
|
|
21
|
+
defaultBaseUrl: "https://code.ylsagi.com/codex",
|
|
22
|
+
requestTimeoutMs: RELAY_REQUEST_TIMEOUT_MS,
|
|
23
|
+
doctorProbeTimeoutMs: RELAY_DOCTOR_PROBE_TIMEOUT_MS
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "ttapi",
|
|
27
|
+
label: "TTAPI",
|
|
28
|
+
apiKind: "openai-sdk",
|
|
29
|
+
transport: "relay",
|
|
30
|
+
defaultBaseUrl: "https://w.ciykj.cn",
|
|
31
|
+
requestTimeoutMs: RELAY_REQUEST_TIMEOUT_MS,
|
|
32
|
+
doctorProbeTimeoutMs: RELAY_DOCTOR_PROBE_TIMEOUT_MS
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "openai",
|
|
36
|
+
label: "OpenAI official",
|
|
37
|
+
apiKind: "openai-sdk",
|
|
38
|
+
transport: "standard",
|
|
39
|
+
defaultBaseUrl: "https://api.openai.com/v1",
|
|
40
|
+
requestTimeoutMs: RELAY_REQUEST_TIMEOUT_MS,
|
|
41
|
+
doctorProbeTimeoutMs: RELAY_DOCTOR_PROBE_TIMEOUT_MS
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "openai-compatible",
|
|
45
|
+
label: "OpenAI-compatible",
|
|
46
|
+
apiKind: "openai-compatible",
|
|
47
|
+
transport: "standard",
|
|
48
|
+
defaultBaseUrl: "",
|
|
40
49
|
requestTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
|
|
41
50
|
doctorProbeTimeoutMs: DEFAULT_DOCTOR_PROBE_TIMEOUT_MS
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
function normalizeProviderName(value) {
|
|
45
|
-
const normalized = String(value ?? "").trim().toLowerCase();
|
|
46
|
-
return normalized || DEFAULT_PROVIDER;
|
|
47
|
-
}
|
|
48
|
-
function normalizeModelName(value) {
|
|
49
|
-
return String(value ?? "").trim();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// src/utils/abort.ts
|
|
53
|
-
function createAbortError(message = "Operation aborted") {
|
|
54
|
-
const error = new Error(message);
|
|
55
|
-
error.name = "AbortError";
|
|
56
|
-
error.code = "ABORT_ERR";
|
|
57
|
-
return error;
|
|
58
|
-
}
|
|
59
|
-
function isAbortError(error) {
|
|
60
|
-
if (!error) {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
if (error instanceof Error) {
|
|
64
|
-
if (error.name === "AbortError") {
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
const code = String(error.code ?? "");
|
|
68
|
-
if (code === "ABORT_ERR" || code === "ERR_ABORTED" || code === "ABORTED") {
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
const message = error.message.toLowerCase();
|
|
72
|
-
if (message.includes("abort") || message.includes("aborted") || message.includes("cancelled") || message.includes("canceled")) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (typeof error === "object" && error && "cause" in error) {
|
|
77
|
-
return isAbortError(error.cause);
|
|
78
|
-
}
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
function throwIfAborted(signal, message) {
|
|
82
|
-
if (signal?.aborted) {
|
|
83
|
-
throw createAbortError(message ?? "Operation aborted");
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function sleepWithSignal(ms, signal) {
|
|
87
|
-
if (!signal) {
|
|
88
|
-
return new Promise((resolve) => {
|
|
89
|
-
setTimeout(resolve, ms);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
if (signal.aborted) {
|
|
93
|
-
return Promise.reject(createAbortError("Sleep aborted"));
|
|
94
|
-
}
|
|
95
|
-
return new Promise((resolve, reject) => {
|
|
96
|
-
const timer = setTimeout(() => {
|
|
97
|
-
signal.removeEventListener("abort", onAbort);
|
|
98
|
-
resolve();
|
|
99
|
-
}, ms);
|
|
100
|
-
const onAbort = () => {
|
|
101
|
-
clearTimeout(timer);
|
|
102
|
-
signal.removeEventListener("abort", onAbort);
|
|
103
|
-
reject(createAbortError("Sleep aborted"));
|
|
104
|
-
};
|
|
105
|
-
signal.addEventListener("abort", onAbort);
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// src/provider/apiRetry.ts
|
|
110
|
-
var API_MAX_RETRIES = 3;
|
|
111
|
-
var API_RETRY_BASE_DELAY_MS = 1200;
|
|
112
|
-
async function withApiRetries(operation, abortSignal) {
|
|
113
|
-
let lastError;
|
|
114
|
-
for (let attempt = 1; attempt <= API_MAX_RETRIES; attempt += 1) {
|
|
115
|
-
try {
|
|
116
|
-
return await operation();
|
|
117
|
-
} catch (error) {
|
|
118
|
-
if (isAbortError(error)) {
|
|
119
|
-
throw error;
|
|
120
|
-
}
|
|
121
|
-
lastError = error;
|
|
122
|
-
if (!isRetryableApiError(error) || attempt === API_MAX_RETRIES) {
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
await sleepWithSignal(API_RETRY_BASE_DELAY_MS * attempt, abortSignal);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
throw lastError;
|
|
129
|
-
}
|
|
130
|
-
function isRetryableApiError(error) {
|
|
131
|
-
const status = error.status;
|
|
132
|
-
if (typeof status === "number") {
|
|
133
|
-
return status === 408 || status === 409 || status === 429 || status >= 500;
|
|
134
|
-
}
|
|
135
|
-
const message = String(error.message ?? error).toLowerCase();
|
|
136
|
-
return message.includes("timeout") || message.includes("network") || message.includes("connection error") || message.includes("connection reset") || message.includes("econnreset") || message.includes("econnrefused") || message.includes("connect timeout") || message.includes("temporarily") || message.includes("rate limit") || message.includes("overloaded");
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// src/provider/usageNormalizer.ts
|
|
140
|
-
function normalizeProviderUsage(usage) {
|
|
141
|
-
if (!usage || typeof usage !== "object") {
|
|
142
|
-
return void 0;
|
|
143
|
-
}
|
|
144
|
-
const record = usage;
|
|
145
|
-
const promptDetails = readObject(record.prompt_tokens_details);
|
|
146
|
-
const completionDetails = readObject(record.completion_tokens_details);
|
|
147
|
-
const outputDetails = readObject(record.output_tokens_details);
|
|
148
|
-
const cacheCreation = readObject(record.cache_creation);
|
|
149
|
-
const inputTokens = readUsageNumber(record.prompt_tokens ?? record.input_tokens);
|
|
150
|
-
const outputTokens = readUsageNumber(record.completion_tokens ?? record.output_tokens);
|
|
151
|
-
const totalTokens = readUsageNumber(record.total_tokens);
|
|
152
|
-
const reasoningTokens = readUsageNumber(
|
|
153
|
-
completionDetails?.reasoning_tokens ?? outputDetails?.reasoning_tokens
|
|
154
|
-
);
|
|
155
|
-
const openAiCachedTokens = readUsageNumber(promptDetails?.cached_tokens);
|
|
156
|
-
const deepSeekHitTokens = readUsageNumber(record.prompt_cache_hit_tokens);
|
|
157
|
-
const deepSeekMissTokens = readUsageNumber(record.prompt_cache_miss_tokens);
|
|
158
|
-
const anthropicCacheReadTokens = readUsageNumber(record.cache_read_input_tokens);
|
|
159
|
-
const anthropicCacheCreationTokens = readUsageNumber(record.cache_creation_input_tokens) ?? sumUsageNumbers([
|
|
160
|
-
cacheCreation?.ephemeral_1h_input_tokens,
|
|
161
|
-
cacheCreation?.ephemeral_5m_input_tokens
|
|
162
|
-
]);
|
|
163
|
-
const geminiCachedTokens = readUsageNumber(record.cachedContentTokenCount ?? record.cached_content_token_count);
|
|
164
|
-
const cacheReadTokens = firstNumber(
|
|
165
|
-
anthropicCacheReadTokens,
|
|
166
|
-
openAiCachedTokens,
|
|
167
|
-
geminiCachedTokens
|
|
168
|
-
);
|
|
169
|
-
const cacheHitTokens = firstNumber(deepSeekHitTokens, cacheReadTokens);
|
|
170
|
-
const cacheMissTokens = deepSeekMissTokens;
|
|
171
|
-
const cacheCreationTokens = anthropicCacheCreationTokens;
|
|
172
|
-
const snapshot = {
|
|
173
|
-
inputTokens,
|
|
174
|
-
outputTokens,
|
|
175
|
-
totalTokens,
|
|
176
|
-
reasoningTokens,
|
|
177
|
-
cacheReadTokens,
|
|
178
|
-
cacheCreationTokens,
|
|
179
|
-
cacheHitTokens,
|
|
180
|
-
cacheMissTokens
|
|
181
|
-
};
|
|
182
|
-
const cacheHitRate = computeCacheHitRate(snapshot);
|
|
183
|
-
if (cacheHitRate !== void 0) {
|
|
184
|
-
snapshot.cacheHitRate = cacheHitRate;
|
|
185
|
-
}
|
|
186
|
-
return Object.values(snapshot).some((value) => typeof value === "number") ? snapshot : void 0;
|
|
187
|
-
}
|
|
188
|
-
function hasProviderUsageSnapshot(usage) {
|
|
189
|
-
return Boolean(usage && Object.values(usage).some((value) => typeof value === "number"));
|
|
190
|
-
}
|
|
191
|
-
function computeCacheHitRate(snapshot) {
|
|
192
|
-
if (typeof snapshot.cacheHitTokens === "number" && typeof snapshot.cacheMissTokens === "number") {
|
|
193
|
-
return ratio(snapshot.cacheHitTokens, snapshot.cacheHitTokens + snapshot.cacheMissTokens);
|
|
194
|
-
}
|
|
195
|
-
if (typeof snapshot.cacheReadTokens === "number") {
|
|
196
|
-
const denominator = (snapshot.inputTokens ?? 0) + snapshot.cacheReadTokens + (snapshot.cacheCreationTokens ?? 0);
|
|
197
|
-
return ratio(snapshot.cacheReadTokens, denominator);
|
|
198
|
-
}
|
|
199
|
-
return void 0;
|
|
200
|
-
}
|
|
201
|
-
function ratio(numerator, denominator) {
|
|
202
|
-
if (denominator <= 0) {
|
|
203
|
-
return void 0;
|
|
204
|
-
}
|
|
205
|
-
return Math.round(numerator / denominator * 1e4) / 1e4;
|
|
206
|
-
}
|
|
207
|
-
function readObject(value) {
|
|
208
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
209
|
-
}
|
|
210
|
-
function firstNumber(...values) {
|
|
211
|
-
return values.find((value) => typeof value === "number");
|
|
212
|
-
}
|
|
213
|
-
function sumUsageNumbers(values) {
|
|
214
|
-
const numbers = values.map(readUsageNumber).filter((value) => typeof value === "number");
|
|
215
|
-
if (numbers.length === 0) {
|
|
216
|
-
return void 0;
|
|
217
51
|
}
|
|
218
|
-
return numbers.reduce((total, value) => total + value, 0);
|
|
219
|
-
}
|
|
220
|
-
function readUsageNumber(value) {
|
|
221
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.round(value) : void 0;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// src/observability/writer.ts
|
|
225
|
-
import fs2 from "fs/promises";
|
|
226
|
-
import path2 from "path";
|
|
227
|
-
|
|
228
|
-
// src/project/statePaths.ts
|
|
229
|
-
import fs from "fs/promises";
|
|
230
|
-
import path from "path";
|
|
231
|
-
var PROJECT_STATE_DIR_NAME = ".kitty";
|
|
232
|
-
var PROJECT_STATE_ENV_FILE_NAME = ".env";
|
|
233
|
-
var PROJECT_STATE_ENV_EXAMPLE_FILE_NAME = ".env.example";
|
|
234
|
-
var PROJECT_STATE_IGNORE_FILE_NAME = ".kittyignore";
|
|
235
|
-
var PRESERVED_PROJECT_STATE_ENTRY_NAMES = [
|
|
236
|
-
PROJECT_STATE_ENV_FILE_NAME,
|
|
237
|
-
PROJECT_STATE_ENV_EXAMPLE_FILE_NAME
|
|
238
52
|
];
|
|
239
|
-
|
|
240
|
-
const normalizedRoot = path.resolve(rootDir);
|
|
241
|
-
const kittyDir = path.join(normalizedRoot, PROJECT_STATE_DIR_NAME);
|
|
242
|
-
const extensionsDir = path.join(kittyDir, "extensions");
|
|
243
|
-
const memoryDir = path.join(kittyDir, "memory");
|
|
244
|
-
const observabilityDir = path.join(kittyDir, "observability");
|
|
245
|
-
return {
|
|
246
|
-
rootDir: normalizedRoot,
|
|
247
|
-
kittyDir,
|
|
248
|
-
cacheDir: path.join(kittyDir, "cache"),
|
|
249
|
-
sessionsDir: path.join(kittyDir, "sessions"),
|
|
250
|
-
changesDir: path.join(kittyDir, "changes"),
|
|
251
|
-
eventsDir: path.join(kittyDir, "events"),
|
|
252
|
-
extensionsDir,
|
|
253
|
-
memoryDir,
|
|
254
|
-
evidenceMemoryDir: path.join(memoryDir, "evidence"),
|
|
255
|
-
projectMemoryDir: path.join(memoryDir, "project"),
|
|
256
|
-
sessionMemoryDir: path.join(memoryDir, "sessions"),
|
|
257
|
-
userMemoryDir: path.join(memoryDir, "user"),
|
|
258
|
-
controlPlaneLedgerFile: path.join(kittyDir, "control-plane.sqlite"),
|
|
259
|
-
observabilityDir,
|
|
260
|
-
observabilityEventsDir: path.join(observabilityDir, "events"),
|
|
261
|
-
observabilityCrashesDir: path.join(observabilityDir, "crashes")
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
async function ensureProjectStateDirectories(rootDir) {
|
|
265
|
-
const paths = getProjectStatePaths(rootDir);
|
|
266
|
-
await fs.mkdir(paths.extensionsDir, { recursive: true });
|
|
267
|
-
await fs.mkdir(paths.cacheDir, { recursive: true });
|
|
268
|
-
await fs.mkdir(paths.sessionsDir, { recursive: true });
|
|
269
|
-
await fs.mkdir(paths.changesDir, { recursive: true });
|
|
270
|
-
await fs.mkdir(paths.eventsDir, { recursive: true });
|
|
271
|
-
await fs.mkdir(paths.evidenceMemoryDir, { recursive: true });
|
|
272
|
-
await fs.mkdir(paths.projectMemoryDir, { recursive: true });
|
|
273
|
-
await fs.mkdir(paths.sessionMemoryDir, { recursive: true });
|
|
274
|
-
await fs.mkdir(paths.userMemoryDir, { recursive: true });
|
|
275
|
-
await fs.mkdir(paths.observabilityEventsDir, { recursive: true });
|
|
276
|
-
await fs.mkdir(paths.observabilityCrashesDir, { recursive: true });
|
|
277
|
-
return paths;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// src/observability/schema.ts
|
|
281
|
-
var OBSERVABILITY_VERSION = 1;
|
|
282
|
-
function buildObservabilityEventRecord(input) {
|
|
283
|
-
return {
|
|
284
|
-
version: OBSERVABILITY_VERSION,
|
|
285
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
286
|
-
event: normalizeText(input.event, "unknown"),
|
|
287
|
-
status: normalizeText(input.status, "unknown"),
|
|
288
|
-
host: normalizeOptionalText(input.host),
|
|
289
|
-
sessionId: normalizeOptionalText(input.sessionId),
|
|
290
|
-
executionId: normalizeOptionalText(input.executionId),
|
|
291
|
-
identityKind: normalizeOptionalText(input.identityKind),
|
|
292
|
-
identityName: normalizeOptionalText(input.identityName),
|
|
293
|
-
durationMs: normalizeOptionalNumber(input.durationMs),
|
|
294
|
-
toolName: normalizeOptionalText(input.toolName),
|
|
295
|
-
model: normalizeOptionalText(input.model),
|
|
296
|
-
error: normalizeObservabilityError(input.error),
|
|
297
|
-
details: normalizeDetails(input.details)
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
function normalizeObservabilityError(error) {
|
|
301
|
-
if (error == null) {
|
|
302
|
-
return void 0;
|
|
303
|
-
}
|
|
304
|
-
if (typeof error === "object" && error !== null && "message" in error) {
|
|
305
|
-
const record = error;
|
|
306
|
-
const message2 = normalizeText(record.message, "");
|
|
307
|
-
if (!message2) {
|
|
308
|
-
return void 0;
|
|
309
|
-
}
|
|
310
|
-
return {
|
|
311
|
-
message: message2,
|
|
312
|
-
code: normalizeOptionalText(record.code),
|
|
313
|
-
details: normalizeValue(record.details)
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
const message = readErrorMessage(error);
|
|
317
|
-
return message ? { message } : void 0;
|
|
318
|
-
}
|
|
319
|
-
function normalizeDetails(details) {
|
|
320
|
-
if (!details || typeof details !== "object") {
|
|
321
|
-
return void 0;
|
|
322
|
-
}
|
|
323
|
-
const normalized = normalizeValue(details);
|
|
324
|
-
return normalized && typeof normalized === "object" && !Array.isArray(normalized) ? normalized : void 0;
|
|
325
|
-
}
|
|
326
|
-
function normalizeValue(value, depth = 0) {
|
|
327
|
-
if (value == null) {
|
|
328
|
-
return void 0;
|
|
329
|
-
}
|
|
330
|
-
if (depth >= 4) {
|
|
331
|
-
return "[truncated]";
|
|
332
|
-
}
|
|
333
|
-
if (typeof value === "string") {
|
|
334
|
-
return value.length <= 2e3 ? value : `${value.slice(0, 1997)}...`;
|
|
335
|
-
}
|
|
336
|
-
if (typeof value === "number") {
|
|
337
|
-
return Number.isFinite(value) ? value : void 0;
|
|
338
|
-
}
|
|
339
|
-
if (typeof value === "boolean") {
|
|
340
|
-
return value;
|
|
341
|
-
}
|
|
342
|
-
if (value instanceof Error) {
|
|
343
|
-
return {
|
|
344
|
-
name: normalizeText(value.name, "Error"),
|
|
345
|
-
message: normalizeText(value.message, "Unknown error"),
|
|
346
|
-
stack: normalizeValue(value.stack, depth + 1)
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
if (Array.isArray(value)) {
|
|
350
|
-
return value.slice(0, 20).map((item) => normalizeValue(item, depth + 1)).filter((item) => item !== void 0);
|
|
351
|
-
}
|
|
352
|
-
if (typeof value === "object") {
|
|
353
|
-
const entries = Object.entries(value).slice(0, 30);
|
|
354
|
-
const normalizedEntries = entries.map(([key, item]) => [key, normalizeValue(item, depth + 1)]).filter(([, item]) => item !== void 0);
|
|
355
|
-
return Object.fromEntries(normalizedEntries);
|
|
356
|
-
}
|
|
357
|
-
return normalizeText(String(value), "");
|
|
358
|
-
}
|
|
359
|
-
function normalizeText(value, fallback) {
|
|
360
|
-
const normalized = String(value ?? "").trim();
|
|
361
|
-
return normalized || fallback;
|
|
362
|
-
}
|
|
363
|
-
function normalizeOptionalText(value) {
|
|
364
|
-
const normalized = String(value ?? "").trim();
|
|
365
|
-
return normalized || void 0;
|
|
366
|
-
}
|
|
367
|
-
function normalizeOptionalNumber(value) {
|
|
368
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.round(value) : void 0;
|
|
369
|
-
}
|
|
370
|
-
function readErrorMessage(error) {
|
|
371
|
-
if (error instanceof Error) {
|
|
372
|
-
return normalizeText(error.message, error.name || "Unknown error");
|
|
373
|
-
}
|
|
374
|
-
if (typeof error === "object" && error !== null && "message" in error) {
|
|
375
|
-
return normalizeText(error.message, "Unknown error");
|
|
376
|
-
}
|
|
377
|
-
return normalizeText(String(error ?? "Unknown error"), "Unknown error");
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// src/observability/writer.ts
|
|
381
|
-
async function appendObservabilityEvent(rootDir, input) {
|
|
382
|
-
const paths = await ensureProjectStateDirectories(rootDir);
|
|
383
|
-
const record = buildObservabilityEventRecord(input);
|
|
384
|
-
const filePath = path2.join(paths.observabilityEventsDir, `${record.timestamp.slice(0, 10)}.jsonl`);
|
|
385
|
-
await fs2.appendFile(filePath, `${JSON.stringify(record)}
|
|
386
|
-
`, "utf8");
|
|
387
|
-
return record;
|
|
388
|
-
}
|
|
389
|
-
async function recordObservabilityEvent(rootDir, input) {
|
|
390
|
-
try {
|
|
391
|
-
await appendObservabilityEvent(rootDir, input);
|
|
392
|
-
} catch {
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// src/provider/cachePolicy.ts
|
|
397
|
-
function resolveProviderCachePolicy(input) {
|
|
398
|
-
const capabilities = resolveProviderCapabilities(input);
|
|
399
|
-
if (capabilities.provider === "openai") {
|
|
400
|
-
return {
|
|
401
|
-
provider: "openai",
|
|
402
|
-
automaticPrefixCache: true,
|
|
403
|
-
promptCacheKey: buildPromptCacheKey(input)
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
if (capabilities.provider === "deepseek") {
|
|
407
|
-
return {
|
|
408
|
-
provider: "deepseek",
|
|
409
|
-
automaticPrefixCache: true
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
return {
|
|
413
|
-
provider: "generic",
|
|
414
|
-
automaticPrefixCache: false
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
function buildPromptCacheKey(input) {
|
|
418
|
-
const seed = input.sessionId || input.projectRoot;
|
|
419
|
-
if (!seed) {
|
|
420
|
-
return void 0;
|
|
421
|
-
}
|
|
422
|
-
return `kitty:${stableHash(seed)}`;
|
|
423
|
-
}
|
|
424
|
-
function stableHash(value) {
|
|
425
|
-
let hash = 2166136261;
|
|
426
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
427
|
-
hash ^= value.charCodeAt(index);
|
|
428
|
-
hash = Math.imul(hash, 16777619);
|
|
429
|
-
}
|
|
430
|
-
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// src/provider/chatRequestBody.ts
|
|
434
|
-
function buildProviderRequestBody(input) {
|
|
435
|
-
const capabilities = resolveProviderCapabilities(input);
|
|
436
|
-
const thinking = capabilities.provider === "deepseek" ? resolveDeepSeekThinking(input.messages, input.thinking ?? "enabled") : input.thinking;
|
|
437
|
-
const body = {
|
|
438
|
-
model: input.model,
|
|
439
|
-
messages: toChatCompletionMessages(input.messages),
|
|
440
|
-
tools: input.tools,
|
|
441
|
-
stream: input.stream
|
|
442
|
-
};
|
|
443
|
-
if (capabilities.provider !== "deepseek" && input.tools?.length) {
|
|
444
|
-
body.tool_choice = "auto";
|
|
445
|
-
}
|
|
446
|
-
if (input.stream) {
|
|
447
|
-
body.stream_options = {
|
|
448
|
-
include_usage: true
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
const cachePolicy = resolveProviderCachePolicy(input);
|
|
452
|
-
if (cachePolicy.promptCacheKey) {
|
|
453
|
-
body.prompt_cache_key = cachePolicy.promptCacheKey;
|
|
454
|
-
}
|
|
455
|
-
if (typeof input.maxOutputTokens === "number" && Number.isFinite(input.maxOutputTokens)) {
|
|
456
|
-
body.max_tokens = Math.max(1, Math.trunc(input.maxOutputTokens));
|
|
457
|
-
}
|
|
458
|
-
if (capabilities.provider === "deepseek") {
|
|
459
|
-
body.thinking = { type: thinking };
|
|
460
|
-
if (thinking === "enabled") {
|
|
461
|
-
body.reasoning_effort = normalizeDeepSeekReasoningEffort(input.reasoningEffort ?? capabilities.defaultReasoningEffort);
|
|
462
|
-
}
|
|
463
|
-
} else if (input.forceReasoning || capabilities.defaultReasoningEnabled) {
|
|
464
|
-
body.thinking = { type: "enabled" };
|
|
465
|
-
}
|
|
466
|
-
return body;
|
|
467
|
-
}
|
|
468
|
-
function resolveDeepSeekThinking(messages, requested) {
|
|
469
|
-
if (requested === "disabled") {
|
|
470
|
-
return "disabled";
|
|
471
|
-
}
|
|
472
|
-
return hasUnreplayableAssistantReasoning(messages) ? "disabled" : "enabled";
|
|
473
|
-
}
|
|
474
|
-
function hasUnreplayableAssistantReasoning(messages) {
|
|
475
|
-
return messages.some(
|
|
476
|
-
(message) => message.role === "assistant" && Array.isArray(message.toolCalls) && message.toolCalls.length > 0 && message.reasoningContent === void 0
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
function normalizeDeepSeekReasoningEffort(effort) {
|
|
480
|
-
if (effort === void 0 || effort === "minimal" || effort === "low" || effort === "medium" || effort === "high") {
|
|
481
|
-
return "high";
|
|
482
|
-
}
|
|
483
|
-
if (effort === "xhigh" || effort === "max") {
|
|
484
|
-
return "max";
|
|
485
|
-
}
|
|
486
|
-
return "high";
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// src/provider/chatCompletionsAdapter.ts
|
|
490
|
-
var chatCompletionsAdapter = {
|
|
53
|
+
var DEEPSEEK_MODEL_BASE = {
|
|
491
54
|
wireApi: "chat.completions",
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
...buildProviderRequestBody({
|
|
500
|
-
provider: request.provider,
|
|
501
|
-
model: request.model,
|
|
502
|
-
messages: request.messages,
|
|
503
|
-
tools: request.tools,
|
|
504
|
-
stream: true,
|
|
505
|
-
forceReasoning: request.forceReasoning,
|
|
506
|
-
thinking: request.thinking,
|
|
507
|
-
reasoningEffort: request.reasoningEffort,
|
|
508
|
-
maxOutputTokens: request.maxOutputTokens,
|
|
509
|
-
sessionId: request.sessionId,
|
|
510
|
-
projectRoot: request.projectRoot
|
|
511
|
-
}),
|
|
512
|
-
signal: request.abortSignal
|
|
513
|
-
}
|
|
514
|
-
);
|
|
515
|
-
if (request.abortSignal?.aborted) {
|
|
516
|
-
abortStream(stream);
|
|
517
|
-
throw createAbortError("Streaming aborted");
|
|
518
|
-
}
|
|
519
|
-
let content = "";
|
|
520
|
-
let reasoningContent = "";
|
|
521
|
-
const toolCallParts = /* @__PURE__ */ new Map();
|
|
522
|
-
for await (const chunk of stream) {
|
|
523
|
-
if (request.abortSignal?.aborted) {
|
|
524
|
-
abortStream(stream);
|
|
525
|
-
throw createAbortError("Streaming aborted");
|
|
526
|
-
}
|
|
527
|
-
usage = normalizeProviderUsage(chunk.usage) ?? usage;
|
|
528
|
-
const delta = chunk.choices?.[0]?.delta;
|
|
529
|
-
if (!delta) {
|
|
530
|
-
continue;
|
|
531
|
-
}
|
|
532
|
-
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
533
|
-
content += delta.content;
|
|
534
|
-
request.callbacks?.onAssistantDelta?.(delta.content);
|
|
535
|
-
}
|
|
536
|
-
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
537
|
-
reasoningContent += delta.reasoning_content;
|
|
538
|
-
request.callbacks?.onReasoningDelta?.(delta.reasoning_content);
|
|
539
|
-
}
|
|
540
|
-
if (Array.isArray(delta.tool_calls)) {
|
|
541
|
-
for (const toolCall of delta.tool_calls) {
|
|
542
|
-
const index = typeof toolCall.index === "number" ? toolCall.index : 0;
|
|
543
|
-
const existing = toolCallParts.get(index) ?? {
|
|
544
|
-
id: toolCall.id ?? `tool-${index}`,
|
|
545
|
-
name: "",
|
|
546
|
-
arguments: ""
|
|
547
|
-
};
|
|
548
|
-
if (toolCall.id) {
|
|
549
|
-
existing.id = toolCall.id;
|
|
550
|
-
}
|
|
551
|
-
if (toolCall.function?.name) {
|
|
552
|
-
existing.name += toolCall.function.name;
|
|
553
|
-
}
|
|
554
|
-
if (toolCall.function?.arguments) {
|
|
555
|
-
existing.arguments += toolCall.function.arguments;
|
|
556
|
-
}
|
|
557
|
-
toolCallParts.set(index, existing);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
return {
|
|
562
|
-
content: content.length > 0 ? content : null,
|
|
563
|
-
reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
|
|
564
|
-
streamedAssistantContent: content.length > 0,
|
|
565
|
-
streamedReasoningContent: reasoningContent.length > 0,
|
|
566
|
-
toolCalls: [...toolCallParts.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
|
|
567
|
-
id: toolCall.id,
|
|
568
|
-
type: "function",
|
|
569
|
-
function: {
|
|
570
|
-
name: toolCall.name,
|
|
571
|
-
arguments: toolCall.arguments
|
|
572
|
-
}
|
|
573
|
-
}))
|
|
574
|
-
};
|
|
575
|
-
} finally {
|
|
576
|
-
request.onRequestMetric?.({
|
|
577
|
-
durationMs: Date.now() - startedAt,
|
|
578
|
-
usage
|
|
579
|
-
});
|
|
580
|
-
}
|
|
55
|
+
capabilities: {
|
|
56
|
+
tools: true,
|
|
57
|
+
reasoning: true,
|
|
58
|
+
reasoningContentReplay: "tool-call-required",
|
|
59
|
+
streaming: true,
|
|
60
|
+
usage: true,
|
|
61
|
+
cache: "provider-automatic"
|
|
581
62
|
},
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
provider: request.provider,
|
|
591
|
-
model: request.model,
|
|
592
|
-
messages: request.messages,
|
|
593
|
-
tools: request.tools,
|
|
594
|
-
stream: false,
|
|
595
|
-
forceReasoning: request.forceReasoning,
|
|
596
|
-
thinking: request.thinking,
|
|
597
|
-
reasoningEffort: request.reasoningEffort,
|
|
598
|
-
maxOutputTokens: request.maxOutputTokens,
|
|
599
|
-
sessionId: request.sessionId,
|
|
600
|
-
projectRoot: request.projectRoot
|
|
601
|
-
}),
|
|
602
|
-
signal: request.abortSignal
|
|
603
|
-
}
|
|
604
|
-
);
|
|
605
|
-
usage = normalizeProviderUsage(completion.usage);
|
|
606
|
-
const message = completion.choices[0]?.message;
|
|
607
|
-
if (!message) {
|
|
608
|
-
throw new Error("API returned no message.");
|
|
609
|
-
}
|
|
610
|
-
return {
|
|
611
|
-
content: typeof message.content === "string" ? message.content : collapseContentParts(message.content),
|
|
612
|
-
reasoningContent: readReasoningContent(message),
|
|
613
|
-
streamedAssistantContent: false,
|
|
614
|
-
streamedReasoningContent: false,
|
|
615
|
-
toolCalls: (message.tool_calls ?? []).filter((call) => call.type === "function").map((call) => ({
|
|
616
|
-
id: call.id,
|
|
617
|
-
type: "function",
|
|
618
|
-
function: {
|
|
619
|
-
name: call.function.name,
|
|
620
|
-
arguments: call.function.arguments
|
|
621
|
-
}
|
|
622
|
-
}))
|
|
623
|
-
};
|
|
624
|
-
} finally {
|
|
625
|
-
request.onRequestMetric?.({
|
|
626
|
-
durationMs: Date.now() - startedAt,
|
|
627
|
-
usage
|
|
628
|
-
});
|
|
629
|
-
}
|
|
63
|
+
request: {
|
|
64
|
+
thinkingDefault: "enabled",
|
|
65
|
+
reasoningEffortDefault: "max",
|
|
66
|
+
maxOutputTokensParam: "max_tokens"
|
|
67
|
+
},
|
|
68
|
+
limit: {
|
|
69
|
+
context: 128e3,
|
|
70
|
+
output: 8e3
|
|
630
71
|
}
|
|
631
72
|
};
|
|
632
|
-
|
|
633
|
-
try {
|
|
634
|
-
stream?.controller?.abort();
|
|
635
|
-
} catch {
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
function toChatCompletionMessages(messages) {
|
|
639
|
-
return messages.map((message) => {
|
|
640
|
-
if (message.role === "tool") {
|
|
641
|
-
return {
|
|
642
|
-
role: "tool",
|
|
643
|
-
content: message.content ?? "",
|
|
644
|
-
tool_call_id: message.toolCallId ?? ""
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
|
-
if (message.role === "assistant" && message.toolCalls?.length) {
|
|
648
|
-
const assistantMessage = {
|
|
649
|
-
role: "assistant",
|
|
650
|
-
content: message.content ?? "",
|
|
651
|
-
tool_calls: message.toolCalls
|
|
652
|
-
};
|
|
653
|
-
if (message.reasoningContent !== void 0) {
|
|
654
|
-
assistantMessage.reasoning_content = message.reasoningContent;
|
|
655
|
-
}
|
|
656
|
-
return assistantMessage;
|
|
657
|
-
}
|
|
658
|
-
const baseMessage = {
|
|
659
|
-
role: message.role,
|
|
660
|
-
content: message.content ?? "",
|
|
661
|
-
name: message.name
|
|
662
|
-
};
|
|
663
|
-
if (message.role === "assistant" && message.reasoningContent !== void 0) {
|
|
664
|
-
baseMessage.reasoning_content = message.reasoningContent;
|
|
665
|
-
}
|
|
666
|
-
return baseMessage;
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// src/provider/responsesAdapter.ts
|
|
671
|
-
var responsesAdapter = {
|
|
73
|
+
var GPT_RESPONSES_MODEL_BASE = {
|
|
672
74
|
wireApi: "responses",
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
...buildResponsesRequestBody(request),
|
|
681
|
-
stream: true
|
|
682
|
-
},
|
|
683
|
-
{
|
|
684
|
-
signal: request.abortSignal
|
|
685
|
-
}
|
|
686
|
-
);
|
|
687
|
-
if (request.abortSignal?.aborted) {
|
|
688
|
-
abortStream2(stream);
|
|
689
|
-
throw createAbortError("Streaming aborted");
|
|
690
|
-
}
|
|
691
|
-
let content = "";
|
|
692
|
-
let reasoningContent = "";
|
|
693
|
-
const toolCalls = /* @__PURE__ */ new Map();
|
|
694
|
-
for await (const event of stream) {
|
|
695
|
-
if (request.abortSignal?.aborted) {
|
|
696
|
-
abortStream2(stream);
|
|
697
|
-
throw createAbortError("Streaming aborted");
|
|
698
|
-
}
|
|
699
|
-
usage = normalizeProviderUsage(event.response?.usage) ?? usage;
|
|
700
|
-
if (event.type === "response.output_text.delta" && typeof event.delta === "string") {
|
|
701
|
-
content += event.delta;
|
|
702
|
-
request.callbacks?.onAssistantDelta?.(event.delta);
|
|
703
|
-
continue;
|
|
704
|
-
}
|
|
705
|
-
if ((event.type === "response.reasoning_text.delta" || event.type === "response.reasoning_summary_text.delta") && typeof event.delta === "string") {
|
|
706
|
-
reasoningContent += event.delta;
|
|
707
|
-
request.callbacks?.onReasoningDelta?.(event.delta);
|
|
708
|
-
continue;
|
|
709
|
-
}
|
|
710
|
-
if (event.type === "response.function_call_arguments.delta" && typeof event.delta === "string") {
|
|
711
|
-
const index = typeof event.output_index === "number" ? event.output_index : 0;
|
|
712
|
-
const existing = toolCalls.get(index) ?? {
|
|
713
|
-
id: event.item_id ?? `tool-${index}`,
|
|
714
|
-
name: "",
|
|
715
|
-
arguments: ""
|
|
716
|
-
};
|
|
717
|
-
existing.arguments += event.delta;
|
|
718
|
-
toolCalls.set(index, existing);
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
if (event.type === "response.function_call_arguments.done") {
|
|
722
|
-
const index = typeof event.output_index === "number" ? event.output_index : 0;
|
|
723
|
-
const existing = toolCalls.get(index) ?? {
|
|
724
|
-
id: event.item_id ?? `tool-${index}`,
|
|
725
|
-
name: "",
|
|
726
|
-
arguments: ""
|
|
727
|
-
};
|
|
728
|
-
if (typeof event.name === "string") {
|
|
729
|
-
existing.name = event.name;
|
|
730
|
-
}
|
|
731
|
-
if (typeof event.arguments === "string" && event.arguments.length > 0) {
|
|
732
|
-
existing.arguments = event.arguments;
|
|
733
|
-
}
|
|
734
|
-
toolCalls.set(index, existing);
|
|
735
|
-
continue;
|
|
736
|
-
}
|
|
737
|
-
if (event.type === "response.output_item.done" && event.item?.type === "function_call") {
|
|
738
|
-
const index = typeof event.output_index === "number" ? event.output_index : 0;
|
|
739
|
-
toolCalls.set(index, {
|
|
740
|
-
id: event.item.call_id ?? event.item.id ?? `tool-${index}`,
|
|
741
|
-
name: event.item.name ?? "",
|
|
742
|
-
arguments: event.item.arguments ?? ""
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
return {
|
|
747
|
-
content: content.length > 0 ? content : null,
|
|
748
|
-
reasoningContent: reasoningContent.length > 0 ? reasoningContent : void 0,
|
|
749
|
-
streamedAssistantContent: content.length > 0,
|
|
750
|
-
streamedReasoningContent: reasoningContent.length > 0,
|
|
751
|
-
toolCalls: [...toolCalls.entries()].sort((left, right) => left[0] - right[0]).map(([, toolCall]) => ({
|
|
752
|
-
id: toolCall.id,
|
|
753
|
-
type: "function",
|
|
754
|
-
function: {
|
|
755
|
-
name: toolCall.name,
|
|
756
|
-
arguments: toolCall.arguments
|
|
757
|
-
}
|
|
758
|
-
}))
|
|
759
|
-
};
|
|
760
|
-
} finally {
|
|
761
|
-
request.onRequestMetric?.({
|
|
762
|
-
durationMs: Date.now() - startedAt,
|
|
763
|
-
usage
|
|
764
|
-
});
|
|
765
|
-
}
|
|
75
|
+
capabilities: {
|
|
76
|
+
tools: true,
|
|
77
|
+
reasoning: true,
|
|
78
|
+
reasoningContentReplay: "never",
|
|
79
|
+
streaming: true,
|
|
80
|
+
usage: true,
|
|
81
|
+
cache: "prompt-cache-key"
|
|
766
82
|
},
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
stream: false
|
|
776
|
-
},
|
|
777
|
-
{
|
|
778
|
-
signal: request.abortSignal
|
|
779
|
-
}
|
|
780
|
-
);
|
|
781
|
-
usage = normalizeProviderUsage(response.usage);
|
|
782
|
-
return {
|
|
783
|
-
content: normalizeOutputText(response),
|
|
784
|
-
reasoningContent: readResponseReasoning(response),
|
|
785
|
-
streamedAssistantContent: false,
|
|
786
|
-
streamedReasoningContent: false,
|
|
787
|
-
toolCalls: readResponseToolCalls(response)
|
|
788
|
-
};
|
|
789
|
-
} finally {
|
|
790
|
-
request.onRequestMetric?.({
|
|
791
|
-
durationMs: Date.now() - startedAt,
|
|
792
|
-
usage
|
|
793
|
-
});
|
|
794
|
-
}
|
|
83
|
+
request: {
|
|
84
|
+
thinkingDefault: "enabled",
|
|
85
|
+
reasoningEffortDefault: "high",
|
|
86
|
+
maxOutputTokensParam: "max_output_tokens"
|
|
87
|
+
},
|
|
88
|
+
limit: {
|
|
89
|
+
context: 4e5,
|
|
90
|
+
output: 128e3
|
|
795
91
|
}
|
|
796
92
|
};
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
body.prompt_cache_key = cachePolicy.promptCacheKey;
|
|
825
|
-
}
|
|
826
|
-
const reasoningEffort = normalizeResponsesReasoningEffort(
|
|
827
|
-
request.reasoningEffort ?? capabilities.defaultReasoningEffort
|
|
828
|
-
);
|
|
829
|
-
if (request.forceReasoning || capabilities.defaultReasoningEnabled || reasoningEffort) {
|
|
830
|
-
body.reasoning = {
|
|
831
|
-
effort: reasoningEffort ?? "high",
|
|
832
|
-
summary: "detailed"
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
return body;
|
|
836
|
-
}
|
|
837
|
-
function normalizeResponsesReasoningEffort(effort) {
|
|
838
|
-
return effort === "max" ? void 0 : effort;
|
|
839
|
-
}
|
|
840
|
-
function toResponsesInput(messages) {
|
|
841
|
-
const items = [];
|
|
842
|
-
for (const message of messages) {
|
|
843
|
-
if (message.role === "tool") {
|
|
844
|
-
items.push({
|
|
845
|
-
type: "function_call_output",
|
|
846
|
-
call_id: message.toolCallId ?? "",
|
|
847
|
-
output: message.content ?? ""
|
|
848
|
-
});
|
|
849
|
-
continue;
|
|
850
|
-
}
|
|
851
|
-
if (message.role === "assistant" && message.toolCalls?.length) {
|
|
852
|
-
if (typeof message.content === "string" && message.content.trim().length > 0) {
|
|
853
|
-
items.push({
|
|
854
|
-
type: "message",
|
|
855
|
-
role: "assistant",
|
|
856
|
-
content: message.content
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
for (const toolCall of message.toolCalls) {
|
|
860
|
-
items.push({
|
|
861
|
-
type: "function_call",
|
|
862
|
-
call_id: toolCall.id,
|
|
863
|
-
name: toolCall.function.name,
|
|
864
|
-
arguments: toolCall.function.arguments
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
continue;
|
|
93
|
+
var MODEL_CATALOG = [
|
|
94
|
+
{
|
|
95
|
+
id: "deepseek-v4-flash",
|
|
96
|
+
providerId: "deepseek",
|
|
97
|
+
label: "DeepSeek V4 Flash",
|
|
98
|
+
...DEEPSEEK_MODEL_BASE
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: "deepseek-v4-pro",
|
|
102
|
+
providerId: "deepseek",
|
|
103
|
+
label: "DeepSeek V4 Pro",
|
|
104
|
+
...DEEPSEEK_MODEL_BASE
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: "gpt-5.5",
|
|
108
|
+
providerId: "yls",
|
|
109
|
+
label: "GPT-5.5 via YLS",
|
|
110
|
+
...GPT_RESPONSES_MODEL_BASE
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "gpt-5.4",
|
|
114
|
+
providerId: "yls",
|
|
115
|
+
label: "GPT-5.4 via YLS",
|
|
116
|
+
...GPT_RESPONSES_MODEL_BASE,
|
|
117
|
+
request: {
|
|
118
|
+
...GPT_RESPONSES_MODEL_BASE.request,
|
|
119
|
+
reasoningEffortDefault: "xhigh"
|
|
868
120
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
if (typeof outputText === "string" && outputText.trim().length > 0) {
|
|
880
|
-
return outputText;
|
|
881
|
-
}
|
|
882
|
-
const output = response.output;
|
|
883
|
-
if (!Array.isArray(output)) {
|
|
884
|
-
return null;
|
|
885
|
-
}
|
|
886
|
-
const fragments = output.flatMap((item) => {
|
|
887
|
-
if (!item || typeof item !== "object" || item.type !== "message") {
|
|
888
|
-
return [];
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "gpt-5.4",
|
|
124
|
+
providerId: "ttapi",
|
|
125
|
+
label: "GPT-5.4 via TTAPI",
|
|
126
|
+
...GPT_RESPONSES_MODEL_BASE,
|
|
127
|
+
request: {
|
|
128
|
+
...GPT_RESPONSES_MODEL_BASE.request,
|
|
129
|
+
thinkingDefault: "disabled",
|
|
130
|
+
reasoningEffortDefault: "xhigh"
|
|
889
131
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "gpt-5.5",
|
|
135
|
+
providerId: "openai",
|
|
136
|
+
label: "GPT-5.5",
|
|
137
|
+
...GPT_RESPONSES_MODEL_BASE
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: "gpt-5.4",
|
|
141
|
+
providerId: "openai",
|
|
142
|
+
label: "GPT-5.4",
|
|
143
|
+
...GPT_RESPONSES_MODEL_BASE,
|
|
144
|
+
request: {
|
|
145
|
+
...GPT_RESPONSES_MODEL_BASE.request,
|
|
146
|
+
reasoningEffortDefault: "xhigh"
|
|
893
147
|
}
|
|
894
|
-
return content.flatMap((part) => {
|
|
895
|
-
if (!part || typeof part !== "object" || part.type !== "output_text") {
|
|
896
|
-
return [];
|
|
897
|
-
}
|
|
898
|
-
return typeof part.text === "string" ? [part.text] : [];
|
|
899
|
-
});
|
|
900
|
-
});
|
|
901
|
-
return fragments.length > 0 ? fragments.join("") : null;
|
|
902
|
-
}
|
|
903
|
-
function readResponseToolCalls(response) {
|
|
904
|
-
const output = response.output;
|
|
905
|
-
if (!Array.isArray(output)) {
|
|
906
|
-
return [];
|
|
907
148
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
function: {
|
|
912
|
-
name: item.name ?? "",
|
|
913
|
-
arguments: item.arguments ?? ""
|
|
914
|
-
}
|
|
915
|
-
}));
|
|
149
|
+
];
|
|
150
|
+
function listModelInfos() {
|
|
151
|
+
return [...MODEL_CATALOG];
|
|
916
152
|
}
|
|
917
|
-
function
|
|
918
|
-
|
|
919
|
-
if (!Array.isArray(output)) {
|
|
920
|
-
return void 0;
|
|
921
|
-
}
|
|
922
|
-
const fragments = output.flatMap((item) => {
|
|
923
|
-
if (!item || typeof item !== "object" || item.type !== "reasoning") {
|
|
924
|
-
return [];
|
|
925
|
-
}
|
|
926
|
-
const reasoningItem = item;
|
|
927
|
-
const summary = Array.isArray(reasoningItem.summary) ? reasoningItem.summary.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
|
|
928
|
-
const content = Array.isArray(reasoningItem.content) ? reasoningItem.content.map((entry) => typeof entry?.text === "string" ? entry.text : "").filter(Boolean) : [];
|
|
929
|
-
return [...content, ...summary];
|
|
930
|
-
});
|
|
931
|
-
return fragments.length > 0 ? fragments.join("") : void 0;
|
|
153
|
+
function findProviderInfo(providerId) {
|
|
154
|
+
return PROVIDER_CATALOG.find((provider) => provider.id === normalizeProviderId(providerId));
|
|
932
155
|
}
|
|
933
|
-
function
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
156
|
+
function findModelInfo(providerId, modelId) {
|
|
157
|
+
const normalizedProvider = normalizeProviderId(providerId);
|
|
158
|
+
const normalizedModel = normalizeModelId(modelId);
|
|
159
|
+
const known = MODEL_CATALOG.find((model) => model.providerId === normalizedProvider && model.id === normalizedModel);
|
|
160
|
+
if (known) {
|
|
161
|
+
return known;
|
|
937
162
|
}
|
|
163
|
+
if (normalizedProvider === "openai-compatible") {
|
|
164
|
+
return createOpenAiCompatibleModelInfo(normalizedModel);
|
|
165
|
+
}
|
|
166
|
+
return void 0;
|
|
938
167
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
const normalized = trimTrailingSlash(baseUrl);
|
|
946
|
-
if (!normalized) {
|
|
947
|
-
return [normalized];
|
|
168
|
+
function resolveModelProfile(input) {
|
|
169
|
+
const configuredProvider = normalizeProviderId(input.provider);
|
|
170
|
+
const configuredModel = normalizeModelId(input.model);
|
|
171
|
+
const provider = findProviderInfo(configuredProvider);
|
|
172
|
+
if (!provider) {
|
|
173
|
+
throw new Error(`Unknown provider: ${configuredProvider}. Check KITTY_PROVIDER.`);
|
|
948
174
|
}
|
|
949
|
-
const
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
if (parsed.pathname === "" || parsed.pathname === "/") {
|
|
953
|
-
candidates.push(trimTrailingSlash(new URL("v1", ensureTrailingSlash(parsed.toString())).toString()));
|
|
954
|
-
}
|
|
955
|
-
} catch {
|
|
956
|
-
return candidates;
|
|
175
|
+
const model = findModelInfo(configuredProvider, configuredModel);
|
|
176
|
+
if (!model) {
|
|
177
|
+
throw new Error(`Unknown model for provider ${configuredProvider}: ${configuredModel}. Check KITTY_MODEL.`);
|
|
957
178
|
}
|
|
958
|
-
return
|
|
179
|
+
return {
|
|
180
|
+
provider,
|
|
181
|
+
model,
|
|
182
|
+
configuredProvider,
|
|
183
|
+
configuredModel
|
|
184
|
+
};
|
|
959
185
|
}
|
|
960
|
-
function
|
|
961
|
-
|
|
186
|
+
function normalizeProviderId(value) {
|
|
187
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
188
|
+
return normalized || "openai-compatible";
|
|
962
189
|
}
|
|
963
|
-
function
|
|
964
|
-
|
|
965
|
-
if (!trimmed) {
|
|
966
|
-
return trimmed;
|
|
967
|
-
}
|
|
968
|
-
return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
|
|
190
|
+
function normalizeModelId(value) {
|
|
191
|
+
return String(value ?? "").trim();
|
|
969
192
|
}
|
|
970
|
-
|
|
971
|
-
// src/provider/client.ts
|
|
972
|
-
function createProviderClientPool(config) {
|
|
973
|
-
const capabilities = resolveProviderCapabilities({
|
|
974
|
-
provider: config.provider,
|
|
975
|
-
model: config.model
|
|
976
|
-
});
|
|
977
|
-
const baseUrls = buildProviderBaseUrlCandidates(config.baseUrl);
|
|
978
|
-
const clients = /* @__PURE__ */ new Map();
|
|
979
|
-
let preferredBaseUrl;
|
|
193
|
+
function createOpenAiCompatibleModelInfo(modelId) {
|
|
980
194
|
return {
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
195
|
+
id: modelId,
|
|
196
|
+
providerId: "openai-compatible",
|
|
197
|
+
label: modelId,
|
|
198
|
+
wireApi: "chat.completions",
|
|
199
|
+
capabilities: {
|
|
200
|
+
tools: true,
|
|
201
|
+
reasoning: false,
|
|
202
|
+
reasoningContentReplay: "never",
|
|
203
|
+
streaming: true,
|
|
204
|
+
usage: true,
|
|
205
|
+
cache: "none"
|
|
206
|
+
},
|
|
207
|
+
request: {
|
|
208
|
+
thinkingDefault: "disabled",
|
|
209
|
+
maxOutputTokensParam: "max_tokens"
|
|
987
210
|
},
|
|
988
|
-
|
|
989
|
-
|
|
211
|
+
limit: {
|
|
212
|
+
context: 128e3,
|
|
213
|
+
output: 8e3
|
|
990
214
|
}
|
|
991
215
|
};
|
|
992
|
-
function getOrCreateClient(baseUrl) {
|
|
993
|
-
const existing = clients.get(baseUrl);
|
|
994
|
-
if (existing) {
|
|
995
|
-
return existing;
|
|
996
|
-
}
|
|
997
|
-
const client = new OpenAI({
|
|
998
|
-
apiKey: config.apiKey,
|
|
999
|
-
baseURL: baseUrl,
|
|
1000
|
-
timeout: capabilities.requestTimeoutMs,
|
|
1001
|
-
maxRetries: 0
|
|
1002
|
-
});
|
|
1003
|
-
clients.set(baseUrl, client);
|
|
1004
|
-
return client;
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
function isProviderClientPool(value) {
|
|
1008
|
-
return Boolean(
|
|
1009
|
-
value && typeof value === "object" && typeof value.candidates === "function" && typeof value.markHealthy === "function"
|
|
1010
|
-
);
|
|
1011
216
|
}
|
|
1012
217
|
|
|
1013
|
-
// src/provider/
|
|
1014
|
-
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
abortSignal,
|
|
1026
|
-
onRequestMetric,
|
|
1027
|
-
observability
|
|
1028
|
-
);
|
|
1029
|
-
}
|
|
1030
|
-
async function tryFetch(adapter, client, messages, request, tools, callbacks, forceReasoning, abortSignal, onRequestMetric, observability) {
|
|
1031
|
-
const startedAt = Date.now();
|
|
1032
|
-
let latestMetric;
|
|
1033
|
-
let resolvedBaseUrl;
|
|
1034
|
-
const forwardMetric = (metric) => {
|
|
1035
|
-
latestMetric = metric;
|
|
1036
|
-
onRequestMetric?.(metric);
|
|
218
|
+
// src/provider/capabilities.ts
|
|
219
|
+
function resolveProviderCapabilities(input) {
|
|
220
|
+
const profile = resolveModelProfile(input);
|
|
221
|
+
return {
|
|
222
|
+
provider: profile.provider.id,
|
|
223
|
+
model: profile.model.id,
|
|
224
|
+
wireApi: profile.model.wireApi,
|
|
225
|
+
supportsReasoningContent: profile.model.capabilities.reasoningContentReplay !== "never",
|
|
226
|
+
defaultReasoningEnabled: profile.model.capabilities.reasoning,
|
|
227
|
+
defaultReasoningEffort: profile.model.request.reasoningEffortDefault,
|
|
228
|
+
requestTimeoutMs: profile.provider.requestTimeoutMs,
|
|
229
|
+
doctorProbeTimeoutMs: profile.provider.doctorProbeTimeoutMs
|
|
1037
230
|
};
|
|
1038
|
-
if (observability) {
|
|
1039
|
-
await recordObservabilityEvent(observability.rootDir, {
|
|
1040
|
-
event: "model.request",
|
|
1041
|
-
status: "started",
|
|
1042
|
-
sessionId: observability.sessionId,
|
|
1043
|
-
identityKind: observability.identityKind,
|
|
1044
|
-
identityName: observability.identityName,
|
|
1045
|
-
model: request.model,
|
|
1046
|
-
details: {
|
|
1047
|
-
provider: request.provider,
|
|
1048
|
-
configuredModel: observability.configuredModel,
|
|
1049
|
-
requestModel: request.model,
|
|
1050
|
-
wireApi: adapter.wireApi,
|
|
1051
|
-
baseUrl: resolvedBaseUrl
|
|
1052
|
-
}
|
|
1053
|
-
});
|
|
1054
|
-
}
|
|
1055
|
-
try {
|
|
1056
|
-
const response = await withApiRetries(
|
|
1057
|
-
() => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
|
|
1058
|
-
resolvedBaseUrl = baseUrl;
|
|
1059
|
-
return adapter.fetchStreaming(providerClient, {
|
|
1060
|
-
provider: request.provider,
|
|
1061
|
-
model: request.model,
|
|
1062
|
-
messages,
|
|
1063
|
-
tools,
|
|
1064
|
-
callbacks,
|
|
1065
|
-
forceReasoning,
|
|
1066
|
-
thinking: request.thinking,
|
|
1067
|
-
reasoningEffort: request.reasoningEffort,
|
|
1068
|
-
maxOutputTokens: request.maxOutputTokens,
|
|
1069
|
-
sessionId: request.sessionId,
|
|
1070
|
-
projectRoot: request.projectRoot,
|
|
1071
|
-
abortSignal,
|
|
1072
|
-
onRequestMetric: forwardMetric
|
|
1073
|
-
});
|
|
1074
|
-
}),
|
|
1075
|
-
abortSignal
|
|
1076
|
-
);
|
|
1077
|
-
if (observability) {
|
|
1078
|
-
await recordObservabilityEvent(observability.rootDir, {
|
|
1079
|
-
event: "model.request",
|
|
1080
|
-
status: "completed",
|
|
1081
|
-
sessionId: observability.sessionId,
|
|
1082
|
-
identityKind: observability.identityKind,
|
|
1083
|
-
identityName: observability.identityName,
|
|
1084
|
-
model: request.model,
|
|
1085
|
-
durationMs: Date.now() - startedAt,
|
|
1086
|
-
details: {
|
|
1087
|
-
provider: request.provider,
|
|
1088
|
-
configuredModel: observability.configuredModel,
|
|
1089
|
-
requestModel: request.model,
|
|
1090
|
-
wireApi: adapter.wireApi,
|
|
1091
|
-
baseUrl: resolvedBaseUrl,
|
|
1092
|
-
usage: latestMetric?.usage,
|
|
1093
|
-
usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
|
|
1094
|
-
}
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
return response;
|
|
1098
|
-
} catch (error) {
|
|
1099
|
-
if (isAbortError(error)) {
|
|
1100
|
-
throw error;
|
|
1101
|
-
}
|
|
1102
|
-
try {
|
|
1103
|
-
const response = await withApiRetries(
|
|
1104
|
-
() => invokeWithProviderClients(client, async (providerClient, baseUrl) => {
|
|
1105
|
-
resolvedBaseUrl = baseUrl;
|
|
1106
|
-
return adapter.fetchNonStreaming(providerClient, {
|
|
1107
|
-
provider: request.provider,
|
|
1108
|
-
model: request.model,
|
|
1109
|
-
messages,
|
|
1110
|
-
tools,
|
|
1111
|
-
callbacks,
|
|
1112
|
-
forceReasoning,
|
|
1113
|
-
thinking: request.thinking,
|
|
1114
|
-
reasoningEffort: request.reasoningEffort,
|
|
1115
|
-
maxOutputTokens: request.maxOutputTokens,
|
|
1116
|
-
sessionId: request.sessionId,
|
|
1117
|
-
projectRoot: request.projectRoot,
|
|
1118
|
-
abortSignal,
|
|
1119
|
-
onRequestMetric: forwardMetric
|
|
1120
|
-
});
|
|
1121
|
-
}),
|
|
1122
|
-
abortSignal
|
|
1123
|
-
);
|
|
1124
|
-
if (observability) {
|
|
1125
|
-
await recordObservabilityEvent(observability.rootDir, {
|
|
1126
|
-
event: "model.request",
|
|
1127
|
-
status: "completed",
|
|
1128
|
-
sessionId: observability.sessionId,
|
|
1129
|
-
identityKind: observability.identityKind,
|
|
1130
|
-
identityName: observability.identityName,
|
|
1131
|
-
model: request.model,
|
|
1132
|
-
durationMs: Date.now() - startedAt,
|
|
1133
|
-
details: {
|
|
1134
|
-
provider: request.provider,
|
|
1135
|
-
configuredModel: observability.configuredModel,
|
|
1136
|
-
requestModel: request.model,
|
|
1137
|
-
wireApi: adapter.wireApi,
|
|
1138
|
-
baseUrl: resolvedBaseUrl,
|
|
1139
|
-
usage: latestMetric?.usage,
|
|
1140
|
-
usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
|
|
1141
|
-
}
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
return response;
|
|
1145
|
-
} catch (fallbackError) {
|
|
1146
|
-
if (!isAbortError(fallbackError) && observability) {
|
|
1147
|
-
await recordObservabilityEvent(observability.rootDir, {
|
|
1148
|
-
event: "model.request",
|
|
1149
|
-
status: "failed",
|
|
1150
|
-
sessionId: observability.sessionId,
|
|
1151
|
-
identityKind: observability.identityKind,
|
|
1152
|
-
identityName: observability.identityName,
|
|
1153
|
-
model: request.model,
|
|
1154
|
-
durationMs: Date.now() - startedAt,
|
|
1155
|
-
error: fallbackError,
|
|
1156
|
-
details: {
|
|
1157
|
-
provider: request.provider,
|
|
1158
|
-
configuredModel: observability.configuredModel,
|
|
1159
|
-
requestModel: request.model,
|
|
1160
|
-
wireApi: adapter.wireApi,
|
|
1161
|
-
baseUrl: resolvedBaseUrl,
|
|
1162
|
-
usage: latestMetric?.usage,
|
|
1163
|
-
usageAvailable: hasProviderUsageSnapshot(latestMetric?.usage)
|
|
1164
|
-
}
|
|
1165
|
-
});
|
|
1166
|
-
}
|
|
1167
|
-
throw fallbackError;
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
function selectProviderWireAdapter(wireApi) {
|
|
1172
|
-
if (wireApi === "responses") {
|
|
1173
|
-
return responsesAdapter;
|
|
1174
|
-
}
|
|
1175
|
-
return chatCompletionsAdapter;
|
|
1176
|
-
}
|
|
1177
|
-
async function invokeWithProviderClients(client, operation) {
|
|
1178
|
-
if (!isProviderClientPool(client)) {
|
|
1179
|
-
return operation(client, void 0);
|
|
1180
|
-
}
|
|
1181
|
-
let lastError;
|
|
1182
|
-
const candidates = client.candidates();
|
|
1183
|
-
for (let index = 0; index < candidates.length; index += 1) {
|
|
1184
|
-
const candidate = candidates[index];
|
|
1185
|
-
try {
|
|
1186
|
-
const result = await operation(candidate.client, candidate.baseUrl);
|
|
1187
|
-
client.markHealthy(candidate.baseUrl);
|
|
1188
|
-
return result;
|
|
1189
|
-
} catch (error) {
|
|
1190
|
-
lastError = error;
|
|
1191
|
-
if (isAbortError(error)) {
|
|
1192
|
-
throw error;
|
|
1193
|
-
}
|
|
1194
|
-
const hasMoreCandidates = index < candidates.length - 1;
|
|
1195
|
-
if (!hasMoreCandidates || !canRetryWithAlternateBaseUrl(error)) {
|
|
1196
|
-
throw error;
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
throw lastError;
|
|
1201
|
-
}
|
|
1202
|
-
function canRetryWithAlternateBaseUrl(error) {
|
|
1203
|
-
const status = error.status;
|
|
1204
|
-
const message = String(error.message ?? error).toLowerCase();
|
|
1205
|
-
return status === 404 || status === 405 || message.includes("404") || message.includes("not found");
|
|
1206
231
|
}
|
|
1207
232
|
|
|
1208
233
|
// src/session/messages.ts
|
|
1209
|
-
function buildChatMessages(systemPrompt, messages, contextWindowMessages, model) {
|
|
234
|
+
function buildChatMessages(systemPrompt, messages, contextWindowMessages, model, provider) {
|
|
1210
235
|
const recentMessages = messages.slice(-contextWindowMessages);
|
|
1211
236
|
return [
|
|
1212
237
|
{
|
|
@@ -1215,7 +240,8 @@ function buildChatMessages(systemPrompt, messages, contextWindowMessages, model)
|
|
|
1215
240
|
},
|
|
1216
241
|
...recentMessages.map(
|
|
1217
242
|
(message, index) => toChatMessage(message, {
|
|
1218
|
-
includeReasoning: shouldIncludeStoredAssistantReasoning(recentMessages, index, model)
|
|
243
|
+
includeReasoning: shouldIncludeStoredAssistantReasoning(recentMessages, index, model, provider),
|
|
244
|
+
provider
|
|
1219
245
|
})
|
|
1220
246
|
)
|
|
1221
247
|
];
|
|
@@ -1300,14 +326,17 @@ function expandStartToToolBoundary(messages, startIndex) {
|
|
|
1300
326
|
}
|
|
1301
327
|
return index;
|
|
1302
328
|
}
|
|
1303
|
-
function modelUsesReasoningContent(model) {
|
|
1304
|
-
|
|
329
|
+
function modelUsesReasoningContent(model, provider) {
|
|
330
|
+
if (provider) {
|
|
331
|
+
return resolveProviderCapabilities({ provider, model }).supportsReasoningContent;
|
|
332
|
+
}
|
|
333
|
+
return listModelInfos().some((item) => item.id === model && item.capabilities.reasoningContentReplay !== "never");
|
|
1305
334
|
}
|
|
1306
335
|
function isAssistantMessageInLatestTurn(messages, index) {
|
|
1307
336
|
return messages[index]?.role === "assistant" && index > findLatestUserIndex(messages);
|
|
1308
337
|
}
|
|
1309
|
-
function shouldIncludeStoredAssistantReasoning(messages, index, model) {
|
|
1310
|
-
return
|
|
338
|
+
function shouldIncludeStoredAssistantReasoning(messages, index, model, provider) {
|
|
339
|
+
return resolveProviderCapabilities({ provider, model }).supportsReasoningContent && messages[index]?.role === "assistant";
|
|
1311
340
|
}
|
|
1312
341
|
|
|
1313
342
|
// src/session/turnFrame.ts
|
|
@@ -1356,7 +385,7 @@ function oneLine(value) {
|
|
|
1356
385
|
}
|
|
1357
386
|
|
|
1358
387
|
// src/session/taskStateHistory.ts
|
|
1359
|
-
import
|
|
388
|
+
import path from "path";
|
|
1360
389
|
function collectActiveFiles(messages) {
|
|
1361
390
|
const files = [];
|
|
1362
391
|
for (const message of messages) {
|
|
@@ -1485,7 +514,7 @@ function normalizeFilePath(value) {
|
|
|
1485
514
|
if (trimmed.length > 260) {
|
|
1486
515
|
return truncate(trimmed, 260);
|
|
1487
516
|
}
|
|
1488
|
-
return trimmed.includes(
|
|
517
|
+
return trimmed.includes(path.sep) || trimmed.includes("/") || trimmed.includes(".") ? trimmed : void 0;
|
|
1489
518
|
}
|
|
1490
519
|
|
|
1491
520
|
// src/session/taskState.ts
|
|
@@ -1658,14 +687,14 @@ function noteSessionDiff(session, change, timestamp = (/* @__PURE__ */ new Date(
|
|
|
1658
687
|
};
|
|
1659
688
|
}
|
|
1660
689
|
function normalizeSessionDiffChange(change, timestamp) {
|
|
1661
|
-
const toolName =
|
|
690
|
+
const toolName = normalizeText(change?.toolName);
|
|
1662
691
|
const changedPaths = takeLastUniquePaths(change?.changedPaths ?? []);
|
|
1663
692
|
if (!toolName || changedPaths.length === 0) {
|
|
1664
693
|
return null;
|
|
1665
694
|
}
|
|
1666
695
|
return {
|
|
1667
696
|
toolName,
|
|
1668
|
-
changeId:
|
|
697
|
+
changeId: normalizeText(change?.changeId) || void 0,
|
|
1669
698
|
changedPaths,
|
|
1670
699
|
diff: truncate2(change?.diff, MAX_DIFF_PREVIEW_CHARS),
|
|
1671
700
|
diagnosticsStatus: normalizeDiagnosticsStatus(change?.diagnosticsStatus),
|
|
@@ -1678,7 +707,7 @@ function takeLastUniquePaths(paths) {
|
|
|
1678
707
|
const seen = /* @__PURE__ */ new Set();
|
|
1679
708
|
const result = [];
|
|
1680
709
|
for (let index = paths.length - 1; index >= 0; index -= 1) {
|
|
1681
|
-
const normalized =
|
|
710
|
+
const normalized = normalizeText(paths[index]);
|
|
1682
711
|
if (!normalized || seen.has(normalized)) {
|
|
1683
712
|
continue;
|
|
1684
713
|
}
|
|
@@ -1696,20 +725,180 @@ function normalizeDiagnosticsStatus(value) {
|
|
|
1696
725
|
function normalizeCount(value) {
|
|
1697
726
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
|
|
1698
727
|
}
|
|
1699
|
-
function
|
|
1700
|
-
return String(value ?? "").trim();
|
|
728
|
+
function normalizeText(value) {
|
|
729
|
+
return String(value ?? "").trim();
|
|
730
|
+
}
|
|
731
|
+
function truncate2(value, maxChars) {
|
|
732
|
+
if (!value) {
|
|
733
|
+
return void 0;
|
|
734
|
+
}
|
|
735
|
+
return value.length <= maxChars ? value : `${value.slice(0, maxChars)}...`;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/session/checkpoint/shared.ts
|
|
739
|
+
import crypto from "crypto";
|
|
740
|
+
import path2 from "path";
|
|
741
|
+
var MAX_COMPLETED_STEPS = 8;
|
|
742
|
+
var MAX_BATCH_TOOLS = 6;
|
|
743
|
+
var MAX_BATCH_PATHS = 6;
|
|
744
|
+
var MAX_SUMMARY_CHARS = 220;
|
|
745
|
+
function fingerprintFocus(focus) {
|
|
746
|
+
return crypto.createHash("sha1").update(focus.trim().toLowerCase()).digest("hex");
|
|
747
|
+
}
|
|
748
|
+
function normalizeText2(value) {
|
|
749
|
+
return String(value ?? "").replace(/\s+/g, " ").trim();
|
|
750
|
+
}
|
|
751
|
+
function truncate3(value, maxChars) {
|
|
752
|
+
if (!value) {
|
|
753
|
+
return void 0;
|
|
754
|
+
}
|
|
755
|
+
return value.length <= maxChars ? value : `${value.slice(0, maxChars)}...`;
|
|
756
|
+
}
|
|
757
|
+
function takeLastUnique2(values, limit) {
|
|
758
|
+
const seen = /* @__PURE__ */ new Set();
|
|
759
|
+
const result = [];
|
|
760
|
+
for (let index = values.length - 1; index >= 0; index -= 1) {
|
|
761
|
+
const normalized = normalizeText2(values[index]);
|
|
762
|
+
if (!normalized || seen.has(normalized)) {
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
seen.add(normalized);
|
|
766
|
+
result.unshift(normalized);
|
|
767
|
+
if (result.length >= limit) {
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return result;
|
|
772
|
+
}
|
|
773
|
+
function safeParseObject2(raw) {
|
|
774
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
try {
|
|
778
|
+
const parsed = JSON.parse(raw);
|
|
779
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
780
|
+
return null;
|
|
781
|
+
}
|
|
782
|
+
return parsed;
|
|
783
|
+
} catch {
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
function readString(value) {
|
|
788
|
+
const normalized = normalizeText2(value);
|
|
789
|
+
return normalized || void 0;
|
|
790
|
+
}
|
|
791
|
+
function normalizeToolBatch(toolBatch) {
|
|
792
|
+
if (!toolBatch) {
|
|
793
|
+
return void 0;
|
|
794
|
+
}
|
|
795
|
+
const tools = takeLastUnique2(toolBatch.tools ?? [], MAX_BATCH_TOOLS);
|
|
796
|
+
if (tools.length === 0) {
|
|
797
|
+
return void 0;
|
|
798
|
+
}
|
|
799
|
+
return {
|
|
800
|
+
tools,
|
|
801
|
+
summary: truncate3(normalizeText2(toolBatch.summary) || `Ran ${tools.join(", ")}`, MAX_SUMMARY_CHARS),
|
|
802
|
+
changedPaths: takeLastUnique2(toolBatch.changedPaths ?? [], MAX_BATCH_PATHS),
|
|
803
|
+
recordedAt: normalizeTimestamp(toolBatch.recordedAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// src/session/checkpoint/derivation.ts
|
|
808
|
+
function deriveCompletedSteps(session) {
|
|
809
|
+
const completedActions = session.taskState?.completedActions ?? [];
|
|
810
|
+
return takeLastUnique2(completedActions, MAX_COMPLETED_STEPS);
|
|
811
|
+
}
|
|
812
|
+
function deriveRecentToolBatchFromMessages(messages, timestamp) {
|
|
813
|
+
let lastToolIndex = messages.length - 1;
|
|
814
|
+
while (lastToolIndex >= 0 && messages[lastToolIndex]?.role !== "tool") {
|
|
815
|
+
lastToolIndex -= 1;
|
|
816
|
+
}
|
|
817
|
+
if (lastToolIndex < 0) {
|
|
818
|
+
return void 0;
|
|
819
|
+
}
|
|
820
|
+
let startIndex = lastToolIndex;
|
|
821
|
+
while (startIndex >= 0 && messages[startIndex]?.role === "tool") {
|
|
822
|
+
startIndex -= 1;
|
|
823
|
+
}
|
|
824
|
+
const toolMessages = messages.slice(startIndex + 1, lastToolIndex + 1).filter((message) => message.role === "tool");
|
|
825
|
+
const toolNames = toolMessages.map((message) => normalizeText2(message.name)).filter(Boolean);
|
|
826
|
+
return buildToolBatch(toolNames, toolMessages, void 0, timestamp);
|
|
827
|
+
}
|
|
828
|
+
function buildToolBatch(toolNames, toolMessages, changedPaths, timestamp) {
|
|
829
|
+
const tools = takeLastUnique2(toolNames, MAX_BATCH_TOOLS);
|
|
830
|
+
if (tools.length === 0) {
|
|
831
|
+
return void 0;
|
|
832
|
+
}
|
|
833
|
+
const batchChangedPaths = takeLastUnique2(
|
|
834
|
+
[
|
|
835
|
+
...changedPaths ?? [],
|
|
836
|
+
...toolMessages.map((message) => readPathFromMessage(message)).filter(Boolean)
|
|
837
|
+
],
|
|
838
|
+
MAX_BATCH_PATHS
|
|
839
|
+
);
|
|
840
|
+
const recordedAt = normalizeTimestamp(
|
|
841
|
+
toolMessages[toolMessages.length - 1]?.createdAt,
|
|
842
|
+
timestamp
|
|
843
|
+
);
|
|
844
|
+
return {
|
|
845
|
+
tools,
|
|
846
|
+
summary: buildToolBatchSummary(tools, batchChangedPaths),
|
|
847
|
+
changedPaths: batchChangedPaths,
|
|
848
|
+
recordedAt
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function readPathFromMessage(message) {
|
|
852
|
+
const payload = safeParseObject2(message.content);
|
|
853
|
+
return readString(payload?.path) ?? readString(payload?.requestedPath);
|
|
854
|
+
}
|
|
855
|
+
function buildToolBatchSummary(toolNames, changedPaths) {
|
|
856
|
+
const fragments = [`Ran ${toolNames.join(", ")}`];
|
|
857
|
+
if (changedPaths.length > 0) {
|
|
858
|
+
fragments.push(`changed ${changedPaths.join(" | ")}`);
|
|
859
|
+
}
|
|
860
|
+
return truncate3(fragments.join("; "), MAX_SUMMARY_CHARS);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// src/session/checkpoint/base.ts
|
|
864
|
+
function createEmptyCheckpoint(timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
865
|
+
return {
|
|
866
|
+
version: 1,
|
|
867
|
+
status: "active",
|
|
868
|
+
completedSteps: [],
|
|
869
|
+
flow: {
|
|
870
|
+
phase: "active",
|
|
871
|
+
runState: {
|
|
872
|
+
status: "idle",
|
|
873
|
+
source: "checkpoint",
|
|
874
|
+
pendingToolCallCount: 0,
|
|
875
|
+
updatedAt: timestamp
|
|
876
|
+
},
|
|
877
|
+
updatedAt: timestamp
|
|
878
|
+
},
|
|
879
|
+
updatedAt: timestamp
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
function createCheckpointForFocus(focus, timestamp) {
|
|
883
|
+
return {
|
|
884
|
+
...createEmptyCheckpoint(timestamp),
|
|
885
|
+
focus,
|
|
886
|
+
focusFingerprint: focus ? fingerprintFocus(focus) : void 0
|
|
887
|
+
};
|
|
1701
888
|
}
|
|
1702
|
-
function
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
889
|
+
function deriveCheckpointFromSession(session, timestamp) {
|
|
890
|
+
const recentToolBatch = deriveRecentToolBatchFromMessages(session.messages, timestamp);
|
|
891
|
+
return {
|
|
892
|
+
...createCheckpointForFocus(normalizeText2(session.taskState?.focus) || void 0, timestamp),
|
|
893
|
+
completedSteps: deriveCompletedSteps(session),
|
|
894
|
+
recentToolBatch
|
|
895
|
+
};
|
|
1707
896
|
}
|
|
1708
897
|
|
|
1709
898
|
// src/agent/runtimeTransition/shared.ts
|
|
1710
899
|
var MAX_REASON_ITEMS = 6;
|
|
1711
900
|
var MAX_REASON_TEXT_CHARS = 220;
|
|
1712
|
-
function
|
|
901
|
+
function takeLastUnique3(values) {
|
|
1713
902
|
const seen = /* @__PURE__ */ new Set();
|
|
1714
903
|
const result = [];
|
|
1715
904
|
for (let index = values.length - 1; index >= 0; index -= 1) {
|
|
@@ -1725,7 +914,7 @@ function takeLastUnique2(values) {
|
|
|
1725
914
|
}
|
|
1726
915
|
return result;
|
|
1727
916
|
}
|
|
1728
|
-
function
|
|
917
|
+
function truncate4(value) {
|
|
1729
918
|
return value.length <= MAX_REASON_TEXT_CHARS ? value : `${value.slice(0, MAX_REASON_TEXT_CHARS)}...`;
|
|
1730
919
|
}
|
|
1731
920
|
function normalizeText3(value) {
|
|
@@ -1738,73 +927,6 @@ function clampWholeNumber(value, min, max, fallback) {
|
|
|
1738
927
|
return Math.max(min, Math.min(max, Math.trunc(value)));
|
|
1739
928
|
}
|
|
1740
929
|
|
|
1741
|
-
// src/agent/runtimeTransition/builders.ts
|
|
1742
|
-
function createToolBatchTransition(input, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1743
|
-
return {
|
|
1744
|
-
action: "continue",
|
|
1745
|
-
reason: {
|
|
1746
|
-
code: "continue.after_tool_batch",
|
|
1747
|
-
toolNames: takeLastUnique2(input.toolNames),
|
|
1748
|
-
changedPaths: takeLastUnique2(input.changedPaths ?? [])
|
|
1749
|
-
},
|
|
1750
|
-
timestamp
|
|
1751
|
-
};
|
|
1752
|
-
}
|
|
1753
|
-
function createEmptyAssistantResponseTransition(timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1754
|
-
return {
|
|
1755
|
-
action: "continue",
|
|
1756
|
-
reason: {
|
|
1757
|
-
code: "continue.empty_assistant_response"
|
|
1758
|
-
},
|
|
1759
|
-
timestamp
|
|
1760
|
-
};
|
|
1761
|
-
}
|
|
1762
|
-
function createProviderRecoveryTransition(input, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1763
|
-
return {
|
|
1764
|
-
action: "recover",
|
|
1765
|
-
reason: {
|
|
1766
|
-
code: "recover.provider_request_retry",
|
|
1767
|
-
consecutiveFailures: Math.max(1, Math.trunc(input.consecutiveFailures)),
|
|
1768
|
-
error: truncate3(normalizeText3(input.error?.message ?? input.error) || "request failed"),
|
|
1769
|
-
configuredModel: normalizeText3(input.configuredModel) || "unknown_model",
|
|
1770
|
-
requestModel: normalizeText3(input.requestModel) || "unknown_model",
|
|
1771
|
-
contextWindowMessages: Math.max(1, Math.trunc(input.requestConfig.contextWindowMessages)),
|
|
1772
|
-
maxContextChars: Math.max(1, Math.trunc(input.requestConfig.maxContextChars)),
|
|
1773
|
-
contextSummaryChars: Math.max(1, Math.trunc(input.requestConfig.contextSummaryChars)),
|
|
1774
|
-
delayMs: Math.max(0, Math.trunc(input.delayMs))
|
|
1775
|
-
},
|
|
1776
|
-
timestamp
|
|
1777
|
-
};
|
|
1778
|
-
}
|
|
1779
|
-
function createFinalizeTransition(input, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1780
|
-
return {
|
|
1781
|
-
action: "finalize",
|
|
1782
|
-
reason: {
|
|
1783
|
-
code: "finalize.completed",
|
|
1784
|
-
changedPaths: takeLastUnique2([...input.changedPaths])
|
|
1785
|
-
},
|
|
1786
|
-
timestamp
|
|
1787
|
-
};
|
|
1788
|
-
}
|
|
1789
|
-
function createExecutionWaitYieldTransition(input, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1790
|
-
return {
|
|
1791
|
-
action: "yield",
|
|
1792
|
-
reason: {
|
|
1793
|
-
code: "yield.execution_wait",
|
|
1794
|
-
executionIds: takeLastUnique2([...input.executionIds]),
|
|
1795
|
-
toolNames: takeLastUnique2([...input.toolNames])
|
|
1796
|
-
},
|
|
1797
|
-
timestamp
|
|
1798
|
-
};
|
|
1799
|
-
}
|
|
1800
|
-
function buildRunTurnResult(input) {
|
|
1801
|
-
return {
|
|
1802
|
-
session: input.session,
|
|
1803
|
-
changedPaths: [...input.changedPaths],
|
|
1804
|
-
transition: input.transition
|
|
1805
|
-
};
|
|
1806
|
-
}
|
|
1807
|
-
|
|
1808
930
|
// src/agent/runtimeTransition/normalize.ts
|
|
1809
931
|
function normalizeRuntimeTransition(transition, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1810
932
|
if (!transition || typeof transition !== "object") {
|
|
@@ -1832,7 +954,7 @@ function normalizeRuntimeTransition(transition, timestamp = (/* @__PURE__ */ new
|
|
|
1832
954
|
function normalizeContinueTransition(reason, timestamp) {
|
|
1833
955
|
switch (reason.code) {
|
|
1834
956
|
case "continue.after_tool_batch": {
|
|
1835
|
-
const toolNames =
|
|
957
|
+
const toolNames = takeLastUnique3(reason.toolNames);
|
|
1836
958
|
if (toolNames.length === 0) {
|
|
1837
959
|
return void 0;
|
|
1838
960
|
}
|
|
@@ -1841,7 +963,7 @@ function normalizeContinueTransition(reason, timestamp) {
|
|
|
1841
963
|
reason: {
|
|
1842
964
|
code: reason.code,
|
|
1843
965
|
toolNames,
|
|
1844
|
-
changedPaths:
|
|
966
|
+
changedPaths: takeLastUnique3(reason.changedPaths ?? [])
|
|
1845
967
|
},
|
|
1846
968
|
timestamp
|
|
1847
969
|
};
|
|
@@ -1867,7 +989,7 @@ function normalizeRecoverTransition(reason, timestamp) {
|
|
|
1867
989
|
reason: {
|
|
1868
990
|
code: reason.code,
|
|
1869
991
|
consecutiveFailures: clampWholeNumber(reason.consecutiveFailures, 1, 50, 1) ?? 1,
|
|
1870
|
-
error:
|
|
992
|
+
error: truncate4(normalizeText3(reason.error) || "request failed"),
|
|
1871
993
|
configuredModel: normalizeText3(reason.configuredModel) || "unknown_model",
|
|
1872
994
|
requestModel: normalizeText3(reason.requestModel) || "unknown_model",
|
|
1873
995
|
contextWindowMessages: clampWholeNumber(reason.contextWindowMessages, 1, 999, 1) ?? 1,
|
|
@@ -1886,7 +1008,7 @@ function normalizeFinalizeTransition(reason, timestamp) {
|
|
|
1886
1008
|
action: "finalize",
|
|
1887
1009
|
reason: {
|
|
1888
1010
|
code: reason.code,
|
|
1889
|
-
changedPaths:
|
|
1011
|
+
changedPaths: takeLastUnique3(reason.changedPaths ?? [])
|
|
1890
1012
|
},
|
|
1891
1013
|
timestamp
|
|
1892
1014
|
};
|
|
@@ -1895,7 +1017,7 @@ function normalizeYieldTransition(reason, timestamp) {
|
|
|
1895
1017
|
if (reason.code !== "yield.execution_wait") {
|
|
1896
1018
|
return void 0;
|
|
1897
1019
|
}
|
|
1898
|
-
const executionIds =
|
|
1020
|
+
const executionIds = takeLastUnique3(reason.executionIds ?? []);
|
|
1899
1021
|
if (executionIds.length === 0) {
|
|
1900
1022
|
return void 0;
|
|
1901
1023
|
}
|
|
@@ -1904,7 +1026,7 @@ function normalizeYieldTransition(reason, timestamp) {
|
|
|
1904
1026
|
reason: {
|
|
1905
1027
|
code: reason.code,
|
|
1906
1028
|
executionIds,
|
|
1907
|
-
toolNames:
|
|
1029
|
+
toolNames: takeLastUnique3(reason.toolNames ?? [])
|
|
1908
1030
|
},
|
|
1909
1031
|
timestamp
|
|
1910
1032
|
};
|
|
@@ -1994,163 +1116,70 @@ function normalizeRunStateSource(source, status) {
|
|
|
1994
1116
|
return "checkpoint";
|
|
1995
1117
|
}
|
|
1996
1118
|
|
|
1997
|
-
// src/
|
|
1998
|
-
|
|
1999
|
-
import path4 from "path";
|
|
2000
|
-
var MAX_COMPLETED_STEPS = 8;
|
|
2001
|
-
var MAX_BATCH_TOOLS = 6;
|
|
2002
|
-
var MAX_BATCH_PATHS = 6;
|
|
2003
|
-
var MAX_SUMMARY_CHARS = 220;
|
|
2004
|
-
function fingerprintFocus(focus) {
|
|
2005
|
-
return crypto2.createHash("sha1").update(focus.trim().toLowerCase()).digest("hex");
|
|
2006
|
-
}
|
|
2007
|
-
function normalizeText4(value) {
|
|
2008
|
-
return String(value ?? "").replace(/\s+/g, " ").trim();
|
|
2009
|
-
}
|
|
2010
|
-
function truncate4(value, maxChars) {
|
|
2011
|
-
if (!value) {
|
|
2012
|
-
return void 0;
|
|
2013
|
-
}
|
|
2014
|
-
return value.length <= maxChars ? value : `${value.slice(0, maxChars)}...`;
|
|
2015
|
-
}
|
|
2016
|
-
function takeLastUnique3(values, limit) {
|
|
2017
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2018
|
-
const result = [];
|
|
2019
|
-
for (let index = values.length - 1; index >= 0; index -= 1) {
|
|
2020
|
-
const normalized = normalizeText4(values[index]);
|
|
2021
|
-
if (!normalized || seen.has(normalized)) {
|
|
2022
|
-
continue;
|
|
2023
|
-
}
|
|
2024
|
-
seen.add(normalized);
|
|
2025
|
-
result.unshift(normalized);
|
|
2026
|
-
if (result.length >= limit) {
|
|
2027
|
-
break;
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
return result;
|
|
2031
|
-
}
|
|
2032
|
-
function safeParseObject2(raw) {
|
|
2033
|
-
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
2034
|
-
return null;
|
|
2035
|
-
}
|
|
2036
|
-
try {
|
|
2037
|
-
const parsed = JSON.parse(raw);
|
|
2038
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2039
|
-
return null;
|
|
2040
|
-
}
|
|
2041
|
-
return parsed;
|
|
2042
|
-
} catch {
|
|
2043
|
-
return null;
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
function readString(value) {
|
|
2047
|
-
const normalized = normalizeText4(value);
|
|
2048
|
-
return normalized || void 0;
|
|
2049
|
-
}
|
|
2050
|
-
function normalizeToolBatch(toolBatch) {
|
|
2051
|
-
if (!toolBatch) {
|
|
2052
|
-
return void 0;
|
|
2053
|
-
}
|
|
2054
|
-
const tools = takeLastUnique3(toolBatch.tools ?? [], MAX_BATCH_TOOLS);
|
|
2055
|
-
if (tools.length === 0) {
|
|
2056
|
-
return void 0;
|
|
2057
|
-
}
|
|
1119
|
+
// src/agent/runtimeTransition/builders.ts
|
|
1120
|
+
function createToolBatchTransition(input, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2058
1121
|
return {
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
1122
|
+
action: "continue",
|
|
1123
|
+
reason: {
|
|
1124
|
+
code: "continue.after_tool_batch",
|
|
1125
|
+
toolNames: takeLastUnique3(input.toolNames),
|
|
1126
|
+
changedPaths: takeLastUnique3(input.changedPaths ?? [])
|
|
1127
|
+
},
|
|
1128
|
+
timestamp
|
|
2063
1129
|
};
|
|
2064
1130
|
}
|
|
2065
|
-
|
|
2066
|
-
// src/session/checkpoint/derivation.ts
|
|
2067
|
-
function deriveCompletedSteps(session) {
|
|
2068
|
-
const completedActions = session.taskState?.completedActions ?? [];
|
|
2069
|
-
return takeLastUnique3(completedActions, MAX_COMPLETED_STEPS);
|
|
2070
|
-
}
|
|
2071
|
-
function deriveRecentToolBatchFromMessages(messages, timestamp) {
|
|
2072
|
-
let lastToolIndex = messages.length - 1;
|
|
2073
|
-
while (lastToolIndex >= 0 && messages[lastToolIndex]?.role !== "tool") {
|
|
2074
|
-
lastToolIndex -= 1;
|
|
2075
|
-
}
|
|
2076
|
-
if (lastToolIndex < 0) {
|
|
2077
|
-
return void 0;
|
|
2078
|
-
}
|
|
2079
|
-
let startIndex = lastToolIndex;
|
|
2080
|
-
while (startIndex >= 0 && messages[startIndex]?.role === "tool") {
|
|
2081
|
-
startIndex -= 1;
|
|
2082
|
-
}
|
|
2083
|
-
const toolMessages = messages.slice(startIndex + 1, lastToolIndex + 1).filter((message) => message.role === "tool");
|
|
2084
|
-
const toolNames = toolMessages.map((message) => normalizeText4(message.name)).filter(Boolean);
|
|
2085
|
-
return buildToolBatch(toolNames, toolMessages, void 0, timestamp);
|
|
2086
|
-
}
|
|
2087
|
-
function buildToolBatch(toolNames, toolMessages, changedPaths, timestamp) {
|
|
2088
|
-
const tools = takeLastUnique3(toolNames, MAX_BATCH_TOOLS);
|
|
2089
|
-
if (tools.length === 0) {
|
|
2090
|
-
return void 0;
|
|
2091
|
-
}
|
|
2092
|
-
const batchChangedPaths = takeLastUnique3(
|
|
2093
|
-
[
|
|
2094
|
-
...changedPaths ?? [],
|
|
2095
|
-
...toolMessages.map((message) => readPathFromMessage(message)).filter(Boolean)
|
|
2096
|
-
],
|
|
2097
|
-
MAX_BATCH_PATHS
|
|
2098
|
-
);
|
|
2099
|
-
const recordedAt = normalizeTimestamp(
|
|
2100
|
-
toolMessages[toolMessages.length - 1]?.createdAt,
|
|
2101
|
-
timestamp
|
|
2102
|
-
);
|
|
1131
|
+
function createEmptyAssistantResponseTransition(timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2103
1132
|
return {
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
1133
|
+
action: "continue",
|
|
1134
|
+
reason: {
|
|
1135
|
+
code: "continue.empty_assistant_response"
|
|
1136
|
+
},
|
|
1137
|
+
timestamp
|
|
2108
1138
|
};
|
|
2109
1139
|
}
|
|
2110
|
-
function
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
1140
|
+
function createProviderRecoveryTransition(input, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1141
|
+
return {
|
|
1142
|
+
action: "recover",
|
|
1143
|
+
reason: {
|
|
1144
|
+
code: "recover.provider_request_retry",
|
|
1145
|
+
consecutiveFailures: Math.max(1, Math.trunc(input.consecutiveFailures)),
|
|
1146
|
+
error: truncate4(normalizeText3(input.error?.message ?? input.error) || "request failed"),
|
|
1147
|
+
configuredModel: normalizeText3(input.configuredModel) || "unknown_model",
|
|
1148
|
+
requestModel: normalizeText3(input.requestModel) || "unknown_model",
|
|
1149
|
+
contextWindowMessages: Math.max(1, Math.trunc(input.requestConfig.contextWindowMessages)),
|
|
1150
|
+
maxContextChars: Math.max(1, Math.trunc(input.requestConfig.maxContextChars)),
|
|
1151
|
+
contextSummaryChars: Math.max(1, Math.trunc(input.requestConfig.contextSummaryChars)),
|
|
1152
|
+
delayMs: Math.max(0, Math.trunc(input.delayMs))
|
|
1153
|
+
},
|
|
1154
|
+
timestamp
|
|
1155
|
+
};
|
|
2120
1156
|
}
|
|
2121
|
-
|
|
2122
|
-
// src/session/checkpoint/base.ts
|
|
2123
|
-
function createEmptyCheckpoint(timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1157
|
+
function createFinalizeTransition(input, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2124
1158
|
return {
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
phase: "active",
|
|
2130
|
-
runState: {
|
|
2131
|
-
status: "idle",
|
|
2132
|
-
source: "checkpoint",
|
|
2133
|
-
pendingToolCallCount: 0,
|
|
2134
|
-
updatedAt: timestamp
|
|
2135
|
-
},
|
|
2136
|
-
updatedAt: timestamp
|
|
1159
|
+
action: "finalize",
|
|
1160
|
+
reason: {
|
|
1161
|
+
code: "finalize.completed",
|
|
1162
|
+
changedPaths: takeLastUnique3([...input.changedPaths])
|
|
2137
1163
|
},
|
|
2138
|
-
|
|
1164
|
+
timestamp
|
|
2139
1165
|
};
|
|
2140
1166
|
}
|
|
2141
|
-
function
|
|
1167
|
+
function createExecutionWaitYieldTransition(input, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2142
1168
|
return {
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
1169
|
+
action: "yield",
|
|
1170
|
+
reason: {
|
|
1171
|
+
code: "yield.execution_wait",
|
|
1172
|
+
executionIds: takeLastUnique3([...input.executionIds]),
|
|
1173
|
+
toolNames: takeLastUnique3([...input.toolNames])
|
|
1174
|
+
},
|
|
1175
|
+
timestamp
|
|
2146
1176
|
};
|
|
2147
1177
|
}
|
|
2148
|
-
function
|
|
2149
|
-
const recentToolBatch = deriveRecentToolBatchFromMessages(session.messages, timestamp);
|
|
1178
|
+
function buildRunTurnResult(input) {
|
|
2150
1179
|
return {
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
1180
|
+
session: input.session,
|
|
1181
|
+
changedPaths: [...input.changedPaths],
|
|
1182
|
+
transition: input.transition
|
|
2154
1183
|
};
|
|
2155
1184
|
}
|
|
2156
1185
|
|
|
@@ -2159,14 +1188,14 @@ function normalizeCheckpoint(checkpoint, timestamp = (/* @__PURE__ */ new Date()
|
|
|
2159
1188
|
if (!checkpoint) {
|
|
2160
1189
|
return void 0;
|
|
2161
1190
|
}
|
|
2162
|
-
const focus =
|
|
1191
|
+
const focus = normalizeText2(checkpoint.focus) || void 0;
|
|
2163
1192
|
const status = checkpoint.status === "completed" ? "completed" : "active";
|
|
2164
1193
|
return {
|
|
2165
1194
|
version: 1,
|
|
2166
1195
|
focus,
|
|
2167
|
-
focusFingerprint:
|
|
1196
|
+
focusFingerprint: normalizeText2(checkpoint.focusFingerprint) || (focus ? fingerprintFocus(focus) : void 0),
|
|
2168
1197
|
status,
|
|
2169
|
-
completedSteps:
|
|
1198
|
+
completedSteps: takeLastUnique2(checkpoint.completedSteps ?? [], 8),
|
|
2170
1199
|
recentToolBatch: normalizeToolBatch(checkpoint.recentToolBatch),
|
|
2171
1200
|
flow: normalizeCheckpointFlow(checkpoint.flow, status, timestamp),
|
|
2172
1201
|
updatedAt: normalizeTimestamp(checkpoint.updatedAt, timestamp)
|
|
@@ -2212,7 +1241,7 @@ function noteCheckpointTurnInput(session, input, timestamp = (/* @__PURE__ */ ne
|
|
|
2212
1241
|
};
|
|
2213
1242
|
}
|
|
2214
1243
|
function resolveCurrentFocusCheckpoint(session, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2215
|
-
const focus =
|
|
1244
|
+
const focus = normalizeText2(session.taskState?.focus) || void 0;
|
|
2216
1245
|
const fingerprint = focus ? fingerprintFocus(focus) : void 0;
|
|
2217
1246
|
const checkpoint = normalizeCheckpoint(session.checkpoint, timestamp) ?? createEmptyCheckpoint(timestamp);
|
|
2218
1247
|
if (!focus) {
|
|
@@ -2532,7 +1561,7 @@ function readSessionMemoryCurrentFocus(summary) {
|
|
|
2532
1561
|
}
|
|
2533
1562
|
|
|
2534
1563
|
// src/session/workset.ts
|
|
2535
|
-
import
|
|
1564
|
+
import path3 from "path";
|
|
2536
1565
|
var MAX_WORKSET_FILES = 20;
|
|
2537
1566
|
function createEmptySessionWorkset(timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2538
1567
|
return {
|
|
@@ -2616,9 +1645,9 @@ function normalizeWorksetEntry(value) {
|
|
|
2616
1645
|
};
|
|
2617
1646
|
}
|
|
2618
1647
|
function normalizeDisplayPath(cwd, targetPath) {
|
|
2619
|
-
const absolutePath =
|
|
2620
|
-
const relative =
|
|
2621
|
-
return relative && !relative.startsWith("..") && !
|
|
1648
|
+
const absolutePath = path3.resolve(cwd, targetPath);
|
|
1649
|
+
const relative = path3.relative(cwd, absolutePath);
|
|
1650
|
+
return relative && !relative.startsWith("..") && !path3.isAbsolute(relative) ? relative : absolutePath;
|
|
2622
1651
|
}
|
|
2623
1652
|
function readString2(value) {
|
|
2624
1653
|
return typeof value === "string" && value.trim() ? value : (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2631,21 +1660,14 @@ function latestWorksetTimestamp(files) {
|
|
|
2631
1660
|
}
|
|
2632
1661
|
|
|
2633
1662
|
export {
|
|
2634
|
-
PROJECT_STATE_DIR_NAME,
|
|
2635
|
-
PROJECT_STATE_ENV_FILE_NAME,
|
|
2636
|
-
PROJECT_STATE_ENV_EXAMPLE_FILE_NAME,
|
|
2637
|
-
PROJECT_STATE_IGNORE_FILE_NAME,
|
|
2638
|
-
PRESERVED_PROJECT_STATE_ENTRY_NAMES,
|
|
2639
|
-
getProjectStatePaths,
|
|
2640
|
-
ensureProjectStateDirectories,
|
|
2641
1663
|
createEmptyAssistantResponseTransition,
|
|
2642
1664
|
createProviderRecoveryTransition,
|
|
2643
1665
|
createFinalizeTransition,
|
|
2644
1666
|
createExecutionWaitYieldTransition,
|
|
2645
1667
|
buildRunTurnResult,
|
|
2646
1668
|
fingerprintFocus,
|
|
2647
|
-
|
|
2648
|
-
|
|
1669
|
+
normalizeText2 as normalizeText,
|
|
1670
|
+
takeLastUnique2 as takeLastUnique,
|
|
2649
1671
|
createEmptyCheckpoint,
|
|
2650
1672
|
normalizeCheckpoint,
|
|
2651
1673
|
normalizeSessionCheckpoint,
|
|
@@ -2654,13 +1676,8 @@ export {
|
|
|
2654
1676
|
noteCheckpointTransition,
|
|
2655
1677
|
noteCheckpointRecovery,
|
|
2656
1678
|
noteCheckpointCompleted,
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
sleepWithSignal,
|
|
2660
|
-
isRetryableApiError,
|
|
2661
|
-
recordObservabilityEvent,
|
|
2662
|
-
createProviderClientPool,
|
|
2663
|
-
fetchAssistantResponse,
|
|
1679
|
+
resolveModelProfile,
|
|
1680
|
+
resolveProviderCapabilities,
|
|
2664
1681
|
buildChatMessages,
|
|
2665
1682
|
createMessage,
|
|
2666
1683
|
createToolMessage,
|