koneck 2.25.40 → 2.25.42
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/engine.d.ts.map +1 -1
- package/dist/engine.js +23 -8
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +5 -1
- package/dist/ink-chat.js.map +1 -1
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +10 -1
- package/dist/plan.js.map +1 -1
- package/dist/sandbox.d.ts +62 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +102 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/tools.d.ts +25 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +103 -4
- package/dist/tools.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +17 -1
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/plan.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAE9D,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,UAAU,EAAE,IAAkC,CAAC;AAE5D,kGAAkG;AAClG,eAAO,MAAM,SAAS,KAAK,CAAC;AAc5B;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAE9D,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,UAAU,EAAE,IAAkC,CAAC;AAE5D,kGAAkG;AAClG,eAAO,MAAM,SAAS,KAAK,CAAC;AAc5B;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAqCpE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAe/C;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAO9C;AAED,sFAAsF;AACtF,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAO1C"}
|
package/dist/plan.js
CHANGED
|
@@ -36,7 +36,16 @@ function coerceState(value) {
|
|
|
36
36
|
* the tool, and the tool only helps if it is used.
|
|
37
37
|
*/
|
|
38
38
|
export function parsePlan(input, now) {
|
|
39
|
-
|
|
39
|
+
let raw = input?.steps ?? input;
|
|
40
|
+
// A numbered list in a string is a plan. A model that wrote one out has done the thinking the
|
|
41
|
+
// tool is for, and refusing it over the container teaches it to stop calling the tool — which
|
|
42
|
+
// is what happened: rejected, it went back to narrating and building nothing.
|
|
43
|
+
if (typeof raw === 'string') {
|
|
44
|
+
raw = raw
|
|
45
|
+
.split('\n')
|
|
46
|
+
.map(line => line.replace(/^\s*(?:[-*•]|\d+[.)])\s*/, '').trim())
|
|
47
|
+
.filter(Boolean);
|
|
48
|
+
}
|
|
40
49
|
if (!Array.isArray(raw)) {
|
|
41
50
|
return 'ERROR: plan needs a "steps" array, e.g. ' +
|
|
42
51
|
'{"steps":[{"text":"design the ledger schema","state":"doing"}]}.';
|
package/dist/plan.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAiBH,MAAM,CAAC,MAAM,UAAU,GAAS,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;AAE5D,kGAAkG;AAClG,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC;AAE5B,MAAM,MAAM,GAAyB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAE1E,SAAS,WAAW,CAAC,KAAc;IACjC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/C,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAiB,CAAC;QAAE,OAAO,IAAiB,CAAC;IACjE,yFAAyF;IACzF,IAAI,qCAAqC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IACrE,IAAI,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IACrD,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACpD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAc,EAAE,GAAW;IACnD,
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a run is allowed to touch, and whether the operating system is enforcing it.
|
|
3
|
+
*
|
|
4
|
+
* There are two layers here and they are not equally strong, which matters enough to say plainly
|
|
5
|
+
* rather than bury. Path checks on KONECK's own file tools are certain: those tools are ours, and
|
|
6
|
+
* a refusal there is a refusal. A shell command is a different matter — once `bash -c` is running
|
|
7
|
+
* it can write anywhere the user can, and no amount of checking the command string changes that.
|
|
8
|
+
* The only real answer for commands is the operating system, so on Linux they are run inside
|
|
9
|
+
* bubblewrap where it is installed.
|
|
10
|
+
*
|
|
11
|
+
* Where no such facility exists — Windows, or a Linux box without bwrap — commands are not
|
|
12
|
+
* confined at all, and this module says so rather than implying a protection that is not there.
|
|
13
|
+
* A sandbox that is believed in but absent is worse than no sandbox, because it changes what
|
|
14
|
+
* people are willing to run.
|
|
15
|
+
*/
|
|
16
|
+
export type SandboxMode = 'off' | 'workspace-write' | 'read-only';
|
|
17
|
+
export interface SandboxSpec {
|
|
18
|
+
mode: SandboxMode;
|
|
19
|
+
/** Absolute directories that may be written, in workspace-write. Empty in read-only. */
|
|
20
|
+
writableRoots: string[];
|
|
21
|
+
}
|
|
22
|
+
/** How commands are confined on this machine, if at all. */
|
|
23
|
+
export type Confinement = 'bubblewrap' | 'none';
|
|
24
|
+
export declare const SANDBOX_MODES: readonly SandboxMode[];
|
|
25
|
+
/**
|
|
26
|
+
* Whether a real sandbox is available for shell commands.
|
|
27
|
+
*
|
|
28
|
+
* Probed once by running it, not by looking for the binary: bubblewrap is present but unusable on
|
|
29
|
+
* plenty of systems — unprivileged user namespaces disabled, or a container that forbids nesting —
|
|
30
|
+
* and finding that out when the first command fails would break the run instead of degrading.
|
|
31
|
+
*/
|
|
32
|
+
export declare function detectConfinement(platform?: string): Confinement;
|
|
33
|
+
/** Only for tests, which must be able to ask the question again. */
|
|
34
|
+
export declare function resetConfinementCache(): void;
|
|
35
|
+
export declare function resolveSandbox(mode: SandboxMode | undefined, cwd: string, extraDirs?: readonly string[]): SandboxSpec;
|
|
36
|
+
export interface PathVerdict {
|
|
37
|
+
allowed: boolean;
|
|
38
|
+
reason?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether a file tool may write to a path.
|
|
42
|
+
*
|
|
43
|
+
* Reads are not restricted. An agent that cannot read outside its workspace cannot look up the
|
|
44
|
+
* library it is being asked to use, and confining reads buys little: the risk being managed here
|
|
45
|
+
* is a run that damages something, not one that learns something.
|
|
46
|
+
*/
|
|
47
|
+
export declare function checkWrite(spec: SandboxSpec, absPath: string): PathVerdict;
|
|
48
|
+
/**
|
|
49
|
+
* The command line that runs a shell command under confinement, or null for none.
|
|
50
|
+
*
|
|
51
|
+
* Everything is bound read-only, the writable roots are bound read-write over the top, and /tmp is
|
|
52
|
+
* a fresh tmpfs. The network is left alone: an agent that cannot reach the network cannot install
|
|
53
|
+
* a dependency, which is most of what it is asked to do, and cutting it off would make the mode
|
|
54
|
+
* unusable rather than safe.
|
|
55
|
+
*/
|
|
56
|
+
export declare function confineCommand(spec: SandboxSpec, cwd: string, file: string, args: readonly string[], confinement?: Confinement): {
|
|
57
|
+
file: string;
|
|
58
|
+
args: string[];
|
|
59
|
+
} | null;
|
|
60
|
+
/** What the mode actually gives you here, said without overstating it. */
|
|
61
|
+
export declare function describeSandbox(spec: SandboxSpec, confinement?: Confinement): string;
|
|
62
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,iBAAiB,GAAG,WAAW,CAAC;AAElE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,wFAAwF;IACxF,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,4DAA4D;AAC5D,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,MAAM,CAAC;AAEhD,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAA4C,CAAC;AAI7F;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,GAAE,MAAyB,GAAG,WAAW,CAWlF;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,IAAI,CAA8B;AAE3E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,WAAW,GAAG,SAAS,EAC7B,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,SAAS,MAAM,EAAO,GAChC,WAAW,CAKb;AAQD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAY1E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,WAAW,GAAE,WAAiC,GAC7C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAazC;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,GAAE,WAAiC,GAAG,MAAM,CAezG"}
|
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { execaSync } from 'execa';
|
|
3
|
+
export const SANDBOX_MODES = ['off', 'workspace-write', 'read-only'];
|
|
4
|
+
let cachedConfinement = null;
|
|
5
|
+
/**
|
|
6
|
+
* Whether a real sandbox is available for shell commands.
|
|
7
|
+
*
|
|
8
|
+
* Probed once by running it, not by looking for the binary: bubblewrap is present but unusable on
|
|
9
|
+
* plenty of systems — unprivileged user namespaces disabled, or a container that forbids nesting —
|
|
10
|
+
* and finding that out when the first command fails would break the run instead of degrading.
|
|
11
|
+
*/
|
|
12
|
+
export function detectConfinement(platform = process.platform) {
|
|
13
|
+
if (cachedConfinement !== null)
|
|
14
|
+
return cachedConfinement;
|
|
15
|
+
if (platform !== 'linux') {
|
|
16
|
+
cachedConfinement = 'none';
|
|
17
|
+
return cachedConfinement;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const probe = execaSync('bwrap', ['--ro-bind', '/', '/', '--', '/bin/true'], { reject: false, timeout: 5_000 });
|
|
21
|
+
cachedConfinement = probe.exitCode === 0 ? 'bubblewrap' : 'none';
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
cachedConfinement = 'none';
|
|
25
|
+
}
|
|
26
|
+
return cachedConfinement;
|
|
27
|
+
}
|
|
28
|
+
/** Only for tests, which must be able to ask the question again. */
|
|
29
|
+
export function resetConfinementCache() { cachedConfinement = null; }
|
|
30
|
+
export function resolveSandbox(mode, cwd, extraDirs = []) {
|
|
31
|
+
const chosen = mode && SANDBOX_MODES.includes(mode) ? mode : 'off';
|
|
32
|
+
if (chosen === 'read-only')
|
|
33
|
+
return { mode: chosen, writableRoots: [] };
|
|
34
|
+
const roots = [cwd, ...extraDirs].map(d => path.resolve(d));
|
|
35
|
+
return { mode: chosen, writableRoots: chosen === 'off' ? [] : roots };
|
|
36
|
+
}
|
|
37
|
+
/** Whether one path sits inside one root, without `/work` matching `/workspace`. */
|
|
38
|
+
function within(root, target) {
|
|
39
|
+
const rel = path.relative(root, target);
|
|
40
|
+
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether a file tool may write to a path.
|
|
44
|
+
*
|
|
45
|
+
* Reads are not restricted. An agent that cannot read outside its workspace cannot look up the
|
|
46
|
+
* library it is being asked to use, and confining reads buys little: the risk being managed here
|
|
47
|
+
* is a run that damages something, not one that learns something.
|
|
48
|
+
*/
|
|
49
|
+
export function checkWrite(spec, absPath) {
|
|
50
|
+
if (spec.mode === 'off')
|
|
51
|
+
return { allowed: true };
|
|
52
|
+
if (spec.mode === 'read-only') {
|
|
53
|
+
return { allowed: false, reason: 'the sandbox is read-only, so nothing can be written or changed' };
|
|
54
|
+
}
|
|
55
|
+
const target = path.resolve(absPath);
|
|
56
|
+
if (spec.writableRoots.some(root => within(root, target)))
|
|
57
|
+
return { allowed: true };
|
|
58
|
+
return {
|
|
59
|
+
allowed: false,
|
|
60
|
+
reason: `${target} is outside the writable area (${spec.writableRoots.join(', ')}). ` +
|
|
61
|
+
'Start KONECK with --add-dir to include another directory.',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The command line that runs a shell command under confinement, or null for none.
|
|
66
|
+
*
|
|
67
|
+
* Everything is bound read-only, the writable roots are bound read-write over the top, and /tmp is
|
|
68
|
+
* a fresh tmpfs. The network is left alone: an agent that cannot reach the network cannot install
|
|
69
|
+
* a dependency, which is most of what it is asked to do, and cutting it off would make the mode
|
|
70
|
+
* unusable rather than safe.
|
|
71
|
+
*/
|
|
72
|
+
export function confineCommand(spec, cwd, file, args, confinement = detectConfinement()) {
|
|
73
|
+
if (spec.mode === 'off' || confinement !== 'bubblewrap')
|
|
74
|
+
return null;
|
|
75
|
+
const wrapper = [
|
|
76
|
+
'--ro-bind', '/', '/',
|
|
77
|
+
'--dev', '/dev',
|
|
78
|
+
'--proc', '/proc',
|
|
79
|
+
'--tmpfs', '/tmp',
|
|
80
|
+
'--die-with-parent', // the sandbox goes when the run does, rather than outliving it
|
|
81
|
+
];
|
|
82
|
+
for (const root of spec.writableRoots)
|
|
83
|
+
wrapper.push('--bind', root, root);
|
|
84
|
+
wrapper.push('--chdir', path.resolve(cwd), '--', file, ...args);
|
|
85
|
+
return { file: 'bwrap', args: wrapper };
|
|
86
|
+
}
|
|
87
|
+
/** What the mode actually gives you here, said without overstating it. */
|
|
88
|
+
export function describeSandbox(spec, confinement = detectConfinement()) {
|
|
89
|
+
if (spec.mode === 'off')
|
|
90
|
+
return 'off — file tools and commands are unrestricted';
|
|
91
|
+
const files = spec.mode === 'read-only'
|
|
92
|
+
? 'file tools cannot write anything'
|
|
93
|
+
: `file tools can write only inside ${spec.writableRoots.join(', ')}`;
|
|
94
|
+
const commands = confinement === 'bubblewrap'
|
|
95
|
+
? spec.mode === 'read-only'
|
|
96
|
+
? 'and commands run in a sandbox with no writable directory'
|
|
97
|
+
: 'and commands run in a sandbox with the same writable area'
|
|
98
|
+
: 'and commands are NOT confined here — no sandbox is available on this system, so a command ' +
|
|
99
|
+
'can still write anywhere you can. They are gated by approval instead';
|
|
100
|
+
return `${spec.mode} — ${files}, ${commands}.`;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ToolDefinition, ReadFilesArgs, WriteFileArgs, ApplyDiffArgs, ExecuteCommandArgs, ListDirArgs, GlobSearchArgs, GrepSearchArgs, MoveFileArgs, DeleteFileArgs, SearchReplaceArgs, FetchUrlArgs, RememberArgs, WebSearchArgs } from './types.js';
|
|
2
|
+
import type { SandboxSpec } from './sandbox.js';
|
|
2
3
|
export declare const TOOL_DEFINITIONS: ToolDefinition[];
|
|
3
4
|
/**
|
|
4
5
|
* Lines returned for one file unless a window is asked for.
|
|
@@ -54,7 +55,7 @@ export declare function shellFor(command: string, platform?: string, env?: NodeJ
|
|
|
54
55
|
* that were neither — Windows built-ins piped through `2>nul`, run under bash.
|
|
55
56
|
*/
|
|
56
57
|
export declare function describeShell(platform?: string, env?: NodeJS.ProcessEnv): string;
|
|
57
|
-
export declare function executeCommand(args: ExecuteCommandArgs, cwd: string, requireApproval: boolean, onOutput?: (lastLine: string) => void): Promise<string>;
|
|
58
|
+
export declare function executeCommand(args: ExecuteCommandArgs, cwd: string, requireApproval: boolean, onOutput?: (lastLine: string) => void, sandbox?: SandboxSpec): Promise<string>;
|
|
58
59
|
export declare function listDir(args: ListDirArgs, cwd: string): Promise<string>;
|
|
59
60
|
export declare function globSearch(args: GlobSearchArgs, cwd: string): Promise<string>;
|
|
60
61
|
export declare function grepSearch(args: GrepSearchArgs, cwd: string): Promise<string>;
|
|
@@ -111,5 +112,27 @@ export declare function unknownToolError(name: string, available: readonly strin
|
|
|
111
112
|
* `command` has made the same mistake whatever name it used for the tool.
|
|
112
113
|
*/
|
|
113
114
|
export declare function normalizeToolArgs(tool: string, argsJson: string): string;
|
|
114
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Required arguments a call is missing, checked against the schema the model was given.
|
|
117
|
+
*
|
|
118
|
+
* Done once here rather than in each tool, because the failure is the same everywhere and the
|
|
119
|
+
* consequence was ugly: `write_file` without a path reached path.resolve and returned
|
|
120
|
+
* `The "paths[1]" argument must be of type string. Received undefined` — a Node internal naming
|
|
121
|
+
* an argument index and a parameter the tool does not have. A run hit that five times running,
|
|
122
|
+
* announcing each time that it would "create the project structure", because nothing in the
|
|
123
|
+
* message said what was wrong.
|
|
124
|
+
*/
|
|
125
|
+
export declare function missingRequiredArgs(tool: string, parsed: unknown): string | null;
|
|
126
|
+
/** The paths a call would change, so the sandbox can be applied in one place. */
|
|
127
|
+
export declare function writeTargets(tool: string, parsed: unknown): string[];
|
|
128
|
+
/**
|
|
129
|
+
* Whether a tool result reports a failure.
|
|
130
|
+
*
|
|
131
|
+
* One definition, used by the engine and by everything that draws a result, because they had
|
|
132
|
+
* drifted: a sandbox denial was recorded as a failure by the engine and drawn with a green tick
|
|
133
|
+
* by the CI writer, which printed one unconditionally. The outcome a user most needs to see —
|
|
134
|
+
* "that write did not happen" — looked exactly like success.
|
|
135
|
+
*/
|
|
136
|
+
export declare function isToolFailure(result: string): boolean;
|
|
137
|
+
export declare function dispatchTool(name: string, argsJson: string, cwd: string, requireApproval: boolean, onOutput?: (lastLine: string) => void, sandbox?: SandboxSpec): Promise<string>;
|
|
115
138
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,cAAc,EACd,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAsT5C,CAAC;AAIF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.js
CHANGED
|
@@ -7,6 +7,7 @@ import { validateCode } from './astValidator.js';
|
|
|
7
7
|
import { tui } from './tui.js';
|
|
8
8
|
import { appendMemory } from './memory.js';
|
|
9
9
|
import { GIT_TOOL_DEFINITIONS, dispatchGitTool } from './git-tools.js';
|
|
10
|
+
import { checkWrite, confineCommand } from './sandbox.js';
|
|
10
11
|
export const TOOL_DEFINITIONS = [
|
|
11
12
|
{
|
|
12
13
|
name: 'read_files',
|
|
@@ -338,6 +339,18 @@ function renderWindow(filePath, content, offset, limit) {
|
|
|
338
339
|
`what you need instead of paging through it.`;
|
|
339
340
|
return header + '\n' + body + footer;
|
|
340
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* A missing required argument, said in a way the model can act on.
|
|
344
|
+
*
|
|
345
|
+
* Without this, `write_file` with no path reached path.resolve and came back as
|
|
346
|
+
* `The "paths[1]" argument must be of type string. Received undefined` — a Node internal about
|
|
347
|
+
* an argument index, mentioning a parameter the tool does not even have. A real run hit it five
|
|
348
|
+
* times in a row, each time announcing it would "create the project structure", because nothing
|
|
349
|
+
* in that message told it what was actually wrong.
|
|
350
|
+
*/
|
|
351
|
+
function missingArg(tool, field, example) {
|
|
352
|
+
return `ERROR: ${tool} needs a "${field}". Example: ${example}`;
|
|
353
|
+
}
|
|
341
354
|
export async function readFiles(args, cwd) {
|
|
342
355
|
// Said plainly rather than thrown. A missing field used to surface as "Cannot read properties
|
|
343
356
|
// of undefined (reading 'map')", which tells the model nothing it can act on.
|
|
@@ -361,6 +374,12 @@ export async function readFiles(args, cwd) {
|
|
|
361
374
|
.join('\n\n');
|
|
362
375
|
}
|
|
363
376
|
export async function writeFile(args, cwd, requireApproval) {
|
|
377
|
+
if (typeof args.path !== 'string' || args.path.trim() === '') {
|
|
378
|
+
return missingArg('write_file', 'path', '{"path":"src/App.jsx","content":"…"}. One file per call — there is no way to write several at once.');
|
|
379
|
+
}
|
|
380
|
+
if (typeof args.content !== 'string') {
|
|
381
|
+
return missingArg('write_file', 'content', '{"path":"src/App.jsx","content":"the file contents"}');
|
|
382
|
+
}
|
|
364
383
|
const abs = path.resolve(cwd, args.path);
|
|
365
384
|
const validation = validateCode(args.content, abs);
|
|
366
385
|
if (!validation.valid) {
|
|
@@ -479,7 +498,7 @@ export function describeShell(platform = process.platform, env = process.env) {
|
|
|
479
498
|
? (env['ComSpec'] ?? 'cmd.exe')
|
|
480
499
|
: (env['SHELL'] ?? '/bin/sh');
|
|
481
500
|
}
|
|
482
|
-
export async function executeCommand(args, cwd, requireApproval, onOutput) {
|
|
501
|
+
export async function executeCommand(args, cwd, requireApproval, onOutput, sandbox = { mode: 'off', writableRoots: [] }) {
|
|
483
502
|
if (requireApproval) {
|
|
484
503
|
const approved = await tui.confirmCommand(args.command);
|
|
485
504
|
if (!approved)
|
|
@@ -488,7 +507,10 @@ export async function executeCommand(args, cwd, requireApproval, onOutput) {
|
|
|
488
507
|
const execCwd = args.cwd ? path.resolve(cwd, args.cwd) : cwd;
|
|
489
508
|
const { ms, clamped } = resolveTimeout(args.timeout);
|
|
490
509
|
const shell = shellFor(args.command);
|
|
491
|
-
|
|
510
|
+
// Wrapped rather than trusted. Checking a command string for what it will touch is guesswork;
|
|
511
|
+
// running it where it cannot reach anything else is not.
|
|
512
|
+
const confined = confineCommand(sandbox, execCwd, shell.file, shell.args);
|
|
513
|
+
const child = execa(confined?.file ?? shell.file, confined?.args ?? shell.args, {
|
|
492
514
|
cwd: execCwd,
|
|
493
515
|
all: true,
|
|
494
516
|
reject: false,
|
|
@@ -1099,7 +1121,75 @@ export function normalizeToolArgs(tool, argsJson) {
|
|
|
1099
1121
|
}
|
|
1100
1122
|
return JSON.stringify(out);
|
|
1101
1123
|
}
|
|
1102
|
-
|
|
1124
|
+
/**
|
|
1125
|
+
* Required arguments a call is missing, checked against the schema the model was given.
|
|
1126
|
+
*
|
|
1127
|
+
* Done once here rather than in each tool, because the failure is the same everywhere and the
|
|
1128
|
+
* consequence was ugly: `write_file` without a path reached path.resolve and returned
|
|
1129
|
+
* `The "paths[1]" argument must be of type string. Received undefined` — a Node internal naming
|
|
1130
|
+
* an argument index and a parameter the tool does not have. A run hit that five times running,
|
|
1131
|
+
* announcing each time that it would "create the project structure", because nothing in the
|
|
1132
|
+
* message said what was wrong.
|
|
1133
|
+
*/
|
|
1134
|
+
export function missingRequiredArgs(tool, parsed) {
|
|
1135
|
+
const def = TOOL_DEFINITIONS.find(d => d.name === tool);
|
|
1136
|
+
const schema = def?.parameters;
|
|
1137
|
+
const required = schema?.required ?? [];
|
|
1138
|
+
if (required.length === 0)
|
|
1139
|
+
return null;
|
|
1140
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
1141
|
+
return `ERROR: ${tool} needs an object of arguments with: ${required.join(', ')}.`;
|
|
1142
|
+
}
|
|
1143
|
+
const args = parsed;
|
|
1144
|
+
const missing = required.filter(key => {
|
|
1145
|
+
const value = args[key];
|
|
1146
|
+
if (value === undefined || value === null)
|
|
1147
|
+
return true;
|
|
1148
|
+
const type = schema?.properties?.[key]?.type;
|
|
1149
|
+
if (type === 'string')
|
|
1150
|
+
return typeof value !== 'string' || value.trim() === '';
|
|
1151
|
+
if (type === 'array')
|
|
1152
|
+
return !Array.isArray(value) || value.length === 0;
|
|
1153
|
+
return false;
|
|
1154
|
+
});
|
|
1155
|
+
if (missing.length === 0)
|
|
1156
|
+
return null;
|
|
1157
|
+
const shape = Object.keys(args).length ? `You sent: ${Object.keys(args).join(', ')}.` : 'You sent nothing.';
|
|
1158
|
+
return `ERROR: ${tool} is missing ${missing.map(m => `"${m}"`).join(' and ')}. ` +
|
|
1159
|
+
`${shape} It needs: ${required.join(', ')}.`;
|
|
1160
|
+
}
|
|
1161
|
+
/** The paths a call would change, so the sandbox can be applied in one place. */
|
|
1162
|
+
export function writeTargets(tool, parsed) {
|
|
1163
|
+
const args = (parsed ?? {});
|
|
1164
|
+
const one = (value) => (typeof value === 'string' && value ? [value] : []);
|
|
1165
|
+
switch (tool) {
|
|
1166
|
+
case 'write_file':
|
|
1167
|
+
case 'apply_diff':
|
|
1168
|
+
case 'search_replace':
|
|
1169
|
+
case 'delete_file':
|
|
1170
|
+
return one(args['path']);
|
|
1171
|
+
case 'move_file':
|
|
1172
|
+
return [...one(args['source']), ...one(args['destination'])];
|
|
1173
|
+
default:
|
|
1174
|
+
// execute_command is not listed: a command declares no paths, and pretending to know what
|
|
1175
|
+
// it will touch by reading the string is exactly the false comfort this module avoids.
|
|
1176
|
+
return [];
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Whether a tool result reports a failure.
|
|
1181
|
+
*
|
|
1182
|
+
* One definition, used by the engine and by everything that draws a result, because they had
|
|
1183
|
+
* drifted: a sandbox denial was recorded as a failure by the engine and drawn with a green tick
|
|
1184
|
+
* by the CI writer, which printed one unconditionally. The outcome a user most needs to see —
|
|
1185
|
+
* "that write did not happen" — looked exactly like success.
|
|
1186
|
+
*/
|
|
1187
|
+
export function isToolFailure(result) {
|
|
1188
|
+
return result.startsWith('ERROR:')
|
|
1189
|
+
|| result.startsWith('POLICY BLOCKED:')
|
|
1190
|
+
|| result.startsWith('DENIED:');
|
|
1191
|
+
}
|
|
1192
|
+
export async function dispatchTool(name, argsJson, cwd, requireApproval, onOutput, sandbox = { mode: 'off', writableRoots: [] }) {
|
|
1103
1193
|
let parsed;
|
|
1104
1194
|
try {
|
|
1105
1195
|
parsed = JSON.parse(normalizeToolArgs(name, argsJson));
|
|
@@ -1107,6 +1197,15 @@ export async function dispatchTool(name, argsJson, cwd, requireApproval, onOutpu
|
|
|
1107
1197
|
catch {
|
|
1108
1198
|
return `ERROR: Malformed JSON arguments for tool "${name}": ${argsJson.slice(0, 200)}`;
|
|
1109
1199
|
}
|
|
1200
|
+
const missing = missingRequiredArgs(name, parsed);
|
|
1201
|
+
if (missing)
|
|
1202
|
+
return missing;
|
|
1203
|
+
// Checked here so no writing tool can be added later and quietly escape it.
|
|
1204
|
+
for (const target of writeTargets(name, parsed)) {
|
|
1205
|
+
const verdict = checkWrite(sandbox, path.resolve(cwd, target));
|
|
1206
|
+
if (!verdict.allowed)
|
|
1207
|
+
return `DENIED: ${verdict.reason}`;
|
|
1208
|
+
}
|
|
1110
1209
|
switch (name) {
|
|
1111
1210
|
case 'read_files':
|
|
1112
1211
|
return readFiles(parsed, cwd);
|
|
@@ -1115,7 +1214,7 @@ export async function dispatchTool(name, argsJson, cwd, requireApproval, onOutpu
|
|
|
1115
1214
|
case 'apply_diff':
|
|
1116
1215
|
return applyDiff(parsed, cwd, requireApproval);
|
|
1117
1216
|
case 'execute_command':
|
|
1118
|
-
return executeCommand(parsed, cwd, requireApproval, onOutput);
|
|
1217
|
+
return executeCommand(parsed, cwd, requireApproval, onOutput, sandbox);
|
|
1119
1218
|
case 'list_dir':
|
|
1120
1219
|
return listDir(parsed, cwd);
|
|
1121
1220
|
case 'glob_search':
|