@kooka/core 0.1.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/LICENSE +201 -0
- package/dist/cjs/index.cjs +990 -0
- package/dist/esm/index.js +909 -0
- package/dist/types/backgroundJobs.d.ts +33 -0
- package/dist/types/backgroundJobs.d.ts.map +1 -0
- package/dist/types/compaction.d.ts +35 -0
- package/dist/types/compaction.d.ts.map +1 -0
- package/dist/types/fsPath.d.ts +4 -0
- package/dist/types/fsPath.d.ts.map +1 -0
- package/dist/types/history.d.ts +48 -0
- package/dist/types/history.d.ts.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/permission.d.ts +11 -0
- package/dist/types/permission.d.ts.map +1 -0
- package/dist/types/shellPaths.d.ts +15 -0
- package/dist/types/shellPaths.d.ts.map +1 -0
- package/dist/types/toolSchema.d.ts +10 -0
- package/dist/types/toolSchema.d.ts.map +1 -0
- package/dist/types/validation.d.ts +29 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,990 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
COMPACTED_TOOL_PLACEHOLDER: () => COMPACTED_TOOL_PLACEHOLDER,
|
|
34
|
+
COMPACTION_AUTO_CONTINUE_TEXT: () => COMPACTION_AUTO_CONTINUE_TEXT,
|
|
35
|
+
COMPACTION_MARKER_TEXT: () => COMPACTION_MARKER_TEXT,
|
|
36
|
+
COMPACTION_PROMPT_TEXT: () => COMPACTION_PROMPT_TEXT,
|
|
37
|
+
COMPACTION_SYSTEM_PROMPT: () => COMPACTION_SYSTEM_PROMPT,
|
|
38
|
+
DEFAULT_BACKGROUND_KILL_GRACE_MS: () => DEFAULT_BACKGROUND_KILL_GRACE_MS,
|
|
39
|
+
DEFAULT_BACKGROUND_TTL_MS: () => DEFAULT_BACKGROUND_TTL_MS,
|
|
40
|
+
appendReasoning: () => appendReasoning,
|
|
41
|
+
appendText: () => appendText,
|
|
42
|
+
cleanupDeadBackgroundJobs: () => cleanupDeadBackgroundJobs,
|
|
43
|
+
createAssistantHistoryMessage: () => createAssistantHistoryMessage,
|
|
44
|
+
createBackgroundJobKey: () => createBackgroundJobKey,
|
|
45
|
+
createHistoryForModel: () => createHistoryForModel,
|
|
46
|
+
createUserHistoryMessage: () => createUserHistoryMessage,
|
|
47
|
+
evaluatePermission: () => evaluatePermission,
|
|
48
|
+
evaluateShellCommand: () => evaluateShellCommand,
|
|
49
|
+
expandHome: () => expandHome,
|
|
50
|
+
extractUsageTokens: () => extractUsageTokens,
|
|
51
|
+
finalizeStreamingParts: () => finalizeStreamingParts,
|
|
52
|
+
findExternalPathReferencesInShellCommand: () => findExternalPathReferencesInShellCommand,
|
|
53
|
+
getBackgroundJob: () => getBackgroundJob,
|
|
54
|
+
getEffectiveHistory: () => getEffectiveHistory,
|
|
55
|
+
getMessageText: () => getMessageText,
|
|
56
|
+
getReservedOutputTokens: () => getReservedOutputTokens,
|
|
57
|
+
isOverflow: () => isOverflow,
|
|
58
|
+
isPathInsideWorkspace: () => isPathInsideWorkspace,
|
|
59
|
+
isPidAlive: () => isPidAlive,
|
|
60
|
+
isSubPath: () => isSubPath,
|
|
61
|
+
killProcessTree: () => killProcessTree,
|
|
62
|
+
listBackgroundJobs: () => listBackgroundJobs,
|
|
63
|
+
markPrunableToolOutputs: () => markPrunableToolOutputs,
|
|
64
|
+
mergeRulesets: () => mergeRulesets,
|
|
65
|
+
normalizeFsPath: () => normalizeFsPath,
|
|
66
|
+
optionalBoolean: () => optionalBoolean,
|
|
67
|
+
optionalNumber: () => optionalNumber,
|
|
68
|
+
optionalString: () => optionalString,
|
|
69
|
+
refreshBackgroundJob: () => refreshBackgroundJob,
|
|
70
|
+
registerBackgroundJob: () => registerBackgroundJob,
|
|
71
|
+
removeBackgroundJob: () => removeBackgroundJob,
|
|
72
|
+
requireString: () => requireString,
|
|
73
|
+
setDynamicToolError: () => setDynamicToolError,
|
|
74
|
+
setDynamicToolOutput: () => setDynamicToolOutput,
|
|
75
|
+
upsertDynamicToolCall: () => upsertDynamicToolCall,
|
|
76
|
+
validateToolArgs: () => validateToolArgs,
|
|
77
|
+
wildcardMatch: () => wildcardMatch
|
|
78
|
+
});
|
|
79
|
+
module.exports = __toCommonJS(index_exports);
|
|
80
|
+
|
|
81
|
+
// src/fsPath.ts
|
|
82
|
+
var os = __toESM(require("os"), 1);
|
|
83
|
+
var path = __toESM(require("path"), 1);
|
|
84
|
+
function normalizeFsPath(value) {
|
|
85
|
+
const resolved = path.resolve(value);
|
|
86
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
87
|
+
}
|
|
88
|
+
function isSubPath(childPath, parentPath) {
|
|
89
|
+
const parent = normalizeFsPath(parentPath);
|
|
90
|
+
const child = normalizeFsPath(childPath);
|
|
91
|
+
return child === parent || child.startsWith(parent + path.sep);
|
|
92
|
+
}
|
|
93
|
+
function expandHome(p) {
|
|
94
|
+
const trimmed = (p || "").trim();
|
|
95
|
+
if (!trimmed) return trimmed;
|
|
96
|
+
if (trimmed === "~") return os.homedir();
|
|
97
|
+
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
|
|
98
|
+
return path.join(os.homedir(), trimmed.slice(2));
|
|
99
|
+
}
|
|
100
|
+
return trimmed;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/permission.ts
|
|
104
|
+
function escapeRegExp(value) {
|
|
105
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
106
|
+
}
|
|
107
|
+
function wildcardToRegExp(pattern) {
|
|
108
|
+
const normalized = String(pattern ?? "").trim();
|
|
109
|
+
if (!normalized || normalized === "*") return /^.*$/;
|
|
110
|
+
let re = "^";
|
|
111
|
+
for (const ch of normalized) {
|
|
112
|
+
if (ch === "*") {
|
|
113
|
+
re += ".*";
|
|
114
|
+
} else if (ch === "?") {
|
|
115
|
+
re += ".";
|
|
116
|
+
} else {
|
|
117
|
+
re += escapeRegExp(ch);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
re += "$";
|
|
121
|
+
return new RegExp(re);
|
|
122
|
+
}
|
|
123
|
+
function wildcardMatch(pattern, value) {
|
|
124
|
+
try {
|
|
125
|
+
return wildcardToRegExp(pattern).test(String(value ?? ""));
|
|
126
|
+
} catch {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function evaluatePermission(permission, pattern, ruleset) {
|
|
131
|
+
let match;
|
|
132
|
+
for (const rule of ruleset) {
|
|
133
|
+
if (!wildcardMatch(rule.permission, permission)) continue;
|
|
134
|
+
if (!wildcardMatch(rule.pattern, pattern)) continue;
|
|
135
|
+
match = rule;
|
|
136
|
+
}
|
|
137
|
+
return match;
|
|
138
|
+
}
|
|
139
|
+
function mergeRulesets(...rulesets) {
|
|
140
|
+
const out = [];
|
|
141
|
+
for (const rs of rulesets) {
|
|
142
|
+
if (!rs) continue;
|
|
143
|
+
out.push(...rs);
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/shellPaths.ts
|
|
149
|
+
var path2 = __toESM(require("path"), 1);
|
|
150
|
+
function stripPunctuation(token) {
|
|
151
|
+
let out = token.trim();
|
|
152
|
+
if (!out) return out;
|
|
153
|
+
out = out.replace(/^[;|&(){}<>]+/, "");
|
|
154
|
+
out = out.replace(/[;|&(){}<>]+$/, "");
|
|
155
|
+
return out.trim();
|
|
156
|
+
}
|
|
157
|
+
function tokenizeShellCommand(command) {
|
|
158
|
+
const tokens = [];
|
|
159
|
+
const s = String(command ?? "");
|
|
160
|
+
let i = 0;
|
|
161
|
+
const isWs = (ch) => ch === " " || ch === " " || ch === "\n" || ch === "\r";
|
|
162
|
+
while (i < s.length) {
|
|
163
|
+
while (i < s.length && isWs(s[i])) i++;
|
|
164
|
+
if (i >= s.length) break;
|
|
165
|
+
let token = "";
|
|
166
|
+
const start = s[i];
|
|
167
|
+
if (start === "'") {
|
|
168
|
+
i++;
|
|
169
|
+
while (i < s.length && s[i] !== "'") {
|
|
170
|
+
token += s[i++];
|
|
171
|
+
}
|
|
172
|
+
if (i < s.length && s[i] === "'") i++;
|
|
173
|
+
tokens.push(token);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (start === '"') {
|
|
177
|
+
i++;
|
|
178
|
+
while (i < s.length) {
|
|
179
|
+
const ch = s[i];
|
|
180
|
+
if (ch === '"') {
|
|
181
|
+
i++;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
if (ch === "\\" && i + 1 < s.length) {
|
|
185
|
+
token += s[i + 1];
|
|
186
|
+
i += 2;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
token += ch;
|
|
190
|
+
i++;
|
|
191
|
+
}
|
|
192
|
+
tokens.push(token);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
while (i < s.length && !isWs(s[i])) {
|
|
196
|
+
const ch = s[i];
|
|
197
|
+
if (ch === "\\" && i + 1 < s.length) {
|
|
198
|
+
token += s[i + 1];
|
|
199
|
+
i += 2;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
token += ch;
|
|
203
|
+
i++;
|
|
204
|
+
}
|
|
205
|
+
if (token) tokens.push(token);
|
|
206
|
+
}
|
|
207
|
+
return tokens;
|
|
208
|
+
}
|
|
209
|
+
function resolveCandidatePath(candidate, cwd) {
|
|
210
|
+
const expanded = expandHome(candidate);
|
|
211
|
+
return path2.isAbsolute(expanded) ? path2.resolve(expanded) : path2.resolve(cwd, expanded);
|
|
212
|
+
}
|
|
213
|
+
function isUrlLike(token) {
|
|
214
|
+
return token.includes("://");
|
|
215
|
+
}
|
|
216
|
+
function isPathLikeToken(token) {
|
|
217
|
+
if (!token) return false;
|
|
218
|
+
if (token === "-") return false;
|
|
219
|
+
if (isUrlLike(token)) return false;
|
|
220
|
+
if (token === "~" || token.startsWith("~/") || token.startsWith("~\\")) return true;
|
|
221
|
+
if (token.startsWith("/")) return true;
|
|
222
|
+
if (/^[A-Za-z]:[\\/]/.test(token)) return true;
|
|
223
|
+
if (token === "." || token === "..") return true;
|
|
224
|
+
if (token.startsWith("./") || token.startsWith(".\\") || token.startsWith("../") || token.startsWith("..\\")) return true;
|
|
225
|
+
if (token.includes("/../") || token.includes("\\..\\") || token.includes("/..\\") || token.includes("\\../")) return true;
|
|
226
|
+
if (token.includes("/") || token.includes("\\")) return true;
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
function findExternalPathReferencesInShellCommand(command, options) {
|
|
230
|
+
const cwd = path2.resolve(options.cwd);
|
|
231
|
+
const workspaceRoot = path2.resolve(options.workspaceRoot);
|
|
232
|
+
const tokens = tokenizeShellCommand(command).map(stripPunctuation).filter(Boolean);
|
|
233
|
+
const candidates = [];
|
|
234
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
235
|
+
const token = tokens[i];
|
|
236
|
+
if (!token) continue;
|
|
237
|
+
if (token === ">" || token === ">>" || token === "<" || token === "<<") {
|
|
238
|
+
const next = tokens[i + 1];
|
|
239
|
+
if (next) candidates.push(next);
|
|
240
|
+
i++;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (token.startsWith(">") || token.startsWith("<")) {
|
|
244
|
+
const maybe = token.replace(/^[><]+/, "");
|
|
245
|
+
if (maybe) candidates.push(maybe);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (isPathLikeToken(token)) {
|
|
249
|
+
candidates.push(token);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const out = /* @__PURE__ */ new Set();
|
|
253
|
+
for (const candidate of candidates) {
|
|
254
|
+
try {
|
|
255
|
+
const abs = resolveCandidatePath(candidate, cwd);
|
|
256
|
+
if (!isSubPath(abs, workspaceRoot)) {
|
|
257
|
+
out.add(abs);
|
|
258
|
+
}
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return [...out];
|
|
263
|
+
}
|
|
264
|
+
function isPathInsideWorkspace(targetPath, workspaceRoot) {
|
|
265
|
+
try {
|
|
266
|
+
return isSubPath(path2.resolve(targetPath), path2.resolve(workspaceRoot));
|
|
267
|
+
} catch {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/validation.ts
|
|
273
|
+
function validateToolArgs(args, schema) {
|
|
274
|
+
const errors = [];
|
|
275
|
+
const validated = {};
|
|
276
|
+
for (const field of schema.required || []) {
|
|
277
|
+
if (args[field] === void 0 || args[field] === null) {
|
|
278
|
+
errors.push(`Missing required field: ${field}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
282
|
+
const value = args[key];
|
|
283
|
+
if (value === void 0 || value === null) {
|
|
284
|
+
if (propSchema.default !== void 0) {
|
|
285
|
+
validated[key] = propSchema.default;
|
|
286
|
+
}
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const typeResult = validateType(value, propSchema, key);
|
|
290
|
+
if (typeResult.error) {
|
|
291
|
+
errors.push(typeResult.error);
|
|
292
|
+
} else {
|
|
293
|
+
validated[key] = typeResult.value;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
valid: errors.length === 0,
|
|
298
|
+
errors,
|
|
299
|
+
data: validated
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function validateType(value, schema, fieldName) {
|
|
303
|
+
switch (schema.type) {
|
|
304
|
+
case "string": {
|
|
305
|
+
if (typeof value !== "string") {
|
|
306
|
+
return { error: `${fieldName}: expected string, got ${typeof value}` };
|
|
307
|
+
}
|
|
308
|
+
if (schema.enum && !schema.enum.includes(value)) {
|
|
309
|
+
return {
|
|
310
|
+
error: `${fieldName}: value '${value}' not in allowed values: ${schema.enum.join(", ")}`
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return { value };
|
|
314
|
+
}
|
|
315
|
+
case "number": {
|
|
316
|
+
if (typeof value === "number") {
|
|
317
|
+
if (isNaN(value)) {
|
|
318
|
+
return { error: `${fieldName}: expected number, got NaN` };
|
|
319
|
+
}
|
|
320
|
+
return { value };
|
|
321
|
+
}
|
|
322
|
+
if (typeof value === "string") {
|
|
323
|
+
const num = parseFloat(value);
|
|
324
|
+
if (!isNaN(num)) {
|
|
325
|
+
return { value: num };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return { error: `${fieldName}: expected number, got ${typeof value}` };
|
|
329
|
+
}
|
|
330
|
+
case "boolean": {
|
|
331
|
+
if (typeof value === "boolean") {
|
|
332
|
+
return { value };
|
|
333
|
+
}
|
|
334
|
+
if (typeof value === "string") {
|
|
335
|
+
const lower = value.toLowerCase();
|
|
336
|
+
if (lower === "true" || lower === "1" || lower === "yes") return { value: true };
|
|
337
|
+
if (lower === "false" || lower === "0" || lower === "no") return { value: false };
|
|
338
|
+
}
|
|
339
|
+
return { error: `${fieldName}: expected boolean, got ${typeof value}` };
|
|
340
|
+
}
|
|
341
|
+
case "array": {
|
|
342
|
+
if (!Array.isArray(value)) {
|
|
343
|
+
return { error: `${fieldName}: expected array, got ${typeof value}` };
|
|
344
|
+
}
|
|
345
|
+
if (schema.items) {
|
|
346
|
+
const validatedItems = [];
|
|
347
|
+
for (let i = 0; i < value.length; i++) {
|
|
348
|
+
const itemResult = validateType(value[i], schema.items, `${fieldName}[${i}]`);
|
|
349
|
+
if (itemResult.error) {
|
|
350
|
+
return itemResult;
|
|
351
|
+
}
|
|
352
|
+
validatedItems.push(itemResult.value);
|
|
353
|
+
}
|
|
354
|
+
return { value: validatedItems };
|
|
355
|
+
}
|
|
356
|
+
return { value };
|
|
357
|
+
}
|
|
358
|
+
case "object": {
|
|
359
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
360
|
+
return { error: `${fieldName}: expected object, got ${typeof value}` };
|
|
361
|
+
}
|
|
362
|
+
if (schema.properties) {
|
|
363
|
+
const nestedResult = validateToolArgs(value, {
|
|
364
|
+
properties: schema.properties,
|
|
365
|
+
required: schema.required
|
|
366
|
+
});
|
|
367
|
+
if (!nestedResult.valid) {
|
|
368
|
+
return { error: `${fieldName}: ${nestedResult.errors.join(", ")}` };
|
|
369
|
+
}
|
|
370
|
+
return { value: nestedResult.data };
|
|
371
|
+
}
|
|
372
|
+
return { value };
|
|
373
|
+
}
|
|
374
|
+
default:
|
|
375
|
+
return { value };
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function requireString(args, field) {
|
|
379
|
+
const value = args[field];
|
|
380
|
+
if (typeof value !== "string") {
|
|
381
|
+
return { error: `${field} is required and must be a string` };
|
|
382
|
+
}
|
|
383
|
+
return { value };
|
|
384
|
+
}
|
|
385
|
+
function optionalString(args, field, defaultValue) {
|
|
386
|
+
const value = args[field];
|
|
387
|
+
if (value === void 0 || value === null) {
|
|
388
|
+
return defaultValue;
|
|
389
|
+
}
|
|
390
|
+
return typeof value === "string" ? value : defaultValue;
|
|
391
|
+
}
|
|
392
|
+
function optionalNumber(args, field, defaultValue) {
|
|
393
|
+
const value = args[field];
|
|
394
|
+
if (value === void 0 || value === null) {
|
|
395
|
+
return defaultValue;
|
|
396
|
+
}
|
|
397
|
+
if (typeof value === "number" && !isNaN(value)) {
|
|
398
|
+
return value;
|
|
399
|
+
}
|
|
400
|
+
if (typeof value === "string") {
|
|
401
|
+
const num = parseFloat(value);
|
|
402
|
+
if (!isNaN(num)) {
|
|
403
|
+
return num;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return defaultValue;
|
|
407
|
+
}
|
|
408
|
+
function optionalBoolean(args, field, defaultValue) {
|
|
409
|
+
const value = args[field];
|
|
410
|
+
if (value === void 0 || value === null) {
|
|
411
|
+
return defaultValue;
|
|
412
|
+
}
|
|
413
|
+
if (typeof value === "boolean") {
|
|
414
|
+
return value;
|
|
415
|
+
}
|
|
416
|
+
if (typeof value === "string") {
|
|
417
|
+
const lower = value.toLowerCase();
|
|
418
|
+
if (lower === "true" || lower === "1" || lower === "yes") return true;
|
|
419
|
+
if (lower === "false" || lower === "0" || lower === "no") return false;
|
|
420
|
+
}
|
|
421
|
+
return defaultValue;
|
|
422
|
+
}
|
|
423
|
+
var SAFE_SHELL_COMMANDS = /* @__PURE__ */ new Set([
|
|
424
|
+
"ls",
|
|
425
|
+
"dir",
|
|
426
|
+
"pwd",
|
|
427
|
+
"echo",
|
|
428
|
+
"cat",
|
|
429
|
+
"head",
|
|
430
|
+
"tail",
|
|
431
|
+
"wc",
|
|
432
|
+
"grep",
|
|
433
|
+
"find",
|
|
434
|
+
"git",
|
|
435
|
+
"npm",
|
|
436
|
+
"npx",
|
|
437
|
+
"yarn",
|
|
438
|
+
"pnpm",
|
|
439
|
+
"node",
|
|
440
|
+
"python",
|
|
441
|
+
"python3",
|
|
442
|
+
"pip",
|
|
443
|
+
"cargo",
|
|
444
|
+
"go",
|
|
445
|
+
"make",
|
|
446
|
+
"cmake",
|
|
447
|
+
"dotnet",
|
|
448
|
+
"mvn",
|
|
449
|
+
"gradle",
|
|
450
|
+
"tsc",
|
|
451
|
+
"eslint",
|
|
452
|
+
"prettier",
|
|
453
|
+
"jest",
|
|
454
|
+
"mocha",
|
|
455
|
+
"pytest",
|
|
456
|
+
"docker",
|
|
457
|
+
"kubectl",
|
|
458
|
+
"terraform",
|
|
459
|
+
"curl",
|
|
460
|
+
"wget",
|
|
461
|
+
"jq",
|
|
462
|
+
"yq"
|
|
463
|
+
]);
|
|
464
|
+
var BLOCKED_SHELL_PATTERNS = [
|
|
465
|
+
/\brm\s+-rf?\s+[/~]/i,
|
|
466
|
+
/\bsudo\b/i,
|
|
467
|
+
/\b(shutdown|reboot|halt)\b/i,
|
|
468
|
+
/\bdd\s+if=/i,
|
|
469
|
+
/\bmkfs/i,
|
|
470
|
+
/\bformat\b/i,
|
|
471
|
+
/>[>&]\s*\/dev\//i
|
|
472
|
+
];
|
|
473
|
+
var SHELL_META_PATTERNS = [
|
|
474
|
+
/;/,
|
|
475
|
+
/&&/,
|
|
476
|
+
/\|\|/,
|
|
477
|
+
/\|(?!\|)/,
|
|
478
|
+
/`/,
|
|
479
|
+
/\$\(/,
|
|
480
|
+
/\$\{/,
|
|
481
|
+
/\n/,
|
|
482
|
+
/\r/
|
|
483
|
+
];
|
|
484
|
+
function evaluateShellCommand(command) {
|
|
485
|
+
for (const pattern of BLOCKED_SHELL_PATTERNS) {
|
|
486
|
+
if (pattern.test(command)) {
|
|
487
|
+
return { verdict: "deny", reason: "Command matches blocked pattern" };
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
for (const pattern of SHELL_META_PATTERNS) {
|
|
491
|
+
if (pattern.test(command)) {
|
|
492
|
+
return {
|
|
493
|
+
verdict: "needs_approval",
|
|
494
|
+
reason: "Command contains shell metacharacters (;, &&, ||, |, `, $()) and requires approval"
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const baseCommand = command.trim().split(/\s+/)[0]?.split("/").pop() || "";
|
|
499
|
+
if (SAFE_SHELL_COMMANDS.has(baseCommand)) {
|
|
500
|
+
return { verdict: "allow" };
|
|
501
|
+
}
|
|
502
|
+
return { verdict: "needs_approval", reason: `Command '${baseCommand}' requires approval` };
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// src/backgroundJobs.ts
|
|
506
|
+
var cp = __toESM(require("node:child_process"), 1);
|
|
507
|
+
var DEFAULT_BACKGROUND_TTL_MS = 10 * 60 * 1e3;
|
|
508
|
+
var DEFAULT_BACKGROUND_KILL_GRACE_MS = 1500;
|
|
509
|
+
var jobsByScope = /* @__PURE__ */ new Map();
|
|
510
|
+
var exitCleanupRegistered = false;
|
|
511
|
+
var sequence = 0;
|
|
512
|
+
function createBackgroundJobKey(args) {
|
|
513
|
+
const normalizedCwd = args.cwd.trim();
|
|
514
|
+
const normalizedCommand = args.command.trim().replace(/\s+/g, " ");
|
|
515
|
+
return `${normalizedCwd}
|
|
516
|
+
${normalizedCommand}`;
|
|
517
|
+
}
|
|
518
|
+
function isPidAlive(pid) {
|
|
519
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
520
|
+
try {
|
|
521
|
+
process.kill(pid, 0);
|
|
522
|
+
return true;
|
|
523
|
+
} catch (err) {
|
|
524
|
+
const code = err.code;
|
|
525
|
+
return code === "EPERM";
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function killProcessTree(pid, signal) {
|
|
529
|
+
if (!Number.isFinite(pid) || pid <= 0) return;
|
|
530
|
+
if (process.platform === "win32") {
|
|
531
|
+
try {
|
|
532
|
+
cp.execFile("taskkill", ["/pid", String(pid), "/T", "/F"], { windowsHide: true }, () => {
|
|
533
|
+
});
|
|
534
|
+
} catch {
|
|
535
|
+
}
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
process.kill(-pid, signal);
|
|
540
|
+
} catch {
|
|
541
|
+
try {
|
|
542
|
+
process.kill(pid, signal);
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function ensureExitCleanup() {
|
|
548
|
+
if (exitCleanupRegistered) return;
|
|
549
|
+
exitCleanupRegistered = true;
|
|
550
|
+
process.once("exit", () => {
|
|
551
|
+
for (const scopeJobs of jobsByScope.values()) {
|
|
552
|
+
for (const job of scopeJobs.values()) {
|
|
553
|
+
killProcessTree(job.pid, "SIGTERM");
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
function ensureScope(scope) {
|
|
559
|
+
let scopeJobs = jobsByScope.get(scope);
|
|
560
|
+
if (!scopeJobs) {
|
|
561
|
+
scopeJobs = /* @__PURE__ */ new Map();
|
|
562
|
+
jobsByScope.set(scope, scopeJobs);
|
|
563
|
+
}
|
|
564
|
+
return scopeJobs;
|
|
565
|
+
}
|
|
566
|
+
function clearTimers(entry) {
|
|
567
|
+
if (entry.ttlTimer) clearTimeout(entry.ttlTimer);
|
|
568
|
+
if (entry.killTimer) clearTimeout(entry.killTimer);
|
|
569
|
+
}
|
|
570
|
+
function snapshot(entry) {
|
|
571
|
+
const { ttlTimer: _ttlTimer, killTimer: _killTimer, ...rest } = entry;
|
|
572
|
+
return rest;
|
|
573
|
+
}
|
|
574
|
+
function getBackgroundJob(scope, key) {
|
|
575
|
+
const entry = jobsByScope.get(scope)?.get(key);
|
|
576
|
+
if (!entry) return void 0;
|
|
577
|
+
return snapshot(entry);
|
|
578
|
+
}
|
|
579
|
+
function listBackgroundJobs(scope) {
|
|
580
|
+
if (scope) {
|
|
581
|
+
const scopeJobs = jobsByScope.get(scope);
|
|
582
|
+
if (!scopeJobs) return [];
|
|
583
|
+
return [...scopeJobs.values()].map(snapshot);
|
|
584
|
+
}
|
|
585
|
+
const jobs = [];
|
|
586
|
+
for (const scopeJobs of jobsByScope.values()) {
|
|
587
|
+
jobs.push(...[...scopeJobs.values()].map(snapshot));
|
|
588
|
+
}
|
|
589
|
+
return jobs;
|
|
590
|
+
}
|
|
591
|
+
function removeBackgroundJob(scope, key) {
|
|
592
|
+
const scopeJobs = jobsByScope.get(scope);
|
|
593
|
+
if (!scopeJobs) return;
|
|
594
|
+
const entry = scopeJobs.get(key);
|
|
595
|
+
if (!entry) return;
|
|
596
|
+
clearTimers(entry);
|
|
597
|
+
scopeJobs.delete(key);
|
|
598
|
+
if (scopeJobs.size === 0) {
|
|
599
|
+
jobsByScope.delete(scope);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
function expireBackgroundJob(scope, key, jobId) {
|
|
603
|
+
const scopeJobs = jobsByScope.get(scope);
|
|
604
|
+
const entry = scopeJobs?.get(key);
|
|
605
|
+
if (!entry || entry.id !== jobId) return;
|
|
606
|
+
if (!isPidAlive(entry.pid)) {
|
|
607
|
+
removeBackgroundJob(scope, key);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
killProcessTree(entry.pid, "SIGTERM");
|
|
611
|
+
entry.killTimer = setTimeout(() => {
|
|
612
|
+
try {
|
|
613
|
+
if (isPidAlive(entry.pid)) {
|
|
614
|
+
killProcessTree(entry.pid, "SIGKILL");
|
|
615
|
+
}
|
|
616
|
+
} finally {
|
|
617
|
+
removeBackgroundJob(scope, key);
|
|
618
|
+
}
|
|
619
|
+
}, DEFAULT_BACKGROUND_KILL_GRACE_MS);
|
|
620
|
+
entry.killTimer.unref?.();
|
|
621
|
+
}
|
|
622
|
+
function refreshBackgroundJob(scope, key, ttlMs) {
|
|
623
|
+
const entry = jobsByScope.get(scope)?.get(key);
|
|
624
|
+
if (!entry) return void 0;
|
|
625
|
+
const newTtlMs = ttlMs ?? entry.ttlMs;
|
|
626
|
+
entry.ttlMs = newTtlMs;
|
|
627
|
+
entry.expiresAt = Date.now() + newTtlMs;
|
|
628
|
+
if (entry.ttlTimer) clearTimeout(entry.ttlTimer);
|
|
629
|
+
if (newTtlMs > 0) {
|
|
630
|
+
entry.ttlTimer = setTimeout(() => {
|
|
631
|
+
expireBackgroundJob(scope, key, entry.id);
|
|
632
|
+
}, newTtlMs);
|
|
633
|
+
entry.ttlTimer.unref?.();
|
|
634
|
+
} else {
|
|
635
|
+
entry.ttlTimer = void 0;
|
|
636
|
+
}
|
|
637
|
+
return snapshot(entry);
|
|
638
|
+
}
|
|
639
|
+
function registerBackgroundJob(args) {
|
|
640
|
+
ensureExitCleanup();
|
|
641
|
+
const scopeJobs = ensureScope(args.scope);
|
|
642
|
+
const existing = scopeJobs.get(args.key);
|
|
643
|
+
if (existing) {
|
|
644
|
+
clearTimers(existing);
|
|
645
|
+
}
|
|
646
|
+
const ttlMs = Number.isFinite(args.ttlMs) && args.ttlMs >= 0 ? Math.floor(args.ttlMs) : DEFAULT_BACKGROUND_TTL_MS;
|
|
647
|
+
const createdAt = Date.now();
|
|
648
|
+
const id = `${createdAt.toString(36)}-${(++sequence).toString(36)}`;
|
|
649
|
+
const entry = {
|
|
650
|
+
id,
|
|
651
|
+
scope: args.scope,
|
|
652
|
+
key: args.key,
|
|
653
|
+
command: args.command,
|
|
654
|
+
cwd: args.cwd,
|
|
655
|
+
pid: args.pid,
|
|
656
|
+
createdAt,
|
|
657
|
+
ttlMs,
|
|
658
|
+
expiresAt: createdAt + ttlMs
|
|
659
|
+
};
|
|
660
|
+
scopeJobs.set(args.key, entry);
|
|
661
|
+
if (ttlMs > 0) {
|
|
662
|
+
entry.ttlTimer = setTimeout(() => {
|
|
663
|
+
expireBackgroundJob(args.scope, args.key, entry.id);
|
|
664
|
+
}, ttlMs);
|
|
665
|
+
entry.ttlTimer.unref?.();
|
|
666
|
+
}
|
|
667
|
+
return snapshot(entry);
|
|
668
|
+
}
|
|
669
|
+
function cleanupDeadBackgroundJobs(scope) {
|
|
670
|
+
const scopes = scope ? [scope] : [...jobsByScope.keys()];
|
|
671
|
+
for (const s of scopes) {
|
|
672
|
+
const scopeJobs = jobsByScope.get(s);
|
|
673
|
+
if (!scopeJobs) continue;
|
|
674
|
+
for (const [key, entry] of scopeJobs.entries()) {
|
|
675
|
+
if (!isPidAlive(entry.pid)) {
|
|
676
|
+
removeBackgroundJob(s, key);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// src/history.ts
|
|
683
|
+
function createUserHistoryMessage(text, options) {
|
|
684
|
+
const metadata = {};
|
|
685
|
+
if (options?.synthetic) {
|
|
686
|
+
metadata.synthetic = true;
|
|
687
|
+
}
|
|
688
|
+
if (options?.compaction) {
|
|
689
|
+
metadata.compaction = options.compaction;
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
id: crypto.randomUUID(),
|
|
693
|
+
role: "user",
|
|
694
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {},
|
|
695
|
+
parts: [{ type: "text", text }]
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
function createAssistantHistoryMessage() {
|
|
699
|
+
return {
|
|
700
|
+
id: crypto.randomUUID(),
|
|
701
|
+
role: "assistant",
|
|
702
|
+
parts: []
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
function getMessageText(message) {
|
|
706
|
+
return message.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
707
|
+
}
|
|
708
|
+
function appendText(message, delta) {
|
|
709
|
+
if (!delta) return;
|
|
710
|
+
const last = message.parts.at(-1);
|
|
711
|
+
if (last && last.type === "text" && last.state !== "done") {
|
|
712
|
+
last.text += delta;
|
|
713
|
+
last.state = "streaming";
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
message.parts.push({ type: "text", text: delta, state: "streaming" });
|
|
717
|
+
}
|
|
718
|
+
function appendReasoning(message, delta) {
|
|
719
|
+
if (!delta) return;
|
|
720
|
+
const last = message.parts.at(-1);
|
|
721
|
+
if (last && last.type === "reasoning" && last.state !== "done") {
|
|
722
|
+
last.text += delta;
|
|
723
|
+
last.state = "streaming";
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
message.parts.push({ type: "reasoning", text: delta, state: "streaming" });
|
|
727
|
+
}
|
|
728
|
+
function upsertDynamicToolCall(message, params) {
|
|
729
|
+
const existing = message.parts.find(
|
|
730
|
+
(p) => p.type === "dynamic-tool" && p.toolCallId === params.toolCallId
|
|
731
|
+
);
|
|
732
|
+
if (existing) {
|
|
733
|
+
if (existing.state === "input-streaming") {
|
|
734
|
+
existing.state = "input-available";
|
|
735
|
+
}
|
|
736
|
+
existing.input = params.input;
|
|
737
|
+
existing.toolName = params.toolName;
|
|
738
|
+
return existing;
|
|
739
|
+
}
|
|
740
|
+
const part = {
|
|
741
|
+
type: "dynamic-tool",
|
|
742
|
+
toolName: params.toolName,
|
|
743
|
+
toolCallId: params.toolCallId,
|
|
744
|
+
state: "input-available",
|
|
745
|
+
input: params.input
|
|
746
|
+
};
|
|
747
|
+
message.parts.push(part);
|
|
748
|
+
return part;
|
|
749
|
+
}
|
|
750
|
+
function setDynamicToolOutput(message, params) {
|
|
751
|
+
const part = upsertDynamicToolCall(message, {
|
|
752
|
+
toolName: params.toolName,
|
|
753
|
+
toolCallId: params.toolCallId,
|
|
754
|
+
input: params.input
|
|
755
|
+
});
|
|
756
|
+
part.state = "output-available";
|
|
757
|
+
part.output = params.output;
|
|
758
|
+
delete part.errorText;
|
|
759
|
+
}
|
|
760
|
+
function setDynamicToolError(message, params) {
|
|
761
|
+
const part = upsertDynamicToolCall(message, {
|
|
762
|
+
toolName: params.toolName,
|
|
763
|
+
toolCallId: params.toolCallId,
|
|
764
|
+
input: params.input
|
|
765
|
+
});
|
|
766
|
+
part.state = "output-available";
|
|
767
|
+
part.output = { success: false, error: params.errorText };
|
|
768
|
+
delete part.errorText;
|
|
769
|
+
}
|
|
770
|
+
function finalizeStreamingParts(message) {
|
|
771
|
+
for (const part of message.parts) {
|
|
772
|
+
if (part.type === "text" || part.type === "reasoning") {
|
|
773
|
+
if (part.state === "streaming") {
|
|
774
|
+
part.state = "done";
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// src/compaction.ts
|
|
781
|
+
var COMPACTION_MARKER_TEXT = "What did we do so far?";
|
|
782
|
+
var COMPACTION_PROMPT_TEXT = "Provide a detailed prompt for continuing our conversation above. Focus on information that would be helpful for continuing the conversation, including what we did, what we are doing, which files we are working on, and what we should do next. Assume a new session will not have access to the previous conversation.";
|
|
783
|
+
var COMPACTION_AUTO_CONTINUE_TEXT = "Continue if you have next steps.";
|
|
784
|
+
var COMPACTED_TOOL_PLACEHOLDER = "[Old tool result content cleared]";
|
|
785
|
+
var COMPACTION_SYSTEM_PROMPT = "You are a helpful AI assistant tasked with summarizing conversations.\n\nWhen asked to summarize, provide a detailed but concise summary of the conversation.\nFocus on information that would be helpful for continuing the conversation, including:\n- What was done\n- What is currently being worked on\n- Which files are being modified\n- What needs to be done next\n- Key user requests, constraints, or preferences that should persist\n- Important technical decisions and why they were made\n\nYour summary should be comprehensive enough to provide context but concise enough to be quickly understood.";
|
|
786
|
+
function asFiniteNumber(value) {
|
|
787
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
788
|
+
}
|
|
789
|
+
function getReservedOutputTokens(params) {
|
|
790
|
+
const maxOutputTokens = Math.max(0, Math.floor(params.maxOutputTokens));
|
|
791
|
+
const modelOutput = params.modelLimit?.output;
|
|
792
|
+
if (typeof modelOutput === "number" && Number.isFinite(modelOutput) && modelOutput > 0) {
|
|
793
|
+
return Math.min(Math.floor(modelOutput), maxOutputTokens);
|
|
794
|
+
}
|
|
795
|
+
return maxOutputTokens;
|
|
796
|
+
}
|
|
797
|
+
function extractUsageTokens(usage) {
|
|
798
|
+
if (!usage || typeof usage !== "object") return void 0;
|
|
799
|
+
const inputTokens = usage.inputTokens;
|
|
800
|
+
const outputTokens = usage.outputTokens;
|
|
801
|
+
if (typeof inputTokens === "number" || inputTokens === void 0) {
|
|
802
|
+
const inputTotal2 = asFiniteNumber(inputTokens);
|
|
803
|
+
const details = usage.inputTokenDetails;
|
|
804
|
+
const inputNoCache2 = asFiniteNumber(details?.noCacheTokens);
|
|
805
|
+
const cacheRead2 = asFiniteNumber(details?.cacheReadTokens);
|
|
806
|
+
const cacheWrite2 = asFiniteNumber(details?.cacheWriteTokens);
|
|
807
|
+
const outputTotal2 = asFiniteNumber(outputTokens);
|
|
808
|
+
const input2 = inputNoCache2 ?? inputTotal2;
|
|
809
|
+
const computedTotal = (inputNoCache2 !== void 0 ? (inputNoCache2 || 0) + (cacheRead2 || 0) : inputTotal2 || 0) + (outputTotal2 || 0);
|
|
810
|
+
const total2 = asFiniteNumber(usage.totalTokens) ?? computedTotal;
|
|
811
|
+
return {
|
|
812
|
+
input: input2,
|
|
813
|
+
output: outputTotal2,
|
|
814
|
+
cacheRead: cacheRead2,
|
|
815
|
+
cacheWrite: cacheWrite2,
|
|
816
|
+
total: total2,
|
|
817
|
+
raw: usage.raw
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
const inputTotal = asFiniteNumber(inputTokens?.total);
|
|
821
|
+
const inputNoCache = asFiniteNumber(inputTokens?.noCache);
|
|
822
|
+
const cacheRead = asFiniteNumber(inputTokens?.cacheRead);
|
|
823
|
+
const cacheWrite = asFiniteNumber(inputTokens?.cacheWrite);
|
|
824
|
+
const outputTotal = asFiniteNumber(outputTokens?.total);
|
|
825
|
+
const input = inputNoCache ?? inputTotal;
|
|
826
|
+
const output = outputTotal;
|
|
827
|
+
const total = (inputNoCache !== void 0 ? (inputNoCache || 0) + (cacheRead || 0) : inputTotal || 0) + (outputTotal || 0);
|
|
828
|
+
return {
|
|
829
|
+
input,
|
|
830
|
+
output,
|
|
831
|
+
cacheRead,
|
|
832
|
+
cacheWrite,
|
|
833
|
+
total,
|
|
834
|
+
raw: usage.raw
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
function getEffectiveHistory(history) {
|
|
838
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
839
|
+
const msg = history[i];
|
|
840
|
+
if (msg.role === "assistant" && msg.metadata?.summary) {
|
|
841
|
+
const maybeMarker = i > 0 ? history[i - 1] : void 0;
|
|
842
|
+
if (maybeMarker?.role === "user" && maybeMarker.metadata?.compaction) {
|
|
843
|
+
return history.slice(i - 1);
|
|
844
|
+
}
|
|
845
|
+
return history.slice(i);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return history;
|
|
849
|
+
}
|
|
850
|
+
function estimateTokensFromString(text) {
|
|
851
|
+
return Math.ceil(text.length / 4);
|
|
852
|
+
}
|
|
853
|
+
function estimateTokensFromUnknown(value) {
|
|
854
|
+
if (value === void 0 || value === null) return 0;
|
|
855
|
+
if (typeof value === "string") return estimateTokensFromString(value);
|
|
856
|
+
try {
|
|
857
|
+
return estimateTokensFromString(JSON.stringify(value));
|
|
858
|
+
} catch {
|
|
859
|
+
return estimateTokensFromString(String(value));
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
function isCompletedToolPart(part) {
|
|
863
|
+
if (!part || typeof part !== "object") return false;
|
|
864
|
+
if (part.type !== "dynamic-tool") return false;
|
|
865
|
+
const state = String(part.state || "");
|
|
866
|
+
return state === "output-available";
|
|
867
|
+
}
|
|
868
|
+
function getToolOutputTokens(part) {
|
|
869
|
+
return estimateTokensFromUnknown(part.output);
|
|
870
|
+
}
|
|
871
|
+
function markPrunableToolOutputs(history, config) {
|
|
872
|
+
if (!config.prune) {
|
|
873
|
+
return { totalToolOutputTokens: 0, prunedTokens: 0, markedParts: 0 };
|
|
874
|
+
}
|
|
875
|
+
let total = 0;
|
|
876
|
+
let pruned = 0;
|
|
877
|
+
let turns = 0;
|
|
878
|
+
const toMark = [];
|
|
879
|
+
for (let msgIndex = history.length - 1; msgIndex >= 0; msgIndex--) {
|
|
880
|
+
const msg = history[msgIndex];
|
|
881
|
+
if (msg.role === "user") turns++;
|
|
882
|
+
if (turns < 2) continue;
|
|
883
|
+
if (msg.role === "assistant" && msg.metadata?.summary) break;
|
|
884
|
+
for (let partIndex = msg.parts.length - 1; partIndex >= 0; partIndex--) {
|
|
885
|
+
const part = msg.parts[partIndex];
|
|
886
|
+
if (!isCompletedToolPart(part)) continue;
|
|
887
|
+
if (part.compactedAt) {
|
|
888
|
+
msgIndex = -1;
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
const estimate = getToolOutputTokens(part);
|
|
892
|
+
total += estimate;
|
|
893
|
+
if (total > config.pruneProtectTokens) {
|
|
894
|
+
pruned += estimate;
|
|
895
|
+
toMark.push({ msgIndex, partIndex, tokens: estimate });
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (pruned <= config.pruneMinimumTokens) {
|
|
900
|
+
return { totalToolOutputTokens: total, prunedTokens: 0, markedParts: 0 };
|
|
901
|
+
}
|
|
902
|
+
const now = Date.now();
|
|
903
|
+
for (const item of toMark) {
|
|
904
|
+
const msg = history[item.msgIndex];
|
|
905
|
+
const part = msg.parts[item.partIndex];
|
|
906
|
+
if (!part || !isCompletedToolPart(part)) continue;
|
|
907
|
+
part.compactedAt = now;
|
|
908
|
+
}
|
|
909
|
+
return { totalToolOutputTokens: total, prunedTokens: pruned, markedParts: toMark.length };
|
|
910
|
+
}
|
|
911
|
+
function createHistoryForModel(history) {
|
|
912
|
+
return history.map((msg) => {
|
|
913
|
+
const copied = {
|
|
914
|
+
...msg,
|
|
915
|
+
metadata: msg.metadata ? { ...msg.metadata } : void 0,
|
|
916
|
+
parts: msg.parts.map((part) => {
|
|
917
|
+
if (part.type !== "dynamic-tool") return part;
|
|
918
|
+
const anyPart = part;
|
|
919
|
+
if (!anyPart.compactedAt) return part;
|
|
920
|
+
if (anyPart.output === void 0) return part;
|
|
921
|
+
const output = anyPart.output;
|
|
922
|
+
const replacement = output && typeof output === "object" && typeof output.success === "boolean" ? {
|
|
923
|
+
...output,
|
|
924
|
+
data: COMPACTED_TOOL_PLACEHOLDER,
|
|
925
|
+
metadata: { ...output.metadata || {}, compacted: true }
|
|
926
|
+
} : COMPACTED_TOOL_PLACEHOLDER;
|
|
927
|
+
return { ...anyPart, output: replacement };
|
|
928
|
+
})
|
|
929
|
+
};
|
|
930
|
+
return copied;
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
function isOverflow(params) {
|
|
934
|
+
if (!params.config.auto) return false;
|
|
935
|
+
const context = params.modelLimit?.context;
|
|
936
|
+
if (!context || context <= 0) return false;
|
|
937
|
+
const usable = context - Math.max(0, params.reservedOutputTokens);
|
|
938
|
+
if (usable <= 0) return false;
|
|
939
|
+
const used = params.lastTokens?.total;
|
|
940
|
+
if (!used || used <= 0) return false;
|
|
941
|
+
return used > usable;
|
|
942
|
+
}
|
|
943
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
944
|
+
0 && (module.exports = {
|
|
945
|
+
COMPACTED_TOOL_PLACEHOLDER,
|
|
946
|
+
COMPACTION_AUTO_CONTINUE_TEXT,
|
|
947
|
+
COMPACTION_MARKER_TEXT,
|
|
948
|
+
COMPACTION_PROMPT_TEXT,
|
|
949
|
+
COMPACTION_SYSTEM_PROMPT,
|
|
950
|
+
DEFAULT_BACKGROUND_KILL_GRACE_MS,
|
|
951
|
+
DEFAULT_BACKGROUND_TTL_MS,
|
|
952
|
+
appendReasoning,
|
|
953
|
+
appendText,
|
|
954
|
+
cleanupDeadBackgroundJobs,
|
|
955
|
+
createAssistantHistoryMessage,
|
|
956
|
+
createBackgroundJobKey,
|
|
957
|
+
createHistoryForModel,
|
|
958
|
+
createUserHistoryMessage,
|
|
959
|
+
evaluatePermission,
|
|
960
|
+
evaluateShellCommand,
|
|
961
|
+
expandHome,
|
|
962
|
+
extractUsageTokens,
|
|
963
|
+
finalizeStreamingParts,
|
|
964
|
+
findExternalPathReferencesInShellCommand,
|
|
965
|
+
getBackgroundJob,
|
|
966
|
+
getEffectiveHistory,
|
|
967
|
+
getMessageText,
|
|
968
|
+
getReservedOutputTokens,
|
|
969
|
+
isOverflow,
|
|
970
|
+
isPathInsideWorkspace,
|
|
971
|
+
isPidAlive,
|
|
972
|
+
isSubPath,
|
|
973
|
+
killProcessTree,
|
|
974
|
+
listBackgroundJobs,
|
|
975
|
+
markPrunableToolOutputs,
|
|
976
|
+
mergeRulesets,
|
|
977
|
+
normalizeFsPath,
|
|
978
|
+
optionalBoolean,
|
|
979
|
+
optionalNumber,
|
|
980
|
+
optionalString,
|
|
981
|
+
refreshBackgroundJob,
|
|
982
|
+
registerBackgroundJob,
|
|
983
|
+
removeBackgroundJob,
|
|
984
|
+
requireString,
|
|
985
|
+
setDynamicToolError,
|
|
986
|
+
setDynamicToolOutput,
|
|
987
|
+
upsertDynamicToolCall,
|
|
988
|
+
validateToolArgs,
|
|
989
|
+
wildcardMatch
|
|
990
|
+
});
|