v0.4.9: a bash call that ends, and an abort that aborts
What's Changed
- #49 — bash: kill the process group, complete on exit; bound an aborted turn in core by @matAtWork
- A patch release: two filed defects with one shared cause, a third found in the same function, two new bounds, and a core backstop. Harness packages now move in lockstep — see Versioning.
Full Changelog: v0.4.8...v0.4.9
The defect
bash -c forks each pipeline stage as its own process, and plugins/bash got both halves of that wrong.
- #47 — abort and
timeoutsignalled the direct child only, with nodetachedand therefore no process group to signal.find / … | head -5lost its shell to the SIGTERM and leftfindtraversing the filesystem, reparented to init. - #48 — completion hung off
'close', which needs the process to have exited and every stdio stream to have reached EOF. The orphan inherited stdout, so the event stream never terminated.
Individually each is a leak. Together the turn is unrecoverable: the session sits at "working" for ever while every abort reports success, because the abort worked and the tool call is simply unreachable. Measured before fixing — 'exit' at 14ms, 'close' at 5021ms behind a five-second orphan.
A third bug in the same function, unfiled: a signal-killed script gives code === null, which the success arm read as exit code 0, so a timeout kill and an abort both reported a clean run. docker-bash carried it too and is fixed with it.
What changed
- Own process group (POSIX; Windows keeps the direct-child kill), every stop signalling the negative pid, SIGTERM → SIGKILL after a grace.
'exit'is authoritative for completion.'close'still wins when it arrives, since it means the output is complete; otherwise an idle drain window ends the call and says so instderrrather than reading a pipe nothing waits for.- A kill is reported as a kill, naming the reason.
- Two bounds for an unattended host, which has no operator to restart it — both defaults rather than limits.
timeoutdefaults to ten minutes, and combined stdout+stderr to 1000000 bytes via a newmaxOutputBytesparam; either can be raised per call. A bound the caller cannot lift is a ceiling on what the tool can be used for rather than a safety net: the only party who knows whether 400KB is a verbose build or ayesloop is the one that wrote the command. The numbers are generous because the failure directions are not symmetric — overflowing output is output whose process was killed, so too low kills legitimate work, while runaway protection barely notices, since anything genuinely runaway trips either number in well under a second.docker-bashaccepts the same param, overriding itsbash_configsetting for that one command. - core — an aborted turn no longer depends on the tool's cooperation. The runner iterated executors with a bare
for await, so any tool that never returns held the turn open for ever. Once aborted, the read is bounded (30s): stop reading, warn, record the call as interrupted, keeping everytool_usepaired with atool_result. Armed only on abort, so a long tool on a healthy turn is never cut short.bashgot there through inherited file descriptors; a generator awaiting something that never settles does too.
function-tools also ships an unrelated pending change: the lambda guidance now states that wrapping a single tool call you are not reducing is the pathological case, not merely the expensive one.
Versioning
core, plugin-api, cli and web-bundle are now a changesets fixed group and move together; plugins version independently. Both halves of the harness assumed lockstep already — the boot banner reads any difference between the CLI and the resolved core/plugin-api versions as two physical copies of a host singleton, and about_matbot reports the app's own version — so a core-only release would have printed a false "run a clean reinstall" warning on every boot while the version it reported stayed put.
Published: core, plugin-api, cli, web-bundle at 0.4.9; function-tools at 0.4.9; tool-bash and tool-docker-bash at 0.4.9 (0.4.8 was cut first, then superseded within this release by the maxOutputBytes change above).