koneck 2.25.37 → 2.25.39
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/config-store.d.ts +1 -0
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +5 -1
- package/dist/config-store.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +34 -2
- package/dist/engine.js.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +158 -16
- package/dist/ink-chat.js.map +1 -1
- package/dist/permissions.d.ts +8 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +69 -1
- package/dist/permissions.js.map +1 -1
- package/dist/plan.d.ts +48 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +108 -0
- package/dist/plan.js.map +1 -0
- package/dist/tools.d.ts +9 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +79 -6
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/permissions.d.ts
CHANGED
|
@@ -43,6 +43,14 @@ export interface AllowRule {
|
|
|
43
43
|
export declare function commandPrefixSuggestion(command: string): string;
|
|
44
44
|
/** Whether a granted rule covers this call. */
|
|
45
45
|
export declare function ruleMatches(rule: AllowRule, tool: string, argsJson: string): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Whether a command only reads.
|
|
48
|
+
*
|
|
49
|
+
* A chained command is never trusted, however it starts: `ls | rm -rf .` begins with `ls`. The
|
|
50
|
+
* same reasoning that keeps a granted prefix from covering a chain applies here, and for the same
|
|
51
|
+
* reason — the check looks at the front of the line and the danger is at the back.
|
|
52
|
+
*/
|
|
53
|
+
export declare function isReadOnlyCommand(command: string): boolean;
|
|
46
54
|
/**
|
|
47
55
|
* What should happen to one tool call: run it, ask, or refuse.
|
|
48
56
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAEhD,wFAAwF;AACxF,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7D,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnC,4FAA4F;IAC5F,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,KAAK,EAAE,SAAS,QAAQ,EAkCpC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAE/C;AAcD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAM/C;AAED,2FAA2F;AAC3F,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAWD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED,+CAA+C;AAC/C,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUpF;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,SAAS,SAAS,EAAE,GAC1B,QAAQ,
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAEhD,wFAAwF;AACxF,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEjD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7D,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnC,4FAA4F;IAC5F,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,KAAK,EAAE,SAAS,QAAQ,EAkCpC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAE/C;AAcD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAM/C;AAED,2FAA2F;AAC3F,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAWD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED,+CAA+C;AAC/C,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUpF;AAiCD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsB1D;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,SAAS,SAAS,EAAE,GAC1B,QAAQ,CAaV;AAMD;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAUjE;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvF;AAED,yDAAyD;AACzD,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,SAAS,EAAE,CAIjF;AAED,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAEpD"}
|
package/dist/permissions.js
CHANGED
|
@@ -40,7 +40,7 @@ export function modeSpec(name) {
|
|
|
40
40
|
/** Tools that only observe. Never gated: an agent reads constantly, and prompting would be noise. */
|
|
41
41
|
const READ_TOOLS = new Set([
|
|
42
42
|
'read_files', 'list_dir', 'glob_search', 'grep_search', 'fetch_url', 'web_search',
|
|
43
|
-
'remember', 'git_status', 'git_diff', 'git_log',
|
|
43
|
+
'remember', 'plan', 'git_status', 'git_diff', 'git_log',
|
|
44
44
|
]);
|
|
45
45
|
/** Tools that change the working tree. */
|
|
46
46
|
const WRITE_TOOLS = new Set([
|
|
@@ -103,6 +103,63 @@ export function ruleMatches(rule, tool, argsJson) {
|
|
|
103
103
|
return false;
|
|
104
104
|
return text.startsWith(rule.prefix);
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Commands that only look, and are run without asking in any mode that would otherwise ask.
|
|
108
|
+
*
|
|
109
|
+
* Without this, careful mode asks about `ls`. A prompt for something that cannot change anything
|
|
110
|
+
* is pure friction, and friction is what teaches people to approve without reading — at which
|
|
111
|
+
* point the prompt that matters gets waved through too. Codex draws the same line and calls the
|
|
112
|
+
* policy "untrusted": trusted commands run, everything else escalates.
|
|
113
|
+
*
|
|
114
|
+
* Membership is decided by what a command can do, not by how harmless it sounds. Anything that
|
|
115
|
+
* writes, installs, connects out, or evaluates code is absent on purpose.
|
|
116
|
+
*/
|
|
117
|
+
const READ_ONLY_COMMANDS = new Set([
|
|
118
|
+
'ls', 'dir', 'pwd', 'cd', 'echo', 'cat', 'head', 'tail', 'wc', 'file', 'stat',
|
|
119
|
+
'find', 'grep', 'rg', 'ag', 'tree', 'which', 'type', 'whoami', 'id', 'hostname',
|
|
120
|
+
'date', 'uptime', 'df', 'du', 'ps', 'env', 'printenv', 'uname', 'basename', 'dirname',
|
|
121
|
+
'sort', 'uniq', 'cut', 'diff', 'md5sum', 'sha256sum', 'true', 'false',
|
|
122
|
+
]);
|
|
123
|
+
/** Subcommands of tools that are safe only in part: `git log` yes, `git push` emphatically not. */
|
|
124
|
+
const READ_ONLY_SUBCOMMANDS = {
|
|
125
|
+
git: new Set(['status', 'log', 'diff', 'show', 'branch', 'remote', 'blame', 'describe',
|
|
126
|
+
'rev-parse', 'ls-files', 'shortlog', 'tag', 'config']),
|
|
127
|
+
npm: new Set(['ls', 'list', 'view', 'outdated', 'why', 'root', 'prefix', 'config']),
|
|
128
|
+
yarn: new Set(['list', 'why', 'info']),
|
|
129
|
+
pnpm: new Set(['list', 'why', 'root']),
|
|
130
|
+
cargo: new Set(['tree', 'metadata']),
|
|
131
|
+
docker: new Set(['ps', 'images', 'version', 'info']),
|
|
132
|
+
kubectl: new Set(['get', 'describe', 'version']),
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Whether a command only reads.
|
|
136
|
+
*
|
|
137
|
+
* A chained command is never trusted, however it starts: `ls | rm -rf .` begins with `ls`. The
|
|
138
|
+
* same reasoning that keeps a granted prefix from covering a chain applies here, and for the same
|
|
139
|
+
* reason — the check looks at the front of the line and the danger is at the back.
|
|
140
|
+
*/
|
|
141
|
+
export function isReadOnlyCommand(command) {
|
|
142
|
+
const text = command.trim();
|
|
143
|
+
if (!text || SHELL_CHAINING.test(text))
|
|
144
|
+
return false;
|
|
145
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
146
|
+
const head = (words[0] ?? '').toLowerCase();
|
|
147
|
+
// Redirection writes files even when the command itself would not.
|
|
148
|
+
if (words.some(w => w === '>' || w === '>>'))
|
|
149
|
+
return false;
|
|
150
|
+
const subcommands = READ_ONLY_SUBCOMMANDS[head];
|
|
151
|
+
if (subcommands) {
|
|
152
|
+
const sub = (words[1] ?? '').toLowerCase();
|
|
153
|
+
return subcommands.has(sub);
|
|
154
|
+
}
|
|
155
|
+
if (!READ_ONLY_COMMANDS.has(head))
|
|
156
|
+
return false;
|
|
157
|
+
// `find … -delete` and `find … -exec` are not reads at all.
|
|
158
|
+
if (head === 'find' && words.some(w => w === '-delete' || w === '-exec' || w === '-execdir')) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
106
163
|
/**
|
|
107
164
|
* What should happen to one tool call: run it, ask, or refuse.
|
|
108
165
|
*
|
|
@@ -113,6 +170,17 @@ export function decide(mode, tool, argsJson, rules) {
|
|
|
113
170
|
const base = modeSpec(mode).policy[toolRisk(tool)];
|
|
114
171
|
if (base !== 'ask')
|
|
115
172
|
return base;
|
|
173
|
+
// A command that cannot change anything is not worth a question. Note this is reached only
|
|
174
|
+
// when the mode says "ask" — plan mode's "deny" is decided above and stays denied.
|
|
175
|
+
if (tool === 'execute_command') {
|
|
176
|
+
let command = '';
|
|
177
|
+
try {
|
|
178
|
+
command = String(JSON.parse(argsJson).command ?? '');
|
|
179
|
+
}
|
|
180
|
+
catch { /* ask */ }
|
|
181
|
+
if (command && isReadOnlyCommand(command))
|
|
182
|
+
return 'allow';
|
|
183
|
+
}
|
|
116
184
|
return rules.some(r => ruleMatches(r, tool, argsJson)) ? 'allow' : 'ask';
|
|
117
185
|
}
|
|
118
186
|
function rulesFile(cwd) {
|
package/dist/permissions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAiCxB,MAAM,CAAC,MAAM,KAAK,GAAwB;IACxC;QACE,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,0DAA0D;QACjE,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE;QACxD,MAAM,EAAE,EAAE;KACX;IACD;QACE,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,kDAAkD;QACzD,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE;QACtD,MAAM,EAAE,EAAE;KACX;IACD;QACE,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,mDAAmD;QAC1D,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;QACpD,MAAM,EAAE,EAAE;KACX;IACD;QACE,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,kDAAkD;QACzD,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;QACtD,uFAAuF;QACvF,oDAAoD;QACpD,MAAM,EACJ,qFAAqF;YACrF,uFAAuF;YACvF,+DAA+D;KAClE;CACF,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC;AACvD,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY;IACjF,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS;
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAiCxB,MAAM,CAAC,MAAM,KAAK,GAAwB;IACxC;QACE,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,0DAA0D;QACjE,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE;QACxD,MAAM,EAAE,EAAE;KACX;IACD;QACE,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,kDAAkD;QACzD,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE;QACtD,MAAM,EAAE,EAAE;KACX;IACD;QACE,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,mDAAmD;QAC1D,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;QACpD,MAAM,EAAE,EAAE;KACX;IACD;QACE,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,kDAAkD;QACzD,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;QACtD,uFAAuF;QACvF,oDAAoD;QACpD,MAAM,EACJ,qFAAqF;YACrF,uFAAuF;YACvF,+DAA+D;KAClE;CACF,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC;AACvD,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY;IACjF,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS;CACxD,CAAC,CAAC;AAEH,0CAA0C;AAC1C,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa;IACxE,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW;CAC/D,CAAC,CAAC;AAEH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IACxC,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IAC1C,qFAAqF;IACrF,kFAAkF;IAClF,OAAO,MAAM,CAAC;AAChB,CAAC;AASD;;;;;;GAMG;AACH,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,IAAI,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAClD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACpD,OAAO,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,WAAW,CAAC,IAAe,EAAE,IAAY,EAAE,QAAgB;IACzE,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,CAAC;QAAC,OAAO,GAAG,MAAM,CAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAA2B,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;IAChH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC5B,6FAA6F;IAC7F,uFAAuF;IACvF,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAGD;;;;;;;;;;GAUG;AACH,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM;IAC7E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU;IAC/E,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS;IACrF,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO;CACtE,CAAC,CAAC;AAEH,mGAAmG;AACnG,MAAM,qBAAqB,GAAkD;IAC3E,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU;QACxE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpE,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnF,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACtC,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACtC,KAAK,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACpC,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IACpD,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;CACjD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,IAAI,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAErD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAE5C,mEAAmE;IACnE,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAE3D,MAAM,WAAW,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3C,OAAO,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAEhD,4DAA4D;IAC5D,IAAI,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,UAAU,CAAC,EAAE,CAAC;QAC7F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CACpB,IAAY,EACZ,IAAY,EACZ,QAAgB,EAChB,KAA2B;IAE3B,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACnD,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAEhC,2FAA2F;IAC3F,mFAAmF;IACnF,IAAI,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC/B,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC;YAAC,OAAO,GAAG,MAAM,CAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAA2B,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC;QAC5G,IAAI,OAAO,IAAI,iBAAiB,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;IAC5D,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AAC3E,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;AACvD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACzC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAkB,EAAE,CACzC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,OAAQ,CAAe,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IACtF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,KAA2B;IACtE,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC1E,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,OAAO,CAAC,KAA2B,EAAE,IAAe;IAClE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAC7B,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,YAAY,CAAC,IAAe;IAC1C,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,2BAA2B,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,OAAO,CAAC;AAC3F,CAAC"}
|
package/dist/plan.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The list of steps a run is working through, kept where the model can see it every turn.
|
|
3
|
+
*
|
|
4
|
+
* The failure this exists for: asked to build a Stokvel platform, a run listed directories, read
|
|
5
|
+
* a file, narrated what it would do next, listed the same directories again, and eventually
|
|
6
|
+
* halted having built nothing. Nothing was holding the shape of the job. Each turn the model saw
|
|
7
|
+
* a transcript and had to reconstruct what it was doing from it, and by the time the transcript
|
|
8
|
+
* was long it reconstructed something different every time.
|
|
9
|
+
*
|
|
10
|
+
* A plan makes that state explicit rather than inferred. It is written by the model, restated to
|
|
11
|
+
* it on every turn, and shown to the person watching — who can then see the agent forget, which
|
|
12
|
+
* is the difference between "it is stuck" and "it says step 4 of 7 and is on step 4".
|
|
13
|
+
*/
|
|
14
|
+
export type StepState = 'todo' | 'doing' | 'done' | 'blocked';
|
|
15
|
+
export interface PlanStep {
|
|
16
|
+
text: string;
|
|
17
|
+
state: StepState;
|
|
18
|
+
/** Why it is blocked, when it is. */
|
|
19
|
+
note?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface Plan {
|
|
22
|
+
steps: PlanStep[];
|
|
23
|
+
/** When the plan was last written, for showing staleness in a long session. */
|
|
24
|
+
updatedAt: number;
|
|
25
|
+
}
|
|
26
|
+
export declare const EMPTY_PLAN: Plan;
|
|
27
|
+
/** At most this many steps. A plan nobody can hold in their head is a transcript with bullets. */
|
|
28
|
+
export declare const MAX_STEPS = 20;
|
|
29
|
+
/**
|
|
30
|
+
* A plan from whatever shape the model sent.
|
|
31
|
+
*
|
|
32
|
+
* Deliberately forgiving. A model that writes `{"steps": ["a", "b"]}` has expressed a perfectly
|
|
33
|
+
* good plan and should not be argued with about the schema; rejecting it teaches it to stop using
|
|
34
|
+
* the tool, and the tool only helps if it is used.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parsePlan(input: unknown, now: number): Plan | string;
|
|
37
|
+
/**
|
|
38
|
+
* The plan as the model is reminded of it, at the top of every turn.
|
|
39
|
+
*
|
|
40
|
+
* Written as instruction rather than decoration: the point is that the next action follows from
|
|
41
|
+
* the plan, and that a step which is finished gets marked so rather than silently redone.
|
|
42
|
+
*/
|
|
43
|
+
export declare function planReminder(plan: Plan): string;
|
|
44
|
+
/** One line for a status bar: how far along, and what is being done now. */
|
|
45
|
+
export declare function planSummary(plan: Plan): string;
|
|
46
|
+
/** What the model is told after it writes a plan, so the next step is unambiguous. */
|
|
47
|
+
export declare function planAck(plan: Plan): string;
|
|
48
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +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,CA0BpE;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
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The list of steps a run is working through, kept where the model can see it every turn.
|
|
3
|
+
*
|
|
4
|
+
* The failure this exists for: asked to build a Stokvel platform, a run listed directories, read
|
|
5
|
+
* a file, narrated what it would do next, listed the same directories again, and eventually
|
|
6
|
+
* halted having built nothing. Nothing was holding the shape of the job. Each turn the model saw
|
|
7
|
+
* a transcript and had to reconstruct what it was doing from it, and by the time the transcript
|
|
8
|
+
* was long it reconstructed something different every time.
|
|
9
|
+
*
|
|
10
|
+
* A plan makes that state explicit rather than inferred. It is written by the model, restated to
|
|
11
|
+
* it on every turn, and shown to the person watching — who can then see the agent forget, which
|
|
12
|
+
* is the difference between "it is stuck" and "it says step 4 of 7 and is on step 4".
|
|
13
|
+
*/
|
|
14
|
+
export const EMPTY_PLAN = { steps: [], updatedAt: 0 };
|
|
15
|
+
/** At most this many steps. A plan nobody can hold in their head is a transcript with bullets. */
|
|
16
|
+
export const MAX_STEPS = 20;
|
|
17
|
+
const STATES = ['todo', 'doing', 'done', 'blocked'];
|
|
18
|
+
function coerceState(value) {
|
|
19
|
+
const text = String(value ?? '').toLowerCase();
|
|
20
|
+
if (STATES.includes(text))
|
|
21
|
+
return text;
|
|
22
|
+
// in_progress, in-progress, active, wip — all mean the same thing to whoever wrote them.
|
|
23
|
+
if (/progress|active|current|wip|working/.test(text))
|
|
24
|
+
return 'doing';
|
|
25
|
+
if (/complete|finish|done/.test(text))
|
|
26
|
+
return 'done';
|
|
27
|
+
if (/block|stuck|wait/.test(text))
|
|
28
|
+
return 'blocked';
|
|
29
|
+
return 'todo';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A plan from whatever shape the model sent.
|
|
33
|
+
*
|
|
34
|
+
* Deliberately forgiving. A model that writes `{"steps": ["a", "b"]}` has expressed a perfectly
|
|
35
|
+
* good plan and should not be argued with about the schema; rejecting it teaches it to stop using
|
|
36
|
+
* the tool, and the tool only helps if it is used.
|
|
37
|
+
*/
|
|
38
|
+
export function parsePlan(input, now) {
|
|
39
|
+
const raw = input?.steps ?? input;
|
|
40
|
+
if (!Array.isArray(raw)) {
|
|
41
|
+
return 'ERROR: plan needs a "steps" array, e.g. ' +
|
|
42
|
+
'{"steps":[{"text":"design the ledger schema","state":"doing"}]}.';
|
|
43
|
+
}
|
|
44
|
+
if (raw.length === 0)
|
|
45
|
+
return 'ERROR: a plan with no steps says nothing. Give at least one step.';
|
|
46
|
+
const steps = [];
|
|
47
|
+
for (const entry of raw.slice(0, MAX_STEPS)) {
|
|
48
|
+
if (typeof entry === 'string') {
|
|
49
|
+
if (entry.trim())
|
|
50
|
+
steps.push({ text: entry.trim(), state: 'todo' });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const obj = entry;
|
|
54
|
+
const text = String(obj.text ?? obj.step ?? obj.title ?? '').trim();
|
|
55
|
+
if (!text)
|
|
56
|
+
continue;
|
|
57
|
+
const note = obj.note === undefined ? undefined : String(obj.note).trim();
|
|
58
|
+
steps.push({
|
|
59
|
+
text: text.slice(0, 200),
|
|
60
|
+
state: coerceState(obj.state ?? obj.status),
|
|
61
|
+
...(note ? { note: note.slice(0, 200) } : {}),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (steps.length === 0)
|
|
65
|
+
return 'ERROR: none of those steps had any text in them.';
|
|
66
|
+
return { steps, updatedAt: now };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The plan as the model is reminded of it, at the top of every turn.
|
|
70
|
+
*
|
|
71
|
+
* Written as instruction rather than decoration: the point is that the next action follows from
|
|
72
|
+
* the plan, and that a step which is finished gets marked so rather than silently redone.
|
|
73
|
+
*/
|
|
74
|
+
export function planReminder(plan) {
|
|
75
|
+
if (plan.steps.length === 0)
|
|
76
|
+
return '';
|
|
77
|
+
const done = plan.steps.filter(s => s.state === 'done').length;
|
|
78
|
+
const current = plan.steps.find(s => s.state === 'doing') ?? plan.steps.find(s => s.state === 'todo');
|
|
79
|
+
const lines = plan.steps.map((s, i) => {
|
|
80
|
+
const mark = s.state === 'done' ? 'x' : s.state === 'doing' ? '>' : s.state === 'blocked' ? '!' : ' ';
|
|
81
|
+
return ` [${mark}] ${i + 1}. ${s.text}${s.note ? ` — ${s.note}` : ''}`;
|
|
82
|
+
});
|
|
83
|
+
return `\n## Your plan (${done}/${plan.steps.length} done)\n` +
|
|
84
|
+
lines.join('\n') +
|
|
85
|
+
(current ? `\n\nCurrent step: ${current.text}` : '\n\nEvery step is done or blocked.') +
|
|
86
|
+
`\nWork the current step. Call \`plan\` again to mark it done and move on, or to change the ` +
|
|
87
|
+
`plan if what you have learned makes it wrong. Do not re-do a step already marked done.\n`;
|
|
88
|
+
}
|
|
89
|
+
/** One line for a status bar: how far along, and what is being done now. */
|
|
90
|
+
export function planSummary(plan) {
|
|
91
|
+
if (plan.steps.length === 0)
|
|
92
|
+
return '';
|
|
93
|
+
const done = plan.steps.filter(s => s.state === 'done').length;
|
|
94
|
+
const current = plan.steps.find(s => s.state === 'doing');
|
|
95
|
+
const blocked = plan.steps.filter(s => s.state === 'blocked').length;
|
|
96
|
+
const suffix = blocked > 0 ? ` ${blocked} blocked` : '';
|
|
97
|
+
return `${done}/${plan.steps.length}${current ? ` ${current.text.slice(0, 48)}` : ''}${suffix}`;
|
|
98
|
+
}
|
|
99
|
+
/** What the model is told after it writes a plan, so the next step is unambiguous. */
|
|
100
|
+
export function planAck(plan) {
|
|
101
|
+
const done = plan.steps.filter(s => s.state === 'done').length;
|
|
102
|
+
const current = plan.steps.find(s => s.state === 'doing') ?? plan.steps.find(s => s.state === 'todo');
|
|
103
|
+
return current
|
|
104
|
+
? `Plan recorded: ${plan.steps.length} steps, ${done} done. Now do this step: ${current.text}`
|
|
105
|
+
: `Plan recorded: all ${plan.steps.length} steps are done or blocked. ` +
|
|
106
|
+
`If the task is finished, say so and stop.`;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=plan.js.map
|
package/dist/plan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { ToolDefinition, ReadFilesArgs, WriteFileArgs, ApplyDiffArgs, ExecuteCommandArgs, ListDirArgs, GlobSearchArgs, GrepSearchArgs, MoveFileArgs, DeleteFileArgs, SearchReplaceArgs, FetchUrlArgs, RememberArgs, WebSearchArgs } from './types.js';
|
|
2
2
|
export declare const TOOL_DEFINITIONS: ToolDefinition[];
|
|
3
|
+
/**
|
|
4
|
+
* Lines returned for one file unless a window is asked for.
|
|
5
|
+
*
|
|
6
|
+
* There was no cap at all: a five thousand line file went whole into the context window and
|
|
7
|
+
* crowded out everything the run had learned before it. What follows is a model that has read a
|
|
8
|
+
* great deal and remembers almost none of it — which is most of what looks like an agent losing
|
|
9
|
+
* the thread.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MAX_READ_LINES = 800;
|
|
3
12
|
export declare function readFiles(args: ReadFilesArgs, cwd: string): Promise<string>;
|
|
4
13
|
export declare function writeFile(args: WriteFileArgs, cwd: string, requireApproval: boolean): Promise<string>;
|
|
5
14
|
export declare function applyDiff(args: ApplyDiffArgs, cwd: string, requireApproval: boolean): Promise<string>;
|
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,
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.js
CHANGED
|
@@ -10,7 +10,9 @@ import { GIT_TOOL_DEFINITIONS, dispatchGitTool } from './git-tools.js';
|
|
|
10
10
|
export const TOOL_DEFINITIONS = [
|
|
11
11
|
{
|
|
12
12
|
name: 'read_files',
|
|
13
|
-
description: 'Read one or more files concurrently. Returns content with line numbers.
|
|
13
|
+
description: 'Read one or more files concurrently. Returns content with line numbers. ' +
|
|
14
|
+
'Long files come back as the first 800 lines with the rest offered by offset — page only ' +
|
|
15
|
+
'if you must; grep_search is usually the better way to find something in a large file.',
|
|
14
16
|
parameters: {
|
|
15
17
|
type: 'object',
|
|
16
18
|
properties: {
|
|
@@ -19,6 +21,14 @@ export const TOOL_DEFINITIONS = [
|
|
|
19
21
|
items: { type: 'string' },
|
|
20
22
|
description: 'File paths to read (absolute or relative to cwd)',
|
|
21
23
|
},
|
|
24
|
+
offset: {
|
|
25
|
+
type: 'number',
|
|
26
|
+
description: 'First line to return, zero-based. For paging through a long file.',
|
|
27
|
+
},
|
|
28
|
+
limit: {
|
|
29
|
+
type: 'number',
|
|
30
|
+
description: 'How many lines to return from offset. Defaults to 800.',
|
|
31
|
+
},
|
|
22
32
|
},
|
|
23
33
|
required: ['paths'],
|
|
24
34
|
},
|
|
@@ -198,6 +208,36 @@ export const TOOL_DEFINITIONS = [
|
|
|
198
208
|
required: ['url'],
|
|
199
209
|
},
|
|
200
210
|
},
|
|
211
|
+
{
|
|
212
|
+
name: 'plan',
|
|
213
|
+
description: 'Record or update the list of steps you are working through. Call it once at the start of ' +
|
|
214
|
+
'any task with more than one step, and again whenever a step is finished, blocked, or the ' +
|
|
215
|
+
'plan turns out to be wrong. The plan is restated to you every turn, so it is how you keep ' +
|
|
216
|
+
'track of a long job instead of re-deriving it from the transcript.',
|
|
217
|
+
parameters: {
|
|
218
|
+
type: 'object',
|
|
219
|
+
properties: {
|
|
220
|
+
steps: {
|
|
221
|
+
type: 'array',
|
|
222
|
+
description: 'The full list of steps, in order. Send the whole list every time, not a delta.',
|
|
223
|
+
items: {
|
|
224
|
+
type: 'object',
|
|
225
|
+
properties: {
|
|
226
|
+
text: { type: 'string', description: 'What the step is' },
|
|
227
|
+
state: {
|
|
228
|
+
type: 'string',
|
|
229
|
+
enum: ['todo', 'doing', 'done', 'blocked'],
|
|
230
|
+
description: 'Exactly one step should be "doing" at a time',
|
|
231
|
+
},
|
|
232
|
+
note: { type: 'string', description: 'Why it is blocked, if it is' },
|
|
233
|
+
},
|
|
234
|
+
required: ['text'],
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
required: ['steps'],
|
|
239
|
+
},
|
|
240
|
+
},
|
|
201
241
|
{
|
|
202
242
|
name: 'remember',
|
|
203
243
|
description: 'Persist a durable fact about this project to .koneck/MEMORY.md, where every future session ' +
|
|
@@ -266,20 +306,53 @@ export const TOOL_DEFINITIONS = [
|
|
|
266
306
|
...GIT_TOOL_DEFINITIONS,
|
|
267
307
|
];
|
|
268
308
|
// ─── Tool handlers ────────────────────────────────────────────────────────────
|
|
309
|
+
/**
|
|
310
|
+
* Lines returned for one file unless a window is asked for.
|
|
311
|
+
*
|
|
312
|
+
* There was no cap at all: a five thousand line file went whole into the context window and
|
|
313
|
+
* crowded out everything the run had learned before it. What follows is a model that has read a
|
|
314
|
+
* great deal and remembers almost none of it — which is most of what looks like an agent losing
|
|
315
|
+
* the thread.
|
|
316
|
+
*/
|
|
317
|
+
export const MAX_READ_LINES = 800;
|
|
318
|
+
/** One file's contents, as a window with its position said out loud. */
|
|
319
|
+
function renderWindow(filePath, content, offset, limit) {
|
|
320
|
+
const lines = content.split('\n');
|
|
321
|
+
const total = lines.length;
|
|
322
|
+
const from = Math.max(0, Math.min(offset, Math.max(0, total - 1)));
|
|
323
|
+
const to = Math.min(total, from + limit);
|
|
324
|
+
const body = lines
|
|
325
|
+
.slice(from, to)
|
|
326
|
+
.map((line, i) => `${String(from + i + 1).padStart(5)} │ ${line}`)
|
|
327
|
+
.join('\n');
|
|
328
|
+
const shownAll = from === 0 && to >= total;
|
|
329
|
+
const header = shownAll
|
|
330
|
+
? `=== ${filePath} (${total} lines) ===`
|
|
331
|
+
: `=== ${filePath} (lines ${from + 1}-${to} of ${total}) ===`;
|
|
332
|
+
// Truncation is stated, never silent. A model that believes it has seen a whole file decides
|
|
333
|
+
// things about the part it never read.
|
|
334
|
+
const footer = shownAll
|
|
335
|
+
? ''
|
|
336
|
+
: `\n… ${total - to} lines below. Read them with ` +
|
|
337
|
+
`{"paths":["${filePath}"],"offset":${to},"limit":${limit}} — or use grep_search to find ` +
|
|
338
|
+
`what you need instead of paging through it.`;
|
|
339
|
+
return header + '\n' + body + footer;
|
|
340
|
+
}
|
|
269
341
|
export async function readFiles(args, cwd) {
|
|
270
342
|
// Said plainly rather than thrown. A missing field used to surface as "Cannot read properties
|
|
271
343
|
// of undefined (reading 'map')", which tells the model nothing it can act on.
|
|
272
344
|
if (!Array.isArray(args.paths) || args.paths.length === 0) {
|
|
273
345
|
return 'ERROR: read_files needs a "paths" array, e.g. {"paths": ["src/App.jsx"]}.';
|
|
274
346
|
}
|
|
347
|
+
const offset = Math.max(0, Math.floor(Number(args.offset ?? 0)) || 0);
|
|
348
|
+
const requested = Math.floor(Number(args.limit ?? MAX_READ_LINES));
|
|
349
|
+
const limit = Number.isFinite(requested) && requested > 0
|
|
350
|
+
? Math.min(requested, MAX_READ_LINES * 4) // an explicit ask can go further, not forever
|
|
351
|
+
: MAX_READ_LINES;
|
|
275
352
|
const results = await Promise.allSettled(args.paths.map(async (filePath) => {
|
|
276
353
|
const abs = path.resolve(cwd, filePath);
|
|
277
354
|
const content = await fs.readFile(abs, 'utf-8');
|
|
278
|
-
|
|
279
|
-
.split('\n')
|
|
280
|
-
.map((line, i) => `${String(i + 1).padStart(5)} │ ${line}`)
|
|
281
|
-
.join('\n');
|
|
282
|
-
return `=== ${filePath} (${content.split('\n').length} lines) ===\n${numbered}`;
|
|
355
|
+
return renderWindow(filePath, content, offset, limit);
|
|
283
356
|
}));
|
|
284
357
|
return results
|
|
285
358
|
.map((r, i) => r.status === 'fulfilled'
|