Conversation
|
I have read and agree to the terms of the CLA.md in this repository, and I agree to grant the copyright and patent licenses described therein for my contributions to this project. — Emasoft |
|
Thank you for your contribution. Please allow for some time to go over it and evaluate it. |
|
@Emasoft Thank you again for the PR. I went over it and noticed some things. Please take a look at them. Also note that I strongly suggest splitting the OTP permission mode into its own PR and for now removing the install-dev.sh as these are unrelated. Specifically for the OTP path I think it might require some proper testing and benchmarking to ensure it works as expected. If you want a symlink-based dev install, we should look at it separately e.g. as a makefile target. Further one thing I'd split is the --continue logic. I do get the reasoning and it seems valuable but this seems to be imprecise. Continue to me means "do what i was last doing" while your behavior is more "resume the latest session in the directory". For that specifically I'd instead suggest using a new flag e.g. --continue-project. Ideally if possible, --continue-project should identify the repo root so it also works from inside a subdirectory. For the remaining changes, these are the things I did notice:
These additional changes would improve the feature, but are separate from the confirmed ones above:
|
|
I agree on almost everything (except maybe the retry limits, since it happened to me often that during the night a service was down for 1 hour and then resumed, so recovering from that is important for continuity. But i will do as you say anyway). I'm working on all the issues you reported, and also on the improvements. I will slowly add the updates to the PR. |
I do see your reasoning. I think there are arguments for both sides but I do see where you are coming from. As you noted this never happened to me so I might be biased, if this happens to you often then I understand it more. I think it might be best to let this be user configurable in the end but this is rather minor and should be rather easy to adjust from what I have seen so far. Thank you again for the quick updates! Feel free to ping me if you want me to review again otherwise I will do so when I see newer commits. |
… help Focused rework of the streaming/retry PR after owner review (5716775820 on PR mlhher#127): the OTP permission mode and install-dev.sh were split out into their own independent PR, so this PR now contains only the streaming/retry work and related improvements. Contents (net diff vs main): - Two-tier stream retry with independent budgets (infrastructure: -max-stream-retries / LATE_MAX_STREAM_RETRIES, default 10, 0 disables; bad-body HTTP 400: 3), 500ms doubling backoff, 30s cap, full jitter; server Retry-After honored as a floor (capped 5 min, cancelable); permanent network causes (x509 trust/hostname/chain, TLS record header, unsupported scheme, HTTP-on-HTTPS) fail fast. - Typed errors: client.StatusError (provider type/code, bounded sanitized diagnostics) and client.StreamInterruptedError for mid-body transport failures (HTTP/2 RST_STREAM, GOAWAY, resets, truncation); the session relay drains the terminal error so a select race can never commit a truncated attempt as a clean turn. - Terminal-400 rollback persisted (pop-to-empty removes the stale history file, meta sidecar kept); per-request history sanitizer (dangling tool_calls closed, empty-ID calls stripped). - Dedicated RecoveryEvent + failure-class-matched recovery toasts; retry status "retry N/M after Xs backoff" (no live-countdown implication); stale retry state cleared on error/stop/close. - --continue restored to global-latest; new --continue-project resolves the repo root (works from subdirs) and matches project dirs by os.SameFile identity; session lookup loads the exact enumerated sidecar, skips vanished metadata, guards nil meta. - Grouped -h with true defaults even when flags precede -h; status-bar agent type; docs (en + zh-CN) updated; plugin hook tests de-flaked (deadline-based pid-file poll, 60s watchdog). Gates: go build ./..., go vet ./..., go test ./... -race -count=1 all green; flagship mid-stream retry tests stress-verified 150/150, 50/50 and 30x under -race.
f5b8050 to
39a3093
Compare
|
The changes look good. Seems like nearly everything has been fixed great work! One remaining thing I did notice was that --max-stream-retries=0 still retries HTTP 400 error responses, despite advertising that it disables retries entirely. Please take a look at that. Please also take a look at the golangci-lint results (these are very minor). In the meantime I will do some more local testing to ensure everything works well and if nothing new comes up this will be merged. Thank you again! |
The flag sets only the infrastructure tier's budget; the bad-body tier resolved its own default budget of 3, so HTTP 400 responses were still retried 3 times despite the documented "0 or negative disables retries entirely" contract (owner review, comment 5736731620). A global disable (flag/env 0 or negative) now silences both tiers at the budget-resolution site; an explicit bad-body budget still applies whenever the global budget is positive. Covered by TestRunLoopGlobalDisableAlsoSilencesBadBodyTier (0 and -1: exactly one POST, zero retry events, no backoff) while the positive-budget tests pin that nothing else changed. Docs updated (en + zh-CN).
Two ineffectual assignments in retry tests (ineffassign); test semantics unchanged.
|
I noticed one more thing, other than that this is merge-ready and will likely be merged once resolved. The new agent-type label in the status bar seems redundant and doesn’t quite conform to the existing color scheme. Subagents are already identified by the breadcrumb on the right. If you want to show the focused agent consistently, including the orchestrator, I suggest adapting that existing label and placing it before the token counter using the current muted grey, since this information doesn’t need to demand attention. Otherwise, feel free to remove the new label and I’ll revisit it in a later pass. |
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.
Description of Changes
Four features plus a hardening round addressing the owner review — all ten review items (see the item map below):
Resilient LLM streaming & recovery
-max-stream-retries/LATE_MAX_STREAM_RETRIES(default 10;0disables); HTTP 400 body rejections get a small dedicated tier (3) because strict gateways often fail transiently while reading the request body. Backoff: 500 ms doubling, 30 s cap, full jitter. Permanent network causes — TLS certificate/trust failures, unsupported schemes, HTTP-on-HTTPS — fail fast instead of burning the budget. ServerRetry-After(delta-seconds or HTTP-date) is honored as a backoff floor, capped at 5 minutes, fully cancelable.errors.As-able errors:client.StatusError(providertype/codepreserved; error bodies read once, bounded at 8 KB, with a sanitized 1024-byte text fallback when no JSON message exists) andclient.StreamInterruptedErrorfor mid-body transport failures (HTTP/2 RST_STREAM, GOAWAY, resets, truncated bodies).Error: stream error: stream interrupted: stream error: stream ID 1; INTERNAL_ERROR; received from peerkilled sessions with zero retries, because body-read errors never surface as*url.Errorand the stdlib's bundled HTTP/2 error type is unexported. The session relay now drains the terminal error before returning — a select race could previously commit a truncated attempt as a clean turn..meta.jsonsidecar is kept so--continuestill finds the session), with an accurate, actionable message. Per request, history is sanitized (dangling assistanttool_callsget synthesized results; empty-ID tool calls are stripped) — the saved history is untouched.RecoveryEventfires the moment a retried attempt actually produces a response, with failure-class-matched toasts ("connection restored" / "request accepted after retry"); stale retry state is cleared on error/stop/close.Owner follow-up (5736731620):
--max-stream-retries=0/negative now silences both tiers — the bad-body tier previously kept its own budget and still retried HTTP 400s (fixed in 2605313); golangci-lint (v2 config) reports 0 findings (a3414b4).--continuerestored; new--continue-project--continueresumes the latest session globally ("do what I was last doing").--continue-projectresumes the latest session of the current project: it resolves the repo root (works from subdirectories; falls back to CWD outside a repo) and matches recorded project directories by identity (os.SameFileafter a lexical fast path), so symlinked roots resolve. The two flags are mutually exclusive. Session lookup loads the exact enumerated sidecar, skips vanished/unreadable metadata, and guards nil meta (no panic, no prefix-collision fallback).Help & status bar
late -hgroups flags by scope and always shows true defaults (even when flags are mutated before-h); the status bar shows the focused agent's type with a stable color per category.Owner review (5716775820) — item map
ECONNRESETretries — done + pinned classifier row. 3. Permanent URL/TLS causes fail fast. 4. DedicatedRecoveryEventwith tested sequences (recovery→final response; exhaustion→new request). 5. Nil-meta deref fixed; exact-file sidecar loads. 6.os.SameFiledirectory identity. 7. Default budget 10, documented in both quickstarts. 8.Retry-Afterhonored (floor, 5-min cap, cancelable). 9. Non-countdown status wording. 10. Bounded, sanitized error diagnostics.Per the owner review, the OTP permission mode (
-force-revaluate-dangerous-commands, OTP registry,otp_codebash parameter) andinstall-dev.shwere split into their own independent draft PR #129 (pending the testing/benchmarking pass requested in the review); a symlink dev-install can return later as a makefile target. This PR is fully independent of it — no shared commits.Behavior changes
-force-revaluate-dangerous-commandsandinstall-dev.shmoved to the independent draft PR feat: OTP gate for dangerous commands + install-dev.sh (split from #127) #129;permission-modekeeps its other two values and the two remaining permission flags are still mutually exclusive.--continueno longer filters by directory; use the new--continue-projectfor project-scoped resume.0disables); a terminal HTTP 400 rolls back the last user message and persists the rollback.Test plan
go test -race ./...— all packages pass (11 stream-retry httptest SSE integration scenarios; orchestrator event-sequence tests; session lookup/sanitizer/rollback tables; plugin tests de-flaked)go vet ./...clean;gofmtclean on all changed files; stress-verified: flagship mid-stream retry tests 150/150, 50/50 and 30× under-racegolangci-lint(v2 config) — 0 findings on the whole tree./test/late-podman-test.sh— Linux-only (the wrapper hard-requires a Linux host peruname -s, and the test usessha256sum), so it cannot run locally on macOS; validated by CI on Linux runnersKnown limitations / follow-ups
data: {"error": ...},event: error) are not parsed yet — a provider erroring mid-stream can still end as a clean-looking stream (pre-existing; follow-up).tool_callsserialize anindexfield, andreasoning_contentrides on historical messages — tolerated by common endpoints; strict ones now fail gracefully via the bad-body tier + rollback.image_unsupportedrollback remains memory-only (self-healing; pre-existing).--continueswallows corrupt-history parse errors into an empty session (atomic writes make this unlikely; follow-up: warn vs not-exist).--force-http1escape hatch (less needed now that h2 mid-stream failures retry).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])