Conversation
…hher#127) Split out of PR mlhher#127 per owner review (5716775820): the OTP permission mode (`-force-revaluate-dangerous-commands`, OTP registry, otp_code bash parameter, parse-fallback hard-block exemption) and install-dev.sh live here so PR mlhher#127 can stay focused on streaming/retry. Requires the dedicated testing/benchmarking pass requested in the review before it is ready.
6 tasks
Owner
|
Thank you a lot for pulling this out! This will make it significantly easier to test this properly and to ensure this does not degrade the LLMs ability. If it does not this can be merged in without issues. I will first try to get #127 ready then will try this one out. Feel free to also test this, if possible not just cloud but if you can also specifically Qwen3.6-35B-A3B and Qwen3.8-27B and (generally) report your findings. |
…ream/official)
Rewrite install-dev.sh as a smart multi-source installer and add a
make install-dev target.
Five sources, each idempotent (every run converges to its state):
- local-dev build current branch -> SYMLINK to <repo>/bin/late
(every make build updates the command in place)
- pinned build current branch -> fixed COPY (dev tracking stops)
- fork-main build Emasoft/late-cli@main from a codeload tarball -> COPY
- upstream-main build mlhher/late-cli@main from a codeload tarball -> COPY
- official run upstream install.sh (latest stable); upstream owns
placement, --target is not applicable
- check detection report only (read-only)
Autodetection report printed on every run: platform -> GOOS/GOARCH,
current install classification + version, repo branch/dirty state,
remote main SHAs, upstream latest release tag, brew formula state,
target dir selection + PATH membership.
Conflict auto-solving: symlink<->copy transitions archive the displaced
binary as <target>.bak-<timestamp> (never overwritten) with a printed
revert hint; brew-managed installs warn and gate behind --yes in
non-interactive mode; post-install verification checks the installed
shape, version, and shadowing with PATH guidance.
Non-interactive flags (any position): --yes, --dry-run (plan only; the
repo build still runs but target changes and downloads are skipped),
--target DIR. Interactive numbered menu annotates each option with
detected state ([CURRENT], SHAs, release tag); invalid input re-prompts,
q quits; piped stdin takes a single choice. Tarball builds use -trimpath
so reinstalls of the same commit are byte-identical.
make install-dev runs ./install-dev.sh.
Dev boxes and CI need a hands-off installer: --choice N selects the menu entry, implies --yes and never prompts (root is refused, targets are validated, previous binaries are archived). README Development section documents the strictly-dev nature and the headless mode.
…install mode
install-dev.sh rev 3:
- Detection additions (check + pre-install report): package managers
reported ALL found (brew, apt-get, dnf, pacman, zypper, apk, npm —
comma list), podman binary presence + version (`podman --version`),
and late-podman installed state (target dir + ~/.local/bin, classified
as symlink/regular file/brew-managed/our copy).
- late-podman install parity with `make install` (which ships both
`late` and `late-podman` to ~/.local/bin): local-dev installs a
SYMLINK to <repo>/late-podman, pinned installs a COPY of
<repo>/late-podman, fork-main/upstream-main copy it from the
extracted tarball root (skipped with a warning if the tarball lacks
it). Same .bak-<timestamp> archiving on mode transitions, plan lines
in --dry-run, and a printed note that late-podman's runtime requires
a Linux host + podman.
- New `uninstall` subcommand (name-invoked only, NOT in the --choice
menu; --choice + uninstall -> usage error):
./install-dev.sh uninstall [--purge] [--with-deps] [--yes] [--dry-run]
[--target DIR]
- removes the installed `late` (symlink or regular file) in the
target dir; nothing inside the brew prefix is ever deleted —
`brew uninstall late` is printed as advice instead; a symlink into
a different repo is removed while that repo is kept
- removes `late-podman` in the target dir / ~/.local/bin only when it
is our own copy (symlink into this repo, sha256 match with
<repo>/late-podman, or an older launcher recognized by its usage
header); foreign files are left untouched
- removes all late.bak-* / late-podman.bak-* archives in the target dir
- --purge additionally deletes user data (each path printed first,
guarded against non-"late" basenames): the config dir exactly as the
Go code computes it (internal/pathutil.LateConfigDir = os.UserConfigDir()/late —
~/Library/Application Support/late on darwin, honoring XDG_CONFIG_HOME
on Linux; contains config.json, mcp_config.json, plugins/ and the
skills/ dir, which lives inside the config dir) and the data dir
~/.local/share/late (internal/pathutil.LateSessionDir parent).
Interactive runs confirm y/N (default N); headless runs require --yes.
- --with-deps PRINTS the exact package-manager removal commands for
late-relevant deps (podman, go; e.g. `brew uninstall podman`,
`sudo apt remove podman`) after checking PM ownership — never
executes them (they may be shared with other projects); documented
in --help
- idempotent: an already-uninstalled system prints "nothing to
uninstall" and exits 0; --dry-run prints every planned removal
without executing
- README Development installer section: uninstall mode, purge semantics,
deps printed not removed, late-podman parity, podman/Linux runtime note.
Shellcheck note: the fork/upstream tarball build now passes GOOS/GOARCH
to go via env-prefix instead of `export` inside the subshell, so the
script's host GOOS stays unmodified (shellcheck SC2030/SC2031 clean).
Paths: install-dev.sh, README.md.
4 tasks
Emasoft
added a commit
to Emasoft/late-cli
that referenced
this pull request
Sep 21, 2026
Union of PR mlhher#131 (feat/subagent-control @ 2d96f26, main-based) into the local/full stack (mlhher#127 resilient streaming + mlhher#129 OTP gate + mlhher#130 bash timeouts). Both feature sets are kept and both test suites pass. Per-file union decisions: - cmd/late/main.go: adopted incoming's budget resolution stack (24h DefaultSubagentTimeout, config.json subagent_timeout precedence via flag.Visit + ResolveSubagentTimeout, per-spawn timeout override via effectiveSubagentBudget) and the nested-spawn busy tracking (BeginNestedSpawn/EndNestedSpawn + parent heartbeat around child.Execute, SetContext(runCtx), child SetIdlePolicy). Kept local/full's transcript writer + cause classification: the classification now uses the EFFECTIVE budget and gained an idle-kill cause (child IdleKillReason() non-empty) that renders as "idle: killed by the harness idle watchdog (<probe summary>)" ahead of the user-cancel case. Flags: union --subagent-timeout (default 24h from appconfig), --subagent-idle-timeout, --subagent-idle-kill-after plus all local/full flags (--bash-timeout, --continue-project, OTP, ...); SetIdlePolicy applied to root and children. - internal/orchestrator/base.go: union of event hardening (non-blocking progress sends via trySendProgress + droppedEvents counter, blocking terminal/ChildAdded sends) with the activity-aware idle watchdog (lastActivity/inFlightTools/nestedSpawns/oldestToolStartAt, activityMiddleware outermost, two-stage tool-kill then agent-kill, injectable tick, SetIdlePolicy/MarkActivity/IdleKillReason). MarkActivity fires in both Execute and run() stream callbacks; the idle event is emitted non-blocking (select/default). - internal/executor/executor.go: harness-note delivery kept; per-call toolCtx + SetInFlightToolCancel/Clear and the "tool cancelled by the harness idle watchdog" result path added. inFlightKill is captured BEFORE the per-call toolCancel() and gates the harness note, so a watchdog tool-kill (failure-shaped "Command failed with exit code -1" from the shell SIGKILL) never asks the coder to report back; a real shell failure or a per-call timeout still gets the note. - internal/tool/implementations.go: single ShellTool.Execute with per-call timeout resolution (resolveShellTimeout: absent=global, 0/negative=unlimited, invalid=error result), global 10m default wired to --bash-timeout via SetShellTimeout, timeout message uses the effective bound. Kept local/full's harness-note-free error path (no duplicated coder sandwich) and the otp_code parameter; Parameters now declare both otp_code and timeout. - internal/tool/shell_command_unix.go, shell_command_windows.go: identical mechanism on both sides (process-group kill + WaitDelay); incoming's comments adopted. - internal/config/config.go: PermissionMode constants + SubagentTimeout entry coexist; ResolvePermissionMode and ResolveSubagentTimeout both kept. - internal/common/interfaces.go: RetryEvent + RecoveryEvent (local/full) and SubagentIdleEvent + ActivityMarker (incoming) kept together. - internal/tui/update.go: SubagentIdleEvent status-line case added to the hardened update loop. - internal/tool/shell_timeout_test.go: local/full's de-flaked timeout/ grandchild/pgrep tests plus incoming's per-call timeout and resolveShellTimeout tests. - docs/quickstart.md: incoming Subagent Control section; Common Flags row updated to the 24h default. Integration test added: TestExecuteToolCalls_WatchdogToolKillDoesNotAttachNote guards the union of the in-flight hook with the harness note. Gates: go build ./... ok, go vet ./... ok, go test ./... -race -count=1 all packages ok, gofmt clean on touched files, ./install-dev.sh check ok.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of Changes
Split out of PR #127 per the owner review (comment 5716775820), so that PR can stay focused on streaming/retry. This PR is independent of #127: it is based on
mainand shares no commits with it.Contents:
-force-revaluate-dangerous-commands): the first attempt at a dangerous bash command is blocked and the agent receives a random single-use 7-digit OTP (crypto/rand) bound to the exact command string; re-running the byte-identical command withotp_codeapproves it once. Hard refusals (unsafe cwd, search gate, AST blocks) and safe commands behave exactly as before; pending codes clear on conversation reset.cdare never downgraded to "needs confirmation" when the shell parser cannot read the command (fail-closed), in every mode.late, or first writable PATH dir when brew is absent).--choice Nmode for dev boxes — see the README Development section.⚠ Per the review, this mode "might require some proper testing and benchmarking to ensure it works as expected" — hence draft status until that pass is done.
Test plan
go test -race ./...green on this branch;go vet ./...clean;gofmtclean on changed filesgolangci-lint(v2 config) — 0 findings on the whole treemain's pre-sandbox hygiene: on loaded hostsTestRunHook_ProcessGroupKillsChildrenOnCancel(fixed 250 ms spawn budget) and the HOME/XDG-sandbox-dependent tests (TestHandlePluginRemove_*,TestRegisterPluginSkills_PreservesSameNamedSkills) can fail — all fail atmaintoo; the test-hygiene hunks from feat: resilient LLM streaming & recovery, --continue-project, grouped help #127 (HOME/XDG sandboxing + deadline-based de-flake) can be ported here on request.Contributor License Agreement (CLA)
To accept your code, we legally need you to agree to our CLA so we can maintain the project's Business Source License (BSL) and future open-source transitions.
xbetween the brackets like this:[x])