Add Codebuff/Freebuff agent support - #1261
Conversation
Review the codebuff/freebuff parser against the public upstream source (https://github.com/CodebuffAI/codebuff, rev b285b56) and fix gaps: - Handle error variant messages (API failures, rate limits, country blocks) as system messages instead of silently dropping them - Extract creditsUsed/directCreditsUsed from run-state.json and map to CostUSD in usage events (1 credit = /bin/bash.01) - Handle ask-user blocks (agent questions) as system messages - Handle plan blocks as system messages with plan content - Handle image blocks in both user and AI messages - Update session-format-sources.md with source evidence, billing documentation, and model name limitations
roborev: Combined Review (
|
High: - Add freebuff: prefix alias in AgentByPrefix so single-session resync, prefix lookup, and reconciliation work correctly for Freebuff sessions Medium: - Fix tool output double-encoding: use output.Raw directly instead of json.Marshal(output.Raw) which wraps strings in extra quotes - Add UsageCapabilities (NoPerMessageTokenData, AICreditsDenominated) to Codebuff registry entry for correct usage messaging and cost display
roborev: Combined Review (
|
… validation - Freebuff sessions now use agent=AgentCodebuff (not AgentFreebuff) so that lifecycle operations keyed by agent type (reconciliation, deletion, baselines) work correctly. AgentLabel distinguishes UI display. - Rewrote parseCodebuffAIMessage to process blocks sequentially, flushing at block-type transitions to preserve text → tool → text ordering. - Reject non-array JSON roots in chat-messages.json with parse error instead of silently returning empty transcript. - Simplified provider_effects.go by removing Freebuff-specific agent mismatch exception (no longer needed since both use AgentCodebuff). - Removed Freebuff prefix exception from validateProviderSessionID.
roborev: Combined Review (
|
roborev: Combined Review (
|
1. Freebuff identity: Reverted to AgentFreebuff for distinct filtering, with prefix alias in AgentByPrefix for lifecycle operations. 2. Model name accuracy: Left model unknown since actual LLM model is selected server-side and can change mid-session. Credits usage events are emitted independently of model. 3. AI block ordering: Agent output now emitted immediately with tool call, system blocks (mode/plan/ask-user) emitted in source order instead of deferred to end. 4. Cross-midnight timestamps: Track date in parseCodebuffMessages and advance when time-of-day wraps past midnight. 5. PeakContextTokens: Removed incorrect peak context assertion since contextTokenCount from run-state.json is the final per-step value, not the peak (compaction can reduce it).
roborev: Combined Review (
|
roborev: Combined Review (
|
…ss, streaming 1. Freebuff lifecycle: Added alias handling in logicalRootsForAgentWatchRoots, tombstoneMissingWatchSourcesForAgentLocked, and baselineEligibleProviders so Freebuff sessions are properly tombstoned and baselined. 2. Text/reasoning interleaving: Replaced separate thinkingBuf/textBuf with unified textEntry buffer that preserves source order of alternating reasoning and regular text blocks. 3. Hash-aware freshness: Added AgentCodebuff to providerFingerprintHashInCacheKey and providerFingerprintHashRequiredForFreshness so same-stat changes in companion files are caught by hash comparison. 4. Streaming discovery: Converted Codebuff provider to use WithStreamingFileDiscovery with codebuffDiscoverEach that streams directory entries instead of materializing the entire archive.
roborev: Combined Review (
|
roborev: Combined Review (
|
…, replacement 1. Session ID collision: Include project name in session ID to prevent collisions across projects with identical timestamps. 2. Freebuff raw-ID fallback: AgentByPrefix now returns definition with freebuff: IDPrefix so callers can strip the prefix correctly. 3. Freebuff usage capabilities: Added alias handling in AgentNameLacksPerMessageTokenData and AgentNameUsesAICredits so Freebuff is treated as a Codebuff alias for capability lookup. 4. ForceReplaceOnParse: Set ForceReplaceOnParse capability for Codebuff since it reparses the entire mutable transcript on every sync.
roborev: Combined Review (
|
roborev: Combined Review (
|
…el, lifecycle 1. ForceReplace: Created codebuffSourceSet wrapper that overrides Parse to set ForceReplace: true on successful parses, ensuring full message replacement for the mutable transcript. 2. codebuffFindFile: Updated to split project:timestamp in raw ID for the new session ID format, with legacy timestamp-only fallback. 3. Usage events: Added placeholder model name "codebuff" so usage queries filter correctly and cost display works. 4. File-path policies: Made alias-aware for both Codebuff and Freebuff in ListSessionIDsByFilePath and HasTrashedSessionByFilePath.
roborev: Combined Review (
|
roborev: Combined Review (
|
…es, rename 1. Session ID stability: Use projectHint (storage directory name) for session IDs instead of cwd-derived project name. Cwd-derived project is used only for display metadata. 2. Model capability: Marked Model as CapabilityNotApplicable since the actual LLM model is unknown and can change mid-session. Removed fictitious "codebuff" model placeholder from usage events. 3. Baseline rows: Use parsed session's agent type for baseline rows instead of provider agent type, so Freebuff sessions receive correct (freebuff, file_path) baseline entries. 4. Rename probe: Made HasActiveSessionSourceBelow alias-aware for both Codebuff and Freebuff to catch rename events for Freebuff-only directories.
roborev: Combined Review (
|
Modified usageEventEligibility to include events with either a model or a cost (CostUSD). Codebuff/Freebuff usage events have empty model but carry CostUSD, which was being excluded from usage reports.
roborev: Combined Review (
|
… DuckDB - Added parentheses to usageEventSourceEligibility for operator precedence - Updated PostgreSQL usageEventEligibility to include cost-only events - Updated DuckDB usageEventSourceEligibility and activity report - Removed 'or' constraint from usage tests to allow OR in eligibility
roborev: Combined Review (
|
1. Usage timestamp fallback: When no message timestamp is parsable, fall back to session directory timestamp, then source mtime, instead of producing year-0001 timestamps. 2. Source freshness: Added Codebuff-specific SourceMtime that computes composite max mtime across chat-messages.json, run-state.json, and chat-meta.json so companion-file-only changes are caught.
1. Shallow watching: Changed codebuffWatchRoots to enumerate project directories and emit shallow watches on <root>/*/chats/ instead of recursive watches on <root>/. Reduces inotify usage from O(N*M) to O(N) where N=projects and M=sessions. 2. CWD filter: Made skippedSourceAllowsCwdFilter query both AgentCodebuff and AgentFreebuff so CWD filtering works for sources containing only Freebuff sessions.
roborev: Combined Review (
|
parser/codebuff.go sets CountsAuthoritative=true when the on-disk chat-messages.json is empty but chat-meta.json reports a non-zero messageCount, so the engine no longer zeros MessageCount for meta-only sessions. Pinned by TestSyncCodebuffMetaOnlySessionKeepsCounts. Add parser.FindCodebuffFreebuffMatches to walk the configured codebuff/freebuff storage layer for bare-timestamp lookup. The newSessionGetCommand RunE pre-flight uses each match CanonicalID to map a bare timestamp into the canonical ID; one match returns the ID, many returns an explicit ambiguity list, none falls through to the standard resolver. Freebuff resolves through its own roots list even though parser.Registry does not enumerate it.
roborev: Combined Review (
|
parseCodebuffTimestamp uses the "03:04 PM" layout, which requires a two-digit hour. The trailing row in TestParseCodebuffMixedFormatMidnightRollover used "2:00 PM", which the parser rejected and fell through to the zero time.Time, so msgs[4].Timestamp.Day() returned 1 for the expected 17. Pad it to "02:00 PM" so the time-only row actually parses and exercises the RFC3339 anchoring the test was meant to pin.
Add parser.FindCodebuffFreebuffMatches to walk the configured codebuff/freebuff storage layer for bare-timestamp lookup, returning one match per (Agent, project, rawID) location for ambiguity surfacing rather than silently picking one. newSessionGetCommand's RunE now pre-resolves bare on-disk Codebuff/Freebuff timestamps via resolveBareCodebuffID using both codebuff: and freebuff: prefixes against svc.Get per matched project, so Freebuff sessions resolve even when the Freebuff roots list is empty because Freebuff is absent from parser.Registry. parseCodebuffMessages anchors currentDate to each absolute (RFC3339) timestamp's local calendar date so subsequent time-only messages do not drift back to the session directory's original date across midnight. prevHour resets to -1 alongside the anchor.
Pin the (chat-messages, chat-meta, run-state) trio for the stat-only freshness gate. Three sync tests touch only one sibling file (chat-meta, run-state) or both, bump the mtime, and assert the engine reparses exactly one session. The composite test uses a shared mtime bump across the two supporting files so a coarse-resolution host filesystem cannot mask one leg's drift; the meta-only and run-state-only tests isolate each leg's contribution to the composite mtime max.
roborev: Combined Review (
|
The post-merge frontend staleness - the codebuffAICredits summary card in UsageSummaryCards, the generated DbUsageTotals field, the usage_summary_codebuff_ai_credits locale key across five locales - is removed. The CodebuffAICredits column in the SQLite, PostgreSQL, and DuckDB usage pipelines disappears; cost-only reported sessions now ride as microdollars on money.Money, like every other agent, with no per-credit shim. The UsageEvent.Model emitted by the parser now mirrors rs.AgentType (e.g. base2-deepseek, base2-free-minimax-m3) rather than the agent name so the daily ModelBreakdown can bucket by template. Emitting is skipped when rs.AgentType is empty so corrupt sessions don't surface as ghost model rows; sess.Agent still partitions codebuff vs freebuff in the agent breakdown.
docs/configuration.md clarifies that the agent template shown in the session detail header is the classification label used server-side to pick the per-step LLM; the literal LLM is not persisted by the CLI and is not visible in the UI. A new paragraph notes that codebuff/freebuff sessions report cost only, and per-model rates for base2-* templates aren't in the embedded pricing tables so cache savings for these rows resolve to zero by design rather than an aggregator bug. The package ABOUTME comment in internal/parser/codebuff.go mirrors this contract.
roborev: Combined Review (
|
…and bare-ID lookup Two medium-severity issues addressed: 1. Incremental cutoff checks can miss deletion of run-state.json or chat-meta.json. Added session directory mtime as a cutoff signal in discoveredFileEffectiveMtime, consistent with the Kilo Legacy branch. Companion-file deletions now trigger reparse via the directory mtime bump even when surviving files mtimes are old. 2. Bare-ID lookup depends on local configuration even when --server is set. Added resolveBareCodebuffIDRemote to resolve Codebuff-family IDs through FindSessionIDsByPartial with canonical-suffix filtering, skipping mustLoadConfig for remote mode. Extracted resolveCodebuffBareID helper to simplify newSessionGetCommand. Tests added: - TestSyncCodebuffCompanionFileDeletionReparsesSession - 7 unit tests for resolveBareCodebuffIDRemote - Extracted codebuffRemoteLookupLimit constant
roborev: Combined Review (
|
Bare timestamps reach `session get` via three routes: local archives, `--server` daemons, and `--pg` stores. The previous resolver routed both remote routes through `findSessionIDsByPartial` plus a suffix filter, which silently mis-classified host-prefixed candidates and could fold multiple matches into a single local row when an ambiguity error should have fired. Drops the remote bare bridge entirely: a non-canonical ID against `--server` or `--pg` now returns an explicit error pointing at `session list` and the four canonical ID shapes. Local reads add a `--machine` flag (default `local`) that gates the dual-prefix probe by `sessions.Machine`, so remote-synced rows in the local archive no longer masquerade as local matches. `isCanonicalServiceSessionID` also recognises the `freebuff:` prefix (Freebuff is intentionally absent from `parser.Registry`, mirroring `parser.AgentByPrefix`'s special case) so freebuff canonical IDs are not misclassified as bare timestamps and trip the new error path. Removes the now-dead `resolveBareCodebuffIDRemote` and `codebuffRemoteLookupLimit`, registers `--machine` as a local flag on `newSessionGetCommand` to avoid colliding with the independent `--machine` flags on `session list`/`search`/export, and updates `docs/session-api.md` to document the new transport split. Adds regression tests for the four-shape error enum, the empty-filter arm of the machine matcher, and Freebuff canonical-id recognition.
roborev: Combined Review (
|
… shape `resolveCodebuffBareID` previously rejected every non-canonical ID under `--server` / `--pg` with the Codebuff-specific error, including bare UUIDs for Codex, Copilot, Gemini, and other agents that `resolveServiceSessionID` resolves by retrying each registered agent prefix. This is a regression from the transport-scope commit. Add `parser.IsCodebuffTimestamp` — a syntactic predicate that checks whether a string matches the four ISO-8601 shapes the Codebuff parser accepts (`YYYY-MM-DDTHH-MM-SS.fffZ`, `...Z`, `...fff`, `YYYY-MM-DD`). Use it as an early-exit gate in `resolveCodebuffBareID` so that inputs which are not Codebuff/Freebuff timestamps pass through to the generic prefix resolver unchanged. The Codebuff-specific error now fires only when the input is actually a Codebuff timestamp that failed to resolve locally. Also: - Fix `dependriving` typo in doc comment. - Scope `docs/session-api.md` subsection to the Codebuff/Freebuff timestamp shape so users know bare UUIDs for other agents still resolve on remote reads. - Add `TestIsCodebuffTimestamp` (9 subcases) and `_ServerBareUUIDForOtherAgent` / `_PGBareUUIDForOtherAgent` regression-fix tests. - Switch bare-input tests from numeric epoch to real ISO timestamps.
roborev: Combined Review (
|
roborev: Combined Review (
|
…erge damage
The merge of main into agents/codebuff (#888663c4) brought in
Omnigent support alongside the new reconciliation-baseline
pipeline. Three structural casualties plus a Freebuff/Codebuff
keying correctness gap had to be repaired for the package to
compile and behave correctly:
- `internal/parser/types.go:870` lost the Codebuff registry
entry's opening `{` during the merge, leaving its keyed
fields dangling outside the Registry slice literal.
- The Omniagent-derived `if persistentArchive / if ok /
if valid { ... } else if ReconciliationSourceResolver { ... }`
chain in `tombstoneMissingWatchSourcesForAgentLocked` had its
`if !present { ... ContainsSource(... physicalPath) ... }`
block displaced into the else-if branch, where `physicalPath`
is not in scope. Moved the block back into `if valid { ... }`
so it keys off the persistentArchive resolver's `physicalPath`
value as intended.
- The new `authoritativeProviders` map was added alongside a
now-redundant `baselineEligibleProviders` map;
`eligibleReconciliationBaselines` consumes
`authoritativeProviders` but `baselineEligibleProviders` was
never read. Collapsed to one consistently named map
(`authoritativeProviders`) and aliased Freebuff in alongside
Codebuff — both admission sites (the eligibility call and
the per-write `authoritativeProviders[write.agent]` check)
now see Freebuff when Codebuff completes, since Freebuff has
no `parser.Registry` entry and shares the codebuff root.
`baselineProcessedSource` was double-constructing the session
source — first with the parsed Freebuff agent (correct) and
then again with `baselineSourceForJob(job)` which restored the
provider agent, dropping the Freebuff key on disk. Drop the
second call; the inline Freebuff/Codebuff resolution remains
where it belongs.
Lint: collapse `isCanonicalServiceSessionID`'s final
`if cond { return true }; return false` into a direct
`return cond` per staticcheck S1008.
SourceMtime for Codebuff now hashes each of chat-messages.json, run-state.json, and chat-meta.json by (size, mtime) so per-file size or mtime changes always trigger fingerprinting. DuckDB HasTokenData tracks token-bearing rows separately from cost-only rows, matching SQLite and Postgres when a Codebuff row reports only cost.
roborev: Combined Review (
|
- Propagate ListSessionIDsByFilePath errors with per-agent tracking and one-shot retry instead of treating failures as zero matches. - Replace aggregate size/mtime freshness with per-component stat-hash digest via MultiFileStatHasher and provider_freshness side-table. - Fix bare-ID resolver: deduplicate IDs, return error on zero matches with lookup failures instead of false ambiguity.
roborev: Combined Review (
|
Forward SourceSetProvider.ComputeMultiFileStatHash so the engines factory type assertion succeeds, register hashers only when the new MultiFileStatHash capability is declared, and stage per-component digests on the process result with a per-row flushPending gate so CWD-filtered or failed writes cannot mark an absent session as fresh. Hash the physical materialized path but persist under the logical rewritten key so remote imports observe downloaded changes. Expose a test-only stagedProviderStatHashes counter on Engine so the new CWD-filter regression test can prove the staging block actually ran.
roborev: Combined Review (
|
- Forward SourceSet.MultiFileStatHash from the default codebuff factory so the engine hasher type-asserts. - Stage the per-component digest at provider policy; persist after a successful session write and clear on tombstone; canonicalize freebuff to codebuff for the side-table key. - Skip the freshness-gate cold-stamp while cwdFilter is active so filtered sources do not get a stale row.
roborev: Combined Review (
|
Parses Codebuff and Freebuff sessions from
~/.config/manicode/projects/<project>/chats/<timestamp>/. Both agents share the same on-disk layout; sessions are classified by theagentTypefield inrun-state.json.Highlights
creditsUsedfromrun-state.jsonand maps to cost (1 credit = $0.01)agentTypeappear as a separate filterUnique considerations
agentTypefield is an agent template name (e.g.base2-free-deepseek), not the actual LLM model -- the real model is selected server-sidecontextTokenCountprovides context window size but per-message token breakdown is not available in the on-disk formatLimitations
agentfield onChatMessageis not populated in the persisted format, so per-turn agent attribution relies on subagent blocks only