Dashboard › opencode › Distillation
31e8b1e5-f3ff-42b0-87d3-ee33f8336a9a["lore_tm_v1_KwA98zT8_ax-RmklM3VaLplq1nmo9pOdIKRpWX_4dSQ","lore_tm_v1_SFUUY_jRpCheBFZDeDhCGKlWzdZ53VZfLb6WulqWPQ0","lore_tm_v1_tfhBDKBX8ZPqURmB95ef95S1xmQtgj8c0LsmdRbsJlg","lore_tm_v1_3SaJk9kMJr9FCaX0scGSdzElob5Jznfx8laaCioZYcI","lore_tm_v1_Xj-jK9W7yetSZCKOMXhzhKmEvfg_d1YaFQLky7KNsak","lore_tm_v1_6XiiLlVq2W4eMx5lqmo_wSUuRP0LAPZU8zp2fGTz4QY","lore_tm_v1_2NxCg0QqFWerUlqt2Pk0IbxlqYeoRd2EcpO85RcFu5M","lore_tm_v1_Yb2ZKsuqGWF3pUBA7bPqe45vHB4jt1SoF0t6fNSfo8s","lore_tm_v1_dHJsql-QAUgg7M0ad5D622Oxs8baicw4AjJ8Ps44Eeo"]
Date: Sep 15, 2026
packages/core/src/session/compaction.ts:155-163 showed token-bounded conversation splitting: entries are accumulated until Token.estimate(conversation[index]) would exceed tokens, then returned as head: conversation.slice(0, split).join("\n\n") and recent: conversation.slice(split).join("\n\n").packages/core/src/session/compaction.ts:166-179 builds either a fresh summary prompt containing <conversation> plus SUMMARY_TEMPLATE, or an update prompt additionally containing <prior-summary>, SUMMARY_UPDATE_INSTRUCTIONS, and SUMMARY_TEMPLATE.packages/core/src/session/compaction.ts:184-203 makes compactAfterOverflow decline compaction when context limits are absent/nonpositive, no eligible selected head or previous compaction exists, or the summary prompt exceeds context - summaryOutput; it caps summary output at SUMMARY_OUTPUT_TOKENS, creates a new SessionMessage.ID, and publishes SessionEvent.Compaction.Started with reason "auto".packages/core/src/session/compaction.ts:205-242 invokes optional beforeStream/afterStream hooks around an LLM stream with no tools and { maxTokens: summaryOutput }; it collects LLMEvent text deltas, marks provider errors, catches LLM.Error, rejects failed or blank summaries, then publishes SessionEvent.Compaction.Ended with summary text and selected.recent using { commit: input.commit }.packages/core/src/session/compaction.ts:244-259 enables automatic compaction only when config.auto is true and estimated system/messages/tools tokens exceed context - Math.max(output, config.buffer); overflow delegates to compactAfterOverflow.packages/core/src/session/projector.ts:370-382 showed SessionEvent.PromptAdmitted requires event.durable.seq and projects a SessionInput containing messageID, sessionID, prompt, delivery, timestamp, and admitted sequence.packages/core/src/session/projector.ts:391-431 handles SessionEvent.Step.Recovered by requiring a durable sequence; when assistantMessageID is present, it verifies that ID is the latest assistant row and decodes as an assistant, otherwise dies with Recovery target is not the latest assistant: ... or Recovery target is not an assistant: .... It then admits a queued continuation prompt Prompt.make({ text: "continue" }), runs normal projection, and inserts session_id, execution_id, assistant_message_id set to recoveryMessageID, continuation_message_id, and creation time into SessionRecoveryTable.packages/core/src/session/projector.ts:383-444 routes ContextUpdated, Synthetic, shell, step, text, tool, reasoning, and Compaction.Ended events through run(db, event); SessionEvent.Retried remains commented out.packages/core/src/session/message-updater.ts:223-235 showed failed and interrupted steps mark the owned assistant completed, set finish = "error", and attach the event error.packages/core/src/session/message-updater.ts:237-281 maps recovery phase "unknown" to Provider Step outcome unknown after server restart and other phases to Session execution interrupted by server restart. Without an assistantMessageID, it appends a synthetic recovery message; otherwise it completes the owned assistant as errored, appends text with ID recovery:${event.data.executionID}, and converts all pending/running tools to error state with message Tool execution outcome unknown after server restart, preserving structured/content only for formerly running tools.packages/core/src/session/message-updater.ts:283-300 appends an empty assistant text part on session.next.text.started, concatenates deltas onto the latest matching text part, and replaces its text with the final event text on session.next.text.ended. monolith. Implement the unchecked items in small reviewed slices: at packages/core/src/session/runner/llm.ts:49.33e64fbe08c166aefbfc3c5f1afd9e432aaa60c06e95d8e273163fc2c38b5142; the displayed result did not identify the underlying input beyond -.packages/core/src/session/projector.ts:119-157 showed run(db, event) requires durable sequence for updates and scopes writes by both message ID and session ID. Its getCurrentAssistant() selects only the latest assistant by descending SessionMessageTable.seq, returning it only if incomplete; this enforces that newer turns supersede stale incomplete rows and older assistant projections are never resumed.packages/core/src/session/projector.ts:159-176 implements getAssistant(messageID) by selecting the assistant row matching both messageID and the current eventβs sessionID, decoding it, and returning it only when its decoded type is "assistant".packages/core/src/session/recovery.ts:102: if (recovered) yield* execution.wake(claim.session_id).packages/core/src/session/projector.ts:486-490: deletion from SessionInputTable is scoped to the session and inputs whose admitted_seq or promoted_seq exceeds the revert boundary sequence.SessionInputTable implementation and pending-input queries to packages/core/src/session/input.ts, schema definition to packages/core/src/session/sql.ts:140, and its foreign-key reference to packages/core/src/session/sql.ts:183 with { onDelete: "cascade" }.packages/core/src/database/schema.gen.ts:277 and migration packages/core/src/database/migration/20260604172448_event_sourced_session_input.ts:37: session_input_session_pending_delivery_seq_idx over session_id, promoted_seq, delivery, and admitted_seq. Older migrations used seq in packages/core/src/database/migration/20260603160727_jittery_ezekiel_stane.ts:13 and a three-column session_input_session_pending_seq_idx in packages/core/src/database/migration/20260603141458_session_input_inbox.ts:21.packages/core/src/session.ts:360-385 showed V2Session.prompt runs uninterruptibly, verifies the session, resolves the prompt, defaults the message ID via SessionMessage.ID.create() and delivery to "steer", admits through SessionInput.admit, maps SessionInput.LifecycleConflict to PromptConflictError, checks admitted-input equivalence, and wakes execution unless input.resume === false.packages/core/src/session.ts:346-358 exposes durable filtered session event streaming after an optional sequence and aggregate history through EventV2.readAggregate with SessionDurable.packages/core/src/session.ts:387-391 currently returns OperationUnavailableError for both "shell" and "skill" operations.packages/core/src/database/migration.ts:11-40 showed migration application is serialized by Semaphore.makeUnsafe(1). If a session table exists it runs applyOnly; a nonempty database without session dies with Database is not empty and has no session table; an empty database installs generated schema, creates the migration journal, and records every generated migration ID in one transaction.packages/core/src/database/migration.ts:43-94 creates the migration journal if absent and, when empty, seeds it from legacy __drizzle_migrations: a legacy name column is copied directly, while journals without name derive %Y%m%d%H%M%S prefixes from created_at and match them against migration IDs. An unmatched timestamp dies with Legacy migration timestamp ${entry.created_at} does not match any known migration.packages/core/src/database/migration.ts:96-106 skips completed migration IDs and applies each remaining migration in its own transaction, inserting the migration journal record only after migration.up(tx) succeeds.