feat(2.5.F): context compaction — /compact, /trim, and automatic compaction (ADR-0062)#65
Conversation
…del refinement PR #64 merged (2026-07-03), so 2.5.D (chat input ergonomics + the `@`/`!` pending-attachment "chip" model) flips from "implemented, pending merge" to ✅ Done across the canonical status homes: - roadmap/current.md + phases/phase-2.5-cli-consolidation.md: 2.5.D status → Done; note the post-implementation refinements recorded in ADR-0061's "Refined at implementation" append — the chip model (inline `@path` marker + read-only `!` preview, expanded into the SAME UNTRUSTED nonce-fenced frame only at submit), the coupled `[chat]` allowlist resolution, and the Backspace/`key.delete` fix. - CLAUDE.md: status paragraph advanced to 2.5.D Done; next pickup is 2.5.F / G. - `@`/`!` phase bullets updated to the chip presentation (frame applied at submit). The spine (2.5.A/B/C/E) plus the first experience-arm workstream (2.5.D) are now complete; 2.5.F / G remain in the experience arm. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ands Accept ADR-0062 for Phase 2.5.F, reversing the roadmap's deferral of model- summarised `/compact` to Phase 3 (maintainer scope decision — build the full manual + automatic compaction story now). Core decisions (revised after four adversarial review passes): - Summary is a session-level system-prompt PREAMBLE (not a transcript message — no alternation hazard); the last user+assistant exchange stays VERBATIM (K≥1). - Append-only persistence: one `role:system` marker row + a typed nullable column (`compaction_dropped_through_sequence`) via a standard migration — NOT a new arm on the closed `DurableContentPart` union (which would be breaking-to-old- readers); nothing is ever deleted; full transcript survives export/audit. - Resume separates the drop boundary (max droppedThroughSequence) from the preamble (newest marker WITH a summary), so a later summary-less `/trim` never blanks a prior `/compact` summary; reseat (ADR-0059) carries the preamble. - Seam amendment: optional `estimateTokens`/`contextLimit`/`managesOwnContext` on `LlmProvider` (Relavium/Zod types only — no vendor type crosses); real provider usage is the authoritative auto-compaction trigger, estimate is the pre-first-turn fallback. - Auto-compaction at `[chat].compact_threshold` (0.8) with thrash/cost guards, a deterministic `/trim` failure-degrade, and honest cost accounting. Purges the house-rule-violating references to the gitignored goose analysis; corrects the pricing-catalog-import and forward-compat claims the first draft got wrong. Adds the README index line. Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eam, migration) The platform-free foundations for context compaction (ADR-0062), ahead of the engine primitive (step 2) and the CLI wiring (step 3): - @relavium/shared: `session:compacted` + `session:trimmed` arms on the closed SessionEvent union (+ SESSION_EVENT_TYPES); an optional, additive `SessionMessage.compaction.droppedThroughSequence` boundary field on the lenient transcript schema; `[chat].auto_compact` + `compact_threshold` config (a fraction in (0,1]). `max_messages` now documented as consumed by `/trim` + auto-compaction. - @relavium/llm: optional `contextLimit` / `managesOwnContext` / `estimateTokens` on the LlmProvider seam — Relavium/Zod seam types only, no vendor type crosses — with shared `contextLimitFor` (pricing-catalog window; undefined for an unrated model) + `estimateRequestTokens` (a character heuristic; a pre-first-turn fallback, since real usage is authoritative) helpers, implemented in all three real adapters (Anthropic, OpenAI/DeepSeek, Gemini). - @relavium/db: one additive nullable `compaction_dropped_through_sequence` column (drizzle migration 0006) round-tripped by the SessionMessage mapper — an older reader that lacks it reads a marker as an inert `system` row (forward-compatible; no destructive change). Tests: new-event accept/reject, config-range, adapter seam-method delegation, and the marker-column round-trip. Full toolchain + format:check + lint:bundle-closure green. Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…overage Verified findings from the Opus review round of step 1 (no blockers/correctness defects were found; these are the low/nit fixes): - Canonical home (CLAUDE.md #8): document `session:compacted` / `session:trimmed` where the wire contract lives — sse-event-schema.md's SessionEvent union + the "complete stream" prose (five→seven), and chat-session.md's `--json` NDJSON enumeration. Fix the stale "five session:* events" comments in session-handle.ts + agent-session.ts, and soften an over-stated run-event.ts comment (consumers use a `default` arm — there is no `assertNever` over the union; a new arm is a forward-compatible no-op until each opts in). - Coverage: estimateRequestTokens adversarial cases (empty→0, mixed/empty-text parts, and a pinned CJK under-count documenting the heuristic's known imprecision — real usage stays authoritative); a summary-less /trim marker db round-trip (empty content + boundary); session:compacted nonNegativeInt count rejects; and a type-level SessionEvent-discriminant pin mirroring RunEvent's. - ADR-0062 scope bullet corrected to name `session:trimmed` as a distinct cost-free arm. Toolchain + format:check green. Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ements Verified findings from the Sonnet review round of step 1 (HIGH: canonical-doc drift, flagged by both reviewers; the rest are integration-seam clarifications): - Update the four canonical reference homes CLAUDE.md #8 requires (the shapes this step changed): llm-provider-seam.md (the three new LlmProvider methods), database-schema.md (the compaction_dropped_through_sequence column), agent-session-spec.md (the SessionMessage.compaction field), and config-spec.md (auto_compact / compact_threshold + prose). - Reconcile the 2.5.F roadmap scope: /compact is no longer Phase-3-deferred — it is built now under ADR-0062 (engine primitive + automatic compaction). Removed the stale "recognized-but-deferred stub" plan and the Phase-3 deferral bullet; noted the 2.5.H context-overflow hint is now the secondary net (auto-compaction pre-empts). - Append "Refined during step-1 review" notes to ADR-0062: the role-filtered write-side boundary algorithm (avoids a silent-data-loss off-by-one when a prior marker row is interleaved), using result.model (not agent.model) for the threshold, the resolve.ts config plumbing obligation, disambiguation from the reserved agent:context_compacted steering event, and the marker-persistence sign-off. format:check green (docs-only). Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…062) The platform-free engine primitive behind /compact, /trim, and auto-compaction: - AgentSession.compact(reason): summarise the earlier working context via the session's OWN bound model into a session-level `#contextPreamble` (XML-wrapped into the per-turn system prompt — reseat-free, no alternation hazard), keep the last user+assistant exchange VERBATIM, and emit session:compacted. The conversation to summarise rides a USER message (never the authored system prompt); the summariser's cost is accounted via cost:updated, but its agent:token/tool events are NOT forwarded (the internal summary never streams into the transcript as a reply). Classified CompactionResult (compacted/nothing_to_compact/failed/cancelled). - AgentSession.trimHistory(n): deterministic, no-LLM drop to the last n messages, snapped to a `user` boundary; emits session:trimmed; leaves the preamble intact (a trim drops older turns without summarising — a prior /compact summary survives). - Auto-compaction: after a CLEAN turn settles, compact if the turn's REAL input tokens exceed compactThreshold × the SERVING model's context window (result.model, canonical id). Guarded by autoCompact config, managesOwnContext, an unknown window, and a ≤1- exchange thrash guard; a summarisation failure degrades to a deterministic /trim to maxMessages. Wired via new SessionDeps (autoCompact/compactThreshold/maxMessages). - reconstructSessionState honours boundary markers with preamble≠boundary: drop boundary = max(droppedThroughSequence); preamble = the NEWEST marker WITH a summary (a later summary-less /trim advances the boundary but never blanks a prior /compact summary — the B4 correctness fix). resume restores the preamble, so a compacted session stays compacted across resume AND a model reseat (same reconstruct path). - Seam: export EstimateTokensInput (@relavium/llm) + CompactionResult/TrimResult and COMPACTION_SYSTEM_PROMPT/DEFAULT_COMPACT_THRESHOLD (@relavium/core). Tests: compact folds+preamble-injects+keeps-last-exchange; nothing_to_compact; trimHistory drop/no-op; auto-compaction fire + all skip guards + failure-degrade; and reconstructSessionState marker/preamble/boundary cases (incl. trim-after-compact and multi-compaction). Engine purity gate + full toolchain + both CI gates green. Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… coverage Verified findings from the Opus review round of the engine primitive (no blockers / crashes / seam or purity violations were found; the state machine was verified sound): - Implement ADR §5 thrash guard (b) — all three reviewers' top finding. Auto-compaction now skips when system+preamble+kept-exchange ALONE would still exceed the budget (an oversized single turn / huge system prompt), so it no longer pays a summariser call on every turn for a small-window session; the overflow surfaces instead. - #maybeAutoCompact consults the SERVING provider (the plan entry whose model produced the turn under fallback), not just the primary, for managesOwnContext/contextLimit. - Degrade an EA7-aborted `cancelled` auto-compaction to a deterministic /trim too (not only `failed`), matching ADR §5 "never a silent overflowing resend"; a terminal cancel() leaves status != idle so a dead session is untouched. - compact() robustness: compute tokensBefore INSIDE the try (a throwing optional estimator can't wedge #status at 'running' past the finally); re-throw a truly UNCLASSIFIED error instead of masking it as 'failed' (mirrors #settleTurnError; BudgetPauseError stays a bounded 'failed'). - Reconcile ADR §7: the summariser's agent:token/tool events are NOT forwarded to the transcript (they'd render as a reply) — the "Summarizing…" moment is driven off the compaction lifecycle (the running indicator / a spinner), not token streaming. Coverage (the Sonnet+Opus gap list): cancel/abort mid-summarisation (cancelled + context unchanged + session usable vs terminal); re-compaction folds the prior preamble; non-zero before>after token deltas; thrash-guard-b skip; failure WITHOUT maxMessages (no throw/trim); compact()/trimHistory() pre-start guard; trimHistory user-boundary snap; and trim-only / droppedThroughSequence:0 / multi-marker resume. Core 963 tests; full toolchain + gates green. Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ummary bound The Sonnet review round found real HIGH bugs the Opus round missed: - trimHistory(1) WIPED the entire transcript: tailFromUserBoundary forward-snapped the last-1 slice (an assistant, for a transcript ending in assistant) to empty, and trimHistory committed the wipe as a "success". `[chat].max_messages` is positiveInt, so `max_messages: 1` is a legal value the auto-compaction failure-degrade could pass. Fixed: never return an empty slice when there was history — floor at the last exchange. - splitFoldable could keep a LONE dangling `user` as the "kept exchange" (a completed empty-text turn leaves a dangling user; sendMessage only appends the assistant when result.text is non-empty), breaking the next turn with two consecutive user messages. Fixed: keep from the last `user` that HAS an assistant reply after it — never a lone user. - The summariser output was UNBOUNDED (no maxTokens), so a compaction could produce a huge preamble that defeats compaction; and thrash-guard-b used the STALE pre-compaction preamble as a proxy (a false-skip risk). Fixed: bound the summary via a new COMPACTION_MAX_SUMMARY_TOKENS (4096) maxTokens, and rewrite guard-b to a PROJECTED FLOOR = base system (not the current preamble the summary replaces) + kept exchange + that bound. - estimator-throw safety was applied to only one of three call sites (compact's tokensBefore); tokensAfter and guard-b were still exposed. Fixed comprehensively: the estimate helper now NEVER throws (a throwing seam estimator is swallowed to 0), so no estimate can wedge #status, escape compact()'s finally, or crash the auto-compaction gate. Tests reworked to realistic windows (guard-b's 4096 floor makes toy windows unrealistic) via a parametrized inputTurn helper; new regressions for trim(1), the dangling-user no-op, and the degrade kept/dropped counts. Core 965 tests; full toolchain + gates green. Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ion UX Wires the ADR-0062 engine primitive into the interactive chat surface: - Config: [chat].auto_compact / compact_threshold resolve through ResolvedChatConfig and thread into SessionDeps (with max_messages) — never hardcoded, so the config is not re-dead. The engine auto-compacts after a turn per the resolved threshold. - Persister: on session:compacted / session:trimmed, append the append-only boundary marker (role:'system' + summary + a ROLE-FILTERED droppedThroughSequence — the step-1-review trap: keptMessageCount is mapped over the real user/assistant sequences, EXCLUDING prior marker rows; seeded from loadMessages on resume) and add the summariser's token usage. Nothing durable is deleted. - Commands: /compact (write) + /trim [n] (read, a FREE positional) in REPL_COMMANDS + ReplCommandContext; /compact shows a Summarizing… notice then the token deltas + the (sanitized) summary; /trim uses [chat].max_messages by default or an inline n, with an actionable no-bound notice. validateSlashTokens gains a free-positional mode. - Auto-compaction UX: the session view surfaces an automatic compaction as a concise inline ⟳ notice (reason 'auto-threshold' only — a manual /compact is noticed by the command), so it is never a silent context swap. Docs: chat-session.md gains a Context-compaction section (the mechanisms, the durable boundary, and the summary-prompt invariant — the ADR's named canonical home). SCOPE NOTE: /clear (a fresh-session lifecycle swap, orthogonal to compaction) is the remaining 2.5.F command — deferred as a clearly-noted follow-up rather than a forbidden no-op stub, so /compact (the explicit ask) + /trim + auto-compaction land complete. Tests: persister role-filtered marker + resume; /compact + /trim command behavior + free-positional validation; config resolution; notice formatters. Full toolchain + format:check + lint:bundle-closure green (1291 CLI tests). Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mpaction crash Two HIGH findings from the Opus review round of the CLI wiring: - SILENT DATA LOSS on resume→compact: the persister seeded its boundary-mapping from a bare role filter, but the engine resumes from reconstructSessionState's fuller projection (drops empty-text rows + rolls back a trailing unanswered `user`). A session that ended on an empty-text turn (a dangling user) therefore seeded the rolled-back row, making the next compaction's droppedThroughSequence off-by-one and silently dropping a kept message (then a two-consecutive-users 400). Fixed by extracting the projection into ONE shared helper (resumableMessageSequences / projectResumableRows in @relavium/core) that BOTH the engine and the host seed from — they can never drift. - REPL CRASH during compaction: auto-compaction runs inside sendMessage AFTER session:turn_completed already flipped the view idle, so a message typed during the multi-second summarisation reached sendMessage → SessionStateError → crash (the same "engine busy, view idle" hazard 2.5.D fixed for `!`-shell). Fixed by a submit-busy flag on BOTH interactive surfaces (chat-ink ChatApp + the Home controller) that gates input + drives the spinner for the WHOLE submit (streaming AND compaction), so Esc still aborts and no keystroke can crash the session. Tests: resumableMessageSequences (dangling-user + boundary + marker exclusion), the session:compacted auto-notice reducer arm (auto surfaces, manual does not), and a reverse-search test updated to await the turn settle (input is now correctly gated while a submit is in flight). Full toolchain + gates green (core 967, CLI 1292). Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…summary cap The Sonnet review round found no blockers (the Opus fixes verified correct), but flagged that the fixes lacked regression guards + two UX-polish items: - Add the resume→/compact integration test: a durable transcript ending on a dangling user, resumed via buildResumedChatSession + a second persister (the adopt/seed branch), then /compact → asserts the boundary is droppedThroughSequence 1, NOT 2. A straight revert of the persister seed line now fails this test (the silent-data-loss guard). - Add the submit-busy input-gate test: a deferred turn holds a submit in flight (the auto-compaction window); a message typed then is gated (never reaches a second processLine, onError not called) and the flag clears on settle (no stuck-busy). - Cap the inline /compact summary preview at 800 chars (a long summary is previewed with a pointer to the full text via /export, never a scrollback-flooding dump). - Disclose honestly (chat-session.md) that the compaction spinner is unlabeled and the footer context-fullness indicator is a follow-up — the compaction engine, persistence, resume, auto-compaction, /compact, and /trim are complete; Esc aborts a compaction. Full toolchain + gates green (core 967, CLI 1294). Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ar remaining ADR-0062's compaction primitive, auto-compaction, persistence, /compact, and /trim landed (pending merge) across three reviewed steps. Note in the 2.5.F acceptance that /clear (a session-lifecycle swap, orthogonal to compaction) + two compaction-moment UX polishes (a labeled spinner + a footer fullness indicator) are the remaining items, and that 2.5.F stays un-marked-Done until merge (roadmap-done-after-merge). Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Sorry @cemililik, your pull request is larger than the review limit of 150000 diff characters
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds ADR-0062 context compaction, deterministic trimming, and ChangesContext Compaction Feature
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request implements context compaction and history trimming for CLI chat sessions as specified in ADR-0062, introducing manual /compact and /trim commands, automatic threshold-triggered compaction, and database persistence for compaction boundaries. The review feedback focuses on code safety and optimization: it suggests removing redundant optional chaining on guaranteed plan entries, optimizing splitFoldable to O(N) complexity, simplifying tailFromUserBoundary with standard array methods, wrapping onSubmit to prevent REPL freezes on synchronous throws, and adding a reason field to session:trimmed to prevent silent fallback trims in the UI.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if (!plan.ok) return 0; | ||
| try { | ||
| // Call inline (not via an extracted method reference) so `estimateTokens` stays bound to its provider. | ||
| return plan.entries[0]?.provider.estimateTokens?.({ system, messages }) ?? 0; |
There was a problem hiding this comment.
Since plan.ok is checked above, plan.entries is guaranteed to be a non-empty array, meaning plan.entries[0] and its provider property are guaranteed to exist. The optional chaining plan.entries[0]?.provider is redundant. More importantly, if plan.entries[0] were actually undefined, this expression would throw a TypeError when trying to access .estimateTokens on undefined. We should use direct property access instead.
| return plan.entries[0]?.provider.estimateTokens?.({ system, messages }) ?? 0; | |
| return plan.entries[0].provider.estimateTokens?.({ system, messages }) ?? 0; |
References
- Do not use optional chaining or nullish coalescing on fields that are guaranteed to be present by upstream schema validation (e.g., Zod). Unreachable defensive code masks contract violations and prevents the system from failing loud when an upstream contract breaks.
| function splitFoldable( | ||
| messages: readonly LlmMessage[], | ||
| ): { readonly foldable: LlmMessage[]; readonly kept: LlmMessage[] } | undefined { | ||
| let keptStart = -1; | ||
| for (let i = messages.length - 1; i >= 0; i -= 1) { | ||
| if ( | ||
| messages[i]?.role === 'user' && | ||
| messages.slice(i + 1).some((message) => message.role === 'assistant') | ||
| ) { | ||
| keptStart = i; // the last `user` that has an assistant reply after it — the start of the last exchange | ||
| break; | ||
| } | ||
| } | ||
| if (keptStart <= 0) return undefined; // no earlier turn precedes the last complete exchange | ||
| return { foldable: messages.slice(0, keptStart), kept: messages.slice(keptStart) }; | ||
| } |
There was a problem hiding this comment.
The current implementation of splitFoldable has an slice and some calls inside the loop. We can optimize this to findLastIndex to locate the last assistant message, and then finding the last user message before that index.
function splitFoldable(
messages: readonly LlmMessage[],
): { readonly foldable: LlmMessage[]; readonly kept: LlmMessage[] } | undefined {
const lastAssistantIdx = messages.findLastIndex((m) => m.role === 'assistant');
if (lastAssistantIdx === -1) return undefined;
const keptStart = messages.slice(0, lastAssistantIdx).findLastIndex((m) => m.role === 'user');
if (keptStart <= 0) return undefined; // no earlier turn precedes the last complete exchange
return { foldable: messages.slice(0, keptStart), kept: messages.slice(keptStart) };
}| function tailFromUserBoundary(messages: readonly LlmMessage[], maxKeep: number): LlmMessage[] { | ||
| if (maxKeep <= 0 || messages.length === 0) return []; | ||
| let slice = messages.slice(Math.max(0, messages.length - maxKeep)); | ||
| while (slice.length > 0 && slice[0]?.role !== 'user') slice = slice.slice(1); | ||
| if (slice.length > 0) return slice; | ||
| // The forward-snap emptied the slice — floor at the last `user` onward (the last exchange), never a wipe. | ||
| let start = messages.length - 1; | ||
| while (start > 0 && messages[start]?.role !== 'user') start -= 1; | ||
| return messages.slice(start); | ||
| } |
There was a problem hiding this comment.
We can simplify tailFromUserBoundary by replacing the manual while loops with standard array methods like findIndex and findLastIndex. This improves readability, maintainability, and performance.
function tailFromUserBoundary(messages: readonly LlmMessage[], maxKeep: number): LlmMessage[] {
if (maxKeep <= 0 || messages.length === 0) return [];
const slice = messages.slice(Math.max(0, messages.length - maxKeep));
const firstUserIdx = slice.findIndex((m) => m.role === 'user');
if (firstUserIdx !== -1) return slice.slice(firstUserIdx);
// The forward-snap emptied the slice — floor at the last `user` onward (the last exchange), never a wipe.
return messages.slice(Math.max(0, messages.findLastIndex((m) => m.role === 'user')));
}| const submit = (message: string, display?: string): void => { | ||
| // Mark the submit in flight so input is gated + the spinner runs for the WHOLE operation (streaming AND any | ||
| // after-turn auto-compaction, ADR-0062) — cleared in EVERY settle branch (success / reject / defensive catch). | ||
| applySubmitBusy(true); | ||
| // Two-arm: a settled turn checks for exit; an UNEXPECTED rejection (the turn core's loud re-throw) goes to | ||
| // onError so the driver always unblocks + tears down (else `exited` never settles and the REPL hangs). The | ||
| // trailing .catch is defensive: a throw inside either callback still routes to onError, never silently lost. | ||
| void props | ||
| .onSubmit(message, display) | ||
| .then( | ||
| () => { | ||
| applySubmitBusy(false); | ||
| if (props.shouldStop()) props.onExit(); | ||
| }, | ||
| (err: unknown) => props.onError(err), | ||
| (err: unknown) => { | ||
| applySubmitBusy(false); | ||
| props.onError(err); | ||
| }, | ||
| ) | ||
| .catch((err: unknown) => props.onError(err)); | ||
| .catch((err: unknown) => { | ||
| applySubmitBusy(false); | ||
| props.onError(err); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
If props.onSubmit throws an error synchronously (e.g., during synchronous validation or due to a reference error before returning a promise), the promise chain is bypassed, and applySubmitBusy(false) is never called. This would leave the REPL permanently busy and frozen. Wrapping the call in Promise.resolve().then(...) ensures that any synchronous throws are safely caught and handled by the promise chain.
const submit = (message: string, display?: string): void => {
// Mark the submit in flight so input is gated + the spinner runs for the WHOLE operation (streaming AND any
// after-turn auto-compaction, ADR-0062) — cleared in EVERY settle branch (success / reject / defensive catch).
applySubmitBusy(true);
// Wrap in Promise.resolve().then() to safely catch any synchronous throws from props.onSubmit
Promise.resolve()
.then(() => props.onSubmit(message, display))
.then(
() => {
applySubmitBusy(false);
if (props.shouldStop()) props.onExit();
},
(err: unknown) => {
applySubmitBusy(false);
props.onError(err);
},
)
.catch((err: unknown) => {
applySubmitBusy(false);
props.onError(err);
});
};
| default: | ||
| // session:exported and any forward-compatible additions: no view change. | ||
| // session:trimmed (the manual /trim command notices its own result), session:exported, and any | ||
| // forward-compatible additions: no view change. | ||
| return base; |
There was a problem hiding this comment.
When automatic context compaction fails, the engine degrades to a deterministic /trim (by calling this.trimHistory), which emits a session:trimmed event. However, since session:trimmed is ignored in the view model (assumed to only be triggered by the manual /trim command which prints its own notice), this automatic fallback trim is completely silent to the user. To fix this, consider adding a reason: 'manual' | 'auto-fallback' field to session:trimmed (symmetric with session:compacted), and handling the 'auto-fallback' case in the view model to display an inline notice so the user is aware of the automatic trim.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
docs/decisions/0062-context-compaction-and-cli-history-commands.md (1)
134-149: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep the contract details in the canonical reference docs.
This ADR is duplicating concrete seam signatures and schema/event field names that should live in
docs/reference/*. Link to those artifacts here instead of restating them, or the decision text will drift from the spec.Also applies to: 268-281
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/decisions/0062-context-compaction-and-cli-history-commands.md` around lines 134 - 149, The ADR section is duplicating the concrete `LlmProvider` seam contract by restating `estimateTokens`, `contextLimit`, and `managesOwnContext` signatures. Update the decision text to point to the canonical `docs/reference/*` docs instead of embedding these method/schema details here, and keep the discussion at a high level so the ADR does not drift from the spec. Use the existing `LlmProvider` seam and the referenced canonical docs as the anchors when revising the wording.Source: Coding guidelines
apps/cli/src/chat/persister.ts (1)
76-116: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low valueIgnored
appendMarker()boolean return could mask a token/transcript desync.
appendMarker()returnsfalsewhen there is nothing older to supersede (Line 103), but both call sites (Lines 185, 192) discard the result. If a livesession:compacted/session:trimmedevent ever fires with akeptMessageCountthat the host'srealMessageSeqsdisagrees with (e.g. a future host/engine drift), the token totals still get incremented and the session flushed toactiveeven though no boundary marker was durably written — silently disagreeing with the in-memory "compacted" state on the next resume.Given how carefully this PR already hardened boundary-mapping drift (the step-3 resume fix), surfacing this case (e.g., a dev-only assertion or warning) would make a future regression fail loudly instead of silently.
🛡️ Optional: surface an unexpected no-op marker write
case 'session:compacted': - appendMarker(event.summary, event.keptMessageCount); + if (!appendMarker(event.summary, event.keptMessageCount)) { + // Should be unreachable if host/engine tracking stay in sync — surfaces a future desync loudly. + console.warn('session:compacted: no boundary marker written (keptMessageCount >= real rows)'); + } totalInputTokens += event.tokensUsed.input;Also applies to: 181-194
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/cli/src/chat/persister.ts` around lines 76 - 116, The appendMarker() call sites in persister.ts currently ignore its boolean result, which can hide a no-op boundary write when keptMessageCount no longer matches realMessageSeqs. Update the session:compacted and session:trimmed handling around appendMarker() to check the return value and surface an unexpected false with a dev-only assertion or warning, so token accounting and flush-to-active only proceed when the durable marker was actually written.packages/core/src/engine/agent-session.ts (1)
796-895: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReduce
compact()'s cognitive complexity (SonarCloud gate).Static analysis flags
compact()at 17/15 allowed cognitive complexity. Consider extracting thecatchblock's error classification into a private helper (mirroring#settleTurnError's pattern), e.g.#classifyCompactionError(err): CompactionResult.♻️ Sketch of the extraction
- } catch (err) { - if (this.#statusIs('cancelled') || err instanceof ToolCancelledError) - return { kind: 'cancelled' }; - if (this.#abortingTurn) return { kind: 'cancelled' }; - if (err instanceof AgentTurnError) { - if (err.code === 'cancelled') return { kind: 'cancelled' }; - return { kind: 'failed', message: err.message }; - } - if (err instanceof BudgetPauseError) return { kind: 'failed', message: err.message }; - throw err; - } finally { + } catch (err) { + return this.#classifyCompactionError(err); + } finally { this.#abort = undefined; this.#abortingTurn = false; if (this.#statusIs('running')) this.#status = 'idle'; } } + + `#classifyCompactionError`(err: unknown): CompactionResult { + if (this.#statusIs('cancelled') || err instanceof ToolCancelledError) return { kind: 'cancelled' }; + if (this.#abortingTurn) return { kind: 'cancelled' }; + if (err instanceof AgentTurnError) { + return err.code === 'cancelled' ? { kind: 'cancelled' } : { kind: 'failed', message: err.message }; + } + if (err instanceof BudgetPauseError) return { kind: 'failed', message: err.message }; + throw err; + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/engine/agent-session.ts` around lines 796 - 895, Reduce the cognitive complexity in compact() by moving the error-classification logic out of the catch block into a private helper, following the same pattern as `#settleTurnError`. Have compact() delegate caught errors to a new helper such as `#classifyCompactionError`(err) that returns the appropriate CompactionResult for cancelled, AgentTurnError, BudgetPauseError, and the default rethrow path, keeping the main compact() flow focused on the happy path.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/cli/src/render/tui/session-view-model.ts`:
- Around line 290-294: The default handling in session-view-model’s event switch
is collapsing the automatic trim fallback into the manual /trim path, so the TUI
never shows a notice for that fallback. Update the session event handling around
the session:trimmed branch to distinguish the auto-compaction fallback from the
manual trim result, and surface a dedicated notice for the fallback case while
keeping the existing manual /trim behavior unchanged. Use the existing
session-view-model flow and any trim-related event symbols in that switch to
route the fallback separately instead of leaving it in the generic default
branch.
In `@docs/reference/cli/chat-session.md`:
- Around line 95-98: The resume behavior description in the chat-session docs is
too broad and incorrectly implies any newest marker overwrites the preamble,
including empty `/trim` markers. Update the wording around the durable boundary
to say that the newest marker with a summary, or the latest non-empty summary,
becomes the preamble, and keep the existing references to the `role: 'system'`
marker row and `chat-resume` behavior aligned with that rule.
In `@packages/core/src/engine/agent-session.ts`:
- Around line 926-963: Best-effort compaction is still using unguarded provider
seam calls in `#maybeAutoCompact`, so exceptions from
serving?.managesOwnContext?.() or serving?.contextLimit?.(model) can escape and
reject sendMessage() after the turn has already completed. Wrap those optional
provider calls in try/catch inside `#maybeAutoCompact`, treat any thrown error as
a non-fatal skip of auto-compaction, and ensure the method returns cleanly just
like the guarded `#estimateTokens` seam does.
---
Nitpick comments:
In `@apps/cli/src/chat/persister.ts`:
- Around line 76-116: The appendMarker() call sites in persister.ts currently
ignore its boolean result, which can hide a no-op boundary write when
keptMessageCount no longer matches realMessageSeqs. Update the session:compacted
and session:trimmed handling around appendMarker() to check the return value and
surface an unexpected false with a dev-only assertion or warning, so token
accounting and flush-to-active only proceed when the durable marker was actually
written.
In `@docs/decisions/0062-context-compaction-and-cli-history-commands.md`:
- Around line 134-149: The ADR section is duplicating the concrete `LlmProvider`
seam contract by restating `estimateTokens`, `contextLimit`, and
`managesOwnContext` signatures. Update the decision text to point to the
canonical `docs/reference/*` docs instead of embedding these method/schema
details here, and keep the discussion at a high level so the ADR does not drift
from the spec. Use the existing `LlmProvider` seam and the referenced canonical
docs as the anchors when revising the wording.
In `@packages/core/src/engine/agent-session.ts`:
- Around line 796-895: Reduce the cognitive complexity in compact() by moving
the error-classification logic out of the catch block into a private helper,
following the same pattern as `#settleTurnError`. Have compact() delegate caught
errors to a new helper such as `#classifyCompactionError`(err) that returns the
appropriate CompactionResult for cancelled, AgentTurnError, BudgetPauseError,
and the default rethrow path, keeping the main compact() flow focused on the
happy path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 22067ce7-a288-4cb1-8735-4f3ad6900c57
📒 Files selected for processing (56)
CLAUDE.mdapps/cli/src/chat/persister.test.tsapps/cli/src/chat/persister.tsapps/cli/src/chat/repl-info.test.tsapps/cli/src/chat/repl-info.tsapps/cli/src/chat/session-host.test.tsapps/cli/src/chat/session-host.tsapps/cli/src/commands/chat.test.tsapps/cli/src/commands/chat.tsapps/cli/src/commands/repl-commands.test.tsapps/cli/src/commands/repl-commands.tsapps/cli/src/config/resolve.test.tsapps/cli/src/config/resolve.tsapps/cli/src/engine/media-wiring.test.tsapps/cli/src/render/tui/chat-ink.tsxapps/cli/src/render/tui/home-app.tsxapps/cli/src/render/tui/home-controller.test.tsapps/cli/src/render/tui/home-controller.tsapps/cli/src/render/tui/palette-reducer.test.tsapps/cli/src/render/tui/session-view-model.test.tsapps/cli/src/render/tui/session-view-model.tsdocs/decisions/0062-context-compaction-and-cli-history-commands.mddocs/decisions/README.mddocs/reference/cli/chat-session.mddocs/reference/contracts/agent-session-spec.mddocs/reference/contracts/config-spec.mddocs/reference/contracts/sse-event-schema.mddocs/reference/desktop/database-schema.mddocs/reference/shared-core/llm-provider-seam.mddocs/roadmap/current.mddocs/roadmap/phases/phase-2.5-cli-consolidation.mdpackages/core/src/engine/agent-session.test.tspackages/core/src/engine/agent-session.tspackages/core/src/engine/session-handle.tspackages/core/src/engine/session-resume.test.tspackages/core/src/engine/session-resume.tspackages/core/src/index.tspackages/db/drizzle/0006_mean_adam_destine.sqlpackages/db/drizzle/meta/0006_snapshot.jsonpackages/db/drizzle/meta/_journal.jsonpackages/db/src/schema.tspackages/db/src/session-store.test.tspackages/db/src/session-store.tspackages/llm/src/adapters/anthropic.tspackages/llm/src/adapters/gemini.tspackages/llm/src/adapters/openai.tspackages/llm/src/adapters/shared.test.tspackages/llm/src/adapters/shared.tspackages/llm/src/index.tspackages/llm/src/types.tspackages/shared/src/config.test.tspackages/shared/src/config.tspackages/shared/src/constants.tspackages/shared/src/run-event.test.tspackages/shared/src/run-event.tspackages/shared/src/session.ts
…bustness Address the PR review + SonarCloud findings (verified against current code; invalid ones skipped): - The auto-compaction failure-degrade trim was SILENT on the interactive surface (it fell to the view-model's default branch). Add a `reason: 'manual' | 'auto-fallback'` to `session:trimmed` (symmetric with `session:compacted`); the engine tags the degrade trim `auto-fallback` and the view surfaces it as a notice, keeping manual `/trim` (self-noticed by the command) unchanged. - Guard the optional provider seam calls in `#maybeAutoCompact` (managesOwnContext / contextLimit) in try/catch — a throwing provider method now SKIPS auto-compaction instead of rejecting sendMessage after the turn already completed (mirrors #estimateTokens). - Extract `#classifyCompactionError` from compact()'s catch (reduces cognitive complexity below the 15 ceiling, mirroring #settleTurnError). - splitFoldable + tailFromUserBoundary: O(N²)→O(N) via index scans (drop the in-loop slice/some), behavior-preserving. - Docs: chat-session.md now says the preamble is the newest marker *with a summary* (not any marker); ADR-0062 §4 points to the canonical seam doc instead of restating the three method signatures; sse-event-schema.md carries the new `session:trimmed.reason`. - Test: `.length).toBe()` → `.toHaveLength()` (SonarCloud). Skipped (with reason): the persister `appendMarker` boolean (false is unreachable given the engine/persister count invariant, and gating the summariser's REAL cost on it would be incorrect); the `plan.entries[0]?.provider` optional chain (required by noUncheckedIndexedAccess — direct access is a type error — and it short-circuits, no throw); the chat-ink onSubmit sync-throw wrap (onSubmit is `processLine`, an async function that cannot throw synchronously — the `.then(_,onError).catch` already clears submitBusy on every real failure). New tests: the auto-fallback trim view-notice (auto surfaces, manual silent) + the `session:trimmed.reason` enum. Full toolchain + format:check + lint:bundle-closure green (core 967, CLI 1295). Refs: ADR-0062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ext-fullness footer (ADR-0062)
Completes Phase 2.5.F (ADR-0062 §7): the fresh-session lifecycle command plus the two
compaction-moment UX polishes.
- /clear — host-level fresh-session swap across `relavium chat`, `chat-resume`, and the
in-Home chat. Standalone re-drives via a new ChatDriver outcome ({kind:'exit'|'clear'});
the Home uses a build-first `clearChat`. `buildChatSession` gains an `agent` override so
the fresh session rebinds the same agent (chat-resume's snapshot agent, no on-disk ref).
Registry entry (effect 'destructive', availableIn ['home','chat']); `clearedNotice`
surfaces the sanitized old sessionId + `chat-resume`. TTY-interactive only — rejected
under --json/plain so one machine stream stays one session lifecycle (ADR-0049).
- session:compacting — a new additive session event (amends ADR-0036) emitted at compact()
start; drives a labeled "Summarizing…" moment; host-cleared when a manual /compact settles
so the flag never latches into a later slash command.
- context-fullness footer (NN% ctx) via a pure @relavium/llm `contextWindowForModel` helper;
skips a zero-input turn so a budget/abort never flashes 0% ctx.
Engine stays platform-free (/clear is host-only, session:compacting is a pure emit — no new
primitive); no vendor type crosses the @relavium/llm seam; no new dependency. Docs updated:
chat-session.md, commands.md, sse-event-schema.md, ADR-0036/0062 amendments (append-only),
phase-2.5 roadmap. Includes the Opus+Sonnet parallel-review fixes: compacting-latch clear,
0%-ctx guard, Home double-clear MCP-child guard, the ADR-0049 /clear-gate + teardownActive
both-live regression tests, and a pricing.test narrowing (no unsafe `as`).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…decision Capture the Node.js upgrade findings without touching 2.5.F or any pin/config. As of 2026-07-05 the supported floor (Node 20.12) is on an EOL line that better-sqlite3 no longer prebuilds, so a fresh CLI install on the declared floor already forces a C++ source build — partially defeating ADR-0021. Raising the floor to >=22 restores prebuilt coverage and unlocks ink 7 / node:sqlite / eslint 10 / vitest 5. Two decoupled changes are framed: (A) dev/CI `.nvmrc` 22->24 (zero-risk, no ADR), and (B) a supported-floor bump (SemVer-major, reopens ADR-0021 -> superseding ADR + tech-stack.md). - docs/roadmap/phases/node-runtime-upgrade.md — the canonical analysis (current-version table, per-dependency verdicts, migration plans for A and B, verification, governance, open questions). - docs/roadmap/deferred-tasks.md — a one-line pointer under "Decisions needed (maintainer call)" + the Last-updated stamp. Analysis only — no code, pins, or config changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/reference/contracts/sse-event-schema.md (1)
263-272: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winEvent count is stale: union has 8 variants, text says "seven".
The
SessionEventunion now lists 8 members (started, turn_started, turn_completed, cancelled, exported, compacting, compacted, trimmed), but line 278 still says "the sevensession:*lifecycle/side events." Docs previously had 5, and 3 were added here — the narrative count should be updated to eight.📝 Proposed fix
-So the **complete typed event stream for a session** is the seven `session:*` lifecycle/side events (the `SessionEvent` union above) +So the **complete typed event stream for a session** is the eight `session:*` lifecycle/side events (the `SessionEvent` union above)Also applies to: 278-278
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/reference/contracts/sse-event-schema.md` around lines 263 - 272, The SessionEvent union now has 8 variants, so update the surrounding prose in the SessionEvent section to say eight rather than seven. Adjust the summary text that describes the `session:*` lifecycle/side events to match the current union members (`SessionStartedEvent`, `SessionTurnStartedEvent`, `SessionTurnCompletedEvent`, `SessionCancelledEvent`, `SessionExportedEvent`, `SessionCompactingEvent`, `SessionCompactedEvent`, `SessionTrimmedEvent`) and keep the narrative consistent with the definitions in this block.apps/cli/src/commands/chat.ts (1)
802-818: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winGuard
/compactlike the other slash commands
built.session.compact('manual')can reject on unexpected compaction errors, and this path has no catch, so the rejection escapeshandleSlashCommandinstead of being surfaced as REPL output. Wrap it intry/catch/finallyand clearstore.clearCompacting()infinallyso the spinner is reset even on failure.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/cli/src/commands/chat.ts` around lines 802 - 818, The `compactHistory` slash-command handler currently awaits `built.session.compact('manual')` without error handling, so unexpected rejections can escape instead of being shown in the REPL. Update `compactHistory` in `handleSlashCommand` to use `try/catch/finally`: keep the existing progress/output behavior, surface failures via `emitOutput` in the catch path, and move `store.clearCompacting()` into `finally` so the spinner state is always reset.Source: Linters/SAST tools
🧹 Nitpick comments (2)
docs/roadmap/phases/node-runtime-upgrade.md (1)
3-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueBlank line splits the blockquote (MD028).
Line 6 is blank inside the blockquote block (lines 3-9), which markdownlint flags and most renderers treat as two separate blockquotes with a visual gap rather than one continuous quote.
📝 Proposed fix
> Status: **Analysis / deferred maintainer decision.** NOT scheduled into any phase; NOT part of > Phase 2.5.F. This note captures the findings so the decision isn't lost — acting on it is a > separate, governed change (see [§6 Governance](`#6-governance--what-acting-on-this-requires`)). - +> > Snapshot date: **2026-07-05.** Every version/EOL number below is a point-in-time fact from that🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/roadmap/phases/node-runtime-upgrade.md` around lines 3 - 9, The blockquote in node-runtime-upgrade.md is split by a blank line, triggering MD028 and rendering as separate quotes. Remove the empty line inside the quoted “Status”/“Snapshot date” section so the entire note stays one continuous blockquote, and keep the surrounding blockquote markers aligned with that section.Source: Linters/SAST tools
apps/cli/src/commands/chat.ts (1)
309-351: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate
rebuildwiring betweenchatCommandandchatResumeCommand.The
rebuildclosure (capturingboundAgentand callingbuildFreshChatWiringwith the same field set, including an identicalonBudgetWarningcallback) is copy-pasted verbatim betweenchatCommand(Lines 309-336) andchatResumeCommand(Lines 466-492). SonarCloud flags this exact duplication.Consider extracting a shared factory, e.g.
createClearRebuild(deps, boundAgent, opened, config, projectConfigDir, now, uuid, providers, mcpSecretResolver), called from both commands, so a future change to the rebuild contract can't silently diverge between the two entry points.Also applies to: 466-510
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/cli/src/commands/chat.ts` around lines 309 - 351, The rebuild wiring in chatCommand and chatResumeCommand is duplicated, including the boundAgent capture, buildFreshChatWiring call, and identical onBudgetWarning handling. Extract that shared closure into a common helper/factory (for example, a createClearRebuild-style function) and have both commands call it so the rebuild contract stays in one place and cannot drift between chatCommand and chatResumeCommand.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/cli/src/commands/chat.test.ts`:
- Around line 516-519: The driveClear ChatDriver in chat.test.ts is marked async
without using await, which triggers the require-await lint error. Remove the
unnecessary async from driveClear and keep its behavior the same by returning
the clear result directly while still calling ctx.startSession() first. Use the
driveClear identifier and ChatDriver typing to locate and update this test
helper.
In `@apps/cli/src/commands/repl-commands.test.ts`:
- Around line 136-139: The REPL effect taxonomy test is missing `/trim` as a
write operation, so update the REPL command definition in REPL_COMMANDS_BY_NAME
(and any related command metadata in repl-commands.ts) to classify `/trim` as
write instead of read. Make sure the change is reflected consistently anywhere
command effects are asserted or derived, and keep the existing effects for
export, compact, and clear unchanged.
---
Outside diff comments:
In `@apps/cli/src/commands/chat.ts`:
- Around line 802-818: The `compactHistory` slash-command handler currently
awaits `built.session.compact('manual')` without error handling, so unexpected
rejections can escape instead of being shown in the REPL. Update
`compactHistory` in `handleSlashCommand` to use `try/catch/finally`: keep the
existing progress/output behavior, surface failures via `emitOutput` in the
catch path, and move `store.clearCompacting()` into `finally` so the spinner
state is always reset.
In `@docs/reference/contracts/sse-event-schema.md`:
- Around line 263-272: The SessionEvent union now has 8 variants, so update the
surrounding prose in the SessionEvent section to say eight rather than seven.
Adjust the summary text that describes the `session:*` lifecycle/side events to
match the current union members (`SessionStartedEvent`,
`SessionTurnStartedEvent`, `SessionTurnCompletedEvent`, `SessionCancelledEvent`,
`SessionExportedEvent`, `SessionCompactingEvent`, `SessionCompactedEvent`,
`SessionTrimmedEvent`) and keep the narrative consistent with the definitions in
this block.
---
Nitpick comments:
In `@apps/cli/src/commands/chat.ts`:
- Around line 309-351: The rebuild wiring in chatCommand and chatResumeCommand
is duplicated, including the boundAgent capture, buildFreshChatWiring call, and
identical onBudgetWarning handling. Extract that shared closure into a common
helper/factory (for example, a createClearRebuild-style function) and have both
commands call it so the rebuild contract stays in one place and cannot drift
between chatCommand and chatResumeCommand.
In `@docs/roadmap/phases/node-runtime-upgrade.md`:
- Around line 3-9: The blockquote in node-runtime-upgrade.md is split by a blank
line, triggering MD028 and rendering as separate quotes. Remove the empty line
inside the quoted “Status”/“Snapshot date” section so the entire note stays one
continuous blockquote, and keep the surrounding blockquote markers aligned with
that section.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 75b02b10-72bf-4eaf-a0f3-1519e3da9377
📒 Files selected for processing (33)
apps/cli/src/chat/repl-info.test.tsapps/cli/src/chat/repl-info.tsapps/cli/src/chat/session-host.tsapps/cli/src/commands/chat.test.tsapps/cli/src/commands/chat.tsapps/cli/src/commands/repl-commands.test.tsapps/cli/src/commands/repl-commands.tsapps/cli/src/home/drive-home.tsxapps/cli/src/render/tui/chat-ink.tsxapps/cli/src/render/tui/chat-projection.test.tsapps/cli/src/render/tui/chat-projection.tsapps/cli/src/render/tui/chat-store.tsapps/cli/src/render/tui/home-controller.test.tsapps/cli/src/render/tui/home-controller.tsapps/cli/src/render/tui/session-view-model.test.tsapps/cli/src/render/tui/session-view-model.tsdocs/decisions/0036-run-loop-substrate-event-bus-and-execution-host.mddocs/decisions/0062-context-compaction-and-cli-history-commands.mddocs/reference/cli/chat-session.mddocs/reference/cli/commands.mddocs/reference/contracts/sse-event-schema.mddocs/roadmap/deferred-tasks.mddocs/roadmap/phases/node-runtime-upgrade.mddocs/roadmap/phases/phase-2.5-cli-consolidation.mdpackages/core/src/engine/agent-session.test.tspackages/core/src/engine/agent-session.tspackages/core/src/engine/session-handle.tspackages/llm/src/index.tspackages/llm/src/pricing.test.tspackages/llm/src/pricing.tspackages/shared/src/constants.tspackages/shared/src/run-event.test.tspackages/shared/src/run-event.ts
✅ Files skipped from review due to trivial changes (7)
- docs/decisions/0036-run-loop-substrate-event-bus-and-execution-host.md
- packages/core/src/engine/session-handle.ts
- docs/reference/cli/commands.md
- docs/roadmap/deferred-tasks.md
- apps/cli/src/render/tui/chat-projection.test.ts
- docs/reference/cli/chat-session.md
- docs/decisions/0062-context-compaction-and-cli-history-commands.md
🚧 Files skipped from review as they are similar to previous changes (6)
- packages/llm/src/index.ts
- apps/cli/src/chat/repl-info.ts
- packages/shared/src/run-event.test.ts
- docs/roadmap/phases/phase-2.5-cli-consolidation.md
- packages/core/src/engine/agent-session.test.ts
- packages/core/src/engine/agent-session.ts
| it('effects are sound: export + compact write, clear destructive, the rest read', () => { | ||
| expect(REPL_COMMANDS_BY_NAME.get('export')?.effect).toBe('write'); | ||
| for (const name of ['help', 'exit', 'cancel', 'workflows', 'cost', 'doctor', 'mode']) { | ||
| expect(REPL_COMMANDS_BY_NAME.get('compact')?.effect).toBe('write'); // /compact spends tokens (ADR-0062) | ||
| expect(REPL_COMMANDS_BY_NAME.get('clear')?.effect).toBe('destructive'); // /clear ends the session (ADR-0062 §7) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect how `effect` is defined for trim/compact/clear in the REPL command registry.
rg -n "effect" apps/cli/src/commands/repl-commands.ts -A2 -B2Repository: HodeTech/Relavium
Length of output: 3933
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== repl-commands.test.ts (relevant section) =="
sed -n '120,155p' apps/cli/src/commands/repl-commands.test.ts
echo
echo "== effect consumers =="
rg -n "\.effect\b|effect:" apps/cli/src -g '*.ts' -g '*.tsx' -A2 -B2
echo
echo "== repl-commands.ts trim/compact/clear section =="
sed -n '175,210p' apps/cli/src/commands/repl-commands.tsRepository: HodeTech/Relavium
Length of output: 16980
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== CommandEffect definition and consumers =="
rg -n "CommandEffect|\.effect\b" apps/cli/src -g '*.ts' -g '*.tsx' -A3 -B3
echo
echo "== repl-commands.test.ts around the effect assertions =="
sed -n '130,150p' apps/cli/src/commands/repl-commands.test.tsRepository: HodeTech/Relavium
Length of output: 6197
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== CommandEffect docs and REPL command interface context =="
sed -n '1,90p' apps/cli/src/commands/manifest.ts
echo
sed -n '1,120p' apps/cli/src/commands/repl-commands.ts
echo
echo "== other mentions of trimHistory =="
rg -n "trimHistory|compactHistory|clearSession" apps/cli/src -g '*.ts' -g '*.tsx' -A2 -B2Repository: HodeTech/Relavium
Length of output: 23267
Mark /trim as write in apps/cli/src/commands/repl-commands.ts:192 — it drops older messages from session history, so read doesn’t match the effect taxonomy.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/cli/src/commands/repl-commands.test.ts` around lines 136 - 139, The REPL
effect taxonomy test is missing `/trim` as a write operation, so update the REPL
command definition in REPL_COMMANDS_BY_NAME (and any related command metadata in
repl-commands.ts) to classify `/trim` as write instead of read. Make sure the
change is reflected consistently anywhere command effects are asserted or
derived, and keep the existing effects for export, compact, and clear unchanged.
…ompact error handling, rebuild dedup)
Fixes the two CI failures plus the PR-review findings on the 2.5.F diff.
CI failures:
- lint: the `driveClear` test helper was `async` with no `await` (require-await) — make it a
plain arrow returning `Promise.resolve({ kind: 'clear' as const })`.
- test: the `replCommandList` slash-hint expected string was missing `/clear`.
Review findings:
- chat.ts `compactHistory`: wrap `compact('manual')` in try/catch/finally so an unclassified
re-throw is surfaced (never crashes the REPL) and `store.clearCompacting()` always runs (the
spinner moment is reset on every path — success, classified failure, or an unclassified throw).
- chat.ts: extract the duplicated `chat` + `chat-resume` `/clear` rebuild closures into a shared
`createClearRebuild` factory so the rebuild contract lives in one place and cannot drift.
- chat-ink.tsx: extract the nested busy-line ternary into a `renderBusyLine` helper.
- sse-event-schema.md: "seven" -> "eight" session:* events (session:compacting was added).
- node-runtime-upgrade.md: merge the split Status/Snapshot blockquote into one (MD028).
Skipped (with reason): reclassifying the `/trim` effect read->write — ADR-0062 §7 explicitly
defines `/trim` as `read` (deterministic, no cost); changing it would contradict the accepted
ADR and belongs in a superseding ADR, not a silent code tweak.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/reference/contracts/sse-event-schema.md (1)
274-274: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAvoid calling compaction result events “terminal”.
Line 274 first says these events are never stream terminals, then calls
session:compacted/session:trimmedterminal. Use “completion event” or “end state” to avoid implementers closingSessionHandle.events.Suggested wording
- and the moment ends on the terminal `session:compacted` (success) / `session:trimmed` `auto-fallback` + and the moment ends on the completion event `session:compacted` (success) / `session:trimmed` `auto-fallback`🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/reference/contracts/sse-event-schema.md` at line 274, Update the wording in the SSE event schema around session:compacting, session:compacted, and session:trimmed so it does not describe the latter two as “terminal” events; keep the distinction that they are side events and use “completion event” or “end state” instead. Adjust the descriptive text in the SSE schema doc to preserve the lifecycle behavior for the SessionHandle.events stream while avoiding language that suggests closing the stream on session:compacted or session:trimmed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@docs/reference/contracts/sse-event-schema.md`:
- Line 274: Update the wording in the SSE event schema around
session:compacting, session:compacted, and session:trimmed so it does not
describe the latter two as “terminal” events; keep the distinction that they are
side events and use “completion event” or “end state” instead. Adjust the
descriptive text in the SSE schema doc to preserve the lifecycle behavior for
the SessionHandle.events stream while avoiding language that suggests closing
the stream on session:compacted or session:trimmed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0226adaa-ce15-4495-bed8-f74ecb9a6019
📒 Files selected for processing (6)
apps/cli/src/commands/chat.test.tsapps/cli/src/commands/chat.tsapps/cli/src/commands/repl-commands.test.tsapps/cli/src/render/tui/chat-ink.tsxdocs/reference/contracts/sse-event-schema.mddocs/roadmap/phases/node-runtime-upgrade.md
✅ Files skipped from review due to trivial changes (1)
- docs/roadmap/phases/node-runtime-upgrade.md
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/cli/src/commands/chat.test.ts
- apps/cli/src/commands/repl-commands.test.ts
…intWidth 100) The clearedNotice-sanitize call and the resume /clear expect(...).toBe(...) exceeded printWidth 100; prettier wraps the call argument / the .toBe() argument respectively. Formatting-only, no behavior change. Verified locally: prettier --check ., and pnpm turbo run lint typecheck test (23 tasks green, 3415 tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@coderabbitai review all |
|
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/llm/src/adapters/gemini.ts (1)
1033-1043: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider a shared seam-defaults object to avoid triplicated boilerplate.
This exact
contextLimit/managesOwnContext/estimateTokensblock is duplicated verbatim across the Anthropic, OpenAI/DeepSeek, and Gemini adapters. Extracting a shared default intoshared.tsand spreading it in each adapter would remove the duplication and prevent future drift (e.g. one adapter forgetting to updatemanagesOwnContextwhen it eventually specializes it).♻️ Proposed shared defaults
+// packages/llm/src/adapters/shared.ts +export const compactionSeamDefaults = { + managesOwnContext(): boolean { + return false; + }, + contextLimit: contextLimitFor, + estimateTokens: estimateRequestTokens, +};async pollMediaJob(...): Promise<MediaJobStatus> { return geminiPollVideo(transport, jobId, key, signal); }, - contextLimit(model: string): number | undefined { - return contextLimitFor(model); - }, - managesOwnContext(): boolean { - return false; - }, - estimateTokens(input: EstimateTokensInput): number { - return estimateRequestTokens(input); - }, + ...compactionSeamDefaults, };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/llm/src/adapters/gemini.ts` around lines 1033 - 1043, Extract the duplicated adapter fallback methods into a shared seam-defaults object in shared.ts and reuse it from Gemini, Anthropic, and OpenAI/DeepSeek instead of repeating the same contextLimit, managesOwnContext, and estimateTokens implementations. Keep the adapter-specific wrappers minimal by spreading the shared defaults into each adapter object, and preserve any existing model-specific overrides by layering them on top of the shared object.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/core/src/engine/agent-session.ts`:
- Around line 562-568: The post-turn auto-compaction path in
`AgentSession.sendMessage` is still unguarded, so a failure from
`#maybeAutoCompact`/`compact('auto-threshold')` can reject an otherwise
successful `sendMessage()` after `session:turn_completed` has already been
emitted. Wrap the `pending` handling in `sendMessage` so the auto-compaction
call is best-effort and never throws outward, matching the surrounding
`#estimateTokens` and `managesOwnContext`/`contextLimit` guard behavior; if
needed, catch and swallow/log any compaction error locally while preserving the
completed turn result.
In `@packages/db/drizzle/0006_mean_adam_destine.sql`:
- Line 1: The committed Drizzle migration SQL is out of sync with the schema, so
regenerate the database artifacts instead of editing only the SQL file. Re-run
the db generation flow for the `@relavium/db` package using the schema in
packages/db/src/schema.ts, then commit the updated migration output and any
related generated artifacts so the migration matches the current schema state.
In `@packages/db/drizzle/meta/0006_snapshot.json`:
- Around line 1-2062: The committed Drizzle snapshot is out of sync with the
current schema, so regenerate the migration artifacts instead of editing this
snapshot manually. Run the database generation flow for `@relavium/db`, then
commit the updated snapshot and paired migration SQL so `schema.ts` and the
migration history match. Use the existing Drizzle migration snapshot files as
the source of truth for this fix.
---
Nitpick comments:
In `@packages/llm/src/adapters/gemini.ts`:
- Around line 1033-1043: Extract the duplicated adapter fallback methods into a
shared seam-defaults object in shared.ts and reuse it from Gemini, Anthropic,
and OpenAI/DeepSeek instead of repeating the same contextLimit,
managesOwnContext, and estimateTokens implementations. Keep the adapter-specific
wrappers minimal by spreading the shared defaults into each adapter object, and
preserve any existing model-specific overrides by layering them on top of the
shared object.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d3dbdfd3-fbb4-4429-add3-4066b836ccf9
📒 Files selected for processing (66)
CLAUDE.mdapps/cli/src/chat/persister.test.tsapps/cli/src/chat/persister.tsapps/cli/src/chat/repl-info.test.tsapps/cli/src/chat/repl-info.tsapps/cli/src/chat/session-host.test.tsapps/cli/src/chat/session-host.tsapps/cli/src/commands/chat.test.tsapps/cli/src/commands/chat.tsapps/cli/src/commands/repl-commands.test.tsapps/cli/src/commands/repl-commands.tsapps/cli/src/config/resolve.test.tsapps/cli/src/config/resolve.tsapps/cli/src/engine/media-wiring.test.tsapps/cli/src/home/drive-home.tsxapps/cli/src/render/tui/chat-ink.tsxapps/cli/src/render/tui/chat-projection.test.tsapps/cli/src/render/tui/chat-projection.tsapps/cli/src/render/tui/chat-store.tsapps/cli/src/render/tui/home-app.tsxapps/cli/src/render/tui/home-controller.test.tsapps/cli/src/render/tui/home-controller.tsapps/cli/src/render/tui/palette-reducer.test.tsapps/cli/src/render/tui/session-view-model.test.tsapps/cli/src/render/tui/session-view-model.tsdocs/decisions/0036-run-loop-substrate-event-bus-and-execution-host.mddocs/decisions/0062-context-compaction-and-cli-history-commands.mddocs/decisions/README.mddocs/reference/cli/chat-session.mddocs/reference/cli/commands.mddocs/reference/contracts/agent-session-spec.mddocs/reference/contracts/config-spec.mddocs/reference/contracts/sse-event-schema.mddocs/reference/desktop/database-schema.mddocs/reference/shared-core/llm-provider-seam.mddocs/roadmap/current.mddocs/roadmap/deferred-tasks.mddocs/roadmap/phases/node-runtime-upgrade.mddocs/roadmap/phases/phase-2.5-cli-consolidation.mdpackages/core/src/engine/agent-session.test.tspackages/core/src/engine/agent-session.tspackages/core/src/engine/session-handle.tspackages/core/src/engine/session-resume.test.tspackages/core/src/engine/session-resume.tspackages/core/src/index.tspackages/db/drizzle/0006_mean_adam_destine.sqlpackages/db/drizzle/meta/0006_snapshot.jsonpackages/db/drizzle/meta/_journal.jsonpackages/db/src/schema.tspackages/db/src/session-store.test.tspackages/db/src/session-store.tspackages/llm/src/adapters/anthropic.tspackages/llm/src/adapters/gemini.tspackages/llm/src/adapters/openai.tspackages/llm/src/adapters/shared.test.tspackages/llm/src/adapters/shared.tspackages/llm/src/index.tspackages/llm/src/pricing.test.tspackages/llm/src/pricing.tspackages/llm/src/types.tspackages/shared/src/config.test.tspackages/shared/src/config.tspackages/shared/src/constants.tspackages/shared/src/run-event.test.tspackages/shared/src/run-event.tspackages/shared/src/session.ts
| @@ -0,0 +1 @@ | |||
| ALTER TABLE `session_messages` ADD `compaction_dropped_through_sequence` integer; No newline at end of file | |||
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Regenerate the DB migration artifacts.
CI already reports this migration is out of sync with packages/db/src/schema.ts, so the committed SQL will keep failing the db generation check until you rerun pnpm --filter @relavium/db db:generate.
🧰 Tools
🪛 GitHub Actions: CI / 2_lint · typecheck · test.txt
[error] 1-1: Migration out of sync. The committed @relavium/db migration is out of sync with src/schema.ts (or an upstream @relavium/shared enum the CHECKs derive from). Regenerate and commit: pnpm --filter @relavium/db db:generate
🪛 GitHub Actions: CI / lint · typecheck · test
[error] 1-1: db generation check failed: The committed @relavium/db migration is out of sync with src/schema.ts (or an upstream @relavium/shared enum the CHECKs derive from). Regenerate and commit with: pnpm --filter @relavium/db db:generate
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/db/drizzle/0006_mean_adam_destine.sql` at line 1, The committed
Drizzle migration SQL is out of sync with the schema, so regenerate the database
artifacts instead of editing only the SQL file. Re-run the db generation flow
for the `@relavium/db` package using the schema in packages/db/src/schema.ts, then
commit the updated migration output and any related generated artifacts so the
migration matches the current schema state.
Source: Pipeline failures
| { | ||
| "version": "6", | ||
| "dialect": "sqlite", | ||
| "id": "213ad2ee-ef8d-4342-ba04-661ac99cb116", | ||
| "prevId": "8a2df4cb-e109-46f9-8b5f-398998fc272f", | ||
| "tables": { | ||
| "agent_sessions": { | ||
| "name": "agent_sessions", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "agent_id": { | ||
| "name": "agent_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "agent_slug": { | ||
| "name": "agent_slug", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "agent_snapshot": { | ||
| "name": "agent_snapshot", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "title": { | ||
| "name": "title", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "model_id": { | ||
| "name": "model_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "working_dir": { | ||
| "name": "working_dir", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "git_ref": { | ||
| "name": "git_ref", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "fs_scope_tier": { | ||
| "name": "fs_scope_tier", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'sandboxed'" | ||
| }, | ||
| "status": { | ||
| "name": "status", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'active'" | ||
| }, | ||
| "context_json": { | ||
| "name": "context_json", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'{}'" | ||
| }, | ||
| "total_input_tokens": { | ||
| "name": "total_input_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "total_output_tokens": { | ||
| "name": "total_output_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "total_cost_microcents": { | ||
| "name": "total_cost_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "exported_workflow_path": { | ||
| "name": "exported_workflow_path", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "deleted_at": { | ||
| "name": "deleted_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "updated_at": { | ||
| "name": "updated_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_agent_sessions_status": { | ||
| "name": "idx_agent_sessions_status", | ||
| "columns": [ | ||
| "status", | ||
| "\"updated_at\" desc" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"agent_sessions\".\"deleted_at\" is null" | ||
| }, | ||
| "idx_agent_sessions_agent": { | ||
| "name": "idx_agent_sessions_agent", | ||
| "columns": [ | ||
| "agent_id", | ||
| "\"created_at\" desc" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"agent_sessions\".\"agent_id\" is not null" | ||
| }, | ||
| "idx_agent_sessions_updated": { | ||
| "name": "idx_agent_sessions_updated", | ||
| "columns": [ | ||
| "\"updated_at\" desc", | ||
| "\"id\" desc" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"agent_sessions\".\"deleted_at\" is null" | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "agent_sessions_agent_id_agents_id_fk": { | ||
| "name": "agent_sessions_agent_id_agents_id_fk", | ||
| "tableFrom": "agent_sessions", | ||
| "tableTo": "agents", | ||
| "columnsFrom": [ | ||
| "agent_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "no action", | ||
| "onUpdate": "no action" | ||
| }, | ||
| "agent_sessions_model_id_model_catalog_id_fk": { | ||
| "name": "agent_sessions_model_id_model_catalog_id_fk", | ||
| "tableFrom": "agent_sessions", | ||
| "tableTo": "model_catalog", | ||
| "columnsFrom": [ | ||
| "model_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "no action", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": { | ||
| "agent_sessions_fs_scope_tier_check": { | ||
| "name": "agent_sessions_fs_scope_tier_check", | ||
| "value": "\"agent_sessions\".\"fs_scope_tier\" in ('sandboxed', 'project', 'full')" | ||
| }, | ||
| "agent_sessions_status_check": { | ||
| "name": "agent_sessions_status_check", | ||
| "value": "\"agent_sessions\".\"status\" in ('active', 'idle', 'exported', 'ended')" | ||
| } | ||
| } | ||
| }, | ||
| "agents": { | ||
| "name": "agents", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "name": { | ||
| "name": "name", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "slug": { | ||
| "name": "slug", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "description": { | ||
| "name": "description", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "model_id": { | ||
| "name": "model_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "system_prompt": { | ||
| "name": "system_prompt", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "''" | ||
| }, | ||
| "tools": { | ||
| "name": "tools", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'[]'" | ||
| }, | ||
| "config": { | ||
| "name": "config", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'{}'" | ||
| }, | ||
| "input_schema": { | ||
| "name": "input_schema", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "output_schema": { | ||
| "name": "output_schema", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "tags": { | ||
| "name": "tags", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'[]'" | ||
| }, | ||
| "source_path": { | ||
| "name": "source_path", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "is_active": { | ||
| "name": "is_active", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "1" | ||
| }, | ||
| "deleted_at": { | ||
| "name": "deleted_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "updated_at": { | ||
| "name": "updated_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_agents_slug": { | ||
| "name": "idx_agents_slug", | ||
| "columns": [ | ||
| "slug" | ||
| ], | ||
| "isUnique": true, | ||
| "where": "\"agents\".\"deleted_at\" is null" | ||
| }, | ||
| "idx_agents_model": { | ||
| "name": "idx_agents_model", | ||
| "columns": [ | ||
| "model_id" | ||
| ], | ||
| "isUnique": false | ||
| }, | ||
| "idx_agents_active": { | ||
| "name": "idx_agents_active", | ||
| "columns": [ | ||
| "is_active", | ||
| "\"created_at\" desc" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"agents\".\"deleted_at\" is null" | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "agents_model_id_model_catalog_id_fk": { | ||
| "name": "agents_model_id_model_catalog_id_fk", | ||
| "tableFrom": "agents", | ||
| "tableTo": "model_catalog", | ||
| "columnsFrom": [ | ||
| "model_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "no action", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": {} | ||
| }, | ||
| "llm_providers": { | ||
| "name": "llm_providers", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "name": { | ||
| "name": "name", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "display_name": { | ||
| "name": "display_name", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "base_url": { | ||
| "name": "base_url", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "api_key_keychain_ref": { | ||
| "name": "api_key_keychain_ref", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "default_headers": { | ||
| "name": "default_headers", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'{}'" | ||
| }, | ||
| "is_active": { | ||
| "name": "is_active", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "1" | ||
| }, | ||
| "deleted_at": { | ||
| "name": "deleted_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "updated_at": { | ||
| "name": "updated_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_llm_providers_name": { | ||
| "name": "idx_llm_providers_name", | ||
| "columns": [ | ||
| "name" | ||
| ], | ||
| "isUnique": true, | ||
| "where": "\"llm_providers\".\"deleted_at\" is null" | ||
| } | ||
| }, | ||
| "foreignKeys": {}, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": {} | ||
| }, | ||
| "media_objects": { | ||
| "name": "media_objects", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "handle": { | ||
| "name": "handle", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "mime_type": { | ||
| "name": "mime_type", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "modality": { | ||
| "name": "modality", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "byte_length": { | ||
| "name": "byte_length", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "duration_ms": { | ||
| "name": "duration_ms", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "last_referenced_at": { | ||
| "name": "last_referenced_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "deleted_at": { | ||
| "name": "deleted_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "media_objects_handle_unique": { | ||
| "name": "media_objects_handle_unique", | ||
| "columns": [ | ||
| "handle" | ||
| ], | ||
| "isUnique": true | ||
| }, | ||
| "idx_media_objects_gc": { | ||
| "name": "idx_media_objects_gc", | ||
| "columns": [ | ||
| "last_referenced_at" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"media_objects\".\"deleted_at\" is null" | ||
| } | ||
| }, | ||
| "foreignKeys": {}, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": { | ||
| "media_objects_modality_check": { | ||
| "name": "media_objects_modality_check", | ||
| "value": "\"media_objects\".\"modality\" in ('image', 'audio', 'video', 'document')" | ||
| } | ||
| } | ||
| }, | ||
| "media_references": { | ||
| "name": "media_references", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "handle": { | ||
| "name": "handle", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "scope_kind": { | ||
| "name": "scope_kind", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "scope_id": { | ||
| "name": "scope_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_media_references_unique": { | ||
| "name": "idx_media_references_unique", | ||
| "columns": [ | ||
| "handle", | ||
| "scope_kind", | ||
| "scope_id" | ||
| ], | ||
| "isUnique": true | ||
| }, | ||
| "idx_media_references_scope": { | ||
| "name": "idx_media_references_scope", | ||
| "columns": [ | ||
| "scope_kind", | ||
| "scope_id" | ||
| ], | ||
| "isUnique": false | ||
| }, | ||
| "idx_media_references_handle": { | ||
| "name": "idx_media_references_handle", | ||
| "columns": [ | ||
| "handle" | ||
| ], | ||
| "isUnique": false | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "media_references_handle_media_objects_handle_fk": { | ||
| "name": "media_references_handle_media_objects_handle_fk", | ||
| "tableFrom": "media_references", | ||
| "tableTo": "media_objects", | ||
| "columnsFrom": [ | ||
| "handle" | ||
| ], | ||
| "columnsTo": [ | ||
| "handle" | ||
| ], | ||
| "onDelete": "cascade", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": { | ||
| "media_references_scope_kind_check": { | ||
| "name": "media_references_scope_kind_check", | ||
| "value": "\"media_references\".\"scope_kind\" in ('run', 'node', 'session', 'workspace')" | ||
| } | ||
| } | ||
| }, | ||
| "messages": { | ||
| "name": "messages", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "step_execution_id": { | ||
| "name": "step_execution_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "run_id": { | ||
| "name": "run_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "sequence_number": { | ||
| "name": "sequence_number", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "role": { | ||
| "name": "role", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "content": { | ||
| "name": "content", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "content_parts": { | ||
| "name": "content_parts", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "tool_calls": { | ||
| "name": "tool_calls", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "tool_call_id": { | ||
| "name": "tool_call_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "name": { | ||
| "name": "name", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "finish_reason": { | ||
| "name": "finish_reason", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_messages_step": { | ||
| "name": "idx_messages_step", | ||
| "columns": [ | ||
| "step_execution_id", | ||
| "sequence_number" | ||
| ], | ||
| "isUnique": false | ||
| }, | ||
| "idx_messages_run": { | ||
| "name": "idx_messages_run", | ||
| "columns": [ | ||
| "run_id", | ||
| "created_at" | ||
| ], | ||
| "isUnique": false | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "messages_step_execution_id_step_executions_id_fk": { | ||
| "name": "messages_step_execution_id_step_executions_id_fk", | ||
| "tableFrom": "messages", | ||
| "tableTo": "step_executions", | ||
| "columnsFrom": [ | ||
| "step_execution_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "cascade", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": {} | ||
| }, | ||
| "model_catalog": { | ||
| "name": "model_catalog", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "provider_id": { | ||
| "name": "provider_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "model_id": { | ||
| "name": "model_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "display_name": { | ||
| "name": "display_name", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "context_window_tokens": { | ||
| "name": "context_window_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "max_output_tokens": { | ||
| "name": "max_output_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "input_cost_per_mtok_microcents": { | ||
| "name": "input_cost_per_mtok_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "output_cost_per_mtok_microcents": { | ||
| "name": "output_cost_per_mtok_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "cached_input_cost_per_mtok_microcents": { | ||
| "name": "cached_input_cost_per_mtok_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "media_image_cost_microcents": { | ||
| "name": "media_image_cost_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "media_audio_cost_microcents": { | ||
| "name": "media_audio_cost_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "media_video_cost_microcents": { | ||
| "name": "media_video_cost_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "media_surface": { | ||
| "name": "media_surface", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'chat'" | ||
| }, | ||
| "supports_tool_calling": { | ||
| "name": "supports_tool_calling", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "0" | ||
| }, | ||
| "supports_vision": { | ||
| "name": "supports_vision", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "0" | ||
| }, | ||
| "supports_streaming": { | ||
| "name": "supports_streaming", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "1" | ||
| }, | ||
| "supports_json_mode": { | ||
| "name": "supports_json_mode", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "0" | ||
| }, | ||
| "capabilities": { | ||
| "name": "capabilities", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'{}'" | ||
| }, | ||
| "deprecation_date": { | ||
| "name": "deprecation_date", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "is_active": { | ||
| "name": "is_active", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "1" | ||
| }, | ||
| "deleted_at": { | ||
| "name": "deleted_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "updated_at": { | ||
| "name": "updated_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_model_catalog_provider_model": { | ||
| "name": "idx_model_catalog_provider_model", | ||
| "columns": [ | ||
| "provider_id", | ||
| "model_id" | ||
| ], | ||
| "isUnique": true, | ||
| "where": "\"model_catalog\".\"deleted_at\" is null" | ||
| }, | ||
| "idx_model_catalog_provider": { | ||
| "name": "idx_model_catalog_provider", | ||
| "columns": [ | ||
| "provider_id" | ||
| ], | ||
| "isUnique": false | ||
| }, | ||
| "idx_model_catalog_active": { | ||
| "name": "idx_model_catalog_active", | ||
| "columns": [ | ||
| "is_active" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"model_catalog\".\"deleted_at\" is null" | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "model_catalog_provider_id_llm_providers_id_fk": { | ||
| "name": "model_catalog_provider_id_llm_providers_id_fk", | ||
| "tableFrom": "model_catalog", | ||
| "tableTo": "llm_providers", | ||
| "columnsFrom": [ | ||
| "provider_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "no action", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": {} | ||
| }, | ||
| "run_costs": { | ||
| "name": "run_costs", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "run_id": { | ||
| "name": "run_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "node_id": { | ||
| "name": "node_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "model_id": { | ||
| "name": "model_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "input_tokens": { | ||
| "name": "input_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "output_tokens": { | ||
| "name": "output_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "cost_microcents": { | ||
| "name": "cost_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_run_costs_run": { | ||
| "name": "idx_run_costs_run", | ||
| "columns": [ | ||
| "run_id" | ||
| ], | ||
| "isUnique": false | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "run_costs_run_id_runs_id_fk": { | ||
| "name": "run_costs_run_id_runs_id_fk", | ||
| "tableFrom": "run_costs", | ||
| "tableTo": "runs", | ||
| "columnsFrom": [ | ||
| "run_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "cascade", | ||
| "onUpdate": "no action" | ||
| }, | ||
| "run_costs_model_id_model_catalog_id_fk": { | ||
| "name": "run_costs_model_id_model_catalog_id_fk", | ||
| "tableFrom": "run_costs", | ||
| "tableTo": "model_catalog", | ||
| "columnsFrom": [ | ||
| "model_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "no action", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": {} | ||
| }, | ||
| "run_events": { | ||
| "name": "run_events", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "run_id": { | ||
| "name": "run_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "step_execution_id": { | ||
| "name": "step_execution_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "seq": { | ||
| "name": "seq", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "event_type": { | ||
| "name": "event_type", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "level": { | ||
| "name": "level", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'info'" | ||
| }, | ||
| "node_id": { | ||
| "name": "node_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "payload_json": { | ||
| "name": "payload_json", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'{}'" | ||
| }, | ||
| "ts": { | ||
| "name": "ts", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_run_events_run_seq": { | ||
| "name": "idx_run_events_run_seq", | ||
| "columns": [ | ||
| "run_id", | ||
| "seq" | ||
| ], | ||
| "isUnique": true | ||
| }, | ||
| "idx_run_events_step": { | ||
| "name": "idx_run_events_step", | ||
| "columns": [ | ||
| "step_execution_id", | ||
| "ts" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"run_events\".\"step_execution_id\" is not null" | ||
| }, | ||
| "idx_run_events_run_type": { | ||
| "name": "idx_run_events_run_type", | ||
| "columns": [ | ||
| "run_id", | ||
| "event_type", | ||
| "ts" | ||
| ], | ||
| "isUnique": false | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "run_events_run_id_runs_id_fk": { | ||
| "name": "run_events_run_id_runs_id_fk", | ||
| "tableFrom": "run_events", | ||
| "tableTo": "runs", | ||
| "columnsFrom": [ | ||
| "run_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "cascade", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": {} | ||
| }, | ||
| "runs": { | ||
| "name": "runs", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "workflow_id": { | ||
| "name": "workflow_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "workflow_path": { | ||
| "name": "workflow_path", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "project_root": { | ||
| "name": "project_root", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "workflow_definition_snapshot": { | ||
| "name": "workflow_definition_snapshot", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "status": { | ||
| "name": "status", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'pending'" | ||
| }, | ||
| "execution_mode": { | ||
| "name": "execution_mode", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'local'" | ||
| }, | ||
| "trigger_type": { | ||
| "name": "trigger_type", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'manual'" | ||
| }, | ||
| "trigger_metadata": { | ||
| "name": "trigger_metadata", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'{}'" | ||
| }, | ||
| "input_json": { | ||
| "name": "input_json", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'{}'" | ||
| }, | ||
| "output_json": { | ||
| "name": "output_json", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "error_json": { | ||
| "name": "error_json", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "started_at": { | ||
| "name": "started_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "completed_at": { | ||
| "name": "completed_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "total_input_tokens": { | ||
| "name": "total_input_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "total_output_tokens": { | ||
| "name": "total_output_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "total_cost_microcents": { | ||
| "name": "total_cost_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "deleted_at": { | ||
| "name": "deleted_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "updated_at": { | ||
| "name": "updated_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_runs_workflow": { | ||
| "name": "idx_runs_workflow", | ||
| "columns": [ | ||
| "workflow_id", | ||
| "\"created_at\" desc" | ||
| ], | ||
| "isUnique": false | ||
| }, | ||
| "idx_runs_status": { | ||
| "name": "idx_runs_status", | ||
| "columns": [ | ||
| "status", | ||
| "\"created_at\" desc" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"runs\".\"deleted_at\" is null" | ||
| }, | ||
| "idx_runs_cost": { | ||
| "name": "idx_runs_cost", | ||
| "columns": [ | ||
| "workflow_id", | ||
| "created_at", | ||
| "total_cost_microcents" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"runs\".\"deleted_at\" is null" | ||
| }, | ||
| "idx_runs_created": { | ||
| "name": "idx_runs_created", | ||
| "columns": [ | ||
| "\"created_at\" desc", | ||
| "\"id\" desc" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"runs\".\"deleted_at\" is null" | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "runs_workflow_id_workflows_id_fk": { | ||
| "name": "runs_workflow_id_workflows_id_fk", | ||
| "tableFrom": "runs", | ||
| "tableTo": "workflows", | ||
| "columnsFrom": [ | ||
| "workflow_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "no action", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": { | ||
| "runs_status_check": { | ||
| "name": "runs_status_check", | ||
| "value": "\"runs\".\"status\" in ('pending', 'running', 'paused', 'completed', 'failed', 'cancelled')" | ||
| }, | ||
| "runs_execution_mode_check": { | ||
| "name": "runs_execution_mode_check", | ||
| "value": "\"runs\".\"execution_mode\" in ('local', 'cloud', 'managed')" | ||
| } | ||
| } | ||
| }, | ||
| "session_messages": { | ||
| "name": "session_messages", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "session_id": { | ||
| "name": "session_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "sequence_number": { | ||
| "name": "sequence_number", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "role": { | ||
| "name": "role", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "content": { | ||
| "name": "content", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "content_parts": { | ||
| "name": "content_parts", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "tool_calls": { | ||
| "name": "tool_calls", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "tool_call_id": { | ||
| "name": "tool_call_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "name": { | ||
| "name": "name", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "finish_reason": { | ||
| "name": "finish_reason", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "model_id": { | ||
| "name": "model_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "input_tokens": { | ||
| "name": "input_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "output_tokens": { | ||
| "name": "output_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "cost_microcents": { | ||
| "name": "cost_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "compaction_dropped_through_sequence": { | ||
| "name": "compaction_dropped_through_sequence", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_session_messages_seq": { | ||
| "name": "idx_session_messages_seq", | ||
| "columns": [ | ||
| "session_id", | ||
| "sequence_number" | ||
| ], | ||
| "isUnique": true | ||
| }, | ||
| "idx_session_messages_session": { | ||
| "name": "idx_session_messages_session", | ||
| "columns": [ | ||
| "session_id", | ||
| "created_at" | ||
| ], | ||
| "isUnique": false | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "session_messages_session_id_agent_sessions_id_fk": { | ||
| "name": "session_messages_session_id_agent_sessions_id_fk", | ||
| "tableFrom": "session_messages", | ||
| "tableTo": "agent_sessions", | ||
| "columnsFrom": [ | ||
| "session_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "cascade", | ||
| "onUpdate": "no action" | ||
| }, | ||
| "session_messages_model_id_model_catalog_id_fk": { | ||
| "name": "session_messages_model_id_model_catalog_id_fk", | ||
| "tableFrom": "session_messages", | ||
| "tableTo": "model_catalog", | ||
| "columnsFrom": [ | ||
| "model_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "no action", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": {} | ||
| }, | ||
| "step_executions": { | ||
| "name": "step_executions", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "run_id": { | ||
| "name": "run_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "node_id": { | ||
| "name": "node_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "node_type": { | ||
| "name": "node_type", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "agent_id": { | ||
| "name": "agent_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "agent_snapshot": { | ||
| "name": "agent_snapshot", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "model_id": { | ||
| "name": "model_id", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "attempt_number": { | ||
| "name": "attempt_number", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 1 | ||
| }, | ||
| "status": { | ||
| "name": "status", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'pending'" | ||
| }, | ||
| "input_json": { | ||
| "name": "input_json", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'{}'" | ||
| }, | ||
| "output_json": { | ||
| "name": "output_json", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "error_json": { | ||
| "name": "error_json", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "started_at": { | ||
| "name": "started_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "completed_at": { | ||
| "name": "completed_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "duration_ms": { | ||
| "name": "duration_ms", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "input_tokens": { | ||
| "name": "input_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "output_tokens": { | ||
| "name": "output_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "cached_tokens": { | ||
| "name": "cached_tokens", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "cost_microcents": { | ||
| "name": "cost_microcents", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": 0 | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "updated_at": { | ||
| "name": "updated_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_step_exec_run": { | ||
| "name": "idx_step_exec_run", | ||
| "columns": [ | ||
| "run_id", | ||
| "created_at" | ||
| ], | ||
| "isUnique": false | ||
| }, | ||
| "idx_step_exec_run_node": { | ||
| "name": "idx_step_exec_run_node", | ||
| "columns": [ | ||
| "run_id", | ||
| "node_id", | ||
| "attempt_number" | ||
| ], | ||
| "isUnique": false | ||
| }, | ||
| "idx_step_exec_agent": { | ||
| "name": "idx_step_exec_agent", | ||
| "columns": [ | ||
| "agent_id", | ||
| "\"created_at\" desc" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"step_executions\".\"agent_id\" is not null" | ||
| }, | ||
| "idx_step_exec_model": { | ||
| "name": "idx_step_exec_model", | ||
| "columns": [ | ||
| "model_id", | ||
| "\"created_at\" desc" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"step_executions\".\"model_id\" is not null" | ||
| }, | ||
| "idx_step_exec_cost": { | ||
| "name": "idx_step_exec_cost", | ||
| "columns": [ | ||
| "model_id", | ||
| "created_at", | ||
| "cost_microcents" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"step_executions\".\"model_id\" is not null" | ||
| } | ||
| }, | ||
| "foreignKeys": { | ||
| "step_executions_run_id_runs_id_fk": { | ||
| "name": "step_executions_run_id_runs_id_fk", | ||
| "tableFrom": "step_executions", | ||
| "tableTo": "runs", | ||
| "columnsFrom": [ | ||
| "run_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "cascade", | ||
| "onUpdate": "no action" | ||
| }, | ||
| "step_executions_agent_id_agents_id_fk": { | ||
| "name": "step_executions_agent_id_agents_id_fk", | ||
| "tableFrom": "step_executions", | ||
| "tableTo": "agents", | ||
| "columnsFrom": [ | ||
| "agent_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "no action", | ||
| "onUpdate": "no action" | ||
| }, | ||
| "step_executions_model_id_model_catalog_id_fk": { | ||
| "name": "step_executions_model_id_model_catalog_id_fk", | ||
| "tableFrom": "step_executions", | ||
| "tableTo": "model_catalog", | ||
| "columnsFrom": [ | ||
| "model_id" | ||
| ], | ||
| "columnsTo": [ | ||
| "id" | ||
| ], | ||
| "onDelete": "no action", | ||
| "onUpdate": "no action" | ||
| } | ||
| }, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": { | ||
| "step_executions_status_check": { | ||
| "name": "step_executions_status_check", | ||
| "value": "\"step_executions\".\"status\" in ('pending', 'running', 'completed', 'failed', 'skipped')" | ||
| } | ||
| } | ||
| }, | ||
| "workflows": { | ||
| "name": "workflows", | ||
| "columns": { | ||
| "id": { | ||
| "name": "id", | ||
| "type": "text", | ||
| "primaryKey": true, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "name": { | ||
| "name": "name", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "slug": { | ||
| "name": "slug", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "description": { | ||
| "name": "description", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "definition": { | ||
| "name": "definition", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "input_schema": { | ||
| "name": "input_schema", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "tags": { | ||
| "name": "tags", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "'[]'" | ||
| }, | ||
| "source_path": { | ||
| "name": "source_path", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "is_active": { | ||
| "name": "is_active", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false, | ||
| "default": "1" | ||
| }, | ||
| "deleted_at": { | ||
| "name": "deleted_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": false, | ||
| "autoincrement": false | ||
| }, | ||
| "created_at": { | ||
| "name": "created_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| }, | ||
| "updated_at": { | ||
| "name": "updated_at", | ||
| "type": "integer", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "autoincrement": false | ||
| } | ||
| }, | ||
| "indexes": { | ||
| "idx_workflows_slug": { | ||
| "name": "idx_workflows_slug", | ||
| "columns": [ | ||
| "slug" | ||
| ], | ||
| "isUnique": true, | ||
| "where": "\"workflows\".\"deleted_at\" is null" | ||
| }, | ||
| "idx_workflows_active": { | ||
| "name": "idx_workflows_active", | ||
| "columns": [ | ||
| "is_active", | ||
| "\"updated_at\" desc" | ||
| ], | ||
| "isUnique": false, | ||
| "where": "\"workflows\".\"deleted_at\" is null" | ||
| } | ||
| }, | ||
| "foreignKeys": {}, | ||
| "compositePrimaryKeys": {}, | ||
| "uniqueConstraints": {}, | ||
| "checkConstraints": {} | ||
| } | ||
| }, | ||
| "views": {}, | ||
| "enums": {}, | ||
| "_meta": { | ||
| "schemas": {}, | ||
| "tables": {}, | ||
| "columns": {} | ||
| }, | ||
| "internal": { | ||
| "indexes": { | ||
| "idx_agent_sessions_status": { | ||
| "columns": { | ||
| "\"updated_at\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| }, | ||
| "idx_agent_sessions_agent": { | ||
| "columns": { | ||
| "\"created_at\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| }, | ||
| "idx_agent_sessions_updated": { | ||
| "columns": { | ||
| "\"updated_at\" desc": { | ||
| "isExpression": true | ||
| }, | ||
| "\"id\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| }, | ||
| "idx_agents_active": { | ||
| "columns": { | ||
| "\"created_at\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| }, | ||
| "idx_runs_workflow": { | ||
| "columns": { | ||
| "\"created_at\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| }, | ||
| "idx_runs_status": { | ||
| "columns": { | ||
| "\"created_at\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| }, | ||
| "idx_runs_created": { | ||
| "columns": { | ||
| "\"created_at\" desc": { | ||
| "isExpression": true | ||
| }, | ||
| "\"id\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| }, | ||
| "idx_step_exec_agent": { | ||
| "columns": { | ||
| "\"created_at\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| }, | ||
| "idx_step_exec_model": { | ||
| "columns": { | ||
| "\"created_at\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| }, | ||
| "idx_workflows_active": { | ||
| "columns": { | ||
| "\"updated_at\" desc": { | ||
| "isExpression": true | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win
CI-confirmed: migration out of sync with schema.ts — must regenerate before merge.
Both CI jobs fail db-generation drift checks, meaning this committed snapshot (and its paired migration SQL) does not match the current packages/db/src/schema.ts definitions.
Run pnpm --filter @relavium/db db:generate and commit the regenerated snapshot/migration.
🧰 Tools
🪛 GitHub Actions: CI / 2_lint · typecheck · test.txt
[error] 1-1: Migration out of sync. The committed @relavium/db migration is out of sync with src/schema.ts (or an upstream @relavium/shared enum the CHECKs derive from). Regenerate and commit: pnpm --filter @relavium/db db:generate
🪛 GitHub Actions: CI / lint · typecheck · test
[error] 1-1: db generation check failed: The committed @relavium/db migration is out of sync with src/schema.ts (or an upstream @relavium/shared enum the CHECKs derive from). Regenerate and commit with: pnpm --filter @relavium/db db:generate
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/db/drizzle/meta/0006_snapshot.json` around lines 1 - 2062, The
committed Drizzle snapshot is out of sync with the current schema, so regenerate
the migration artifacts instead of editing this snapshot manually. Run the
database generation flow for `@relavium/db`, then commit the updated snapshot and
paired migration SQL so `schema.ts` and the migration history match. Use the
existing Drizzle migration snapshot files as the source of truth for this fix.
Source: Pipeline failures
… adapter seam defaults
Two verified PR-review findings on the 2.5.F branch.
- **agent-session.ts (valid):** the post-turn auto-compaction was best-effort for its seam-method calls
(managesOwnContext/contextLimit are try/caught) but NOT for `compact('auto-threshold')`, which RE-THROWS
an unclassified error — so such a throw would reject an already-completed `sendMessage()` after
`session:turn_completed` had fired. Guard the `compact()` call and treat an unclassified throw as a failure
so the deterministic `/trim` fallback still bounds the next turn (ADR-0062 §5); the completed turn stands.
- **llm adapters (valid nitpick):** the three adapters (Gemini, Anthropic, OpenAI/DeepSeek) carried
byte-identical `contextLimit` / `managesOwnContext` / `estimateTokens` wrappers. Extract them into a shared
`CONTEXT_SEAM_DEFAULTS` in adapters/shared.ts (reusing the existing `contextLimitFor`/`estimateRequestTokens`)
and spread it into each adapter; drop the now-unused imports. An adapter can still layer a specialised
`estimateTokens` after the spread.
Skipped (with reason):
- **Drizzle 0006 migration + snapshot "out of sync":** INVALID. `drizzle-kit generate` reports "No schema
changes, nothing to migrate" and leaves the tree clean — the committed migration + snapshot already match
schema.ts. Nothing to regenerate (the bot flagged it speculatively without running drizzle-kit).
Verified locally: prettier --check + pnpm turbo run lint typecheck test — 23 tasks green, 3415 tests
(including the four adapter conformance suites and the auto-compaction engine tests).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
PR #65 (ADR-0062 context commands) merged to main on 2026-07-05. Reconcile the roadmap + agent guide to reflect the completed state: - CLAUDE.md: replace the "next pickup is 2.5.F / G" status tail with 2.5.F ✅ Done — /clear (host-level fresh-session swap, TTY-interactive only), the session:compacting "Summarizing…" moment (amends ADR-0036), and the footer context-fullness indicator (contextWindowForModel); next is 2.5.G. - docs/roadmap/current.md: same reconciliation on the experience-arm pointer. - docs/roadmap/phases/phase-2.5-cli-consolidation.md: Status blockquote, the 2.5.F section header, and the closing callout now read Done/merged (incl. the Opus + Sonnet review round the merged PR carried). Spine 2.5.A/B/C/E + experience arm 2.5.D/F complete; next 2.5.G, additive lanes 2.5.H / I / J in parallel. Docs-only; no code or ADR change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>



What & why
Phase 2.5.F — the CLI conversation-history commands. Per an explicit maintainer scope decision, this reverses the roadmap's Phase-3 deferral of
/compactand builds the full context-compaction story: a longrelavium chat/ Home conversation no longer hard-fails at the model's context window. Design: ADR-0062 (Accepted).Delivered in three reviewed steps, each with an Opus + Sonnet adversarial review round (findings fixed and committed before moving on):
@relavium/shared(session:compacted/session:trimmedevents +SessionMessage.compactionfield +[chat].auto_compact/compact_threshold),@relavium/llmseam (estimateTokens/contextLimit/managesOwnContext+ 3 adapters),@relavium/db(nullable boundary column, migration 0006)AgentSession.compact()/trimHistory()+ system-prompt preamble (keep last exchange) + auto-threshold +reconstructSessionStatemarker-honoringSessionDeps, persister append-only marker (role-filtered boundary),/compact+/trim, auto-compaction notice + input-gated spinner + Esc-abortDesign highlights
role:'system'marker row (summary +droppedThroughSequence); nothing is deleted — the full transcript survives for/export+ audit. Resume separates the drop boundary (max(droppedThroughSequence)) from the preamble (newest marker with a summary), so a later summary-less/trimnever blanks a prior/compactsummary.[chat].compact_threshold(0.8) of the serving model's window, driven by real provider usage; thrash-guarded, cost-accounted, and surfaced as an inline⟳notice — never a silent spend. Degrades to a deterministic/trimon failure.resumableMessageSequences) so the engine and the host persister can never drift (the fix for a silent kept-message-loss trap the review caught).Two review rounds caught real bugs (all fixed)
/trim 1; a lone-dangling-userkept slice; an unbounded summary (thrash-guard hardening).Scope note
/clear(a fresh-session lifecycle swap, orthogonal to compaction) + two compaction-moment UX polishes (a labeled "Summarizing…" spinner — needs asession:compactingstart signal — and a footer context-fullness indicator) are the remaining 2.5.F items, documented honestly inchat-session.md+ the roadmap rather than shipped as a forbidden no-op stub.Verification
pnpm turbo run lint typecheck test build24/24 green (core 967, CLI 1294 tests) +format:check+lint:bundle-closure. Engine purity +@relavium/llmseam gates pass. No new runtime dependency; no vendor type crosses the seam.Not marked Done in the roadmap until merged (roadmap-done-after-merge).
🤖 Generated with Claude Code
Summary by CodeRabbit
/compact,/trim [n], and interactive-only/clear, with durable, resume-preserving compaction/trim boundaries.auto_compactandcompact_threshold, plus new compaction/trim lifecycle events and notices./clear.