@librechat/agents 3.1.77 → 3.1.78-dev.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/dist/cjs/common/enum.cjs +54 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +148 -4
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs +291 -0
- package/dist/cjs/hooks/createWorkspacePolicyHook.cjs.map +1 -0
- package/dist/cjs/main.cjs +90 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/anthropicToolCache.cjs +102 -0
- package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -0
- package/dist/cjs/messages/prune.cjs +27 -0
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/recency.cjs +99 -0
- package/dist/cjs/messages/recency.cjs.map +1 -0
- package/dist/cjs/run.cjs +30 -0
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +100 -6
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +635 -23
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/local/CompileCheckTool.cjs +227 -0
- package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -0
- package/dist/cjs/tools/local/FileCheckpointer.cjs +90 -0
- package/dist/cjs/tools/local/FileCheckpointer.cjs.map +1 -0
- package/dist/cjs/tools/local/LocalCodingTools.cjs +1098 -0
- package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -0
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs +1042 -0
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -0
- package/dist/cjs/tools/local/LocalExecutionTools.cjs +122 -0
- package/dist/cjs/tools/local/LocalExecutionTools.cjs.map +1 -0
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs +453 -0
- package/dist/cjs/tools/local/LocalProgrammaticToolCalling.cjs.map +1 -0
- package/dist/cjs/tools/local/attachments.cjs +183 -0
- package/dist/cjs/tools/local/attachments.cjs.map +1 -0
- package/dist/cjs/tools/local/bashAst.cjs +129 -0
- package/dist/cjs/tools/local/bashAst.cjs.map +1 -0
- package/dist/cjs/tools/local/editStrategies.cjs +188 -0
- package/dist/cjs/tools/local/editStrategies.cjs.map +1 -0
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +141 -0
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -0
- package/dist/cjs/tools/local/syntaxCheck.cjs +182 -0
- package/dist/cjs/tools/local/syntaxCheck.cjs.map +1 -0
- package/dist/cjs/tools/local/textEncoding.cjs +30 -0
- package/dist/cjs/tools/local/textEncoding.cjs.map +1 -0
- package/dist/cjs/tools/local/workspaceFS.cjs +51 -0
- package/dist/cjs/tools/local/workspaceFS.cjs.map +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +53 -1
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +149 -5
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs +289 -0
- package/dist/esm/hooks/createWorkspacePolicyHook.mjs.map +1 -0
- package/dist/esm/main.mjs +17 -2
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/messages/anthropicToolCache.mjs +99 -0
- package/dist/esm/messages/anthropicToolCache.mjs.map +1 -0
- package/dist/esm/messages/prune.mjs +26 -1
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/recency.mjs +97 -0
- package/dist/esm/messages/recency.mjs.map +1 -0
- package/dist/esm/run.mjs +30 -0
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +100 -6
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +635 -23
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/local/CompileCheckTool.mjs +223 -0
- package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -0
- package/dist/esm/tools/local/FileCheckpointer.mjs +87 -0
- package/dist/esm/tools/local/FileCheckpointer.mjs.map +1 -0
- package/dist/esm/tools/local/LocalCodingTools.mjs +1075 -0
- package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -0
- package/dist/esm/tools/local/LocalExecutionEngine.mjs +1022 -0
- package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -0
- package/dist/esm/tools/local/LocalExecutionTools.mjs +117 -0
- package/dist/esm/tools/local/LocalExecutionTools.mjs.map +1 -0
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs +448 -0
- package/dist/esm/tools/local/LocalProgrammaticToolCalling.mjs.map +1 -0
- package/dist/esm/tools/local/attachments.mjs +180 -0
- package/dist/esm/tools/local/attachments.mjs.map +1 -0
- package/dist/esm/tools/local/bashAst.mjs +126 -0
- package/dist/esm/tools/local/bashAst.mjs.map +1 -0
- package/dist/esm/tools/local/editStrategies.mjs +185 -0
- package/dist/esm/tools/local/editStrategies.mjs.map +1 -0
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +137 -0
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -0
- package/dist/esm/tools/local/syntaxCheck.mjs +179 -0
- package/dist/esm/tools/local/syntaxCheck.mjs.map +1 -0
- package/dist/esm/tools/local/textEncoding.mjs +27 -0
- package/dist/esm/tools/local/textEncoding.mjs.map +1 -0
- package/dist/esm/tools/local/workspaceFS.mjs +49 -0
- package/dist/esm/tools/local/workspaceFS.mjs.map +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +39 -1
- package/dist/types/graphs/Graph.d.ts +34 -0
- package/dist/types/hooks/createWorkspacePolicyHook.d.ts +95 -0
- package/dist/types/hooks/index.d.ts +2 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/messages/anthropicToolCache.d.ts +51 -0
- package/dist/types/messages/index.d.ts +2 -0
- package/dist/types/messages/prune.d.ts +11 -0
- package/dist/types/messages/recency.d.ts +64 -0
- package/dist/types/run.d.ts +21 -0
- package/dist/types/tools/ToolNode.d.ts +145 -2
- package/dist/types/tools/local/CompileCheckTool.d.ts +31 -0
- package/dist/types/tools/local/FileCheckpointer.d.ts +39 -0
- package/dist/types/tools/local/LocalCodingTools.d.ts +57 -0
- package/dist/types/tools/local/LocalExecutionEngine.d.ts +149 -0
- package/dist/types/tools/local/LocalExecutionTools.d.ts +9 -0
- package/dist/types/tools/local/LocalProgrammaticToolCalling.d.ts +21 -0
- package/dist/types/tools/local/attachments.d.ts +84 -0
- package/dist/types/tools/local/bashAst.d.ts +11 -0
- package/dist/types/tools/local/editStrategies.d.ts +28 -0
- package/dist/types/tools/local/index.d.ts +12 -0
- package/dist/types/tools/local/resolveLocalExecutionTools.d.ts +38 -0
- package/dist/types/tools/local/syntaxCheck.d.ts +42 -0
- package/dist/types/tools/local/textEncoding.d.ts +21 -0
- package/dist/types/tools/local/workspaceFS.d.ts +49 -0
- package/dist/types/types/hitl.d.ts +56 -27
- package/dist/types/types/run.d.ts +8 -1
- package/dist/types/types/summarize.d.ts +30 -0
- package/dist/types/types/tools.d.ts +341 -6
- package/package.json +21 -2
- package/src/common/enum.ts +54 -0
- package/src/graphs/Graph.ts +164 -6
- package/src/hooks/__tests__/compactHooks.test.ts +38 -2
- package/src/hooks/__tests__/createWorkspacePolicyHook.test.ts +393 -0
- package/src/hooks/createWorkspacePolicyHook.ts +355 -0
- package/src/hooks/index.ts +6 -0
- package/src/index.ts +1 -0
- package/src/messages/__tests__/anthropicToolCache.test.ts +125 -0
- package/src/messages/__tests__/recency.test.ts +267 -0
- package/src/messages/anthropicToolCache.ts +116 -0
- package/src/messages/index.ts +2 -0
- package/src/messages/prune.ts +27 -1
- package/src/messages/recency.ts +155 -0
- package/src/run.ts +31 -0
- package/src/scripts/compare_pi_vs_ours.ts +840 -0
- package/src/scripts/local_engine.ts +166 -0
- package/src/scripts/local_engine_checkpointer.ts +205 -0
- package/src/scripts/local_engine_compile.ts +263 -0
- package/src/scripts/local_engine_hooks.ts +226 -0
- package/src/scripts/local_engine_image.ts +201 -0
- package/src/scripts/local_engine_ptc.ts +151 -0
- package/src/scripts/local_engine_workspace.ts +258 -0
- package/src/scripts/summarization-recency.ts +462 -0
- package/src/specs/prune.test.ts +39 -0
- package/src/summarization/__tests__/node.test.ts +499 -3
- package/src/summarization/node.ts +124 -7
- package/src/tools/ToolNode.ts +769 -20
- package/src/tools/__tests__/LocalExecutionTools.test.ts +2647 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +175 -0
- package/src/tools/__tests__/ToolNode.outputReferences.test.ts +114 -0
- package/src/tools/__tests__/ToolNode.session.test.ts +84 -0
- package/src/tools/__tests__/directToolHITLResumeScope.test.ts +467 -0
- package/src/tools/__tests__/directToolHooks.test.ts +411 -0
- package/src/tools/__tests__/localToolNames.test.ts +73 -0
- package/src/tools/__tests__/workspaceSeam.test.ts +134 -0
- package/src/tools/local/CompileCheckTool.ts +278 -0
- package/src/tools/local/FileCheckpointer.ts +93 -0
- package/src/tools/local/LocalCodingTools.ts +1342 -0
- package/src/tools/local/LocalExecutionEngine.ts +1329 -0
- package/src/tools/local/LocalExecutionTools.ts +167 -0
- package/src/tools/local/LocalProgrammaticToolCalling.ts +594 -0
- package/src/tools/local/__tests__/FileCheckpointer.test.ts +120 -0
- package/src/tools/local/__tests__/editStrategies.test.ts +134 -0
- package/src/tools/local/attachments.ts +251 -0
- package/src/tools/local/bashAst.ts +151 -0
- package/src/tools/local/editStrategies.ts +188 -0
- package/src/tools/local/index.ts +12 -0
- package/src/tools/local/resolveLocalExecutionTools.ts +208 -0
- package/src/tools/local/syntaxCheck.ts +243 -0
- package/src/tools/local/textEncoding.ts +37 -0
- package/src/tools/local/workspaceFS.ts +89 -0
- package/src/types/hitl.ts +56 -27
- package/src/types/run.ts +12 -1
- package/src/types/summarize.ts +31 -0
- package/src/types/tools.ts +359 -7
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `compile_check` — a thin LLM-callable wrapper around the project's
|
|
3
|
+
* standard typecheck/lint command. Lets the agent answer "did my
|
|
4
|
+
* change break anything?" without us shipping a real LSP client.
|
|
5
|
+
*
|
|
6
|
+
* Auto-detection priority (first hit wins):
|
|
7
|
+
*
|
|
8
|
+
* 1. `local.compileCheck.command` — explicit override
|
|
9
|
+
* 2. `tsconfig.json` → `npx --no-install tsc --noEmit`
|
|
10
|
+
* 3. `package.json` with a typescript dep → same as 2
|
|
11
|
+
* 4. `pyproject.toml` or `setup.py` / `setup.cfg`
|
|
12
|
+
* with a dev dep on mypy → `python3 -m mypy .`
|
|
13
|
+
* else → `python3 -m py_compile <every .py>`
|
|
14
|
+
* (bounded by find-walk so node_modules
|
|
15
|
+
* and `.venv` don't blow up)
|
|
16
|
+
* 5. `Cargo.toml` → `cargo check --message-format=short`
|
|
17
|
+
* 6. `go.mod` → `go vet ./...`
|
|
18
|
+
* 7. otherwise → tells the agent there's
|
|
19
|
+
* no detected toolchain.
|
|
20
|
+
*
|
|
21
|
+
* Output is the spawn process's stdout/stderr passed through
|
|
22
|
+
* `truncateLocalOutput` so a 10MB tsc dump can't blow context. The
|
|
23
|
+
* exit code is reported.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { resolve } from 'path';
|
|
27
|
+
import { tool } from '@langchain/core/tools';
|
|
28
|
+
import type { DynamicStructuredTool } from '@langchain/core/tools';
|
|
29
|
+
import type * as t from '@/types';
|
|
30
|
+
import {
|
|
31
|
+
getLocalCwd,
|
|
32
|
+
getWorkspaceFS,
|
|
33
|
+
spawnLocalProcess,
|
|
34
|
+
truncateLocalOutput,
|
|
35
|
+
validateBashCommand,
|
|
36
|
+
} from './LocalExecutionEngine';
|
|
37
|
+
import type { WorkspaceFS } from './workspaceFS';
|
|
38
|
+
import { Constants } from '@/common';
|
|
39
|
+
|
|
40
|
+
/** Back-compat alias; canonical name lives on `Constants.COMPILE_CHECK`. */
|
|
41
|
+
export const CompileCheckToolName = Constants.COMPILE_CHECK;
|
|
42
|
+
|
|
43
|
+
const CompileCheckSchema: t.JsonSchemaType = {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: {
|
|
46
|
+
command: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description:
|
|
49
|
+
'Optional explicit command to run instead of the auto-detected one. Runs verbatim from the local engine cwd; honours the standard sandbox/AST gate.',
|
|
50
|
+
},
|
|
51
|
+
timeout_ms: {
|
|
52
|
+
type: 'integer',
|
|
53
|
+
description:
|
|
54
|
+
'Optional timeout in milliseconds. Defaults to 120000 (2 min).',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
type DetectedKind =
|
|
60
|
+
| 'typescript'
|
|
61
|
+
| 'python-mypy'
|
|
62
|
+
| 'python-compile'
|
|
63
|
+
| 'rust'
|
|
64
|
+
| 'go'
|
|
65
|
+
| 'unknown';
|
|
66
|
+
|
|
67
|
+
type Detection = {
|
|
68
|
+
kind: DetectedKind;
|
|
69
|
+
command: string;
|
|
70
|
+
reason: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
async function pathExists(fs: WorkspaceFS, p: string): Promise<boolean> {
|
|
74
|
+
try {
|
|
75
|
+
await fs.stat(p);
|
|
76
|
+
return true;
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Probes for project markers via the configured WorkspaceFS so a Run
|
|
83
|
+
// with `local.exec.fs` (in-memory or remote engine) detects the right
|
|
84
|
+
// toolchain against the actual workspace — not the host filesystem.
|
|
85
|
+
// Codex P1 #25.
|
|
86
|
+
async function detect(cwd: string, fs: WorkspaceFS): Promise<Detection> {
|
|
87
|
+
if (await pathExists(fs, resolve(cwd, 'tsconfig.json'))) {
|
|
88
|
+
return {
|
|
89
|
+
kind: 'typescript',
|
|
90
|
+
command: 'npx --no-install tsc --noEmit',
|
|
91
|
+
reason: 'tsconfig.json present',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (await pathExists(fs, resolve(cwd, 'package.json'))) {
|
|
95
|
+
const pkgRaw = await fs
|
|
96
|
+
.readFile(resolve(cwd, 'package.json'), 'utf8')
|
|
97
|
+
.catch(() => '');
|
|
98
|
+
if (pkgRaw.includes('"typescript"')) {
|
|
99
|
+
return {
|
|
100
|
+
kind: 'typescript',
|
|
101
|
+
command: 'npx --no-install tsc --noEmit',
|
|
102
|
+
reason: 'package.json declares typescript',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (await pathExists(fs, resolve(cwd, 'Cargo.toml'))) {
|
|
107
|
+
return {
|
|
108
|
+
kind: 'rust',
|
|
109
|
+
command: 'cargo check --message-format=short',
|
|
110
|
+
reason: 'Cargo.toml present',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (await pathExists(fs, resolve(cwd, 'go.mod'))) {
|
|
114
|
+
return {
|
|
115
|
+
kind: 'go',
|
|
116
|
+
command: 'go vet ./...',
|
|
117
|
+
reason: 'go.mod present',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (
|
|
121
|
+
(await pathExists(fs, resolve(cwd, 'pyproject.toml'))) ||
|
|
122
|
+
(await pathExists(fs, resolve(cwd, 'setup.py'))) ||
|
|
123
|
+
(await pathExists(fs, resolve(cwd, 'setup.cfg')))
|
|
124
|
+
) {
|
|
125
|
+
const pyToml = await fs
|
|
126
|
+
.readFile(resolve(cwd, 'pyproject.toml'), 'utf8')
|
|
127
|
+
.catch(() => '');
|
|
128
|
+
if (pyToml.includes('mypy')) {
|
|
129
|
+
return {
|
|
130
|
+
kind: 'python-mypy',
|
|
131
|
+
command: 'python3 -m mypy .',
|
|
132
|
+
reason: 'pyproject.toml declares mypy',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
kind: 'python-compile',
|
|
137
|
+
command:
|
|
138
|
+
'python3 -c "import compileall, sys; sys.exit(0 if compileall.compile_dir(\'.\', quiet=1, rx=__import__(\'re\').compile(r\'(node_modules|\\.venv|\\.git|build|dist)\')) else 1)"',
|
|
139
|
+
reason: 'Python project (no mypy detected)',
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
kind: 'unknown',
|
|
144
|
+
command: '',
|
|
145
|
+
reason:
|
|
146
|
+
'no recognised project marker (tsconfig.json, package.json[typescript], Cargo.toml, go.mod, pyproject.toml, setup.py)',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
151
|
+
|
|
152
|
+
export function createCompileCheckTool(
|
|
153
|
+
config: t.LocalExecutionConfig = {}
|
|
154
|
+
): DynamicStructuredTool {
|
|
155
|
+
return tool(
|
|
156
|
+
async (rawInput) => {
|
|
157
|
+
const input = rawInput as {
|
|
158
|
+
command?: string;
|
|
159
|
+
timeout_ms?: number;
|
|
160
|
+
};
|
|
161
|
+
const cwd = getLocalCwd(config);
|
|
162
|
+
const fs = getWorkspaceFS(config);
|
|
163
|
+
const overrideCommand = input.command ?? config.compileCheck?.command;
|
|
164
|
+
let detection: Detection;
|
|
165
|
+
if (overrideCommand != null && overrideCommand.trim() !== '') {
|
|
166
|
+
detection = {
|
|
167
|
+
kind: 'unknown',
|
|
168
|
+
command: overrideCommand,
|
|
169
|
+
reason: 'explicit override',
|
|
170
|
+
};
|
|
171
|
+
} else {
|
|
172
|
+
detection = await detect(cwd, fs);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (detection.command === '') {
|
|
176
|
+
const explainer =
|
|
177
|
+
`compile_check: ${detection.reason}. Pass an explicit \`command\` (e.g. \`npm run typecheck\`) to override.`;
|
|
178
|
+
return [
|
|
179
|
+
explainer,
|
|
180
|
+
{
|
|
181
|
+
kind: detection.kind,
|
|
182
|
+
ran: false,
|
|
183
|
+
reason: detection.reason,
|
|
184
|
+
cwd,
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Codex P1 #21: route the resolved command through the same
|
|
190
|
+
// safety gates the rest of the local engine uses. Without this
|
|
191
|
+
// a host with `readOnly: true` (or relying on the destructive-
|
|
192
|
+
// command guard) could be bypassed by passing a `command`
|
|
193
|
+
// override to compile_check that performs writes/deletes.
|
|
194
|
+
// Auto-detected commands (tsc/cargo/etc.) pass these gates
|
|
195
|
+
// unchanged — the validation is only blocking for genuinely
|
|
196
|
+
// mutating overrides.
|
|
197
|
+
const validation = await validateBashCommand(detection.command, config);
|
|
198
|
+
if (!validation.valid) {
|
|
199
|
+
const explainer =
|
|
200
|
+
`compile_check refused to run \`${detection.command}\`: ${validation.errors.join('; ')}`;
|
|
201
|
+
return [
|
|
202
|
+
explainer,
|
|
203
|
+
{
|
|
204
|
+
kind: detection.kind,
|
|
205
|
+
ran: false,
|
|
206
|
+
reason: validation.errors.join('; '),
|
|
207
|
+
cwd,
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const timeoutMs =
|
|
213
|
+
input.timeout_ms ??
|
|
214
|
+
config.compileCheck?.timeoutMs ??
|
|
215
|
+
DEFAULT_TIMEOUT_MS;
|
|
216
|
+
const result = await spawnLocalProcess(
|
|
217
|
+
config.shell ?? (process.platform === 'win32' ? 'bash.exe' : 'bash'),
|
|
218
|
+
['-lc', detection.command],
|
|
219
|
+
{
|
|
220
|
+
...config,
|
|
221
|
+
timeoutMs,
|
|
222
|
+
maxOutputChars: config.maxOutputChars ?? 8000,
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const passed =
|
|
227
|
+
result.exitCode === 0 && !result.timedOut;
|
|
228
|
+
const headline = passed
|
|
229
|
+
? `compile_check (${detection.kind}) PASSED via \`${detection.command}\``
|
|
230
|
+
: `compile_check (${detection.kind}) FAILED via \`${detection.command}\` ` +
|
|
231
|
+
`(exit=${result.exitCode ?? 'unknown'}${result.timedOut ? ', timed_out=true' : ''})`;
|
|
232
|
+
|
|
233
|
+
let body = '';
|
|
234
|
+
if (result.stdout !== '') {
|
|
235
|
+
body += `\n\nstdout:\n${truncateLocalOutput(result.stdout, 4000)}`;
|
|
236
|
+
}
|
|
237
|
+
if (result.stderr !== '') {
|
|
238
|
+
body += `\n\nstderr:\n${truncateLocalOutput(result.stderr, 4000)}`;
|
|
239
|
+
}
|
|
240
|
+
if (result.fullOutputPath != null) {
|
|
241
|
+
body += `\n\nfull_output_path: ${result.fullOutputPath}`;
|
|
242
|
+
}
|
|
243
|
+
const summary = `${headline}${body}\n\nworking_directory: ${cwd}\nreason: ${detection.reason}`;
|
|
244
|
+
|
|
245
|
+
return [
|
|
246
|
+
summary,
|
|
247
|
+
{
|
|
248
|
+
kind: detection.kind,
|
|
249
|
+
ran: true,
|
|
250
|
+
passed,
|
|
251
|
+
exit_code: result.exitCode,
|
|
252
|
+
timed_out: result.timedOut,
|
|
253
|
+
command: detection.command,
|
|
254
|
+
cwd,
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: CompileCheckToolName,
|
|
260
|
+
description:
|
|
261
|
+
'Run the project\'s standard typecheck or lint pass and return its output. Auto-detects from project markers (tsconfig.json/package.json -> tsc; Cargo.toml -> cargo check; go.mod -> go vet; pyproject.toml -> mypy or py_compile). Pass `command` to override.',
|
|
262
|
+
schema: CompileCheckSchema,
|
|
263
|
+
responseFormat: Constants.CONTENT_AND_ARTIFACT,
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export function createCompileCheckToolDefinition(): t.LCTool {
|
|
269
|
+
return {
|
|
270
|
+
name: CompileCheckToolName,
|
|
271
|
+
description:
|
|
272
|
+
'Run the project\'s standard typecheck or lint pass and return its output.',
|
|
273
|
+
parameters: CompileCheckSchema,
|
|
274
|
+
allowed_callers: ['direct', 'code_execution'],
|
|
275
|
+
responseFormat: Constants.CONTENT_AND_ARTIFACT,
|
|
276
|
+
toolType: 'builtin',
|
|
277
|
+
};
|
|
278
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { dirname } from 'path';
|
|
2
|
+
import { nodeWorkspaceFS } from './workspaceFS';
|
|
3
|
+
import type { WorkspaceFS } from './workspaceFS';
|
|
4
|
+
import type * as t from '@/types';
|
|
5
|
+
|
|
6
|
+
type Snapshot =
|
|
7
|
+
| { kind: 'absent' }
|
|
8
|
+
| { kind: 'present'; content: Buffer };
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Per-Run snapshot store for write_file / edit_file. Captures the
|
|
12
|
+
* pre-write byte content of every path the local engine is about to
|
|
13
|
+
* mutate so a later `rewind()` can restore the working tree to its
|
|
14
|
+
* original state. Notes:
|
|
15
|
+
*
|
|
16
|
+
* - Idempotent per path: subsequent captures preserve the first
|
|
17
|
+
* snapshot (so rewind always restores the *original* content).
|
|
18
|
+
* - Captures missing files as `{ kind: 'absent' }`; rewind deletes
|
|
19
|
+
* those paths so created files are removed.
|
|
20
|
+
* - In-memory: snapshots live for the lifetime of this instance and
|
|
21
|
+
* are not persisted across processes. Tie the lifetime to a Run.
|
|
22
|
+
* - Bounded by `maxBytesPerFile` (default 32 MiB) to bound memory.
|
|
23
|
+
* A file larger than the cap is recorded but not snapshotted; the
|
|
24
|
+
* rewind of that path is best-effort and the caller is told via
|
|
25
|
+
* the result count not to trust it.
|
|
26
|
+
*/
|
|
27
|
+
export class LocalFileCheckpointerImpl implements t.LocalFileCheckpointer {
|
|
28
|
+
private snapshots = new Map<string, Snapshot>();
|
|
29
|
+
private oversizePaths = new Set<string>();
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly maxBytesPerFile: number = 32 * 1024 * 1024,
|
|
33
|
+
private readonly fs: WorkspaceFS = nodeWorkspaceFS
|
|
34
|
+
) {}
|
|
35
|
+
|
|
36
|
+
async captureBeforeWrite(absolutePath: string): Promise<void> {
|
|
37
|
+
if (this.snapshots.has(absolutePath) || this.oversizePaths.has(absolutePath)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let info;
|
|
41
|
+
try {
|
|
42
|
+
info = await this.fs.stat(absolutePath);
|
|
43
|
+
} catch {
|
|
44
|
+
this.snapshots.set(absolutePath, { kind: 'absent' });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!info.isFile()) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (info.size > this.maxBytesPerFile) {
|
|
51
|
+
this.oversizePaths.add(absolutePath);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const content = (await this.fs.readFile(absolutePath)) as Buffer;
|
|
55
|
+
this.snapshots.set(absolutePath, { kind: 'present', content });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async rewind(): Promise<number> {
|
|
59
|
+
let restored = 0;
|
|
60
|
+
for (const [path, snapshot] of this.snapshots.entries()) {
|
|
61
|
+
if (snapshot.kind === 'absent') {
|
|
62
|
+
await this.fs.unlink(path).catch(() => undefined);
|
|
63
|
+
restored++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
await this.fs.mkdir(dirname(path), { recursive: true });
|
|
68
|
+
await this.fs.writeFile(path, snapshot.content);
|
|
69
|
+
restored++;
|
|
70
|
+
} catch {
|
|
71
|
+
// Best-effort: ignore individual restore failures so the rest
|
|
72
|
+
// of the rewind continues.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return restored;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
capturedPaths(): string[] {
|
|
79
|
+
return [...this.snapshots.keys(), ...this.oversizePaths];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Convenience factory so callers don't have to reach for the impl
|
|
85
|
+
* class directly. Accepts an optional `WorkspaceFS` so a host using a
|
|
86
|
+
* non-default engine (remote sandbox, in-memory test FS, etc.) can
|
|
87
|
+
* route the checkpointer through the same I/O.
|
|
88
|
+
*/
|
|
89
|
+
export function createLocalFileCheckpointer(
|
|
90
|
+
options: { maxBytesPerFile?: number; fs?: WorkspaceFS } = {}
|
|
91
|
+
): t.LocalFileCheckpointer {
|
|
92
|
+
return new LocalFileCheckpointerImpl(options.maxBytesPerFile, options.fs);
|
|
93
|
+
}
|