Skip to content

feat(sandbox): write connect logs to retask.log with rotation, show the path in the TUI - #18

Merged
hoaitan merged 4 commits into
mainfrom
feat/connect-log-file
Aug 8, 2026
Merged

feat(sandbox): write connect logs to retask.log with rotation, show the path in the TUI#18
hoaitan merged 4 commits into
mainfrom
feat/connect-log-file

Conversation

@nwebbot

@nwebbot nwebbot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Why

retask sandbox connect logs only to the TUI panel (stderr when headless). Nothing survives the run — a reconnect, a session bootstrap or a prompt-watcher decision from twenty minutes ago leaves no evidence to debug from.

What

Every log line now goes to two places: the TUI panel (stderr when headless) exactly as before, and retask.log in the folder connect was started from — for a sandbox, the session folder, next to the work the log describes.

The Logs divider shows the active path, elided from the left as the terminal narrows:

─ Logs (/Users/bot/retask-sessions/nweb/session-18dcdfe8/retask.log) ───────────────

Rotation follows the Unix convention: the live file keeps its name, older generations shift down through retask.log.1, retask.log.2, … up to --log-backups, and the oldest is discarded. Restarting connect appends rather than truncating.

Both behaviours are on by default; each can be turned off on its own.

Flag Default Environment
--log-file retask.log RETASK_SANDBOX_LOG_FILE
--no-log-file false RETASK_SANDBOX_NO_LOG_FILE=1
--log-max-size 10MB (0 = no rotation) RETASK_SANDBOX_LOG_MAX_SIZE
--log-backups 5 (0 = keep none) RETASK_SANDBOX_LOG_BACKUPS
--no-log-path false RETASK_SANDBOX_NO_LOG_PATH=1

The flag always wins over its environment variable, matching the existing --session-buffer handling. --log-file "" is accepted as a synonym for --no-log-file.

Included fix: the JWT was being logged

Verifying this end-to-end surfaced a pre-existing leak. Both lane URLs carry the session JWT as a query parameter, and a failed WebSocket dial reports the URL it tried — so every reconnect logged the whole token:

msg=disconnected error="... Get \"http://proxy/ws/data-lane?sandbox_id=...&token=eyJhbGciOiJSUzI1NiI...\": connection refused"

Today that only reaches the TUI and stderr. This PR would have written it to a file in the sandbox folder, so the first commit scrubs the token at both dial sites:

msg=disconnected error="... Get \"http://proxy/ws/data-lane?sandbox_id=...&token=REDACTED\": connection refused"

redactErr keeps the original error reachable through Unwrap, so errors.Is / errors.As are unaffected.

Dependency

