Dashboard › opencode › Distillation
0ee9ee46-0b5a-4df5-b1e3-a4b83963f2e8["lore_tm_v1_nVO1hiK4l47VrAU3gjoSsoLt1sL1Np2p6JuT-V8trd4","lore_tm_v1__nqwoCM-O-0xmmAUQ6fbApfrvdxi87cnypUuz2Ig2SU","lore_tm_v1_PVR_HCKXGLwWydjVCTrN9Lk94ZzxcaAG1L4IJkZPrd8","lore_tm_v1_ChGHRR6U7W5uR9o2BrojQhJOAXCWtnaS4vAPGqsR2ng"]
Date: Sep 14, 2026
SessionInput.find queries SessionInputTable by SessionMessage.ID, terminates database defects with Effect.orDie, and returns either undefined or an Admitted value decoded by fromRow.SessionInput.admit is idempotent by message ID: it returns an existing input when found; otherwise publishes SessionEvent.PromptAdmitted, requires event.durable.seq as admittedSeq, and on a publication defect retries find(db, input.id), returning the stored row if projection succeeded or re-dying with the original defect.SessionInput.LifecycleConflict is a Schema.TaggedErrorClass carrying id: SessionMessage.ID.SessionInput.projectAdmitted rejects an input whose ID already exists in SessionMessageTable, inserts into SessionInputTable with .onConflictDoNothing(), and raises LifecycleConflict if no row was inserted.SessionInput.projectPrompted first conditionally sets promoted_seq where message ID and Session ID match and promoted_seq is null; it validates the resulting projection with matchesProjection. If already stored, it requires matching projection data and the same promotedSeq; if absent, it inserts a row with both admitted_seq and promoted_seq equal to input.promotedSeq.SessionInput.hasPending checks for at least one SessionInputTable row matching sessionID and delivery with promoted_seq IS NULL.SessionInput.equivalent requires equal delivery, sessionID, and prompts whose encoded forms have equal JSON.stringify output; matchesProjection additionally requires equal epoch-millisecond timeCreated values.SessionInput.publish publishes SessionEvent.Prompted for each selected row and returns rows.length. If publication defects with LifecycleConflict, it suppresses the defect only when find(db, id) shows the input already has a promotedSeq; all other defects remain fatal.SessionInput.promoteSteers selects all unpromoted "steer" inputs for a Session whose admitted_seq <= cutoff, orders them by ascending admitted_seq, and publishes them all.SessionInput.promoteNextQueued selects the earliest unpromoted "queue" input by ascending admitted_seq; it returns false if absent and otherwise publishes exactly that row and returns true.packages/core/package.json identifies @opencode-ai/core version 1.18.30 as a private MIT-licensed ESM package. Scripts are db: bun drizzle-kit, migration: bun run script/migration.ts, fix-node-pty: bun run script/fix-node-pty.ts, test: bun test --only-failures, and typecheck: tsgo --noEmit; binary opencode points to ./bin/opencode.packages/core/package.json exports ./effect/layer-node, ./effect/app-node, ./session/runner, ./system-context, and wildcard ./*; conditional imports #sqlite, #pty, and #fff each distinguish Bun and Node implementations and default to the Bun implementation.ab9408c81cc9916177641ae8e9f08624fc27b5b9 and head dc661f30cb2055ad6248fdc59b14998d834497be; headβs sole parent exactly matched the base, the ancestry check passed, binary diff SHA-256 e414f577c79eab2ae49be387b2f6fb48b013f9d8a61618d5298d62229bf8e4eb matched exactly, and git diff --check passed.git show; it did not read the worktree, run tests or services, access databases, or independently hash the built artifact because of the immutable-object boundary. The supplied artifact hash was treated only as supporting evidence.packages/core/src/session/execution/local.ts:17-37 creates a process owner and inserts an execution claim, but packages/core/src/session/recovery.ts:21-24 selects every claim without checking owner liveness or age, packages/core/src/session/recovery.ts:66-80 deletes without proving owner death, and the superseded-assistant path at packages/core/src/session/recovery.ts:33-35 deletes by execution ID alone. A second server can therefore treat an active execution as abandoned, remove its claim, inject a continuation, and start another drain.packages/core/src/session/runner/llm.ts:236-253, :306-312, :325-333, and :391-402 match only Session or assistant IDs, not execution ID or owner, and do not require one affected row. A stale runner continues to llm.stream after a zero-row update and can mutate a replacement claim, permitting concurrent provider calls and duplicate tool side effects for one Session.packages/core/src/session/recovery.ts:49-84 atomically admits the continuation and deletes the execution row, but execution.wake occurs later at packages/core/src/session/recovery.ts:104. A crash between them leaves a pending continuation in session_input, a committed recovery marker, and no session_execution row; the next startup scans only session_execution at line 21, so the Session remains idle until an unrelated external action wakes it. Required direction: startup must redrive committed, unpromoted recovery admissions.Step.Ended transaction changes the claim to { phase: "safe", assistant_message_id: null } at packages/core/src/session/runner/llm.ts:391-402, while the outer drain deletes it later at packages/core/src/session/execution/local.ts:38-47. If the process dies in that gap, packages/core/src/session/recovery.ts:37-61 treats the null-assistant claim as interrupted without checking whether the latest assistant completed; packages/core/src/session/projector.ts:410-417 admits another queued prompt and packages/core/src/session/message-updater.ts:237-247 creates model-visible synthetic context, potentially causing an unsolicited extra Step and repeated actions.packages/core/src/session/runner/llm.ts:224-225 invokes automatic compaction before marking execution unknown at :304-312; packages/core/src/session/compaction.ts:202-220 calls the provider, and ordinary compactIfNeeded supplies no commit hook. A crash during the provider request leaves execution "safe", so recovery can inject a continuation and repeat an uncertain compaction request, including its cost and disclosed history, without an explicit retry decision.Step.Recovered accepts semantic forgery and cross-Session references. packages/schema/src/session-event.ts:207-220 permits arbitrary prompt, delivery, error, recoveryMessageID, and optional assistantMessageID; packages/core/src/session/projector.ts:410-417 admits that prompt directly, while packages/core/src/session/message-updater.ts:237-274 makes the supplied error model-visible. A schema-valid replay can thus become arbitrary queued model input instead of a fixed continue/queue recovery operation.packages/core/src/session/projector.ts:393-408 validates assistantMessageID against the current Session but stores the distinct recoveryMessageID as assistant_message_id at :420-427; packages/core/src/session/sql.ts:168-190 constrains that ID only against global session_message.id, not (session_id, id). A replay can therefore name the current Sessionβs latest assistant while referencing a recovery message belonging to another Session, and the database accepts the inconsistency.packages/core/src/session/recovery.ts:21-107 loads every claim and processes all rows under one startup effect; one unrecoverable row aborts the global layer at :110-117, with no per-row quarantine or bound. Many legitimate abandoned Sessions can also launch many background drains after the sequential scan.packages/core/src/database/migration/20260914214636_session-execution.ts:9-16 declares id text PRIMARY KEY without explicit NOT NULL, despite SQLite rowid tables allowing null non-integer primary keys; packages/core/src/database/migration/20260914222526_session-execution-phase.ts:8 adds arbitrary non-null text without a CHECK restricting values to "safe" or "unknown".packages/core/src/session/recovery.ts:25-32, so malformed projected JSON can fail startup even though initial recovery needs only the ID.packages/core/src/session/sql.ts:187-189 prevent duplicate execution and continuation markers, and packages/core/src/session/recovery.ts:87-101 recognizes a concurrently completed recovery.packages/core/src/session/recovery.ts:25-35, and the projector repeats that validation transactionally at packages/core/src/session/projector.ts:393-408.packages/core/src/session/runner/llm.ts:304-312; local tool calls are durably published before execution at :269-298; recovery settles pending and running tools as unknown at packages/core/src/session/message-updater.ts:261-272 instead of replaying them directly.session_execution and session_recovery tables, never reads or writes event_sequence.owner_id, and never calls EventV2.claim.@opencode-ai/server import in packages/core/src, and packages/core/package.json has no Server dependency.SessionPrompt.prompt or SessionPrompt.loop call; its only SessionPrompt occurrence is a comment at packages/core/src/session/runner/llm.ts:49. The integrated legacy server still registers existing V1 SessionPrompt.node at packages/opencode/src/server/routes/instance/httpapi/server.ts:247, but the V2 recovery graph at lines 300-303 does not invoke it.packages/core/src/database/migration.gen.ts:43-45, and Session/recovery uniqueness indexes match the generated schema.DO-NOT-MERGE.