Repository navigation
Releases
Releases · dickwu/sub-bot
v0.10.3
Fixed
- macOS Keychain items asked for authorization after every
brew upgrade:sub-bot keys setcreated items through Security.framework (SecItemAdd) from the sub-bot binary itself. The Homebrew binary is ad-hoc/linker-signed, so the item ACL trusted only that binary'scdhash(and the partition list wascdhash:<hash>); every upgrade changed the cdhash, so the new binary triggered an authorization dialog on first read — or, under launchd with nobody to click it, timed out (no answer from macOS Keychain within 30s …) and the service sat in a restart loop. Trusting a stable path (/opt/homebrew/bin/sub-bot) does not help because both checks are cdhash-based. Keychain items are now read, written and deleted through Apple's/usr/bin/security(same approach asgh/ go-keyring): items created bysecurity add-generic-passwordcarry the ACLidentifier "com.apple.security" and anchor applewith partitionapple-tool:, andsecurity find-generic-password -wreads them without any prompt regardless of which sub-bot build spawns it.keys setdeletes an existing item first (an in-place update would keep the legacy ACL), then re-adds and verifies by reading back. The secret never appears in a command line: the add goes throughsecurity -ion stdin as-X <hex>(visible inpsonly assecurity -i); service/account names used for writing are restricted to[A-Za-z0-9._/@:-]. Exit codes of the tool are mapped to the same errors as before (44→ not found,36→ interaction not allowed,51/128→ denied); the tool's stdout never enters errors or logs.keys liststill uses a Security.framework attribute search (no data read, no prompt). One-time migration needed for items saved by sub-bot <= 0.10.2 — seekeys reauth.
Added
sub-bot keys reauth(macOS): migrates Keychain items created by older sub-bot versions (ACL tied to an old binary) to the/usr/bin/security-owned form, so later upgrades need nothing. Targets: managed items under servicesub-bot(or--service) plusapiKeyKeychainentries of--config, filtered by--provider/--account, deduplicated. Each item is read once (a legacy item shows one dialog — click "Always Allow" / enter the login password; the usualkeychainTimeoutSecstimeout and waiting hint apply), deleted, re-added and read back. Reportsmigrated/not found/FAILED: <reason>per item; non-zero exit if any failed.--from-envtakes the key fromOPENAI_API_KEY/ANTHROPIC_API_KEY(named accounts: theirapiKeyEnv) instead of reading the old item — zero dialogs.--dry-runlists the targets without reading or changing anything;--jsonoutputs an array likekeys list. On Linux it prints that Keychain is macOS-only and exits 0. Output and logs never contain the key.- New
KeychainError::Tool(keychain tool failed (service=…, account=…): <reason>) for/usr/bin/securitynot being runnable, a name that cannot be written safely, or an add that reported success but does not read back.
Changed
- Keychain error and hint texts now point to
sub-bot keys reauth(one-time migration) instead of re-runningkeys setafter every upgrade; thekeychain error <code>text may now carry the exit status of/usr/bin/security(= OSStatus & 0xff) for read/write/delete, and the OSStatus for attribute searches.
Tests
strip_thinking_keeps_text_and_tool_calls_and_drops_emptied_messagesno longer fails intermittently (expected messages are cloned from the inputs instead of being rebuilt with a fresh timestamp).
Docs
- README: Keychain section rewritten — since 0.10.3 upgrades need no action; one-time
keys reauth(orkeys reauth --from-env/keys set) after upgrading from <= 0.10.2, thenlaunchctl kickstart -k gui/$(id -u)/com.dickwu.sub-bot; the trade-off (any process running as the same user can read the item withsecurity find-generic-password -wwithout a prompt, comparable to a 0600apiKeyFile) and how to opt out of it.
v0.10.2
Fixed
- One HTTP request reported as two issues: an unauthenticated request (401) wrote the same issue id twice to
issues.log— firstcount: 1, a few ms latercount: 2— and a dedup summary was delivered at the end of the window. Theauthmiddleware's WARNunauthorized request …and the per-request access log WARNrequest finished status=401both becamelog_warnissues fromsub_bot::httpwith the same dedup key, so the access log was counted as a repeat of the auth failure (any other 4xx in the same window was folded in too). The access log no longer acts as an issue source on its own: 4xxrequest finishedlines are markedissue = "none"(client errors; the caller already gets the reason, and the cases that matter — 401, 409 session rehydration failure, 429session_limit— have their own issue), and 5xx lines are markedissue = "fallback": they are reported only when nothing more specific was reported for that request (e.g.session rehydration failed,session purge failed,list models failed, a typedrun_timeouton 504). One 401 now yields exactly one issue withcount: 1and no summary. - MCP tool call timeout reported twice: the
MCP tool call timed outWARN became alog_warnissue in addition to the session'stool_timeoutissue for the same failure; it is now a typed (diagnostic-only) log line. - Issue redaction masked plain words: the
Bearer <value>pattern matched ordinary text, turningunauthorized request (missing or unknown bearer token)into… missing or unknown [redacted].Bearervalues are now masked only when they look like a credential (>= 8 chars containing a digit or symbol such as-._~+/=, or >= 20 chars), so phrases likebearer token,api key,token expired,Authorization header missingstay readable. Still masked:Authorization: Bearer …(alsoBasic/Tokenschemes),Bearer sk-…, JWTs,sk-…, GitHub tokens (ghp_/gho_/ghu_/ghs_/ghr_/github_pat_; arbitrarygh*_identifiers no longer),api_key|api key|token|secret|password|authorization = <value>assignments including JSON ("api_key":"…"), and configured secret values.
Docs
- README (Keychain / LaunchAgent): what to do after
brew upgrade(re-authorize withkeys checkin a GUI terminal, thenlaunchctl kickstart -k), why skipping it leaves the service in a restart loop, and a samplecom.dickwu.sub-botLaunchAgent plist.
v0.10.1
Fixed
- Context compaction broke Anthropic extended-thinking sessions: after a compaction every following Anthropic call failed with HTTP 400
Invalid signature in thinking block … bound to a different conversation, because the kept assistant messages still carried signedthinkingblocks whose signature is bound to the (now rewritten) preceding conversation. The session stayed broken, including after a restart. Kept messages created before a compaction now have theirthinking/redacted_thinkingblocks removed from the context sent to the model — for live auto compaction, manual compaction (HTTP / RPC / CLI) and when the context is rebuilt from the JSONL on resume. Text, tool calls and tool_use / tool_result pairing are unchanged; an assistant message left with no content is dropped (it has no tool calls). Messages produced after a compaction keep their thinking until a later compaction keeps them. The JSONL still stores the original messages, so sessions already broken by 0.10.0 heal when rehydrated with 0.10.1.
v0.10.0
Added
- Built-in
grep/findtools backed by ripgrep's library crates (grep-searcher,grep-regex,grep-matcher,ignore,globset) embedded in the binary — norginstall needed. ripgrep-like semantics: smart-case, fixed strings, multiline, context lines, binary-file skipping, max-count, glob / type filters,.gitignoreaware. Included in thedefault,readonlyandno-shellpresets. - Session persistence & resume: sessions are saved under
<dataDir>/sessionsby default (sessionDir,persistSessions,--no-persist-sessions). Resume after restart viarun --resume,rpc --resume,ask --resume, or simply by using the session id over HTTP.GET /v1/sessions?limit=,DELETE /v1/sessions/{id}?purge=true. - Context compaction: automatic (
compaction.thresholdPct,keepRecentTokens,model,maxSummaryTokens, per-profilecontextWindow) and manual (POST /v1/sessions/{id}/compact, RPCcompact);compactionevents andcompaction_failedissues. - Tool approval: per-profile
toolApprovalpolicy (allow/ask/deny, globs, timeout +onTimeout).approval_required/approval_resolvedevents,GET/POST /v1/sessions/{id}/approvals[/{callId}], RPCapprove,approval_requiredwebhook event, interactive TTY prompt plus--auto-approve/--auto-denyforask/run. - MCP client (official
rmcpSDK): stdio and streamable HTTP servers via global / per-profilemcpServers; tools exposed asmcp__<server>__<tool>; crash detection with restart backoff;tools/list_changedrefresh;GET /v1/mcp,sub-bot mcp list; MCP tools go throughtoolApproval. - Licensed under MIT OR Apache-2.0 (
LICENSE-MIT,LICENSE-APACHE);THIRD-PARTY-NOTICES.mdcarries ripgrep's MIT/Unlicense attribution. Release archives now include these files and this changelog.
Fixed
session_runtracing span no longer recordsreasoning_efforttwice (the requested value isrequested_reasoning_effort).- Session listing sort key now equals the returned
updatedAtfor disk-only sessions.
v0.9.3
v0.9.3: typed WARN logs for keychain_waiting / account_unavailable / …
v0.9.2
v0.9.2: Keychain reads time out (keychainTimeoutSecs, warn + keychain…
v0.9.1
v0.9.1: profile-pinned named accounts no longer require the provider'…
v0.9.0
v0.8.1
v0.8.0
v0.8.0: top-3 reasoning efforts (incl. max, extensible levels), dynam…