Requires agentfleet v0.8.0 (#14, merged), which adds LogFileConfig / OpenLogFile (the rotating writer) and the TUI LogPath / ShowLogPath options. Now released — go.mod and go.sum are locked to the published tag, resolved through proxy.golang.org and verified against sum.golang.org.

v0.8.0 also raises its go directive to 1.26.5 to clear GO-2026-5856 (Encrypted Client Hello privacy leak in crypto/tls), so this branch bumps to go 1.26.5 as well — a main module must declare at least what its dependencies require.

Testing

go build ./..., go vet ./... and go test ./... all pass locally against the pinned agentfleet.

New unit coverage: flag defaults, every override, environment fallbacks and flag-beats-environment precedence, invalid sizes and counts, the tee reaching both the buffer and the file with identical content, the disabled path creating no file at all, rotation producing retask.log.1/.2 and capping at --log-backups, plus token redaction across both lane URL shapes with the error chain preserved.

Verified end-to-end by running the built binary against an unreachable proxy in a scratch folder:

  • headlessretask.log, retask.log.1, retask.log.2 written, capped at --log-backups 2, contents identical to stderr, zero JWT occurrences on disk.
  • TUI (real pty, 120×40) — divider rendered as ─ Logs (…/scratchpad/e2etui/retask.log) ─ with token=REDACTED in the panel below it.

🤖 Generated with Claude Code

nwebbot and others added 3 commits August 6, 2026 23:53
Both lane URLs carry the session JWT as a query parameter, and a failed
WebSocket dial reports the URL it tried — so every reconnect logged the
whole token. Today that only reaches the TUI panel and stderr, but it is
about to reach a file on disk, and a long-lived token sitting in a log is
not something to fix afterwards.

Scrub the token value at both dial sites. redactErr keeps the original
error reachable through Unwrap, so errors.Is and errors.As are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…the TUI

`retask sandbox connect` logged only to the TUI panel, or to stderr when
headless. Nothing survived the run, so a reconnect, a session bootstrap or
a prompt-watcher decision from twenty minutes ago left no evidence.

Tee the slog handler: every line still reaches the TUI (stderr when
headless) and is now also appended to retask.log in the folder the command
was started from — for a sandbox, the session folder, next to the work the
log describes. The TUI Logs divider shows the active path:

  ─ Logs (…/scratchpad/e2etui/retask.log) ──────────────────────────

Rotation is Unix-style and handled by agentfleet: the live file keeps its
name and older generations shift down through retask.log.1, retask.log.2,
... up to --log-backups before being discarded.

Both behaviours are on by default and can be turned off individually:

  --log-file      log file path, relative to the current folder (retask.log)
  --no-log-file   log to the TUI/stderr only
  --log-max-size  rotation threshold (10MB); 0 disables rotation
  --log-backups   generations kept (5); 0 keeps none
  --no-log-path   hide the path from the TUI Logs divider

Each flag has a RETASK_SANDBOX_* environment counterpart, following the
existing connect flags; the flag always wins.

Requires agentfleet v0.8.0 for LogFileConfig/OpenLogFile and the TUI
LogPath/ShowLogPath options.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
agentfleet v0.8.0 raised its go directive to 1.26.5 to clear GO-2026-5856
(Encrypted Client Hello privacy leak in crypto/tls), so the main module has
to declare at least the same. govulncheck is clean on this tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nwebbot

nwebbot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up: agentfleet's pipeline flagged GO-2026-5856 (Encrypted Client Hello privacy leak in crypto/tls, fixed in go1.26.5). CI installs the toolchain from the go directive, so hoaitan/agentfleet#14 bumps it to 1.26.5 — same fix as db264ab, which bumped to 1.26.4 for GO-2026-5039. All five agentfleet checks pass now, govulncheck included.

That raises agentfleet v0.8.0's minimum, so this branch bumps go 1.26.4go 1.26.5 too (a main module must declare at least what its dependencies require). go.sum was regenerated against the updated tree. go build, go vet, go test ./... and govulncheck ./... are all clean here.

The pinned checksum was computed from the PR branch before the tag
existed. v0.8.0 carries four dependabot CI commits on top of it, so the
module hash moved; the go.mod hash is unchanged. Re-resolved through
proxy.golang.org and verified against sum.golang.org.

No Go source differs between the branch tip and the tag — only
.github/workflows — so the build and tests are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nwebbot

nwebbot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

agentfleet v0.8.0 is released — this branch is now locked to the published module and is unblocked.

The pinned checksum did move, as flagged: v0.8.0 carries four dependabot CI commits on top of the merged branch, so the module hash changed (lGKB68j6…Xj6C1Kkz…). The go.mod hash is unchanged, and no Go source differs between the branch tip and the tag — only .github/workflows — so nothing in the build or tests is affected. Re-resolved through proxy.golang.org and verified against sum.golang.org with no local proxy or GOPRIVATE overrides.

Re-verified end-to-end against the published module on go1.26.5:

  • go build ./..., go vet ./..., go test -count=1 ./..., go mod verify, govulncheck ./... — all clean
  • headless run against an unreachable proxy → retask.log, retask.log.1, retask.log.2, capped at --log-backups 2
  • real TUI in a 120×40 pty → ─ Logs (…/scratchpad/e2e-tui-final/retask.log) ─, with token=REDACTED and zero JWT occurrences on disk

Ready to merge.

@hoaitan
hoaitan merged commit 355dc36 into main Aug 8, 2026
@hoaitan
hoaitan deleted the feat/connect-log-file branch August 8, 2026 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants