Skip to content

Commit e37ec59

Browse files
committed
fix(levelcode-ai): #107 review — the shell prompt no longer says a stopped command changes nothing
The bug: the browser prompt told the model "A stopped command changes nothing." That holds for a command stopped while it runs (its writes were only staged), but not after it has ended: a Stop, or the agent's commandTimeout, while its file changes are in the review settles the run at once (the shell's killRun), and what the review had already applied stays in Keep/Undo (shellCommit checks for the Stop between changes). The model got "[stopped by user]" / "[timed out]" behind the shell's note "stopped while the review of its file changes was going on ... it may still apply some of them", and a prompt that contradicted it. host.js said the same thing in its SpawnOptions/ChildLike JSDoc. The fix: the prompt says a command stopped while it runs changes nothing, and one stopped while its file changes were being reviewed may have applied some of them (then in the Keep/Undo review), and that its result says so. The JSDoc of `commit`'s control and of `kill` in host.js now describe the same thing (comments only). The desktop prompt never had the sentence and is unchanged. Test: shellSeam.test.js "BROWSER, shell registered: caps.shell is live, run_command is offered as what it is" no longer pins the old sentence; it asserts it is gone and the new one is there. It fails on the unfixed prompt and passes now. agentRunCommand.test.js is untouched.
1 parent 1a47179 commit e37ec59

3 files changed

Lines changed: 13 additions & 7 deletions

File tree

‎extensions/levelcode-ai/agent.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ function shellNote(info) {
8181
'- Prefer the file tools for reading, searching, creating and editing: they are cheaper and reviewed as edits. Use run_command for what a shell does best: counting, sorting, de-duplicating, jq on JSON, bulk rename/move/delete, diffing two files, find with several conditions.',
8282
'- You see at most 8000 characters of output (the start and the end), so keep commands narrow (head, grep -c, wc -l). A command is stopped at the user\'s time limit, after about '
8383
+ (lim.commandCount || 100000) + ' commands or loop iterations, or when it reads a file over ' + (mib(lim.fileReadBytes) || 4) + ' MiB; exit 124 means time, 126 means a limit of this shell. '
84-
+ 'Change the command; do not retry it unchanged. A stopped command changes nothing. head/tail size suffixes (K, M) are ignored: use plain numbers.',
84+
+ 'Change the command; do not retry it unchanged. A command stopped while it runs changes nothing; one stopped while its file changes were being reviewed may have applied some of them (they are then in the Keep/Undo review), and its result says so. head/tail size suffixes (K, M) are ignored: use plain numbers.',
8585
'- Regular expressions are RE2: no back-references and no lookahead (grep -P is not there; sed has no \\U \\L). Dates and times are UTC unless the command sets TZ. /tmp is empty at the start of every call and is not kept. '
8686
+ 'The whole script is parsed before any of it runs, so a syntax error anywhere runs nothing. grep -r does not enter a folder named .git; there is one file time (find -atime/-ctime read the modification time).',
8787
'- A command\'s file changes are applied only if it runs to its end, and then appear in the user\'s Keep/Undo review like your own edits. Commands change plain UTF-8 text files (up to 1 MiB, up to 300 files per command, line endings kept). '

‎extensions/levelcode-ai/host.js‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,18 +201,21 @@ const SHELL_API_VERSION = 1;
201201
* the whole set and reviews it, or refuses it whole; it returns what the shell still has to do itself (make and
202202
* remove directories), lines to append to the command's output, and how many changes it could NOT apply (the
203203
* shell then does not report success). Without it the shell writes the changes straight to the workspace (the
204-
* user's own terminal). `control.cancelled()` turns true when the run was stopped while the set was being
205-
* checked: the caller then applies nothing (once the first write has begun nothing interrupts it).
204+
* user's own terminal). `control.cancelled()` turns true when the run is stopped (or the shell stops waiting for
205+
* the review): while the set is being checked the caller then applies nothing; once it is applying, what is already
206+
* in the review stays and nothing more is started (a change in flight is not interrupted).
206207
*
207208
* @typedef {object} ChildLike The part of a Node ChildProcess that agent.js's runCommand uses.
208209
* @property {number} [pid] leave undefined: agent.js then kills with child.kill(signal)
209210
* @property {{ on: (ev: 'data', cb: (chunk: string|Uint8Array) => void) => any }} stdout strings (or Buffers)
210211
* @property {{ on: (ev: 'data', cb: (chunk: string|Uint8Array) => void) => any }} stderr
211212
* @property {(ev: 'close'|'error', cb: Function) => any} on 'close'(code) once, after every 'data'
212213
* @property {(signal?: string) => boolean} kill
213-
* 'SIGTERM' (Stop) and 'SIGKILL' (the timeout, or a Stop that was not heard within 1.5 s): the command is stopped
214-
* and NONE of its file changes are applied — they were only ever staged — and the result says so. 'close' is
215-
* emitted once. agent.js reports 130 for a stop and 124 for a timeout whatever the code of 'close'.
214+
* 'SIGTERM' (Stop) and 'SIGKILL' (the timeout, or a Stop that was not heard within 1.5 s). A command that is still
215+
* running is stopped and NONE of its file changes are applied — they were only ever staged. One that has ended and
216+
* whose changes are being reviewed (`commit`) is settled at once, and what the review had applied stays in it. The
217+
* result says which. 'close' is emitted once. agent.js reports 130 for a stop and 124 for a timeout whatever the
218+
* code of 'close'.
216219
*/
217220

218221
/**

‎extensions/levelcode-ai/test/shellSeam.test.js‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,10 @@ function ctxFor(over) {
154154
assert.match(t.system, /run_command runs ONE command line in LevelCode Shell/);
155155
assert.match(t.system, /Programs that exist: cat ls grep\. Shell builtins work too\. Everything else answers "command not found" \(exit 127\) — node, npm, git …/);
156156
assert.match(t.system, /There is no network\. Nothing runs in the background/);
157-
assert.match(t.system, /A stopped command changes nothing/);
157+
// A Stop, or the time limit, while the command's changes are being reviewed settles the run at once, and what the
158+
// review had applied stays in Keep/Undo (the shell's note says so): the prompt must not promise that nothing changed.
159+
assert.ok(!/A stopped command changes nothing/.test(t.system), 'the prompt promises what a Stop during the review does not keep');
160+
assert.match(t.system, /A command stopped while it runs changes nothing; one stopped while its file changes were being reviewed may have applied some of them \(they are then in the Keep\/Undo review\), and its result says so\./);
158161
assert.match(t.system, /NONE of that command's changes are applied/);
159162
assert.ok(!/dev servers|tail -f|There is no terminal|Run the extension unit tests/.test(t.system));
160163
});

0 commit comments

Comments
 (0)