arisa 5.1.68 → 5.2.7
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 +7 -4
- package/package.json +1 -1
- package/src/core/agent/agent-manager.js +46 -6
- package/src/core/agent/agent-session-lifecycle.js +80 -3
- package/src/core/agent/core-tools.js +1 -1
- package/src/core/agent/pi-auth-login.js +1 -1
- package/src/core/agent/pi-runtime.js +1 -1
- package/src/core/agent/runtime-context.js +1 -1
- package/src/core/agent/worker-heap-circuit-breaker.js +122 -0
- package/src/core/artifacts/artifact-store.js +1 -1
- package/src/core/capabilities/capability-service.js +1 -1
- package/src/core/config/config-defaults.js +30 -1
- package/src/core/config/config-store.js +1 -1
- package/src/core/conversation/session-seed-store.js +1 -1
- package/src/core/tasks/task-store.js +1 -1
- package/src/core/tools/daemon-client.js +180 -0
- package/src/core/tools/daemon-processes.js +19 -3
- package/src/core/tools/daemon-protocol.js +72 -0
- package/src/core/tools/daemon-runtime.js +13 -490
- package/src/core/tools/daemon-worker.js +310 -0
- package/src/core/tools/ipc-client.js +2 -2
- package/src/core/tools/memory-pressure.js +56 -0
- package/src/core/tools/official-tool-installer.js +1 -1
- package/src/core/tools/tool-config.js +1 -1
- package/src/core/tools/tool-process-output.js +100 -0
- package/src/core/tools/tool-process-runner.js +175 -0
- package/src/core/tools/tool-registry.js +99 -187
- package/src/core/tools/tool-resource-note-store.js +1 -1
- package/src/core/tools/tool-usage-store.js +1 -1
- package/src/core/tools/weighted-resource-governor.js +188 -38
- package/src/index.js +14 -2
- package/src/official-tools.lock.json +424 -50
- package/src/platform/paths.js +152 -0
- package/src/runtime/bootstrap-cli.js +121 -0
- package/src/runtime/bootstrap-config.js +97 -0
- package/src/runtime/bootstrap-telegram.js +325 -0
- package/src/runtime/bootstrap.js +6 -543
- package/src/runtime/doctor.js +6 -3
- package/src/runtime/flush.js +1 -1
- package/src/runtime/ipc/ipc-server.js +1 -1
- package/src/runtime/log-viewer.js +1 -1
- package/src/runtime/oom-protection.js +20 -0
- package/src/runtime/paths.js +3 -151
- package/src/runtime/restart-receipt.js +1 -1
- package/src/runtime/service-manager.js +1 -1
- package/src/runtime/service-supervisor.js +14 -0
- package/src/runtime/slave-cli.js +1 -1
- package/src/runtime/tool-process-supervisor.js +1 -1
- package/src/runtime/tui.js +200 -0
- package/src/runtime/update-manager.js +1 -1
- package/src/runtime/worker-recovery-report.js +142 -0
- package/src/transport/telegram/bot.js +42 -320
- package/src/transport/telegram/prompt-builders.js +8 -3
- package/src/transport/telegram/telegram-prompt-controller.js +346 -0
- package/src/transport/telegram/workspace-topic-store.js +1 -1
- package/test/agent-session-lifecycle.test.js +92 -0
- package/test/architecture-boundaries.test.js +29 -0
- package/test/bootstrap.test.js +65 -0
- package/test/daemon-process-invocation.test.js +27 -0
- package/test/daemon-runtime.test.js +36 -1
- package/test/doctor.test.js +22 -0
- package/test/memory-pressure.test.js +36 -0
- package/test/model-selection.test.js +11 -1
- package/test/official-tool-dependencies.test.js +1 -1
- package/test/official-tool-installer.test.js +18 -1
- package/test/oom-protection.test.js +32 -0
- package/test/paths.test.js +7 -0
- package/test/pi-compaction.test.js +9 -0
- package/test/service-manager.test.js +6 -1
- package/test/telegram-prompt-controller.test.js +81 -0
- package/test/telegram-text-artifact.test.js +30 -0
- package/test/tool-registry-run.test.js +108 -4
- package/test/tui.test.js +41 -0
- package/test/weighted-resource-governor.test.js +97 -5
- package/test/worker-heap-circuit-breaker.test.js +79 -0
- package/test/worker-recovery-report.test.js +69 -0
- package/test-fixtures/fake-daemon.js +5 -0
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { mkdir, readdir, readFile, rmdir, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
-
import {
|
|
4
|
+
import { getToolConfigPath, getToolStateDir, getToolTmpDir, getChatToolTmpDir, toolsDir as userToolsRoot } from "../../platform/paths.js";
|
|
6
5
|
import { loadToolConfig, parseConfigModule, writeToolConfig } from "./tool-config.js";
|
|
7
6
|
import { normalizeToolResult } from "./tool-result.js";
|
|
8
7
|
import { readDaemonDiagnostic } from "./daemon-processes.js";
|
|
9
|
-
import { createDaemonRuntime
|
|
8
|
+
import { createDaemonRuntime } from "./daemon-runtime.js";
|
|
10
9
|
import { daemonConfigDefaults } from "../config/config-defaults.js";
|
|
11
10
|
import { SkillRegistry } from "../skills/skill-registry.js";
|
|
12
11
|
import { ToolUsageStore } from "./tool-usage-store.js";
|
|
13
12
|
import { inspectToolDependencies, normalizeToolDependencies } from "./tool-dependencies.js";
|
|
14
13
|
import { normalizeToolExecution, WeightedResourceGovernor } from "./weighted-resource-governor.js";
|
|
14
|
+
import {
|
|
15
|
+
isolatedToolProcessInvocation,
|
|
16
|
+
runToolHelpProcess,
|
|
17
|
+
runToolProcess,
|
|
18
|
+
toolProcessEnv
|
|
19
|
+
} from "./tool-process-runner.js";
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
21
|
+
export { createToolOutputParser } from "./tool-process-output.js";
|
|
22
|
+
export { isolatedToolProcessInvocation } from "./tool-process-runner.js";
|
|
19
23
|
|
|
20
24
|
const defaultToolHelpTimeoutMs = 10_000;
|
|
21
25
|
const defaultToolRunTimeoutMs = 30 * 60_000;
|
|
@@ -38,43 +42,15 @@ function concurrentExecutionKey(name, chatId, request) {
|
|
|
38
42
|
return createHash("sha256").update(serialized).digest("hex");
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const finish = (callback, value) => {
|
|
52
|
-
clearTimeout(timeout);
|
|
53
|
-
clearTimeout(forceTimer);
|
|
54
|
-
callback(value);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
child.once("error", (error) => finish(reject, error));
|
|
58
|
-
child.once("close", (code) => {
|
|
59
|
-
if (!timedOut) {
|
|
60
|
-
finish(resolve, code);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const error = new Error(`${label} timed out after ${timeoutMs}ms`);
|
|
64
|
-
error.code = "TOOL_PROCESS_TIMEOUT";
|
|
65
|
-
finish(reject, error);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function runProcess(command, args, { timeoutMs, killGraceMs, label, ...options } = {}) {
|
|
71
|
-
const child = spawn(command, args, { ...options, stdio: ["ignore", "pipe", "pipe"] });
|
|
72
|
-
let stdout = "";
|
|
73
|
-
let stderr = "";
|
|
74
|
-
child.stdout.on("data", (d) => { stdout += d.toString(); });
|
|
75
|
-
child.stderr.on("data", (d) => { stderr += d.toString(); });
|
|
76
|
-
const code = await waitForToolProcess(child, { timeoutMs, killGraceMs, label });
|
|
77
|
-
return { code, stdout, stderr };
|
|
45
|
+
function executionForLease(execution, lease) {
|
|
46
|
+
if (!execution) return null;
|
|
47
|
+
return {
|
|
48
|
+
...execution,
|
|
49
|
+
maxHeapMb: lease.heapLimitMb || execution.maxHeapMb,
|
|
50
|
+
maxMemoryMb: lease.memoryLimitMb || execution.maxMemoryMb,
|
|
51
|
+
memoryHighPercent: lease.memoryHighPercent,
|
|
52
|
+
swapMaxMb: lease.swapMaxMb
|
|
53
|
+
};
|
|
78
54
|
}
|
|
79
55
|
|
|
80
56
|
function requirementNames(requirements) {
|
|
@@ -85,123 +61,6 @@ function requirementNames(requirements) {
|
|
|
85
61
|
return [];
|
|
86
62
|
}
|
|
87
63
|
|
|
88
|
-
export function createToolOutputParser(name, { onEvent, maxFrameBytes = 1_048_576 } = {}) {
|
|
89
|
-
let buffer = "";
|
|
90
|
-
let mode = "unknown";
|
|
91
|
-
let rawOutput = "";
|
|
92
|
-
let terminalResult = null;
|
|
93
|
-
let activeJobId = null;
|
|
94
|
-
let sequence = 0;
|
|
95
|
-
let terminalSeen = false;
|
|
96
|
-
|
|
97
|
-
async function parseEvent(line) {
|
|
98
|
-
let event;
|
|
99
|
-
try {
|
|
100
|
-
event = JSON.parse(line);
|
|
101
|
-
} catch {
|
|
102
|
-
throw new Error(`Invalid NDJSON from ${name}`);
|
|
103
|
-
}
|
|
104
|
-
if (event?.version !== DAEMON_PROTOCOL_VERSION || !DAEMON_EVENT_TYPES.includes(event?.type)) {
|
|
105
|
-
throw new Error(`Invalid versioned tool event from ${name}`);
|
|
106
|
-
}
|
|
107
|
-
if (typeof event.jobId !== "string" || !event.jobId) throw new Error(`Tool event from ${name} is missing jobId`);
|
|
108
|
-
if (activeJobId == null) activeJobId = event.jobId;
|
|
109
|
-
if (event.jobId !== activeJobId) throw new Error(`Tool ${name} multiplexed an unexpected jobId`);
|
|
110
|
-
if (!Number.isSafeInteger(event.sequence) || event.sequence !== sequence + 1) {
|
|
111
|
-
throw new Error(`Invalid tool event sequence from ${name}: ${event.sequence}`);
|
|
112
|
-
}
|
|
113
|
-
if (terminalSeen) throw new Error(`Tool ${name} emitted more than one terminal event`);
|
|
114
|
-
sequence = event.sequence;
|
|
115
|
-
terminalSeen = event.type === "completed" || event.type === "failed";
|
|
116
|
-
await onEvent?.(event);
|
|
117
|
-
if (terminalSeen) {
|
|
118
|
-
terminalResult = event.type === "completed"
|
|
119
|
-
? event.payload?.result ?? event.payload?.output ?? event.payload
|
|
120
|
-
: { ok: false, error: event.payload?.error || `Tool failed: ${name}`, ...(event.payload?.code ? { code: event.payload.code } : {}) };
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async function consumeLine(line) {
|
|
125
|
-
if (Buffer.byteLength(line, "utf8") > maxFrameBytes) throw new Error(`Tool event from ${name} exceeds ${maxFrameBytes} bytes`);
|
|
126
|
-
if (mode === "unknown") {
|
|
127
|
-
let candidate;
|
|
128
|
-
try {
|
|
129
|
-
candidate = JSON.parse(line);
|
|
130
|
-
} catch {
|
|
131
|
-
mode = "legacy";
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
if (candidate?.version === DAEMON_PROTOCOL_VERSION && DAEMON_EVENT_TYPES.includes(candidate?.type)) {
|
|
135
|
-
mode = "ndjson";
|
|
136
|
-
rawOutput = "";
|
|
137
|
-
return parseEvent(line);
|
|
138
|
-
}
|
|
139
|
-
mode = "legacy";
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
if (mode === "legacy") return;
|
|
143
|
-
return parseEvent(line);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
async push(chunk) {
|
|
148
|
-
const text = chunk.toString("utf8");
|
|
149
|
-
if (mode !== "ndjson") rawOutput += text;
|
|
150
|
-
if (mode === "legacy") return;
|
|
151
|
-
buffer += text;
|
|
152
|
-
if (Buffer.byteLength(buffer, "utf8") > maxFrameBytes && !buffer.includes("\n")) {
|
|
153
|
-
throw new Error(`Tool event from ${name} exceeds ${maxFrameBytes} bytes`);
|
|
154
|
-
}
|
|
155
|
-
let newlineIndex = buffer.indexOf("\n");
|
|
156
|
-
while (newlineIndex !== -1) {
|
|
157
|
-
const line = buffer.slice(0, newlineIndex).trim();
|
|
158
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
159
|
-
if (line) await consumeLine(line);
|
|
160
|
-
newlineIndex = buffer.indexOf("\n");
|
|
161
|
-
}
|
|
162
|
-
},
|
|
163
|
-
async finish() {
|
|
164
|
-
const tail = buffer.trim();
|
|
165
|
-
buffer = "";
|
|
166
|
-
if (tail) await consumeLine(tail);
|
|
167
|
-
if (mode !== "ndjson") return { mode: "legacy", output: rawOutput };
|
|
168
|
-
if (!terminalSeen) throw new Error(`Tool ${name} ended without a terminal event`);
|
|
169
|
-
return { mode: "ndjson", result: terminalResult };
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function runToolProcess(command, args, { onEvent, maxFrameBytes, timeoutMs, killGraceMs, label, ...options } = {}) {
|
|
175
|
-
const child = spawn(command, args, { ...options, stdio: ["ignore", "pipe", "pipe"] });
|
|
176
|
-
const parser = createToolOutputParser(path.basename(args[0] || command), { onEvent, maxFrameBytes });
|
|
177
|
-
const stderrChunks = [];
|
|
178
|
-
let stderrBytes = 0;
|
|
179
|
-
const stdoutTask = (async () => {
|
|
180
|
-
for await (const chunk of child.stdout) await parser.push(chunk);
|
|
181
|
-
return parser.finish();
|
|
182
|
-
})();
|
|
183
|
-
const stderrTask = (async () => {
|
|
184
|
-
for await (const chunk of child.stderr) {
|
|
185
|
-
if (stderrBytes >= maxFrameBytes) continue;
|
|
186
|
-
const accepted = chunk.subarray(0, maxFrameBytes - stderrBytes);
|
|
187
|
-
stderrChunks.push(accepted);
|
|
188
|
-
stderrBytes += accepted.length;
|
|
189
|
-
}
|
|
190
|
-
return Buffer.concat(stderrChunks).toString("utf8");
|
|
191
|
-
})();
|
|
192
|
-
child.stdout.resume();
|
|
193
|
-
child.stderr.resume();
|
|
194
|
-
let code;
|
|
195
|
-
try {
|
|
196
|
-
code = await waitForToolProcess(child, { timeoutMs, killGraceMs, label });
|
|
197
|
-
} catch (error) {
|
|
198
|
-
await Promise.allSettled([stdoutTask, stderrTask]);
|
|
199
|
-
throw error;
|
|
200
|
-
}
|
|
201
|
-
const [parsed, stderr] = await Promise.all([stdoutTask, stderrTask]);
|
|
202
|
-
return { code, parsed, stderr };
|
|
203
|
-
}
|
|
204
|
-
|
|
205
64
|
function normalizeCategory(category) {
|
|
206
65
|
if (typeof category !== "string") return null;
|
|
207
66
|
const trimmed = category.trim();
|
|
@@ -407,29 +266,44 @@ export class ToolRegistry {
|
|
|
407
266
|
async help(name) {
|
|
408
267
|
const tool = this.get(name);
|
|
409
268
|
if (!tool) throw new Error(`Tool not found: ${name}`);
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
269
|
+
const lease = await this.executionGovernor.acquire(tool.execution, `${name}:help`);
|
|
270
|
+
try {
|
|
271
|
+
const execution = executionForLease(tool.execution, lease);
|
|
272
|
+
const nodeArgs = [
|
|
273
|
+
...(execution?.maxHeapMb ? [`--max-old-space-size=${execution.maxHeapMb}`] : []),
|
|
274
|
+
tool.entry,
|
|
275
|
+
"--help"
|
|
276
|
+
];
|
|
277
|
+
const invocation = isolatedToolProcessInvocation(nodeArgs, execution);
|
|
278
|
+
const result = await runToolHelpProcess(invocation.command, invocation.args, {
|
|
279
|
+
cwd: tool.dir,
|
|
280
|
+
env: toolProcessEnv(),
|
|
281
|
+
timeoutMs: this.helpTimeoutMs,
|
|
282
|
+
killGraceMs: this.killGraceMs,
|
|
283
|
+
maxOutputBytes: execution?.maxOutputBytes || daemonConfigDefaults.ipcFrameBytes,
|
|
284
|
+
label: `Tool help for ${name}`
|
|
285
|
+
});
|
|
286
|
+
const help = result.stdout || result.stderr;
|
|
287
|
+
const skills = await this.resolveSkills(name);
|
|
288
|
+
const sections = [
|
|
289
|
+
help.trimEnd(),
|
|
290
|
+
formatSemanticMetadata(tool),
|
|
291
|
+
formatToolDependencies(tool, this.tools)
|
|
292
|
+
];
|
|
293
|
+
if (skills.length) {
|
|
294
|
+
const skillHelp = skills.map((item) => [
|
|
295
|
+
`- ${item.name}${item.when ? ` (${item.when})` : ""}`,
|
|
296
|
+
item.description ? ` ${item.description}` : null,
|
|
297
|
+
item.found ? ` path: ${item.path}` : " warning: skill not found"
|
|
298
|
+
].filter(Boolean).join("\n")).join("\n");
|
|
299
|
+
sections.push(`Assigned skills:\n${skillHelp}`);
|
|
300
|
+
}
|
|
301
|
+
lease.release({ success: result.code === 0 });
|
|
302
|
+
return `${sections.filter(Boolean).join("\n\n")}\n`;
|
|
303
|
+
} catch (error) {
|
|
304
|
+
lease.release({ memoryLimited: error?.code === "TOOL_PROCESS_MEMORY_LIMIT" });
|
|
305
|
+
throw error;
|
|
431
306
|
}
|
|
432
|
-
return `${sections.filter(Boolean).join("\n\n")}\n`;
|
|
433
307
|
}
|
|
434
308
|
|
|
435
309
|
async resolveSkills(name) {
|
|
@@ -522,6 +396,7 @@ export class ToolRegistry {
|
|
|
522
396
|
const tmpDir = chatId != null ? getChatToolTmpDir(chatId, name) : getToolTmpDir(name);
|
|
523
397
|
const requestFile = path.join(tmpDir, `.request-${Date.now()}-${randomUUID()}.json`);
|
|
524
398
|
let lease = null;
|
|
399
|
+
let leaseOutcome = {};
|
|
525
400
|
let result;
|
|
526
401
|
try {
|
|
527
402
|
lease = await this.executionGovernor.acquire(tool.execution, name);
|
|
@@ -543,11 +418,25 @@ export class ToolRegistry {
|
|
|
543
418
|
result = await runtime.submit(enrichedRequest, { onEvent });
|
|
544
419
|
} else {
|
|
545
420
|
await writeFile(requestFile, `${JSON.stringify(enrichedRequest, null, 2)}\n`, "utf8");
|
|
546
|
-
const
|
|
421
|
+
const execution = executionForLease(tool.execution, lease);
|
|
422
|
+
const nodeArgs = [
|
|
423
|
+
...(execution?.maxHeapMb ? [`--max-old-space-size=${execution.maxHeapMb}`] : []),
|
|
424
|
+
tool.entry,
|
|
425
|
+
"run",
|
|
426
|
+
"--request-file",
|
|
427
|
+
requestFile
|
|
428
|
+
];
|
|
429
|
+
const processInvocation = isolatedToolProcessInvocation(nodeArgs, execution);
|
|
430
|
+
if (processInvocation.isolated) {
|
|
431
|
+
this.logger?.log("tools", `${name} isolated at ${execution.maxMemoryMb} MiB total memory (${execution.maxHeapMb} MiB heap)`);
|
|
432
|
+
}
|
|
433
|
+
const processResult = await runToolProcess(processInvocation.command, processInvocation.args, {
|
|
547
434
|
cwd: tool.dir,
|
|
548
|
-
env:
|
|
435
|
+
env: toolProcessEnv(),
|
|
549
436
|
onEvent,
|
|
437
|
+
parserName: name,
|
|
550
438
|
maxFrameBytes: daemonConfigDefaults.ipcFrameBytes,
|
|
439
|
+
maxOutputBytes: tool.execution?.maxOutputBytes || daemonConfigDefaults.ipcFrameBytes,
|
|
551
440
|
timeoutMs: this.runTimeoutMs,
|
|
552
441
|
killGraceMs: this.killGraceMs,
|
|
553
442
|
label: `Tool run for ${name}`
|
|
@@ -555,11 +444,21 @@ export class ToolRegistry {
|
|
|
555
444
|
if (processResult.stderr.trim()) {
|
|
556
445
|
this.logger?.log("tools", `${name} stderr: ${processResult.stderr.trim()}`);
|
|
557
446
|
}
|
|
447
|
+
if (processResult.code !== 0) {
|
|
448
|
+
const memoryLimited = /heap limit|heap out of memory|allocation failed.*memory|memory cgroup out of memory|\bkilled\b/i.test(processResult.stderr)
|
|
449
|
+
|| (processInvocation.isolated && [9, 134, 137].includes(processResult.code));
|
|
450
|
+
const error = new Error(memoryLimited
|
|
451
|
+
? `Tool ${name} exceeded its isolated memory limit`
|
|
452
|
+
: `Tool ${name} exited with code ${processResult.code}`);
|
|
453
|
+
error.code = memoryLimited ? "TOOL_PROCESS_MEMORY_LIMIT" : "TOOL_PROCESS_EXIT";
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
558
456
|
result = processResult.parsed.mode === "ndjson"
|
|
559
457
|
? processResult.parsed.result
|
|
560
458
|
: JSON.parse(processResult.parsed.output);
|
|
561
459
|
}
|
|
562
460
|
const normalized = normalizeToolResult(name, result);
|
|
461
|
+
leaseOutcome = { success: normalized.ok !== false };
|
|
563
462
|
if (normalized.ok === false) {
|
|
564
463
|
this.logger?.log("tools", `${name} -> ${normalized.status || "error"}: ${normalized.error || "unknown error"}`);
|
|
565
464
|
} else {
|
|
@@ -567,7 +466,20 @@ export class ToolRegistry {
|
|
|
567
466
|
}
|
|
568
467
|
return normalized;
|
|
569
468
|
} catch (error) {
|
|
570
|
-
|
|
469
|
+
leaseOutcome = { memoryLimited: error?.code === "TOOL_PROCESS_MEMORY_LIMIT" };
|
|
470
|
+
if (error?.code === "TOOL_RESOURCE_PRESSURE") {
|
|
471
|
+
return normalizeToolResult(name, {
|
|
472
|
+
ok: false,
|
|
473
|
+
status: "retryable",
|
|
474
|
+
error: error.message,
|
|
475
|
+
resolution: {
|
|
476
|
+
type: "retry_later",
|
|
477
|
+
retry: true,
|
|
478
|
+
message: "The tool was not started. Retry after host memory pressure falls."
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
if (["TOOL_PROCESS_TIMEOUT", "TOOL_OUTPUT_LIMIT", "TOOL_PROCESS_MEMORY_LIMIT", "TOOL_PROCESS_EXIT"].includes(error?.code)) {
|
|
571
483
|
return normalizeToolResult(name, {
|
|
572
484
|
ok: false,
|
|
573
485
|
status: "outcome_uncertain",
|
|
@@ -575,7 +487,7 @@ export class ToolRegistry {
|
|
|
575
487
|
resolution: {
|
|
576
488
|
type: "status_check_required",
|
|
577
489
|
retry: false,
|
|
578
|
-
message: "The tool process
|
|
490
|
+
message: "The isolated tool process ended without a confirmed result. Check external state before retrying."
|
|
579
491
|
}
|
|
580
492
|
});
|
|
581
493
|
}
|
|
@@ -584,7 +496,7 @@ export class ToolRegistry {
|
|
|
584
496
|
error: error?.message || `Invalid tool response for ${name}`
|
|
585
497
|
});
|
|
586
498
|
} finally {
|
|
587
|
-
lease?.release();
|
|
499
|
+
lease?.release(leaseOutcome);
|
|
588
500
|
await unlink(requestFile).catch(() => {});
|
|
589
501
|
await rmdir(tmpDir).catch(() => {});
|
|
590
502
|
if (chatId != null) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { getChatToolResourceNotesFile } from "../../
|
|
3
|
+
import { getChatToolResourceNotesFile } from "../../platform/paths.js";
|
|
4
4
|
|
|
5
5
|
export const maxToolResourceNoteCharacters = 200;
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { getChatToolUsageFile } from "../../
|
|
3
|
+
import { getChatToolUsageFile } from "../../platform/paths.js";
|
|
4
4
|
|
|
5
5
|
function emptyUsage() {
|
|
6
6
|
return { version: 1, tools: {} };
|