Dashboard › opencode › Session 0sqfYslviXri
0sqfYslviXri62EpGRead-only adversarial correctness review of frozen v11. Base ab9408c81cc9916177641ae8e9f08624fc27b5b9. Patch /tmp/opencode/session-recovery-final-v11.patch expected SHA-256 439358fbe6d556d25917c7152b1073bb0bac951927a9b28be050f89d7fc11f33. SEA /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server expected SHA-256 f629bfea3c141ae5b650902371176b605c79bdbb1aa266752c9e4641e1da1795. Do not mutate files/refs/index/services or run builds/tests. Verify hashes, clean apply, inspect all 33 files. Primary closure: a same-Session recovery row with the same execution ID but mismatched assistant, continuation, or phase must never suppress quarantine, whether wake_pending is true or false. Verify the initial ready/no-assistant pending-intent check excludes only the current execution ID, so current identity reaches full projector validation while ordinary input and other recovery execution intent still clear redundant claims. Verify the projection-defect handler compares full identity and quarantines exact execution+owner or proves responsibility transferred. Inspect regression quarantines an execution claim blocked by a conflicting recovery identity with default pending wake. Reassess all ownership, cancellation, uncertainty, replay, fairness, migration, and dependency invariants. The final synchronous check-to-external-call race is accepted; process-local interrupt routing is documented residual. Evidence: recovery 231/0/683; Core 1168/0/3272; package typechecks and manifest/migration/diff gates from v10 remain applicable except the changed Core file was re-typechecked; exact v11 SEA smoke passed health/schema/one continuation/idempotence. Return substantive file:line findings, PASS/CONCERN/MUST-FIX, residuals, and end exactly MERGE or DO-NOT-MERGE. No recall.
No files found
/home/byk/Code/opencode/AGENTS.md /home/byk/Code/opencode/packages/codemode/AGENTS.md /home/byk/Code/opencode/packages/session-ui/AGENTS.md /home/byk/Code/opencode/packages/schema/AGENTS.md /home/byk/Code/opencode/packages/opencode/AGENTS.md /home/byk/Code/opencode/packages/opencode/test/AGENTS.md /home/byk/Code/opencode/packages/stats/AGENTS.md /home/byk/Code/opencode/packages/opencode/test/server/AGENTS.md /home/byk/Code/opencode/packages/ui/AGENTS.md /home/byk/Code/opencode/packages/effect-drizzle-sqlite/AGENTS.md /home/byk/Code/opencode/packages/llm/AGENTS.md /home/byk/Code/opencode/packages/opencode/src/session/llm/AGENTS.md /home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/AGENTS.md /home/byk/Code/opencode/packages/desktop/AGENTS.md /home/byk/Code/opencode/packages/core/src/tool/AGENTS.md /home/byk/Code/opencode/packages/app/AGENTS.md /home/byk/Code/opencode/packages/app/e2e/AGENTS.md /home/byk/Code/opencode/packages/app/e2e/performance/AGENTS.md
/home/byk/Code/opencode/CONTRIBUTING.md
439358fbe6d556d25917c7152b1073bb0bac951927a9b28be050f89d7fc11f33 /tmp/opencode/session-recovery-final-v11.patch f629bfea3c141ae5b650902371176b605c79bdbb1aa266752c9e4641e1da1795 /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server dc661f30cb2055ad6248fdc59b14998d834497be ab9408c81cc9916177641ae8e9f08624fc27b5b9 M .lore.md M AGENTS.md M packages/core/schema.json M packages/core/src/database/migration.gen.ts M packages/core/src/database/migration/20260914170650_session-recovery.ts D packages/core/src/database/migration/20260914214636_session-execution.ts D packages/core/src/database/migration/20260914222526_session-execution-phase.ts M packages/core/src/database/schema.gen.ts M packages/core/src/session.ts M packages/core/src/session/compaction.ts M packages/core/src/session/context-epoch.ts M packages/core/src/session/execution/local.ts M packages/core/src/session/input.ts M packages/core/src/session/message-updater.ts M ...
<skill_content name="effect">
# Skill: effect
# Effect
This codebase uses Effect for typed, composable TypeScript services, schemas, and workflows.
## Source Of Truth
Use the current Effect v4 / effect-smol source, not memory or older Effect v2/v3 examples.
1. If `.opencode/references/effect-smol` is missing, clone `https://github.com/Effect-TS/effect-smol` there. Do this in the project, not in the skill folder.
2. Search `.opencode/references/effect-smol` for exact APIs, examples, tests, and naming patterns before answering or implementing Effect-specific code.
3. Also inspect existing repo code for local house style before introducing new patterns.
4. Prefer answers and implementations backed by specific source files or nearby repo examples.
## Guidelines
- Prefer current Effect v4 APIs and project-local patterns over old blog posts, examples, or package-memory guesses.
- Use `Effect.gen(function* () { ... })` for multi-step workflows.
- Use `Effect.fn("Name")` or ...
<path>/home/byk/Code/opencode/packages/core/package.json</path>
<type>file</type>
<content>
1: {
2: "$schema": "https://json.schemastore.org/package.json",
3: "version": "1.18.30",
4: "name": "@opencode-ai/core",
5: "type": "module",
6: "license": "MIT",
7: "private": true,
8: "scripts": {
9: "db": "bun drizzle-kit",
10: "migration": "bun run script/migration.ts",
11: "fix-node-pty": "bun run script/fix-node-pty.ts",
12: "test": "bun test --only-failures",
13: "typecheck": "tsgo --noEmit"
14: },
15: "bin": {
16: "opencode": "./bin/opencode"
17: },
18: "exports": {
19: "./effect/layer-node": "./src/effect/layer-node.ts",
20: "./effect/app-node": "./src/effect/app-node.ts",
21: "./session/runner": "./src/session/runner/index.ts",
22: "./system-context": "./src/system-context/index.ts",
23: "./*": "./src/*.ts"
24: },
25: "imports": {
26: "#sqlite": {
27: "bun": "./src/database/sqlite.bun.ts",
28: ...
<path>/home/byk/Code/opencode/packages/opencode/AGENTS.md</path>
<type>file</type>
<content>
1: # opencode database guide
2:
3: ## Database
4:
5: - **Schema**: Drizzle schema lives in `packages/core/src/**/*.sql.ts`.
6: - **Migrations**: database migrations live in `packages/core` and are applied by core.
7:
8: ## Development server
9:
10: - Running `bun dev` from `packages/opencode` starts the live interactive TUI. Do not run it as a blocking foreground command when you need to inspect the result.
11: - Start it in `tmux` instead: `tmux new-session -d -s opencode-dev 'bun dev'`.
12: - Capture the current TUI output with: `tmux capture-pane -pt opencode-dev`.
13: - Stop the session explicitly when done: `tmux kill-session -t opencode-dev`.
14:
15: # Module shape
16:
17: Do not use `export namespace Foo { ... }` for module organization. It is not
18: standard ESM, it prevents tree-shaking, and it breaks Node's native TypeScript
19: runner. ...
<path>/home/byk/Code/opencode/packages/opencode/test/AGENTS.md</path>
<type>file</type>
<content>
1: # Test Fixtures Guide
2:
3: ## Temporary Directory Fixture
4:
5: The `tmpdir` function in `fixture/fixture.ts` creates temporary directories for tests with automatic cleanup.
6:
7: ### Basic Usage
8:
9: ```typescript
10: import { tmpdir } from "./fixture/fixture"
11:
12: test("example", async () => {
13: await using tmp = await tmpdir()
14: // tmp.path is the temp directory path
15: // automatically cleaned up when test ends
16: })
17: ```
18:
19: ### Options
20:
21: - `git?: boolean` - Initialize a git repo with a root commit
22: - `config?: Partial<Config.Info>` - Write an `opencode.json` config file
23: - `init?: (dir: string) => Promise<T>` - Custom setup function, returns value accessible as `tmp.extra`
24: - `dispose?: (dir: string) => Promise<T>` - Custom cleanup function
25:
26: ### Examples
27:
28: **Git repository:**
29:
30: ```typescript
31: await using tmp = ...
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/AGENTS.md</path>
<type>file</type>
<content>
1: # HttpApi Route Patterns
2:
3: Use `HttpApiBuilder.group(...)` for normal HTTP endpoints, including streaming HTTP responses such as server-sent events. Handlers should yield stable services once while building the handler layer, then close over those services in endpoint implementations.
4:
5: ```ts
6: export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session", (handlers) =>
7: Effect.gen(function* () {
8: const session = yield* Session.Service
9:
10: return handlers.handle("list", () => session.list())
11: }),
12: )
13: ```
14:
15: For SSE endpoints, stay in `HttpApiBuilder.group(...)` and return `HttpServerResponse.stream(...)` from the handler. Annotate the endpoint success schema with `HttpApiSchema.asText({ contentType: "text/event-stream" })` so OpenAPI documents the stream content type. ...
<path>/home/byk/Code/opencode/CONTRIBUTING.md</path> <type>file</type> <content> 1: # Contributing to OpenCode 2: 3: We want to make it easy for you to contribute to OpenCode. Here are the most common type of changes that get merged: 4: 5: - Bug fixes 6: - Additional LSPs / Formatters 7: - Improvements to LLM performance 8: - Support for new providers 9: - Fixes for environment-specific quirks 10: - Missing standard behavior 11: - Documentation improvements 12: 13: However, any UI or core product feature must go through a design review with the core team before implementation. 14: 15: If you are unsure if a PR would be accepted, feel free to ask a maintainer or look for issues with any of the following labels: 16: 17: - [`help wanted`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelp-wanted) 18: - [`good first issue`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) 19: - ...
packages/core/schema.json | 579 +++++ packages/core/src/database/migration.gen.ts | 1 .../migration/20260914170650_session-recovery.ts | 58 + packages/core/src/database/schema.gen.ts | 49 packages/core/src/session.ts | 9 packages/core/src/session/compaction.ts | 47 packages/core/src/session/context-epoch.ts | 88 - packages/core/src/session/execution/local.ts | 155 + packages/core/src/session/input.ts | 373 +++ packages/core/src/session/message-updater.ts | 69 + packages/core/src/session/projector.ts | 89 + packages/core/src/session/recovery.ts | 496 +++++ packages/core/src/session/run-coordinator.ts | 35 packages/core/src/session/runner/index.ts | 4 packages/core/src/session/runner/llm.ts | 376 ++- .../core/src/session/runner/publish-llm-event.ts | 126 + ...
clean in-memory apply against ab9408c81cc9916177641ae8e9f08624fc27b5b9: 33 files patched bytes match current worktree: 33/33 MATCH 25 packages/core/schema.json MATCH 1 packages/core/src/database/migration.gen.ts MATCH 1 packages/core/src/database/migration/20260914170650_session-recovery.ts MATCH 7 packages/core/src/database/schema.gen.ts MATCH 2 packages/core/src/session.ts MATCH 4 packages/core/src/session/compaction.ts MATCH 10 packages/core/src/session/context-epoch.ts MATCH 1 packages/core/src/session/execution/local.ts MATCH 16 packages/core/src/session/input.ts MATCH 3 packages/core/src/session/message-updater.ts MATCH 3 packages/core/src/session/projector.ts MATCH 1 packages/core/src/session/recovery.ts MATCH 3 packages/core/src/session/run-coordinator.ts MATCH 2 packages/core/src/session/runner/index.ts MATCH 15 packages/core/src/session/runner/llm.ts MATCH 25 packages/core/src/session/runner/publish-llm-event.ts MATCH 5 ...
<path>/home/byk/Code/opencode/packages/core/src/session/recovery.ts</path>
<type>file</type>
<content>
1: export * as SessionRecovery from "./recovery"
2:
3: import { and, asc, desc, eq, exists, gt, isNull, lte, or, sql } from "drizzle-orm"
4: import { Clock, DateTime, Effect, Layer, Option, Schedule, Schema } from "effect"
5: import { Database } from "../database/database"
6: import { makeGlobalNode } from "../effect/app-node"
7: import { EventV2 } from "../event"
8: import { SessionEvent } from "./event"
9: import { SessionExecution } from "./execution"
10: import { SessionProjector } from "./projector"
11: import { SessionExecutionClaim } from "./execution/claim"
12: import { SessionInput } from "./input"
13: import { continuation } from "./recovery-id"
14: import { SessionSchema } from "./schema"
15: import {
16: MAX_WAKE_ATTEMPTS,
17: SESSION_EXECUTION_LEASE_MS,
18: SessionExecutionTable,
19: SessionInputTable,
20: SessionRecoveryTable,
21: SessionMessageTable,
22: ...
<path>/home/byk/Code/opencode/packages/core/src/session/input.ts</path>
<type>file</type>
<content>
1: export * as SessionInput from "./input"
2:
3: import { and, asc, desc, eq, isNull, lte } from "drizzle-orm"
4: import { DateTime, Effect, Option, Schema } from "effect"
5: import { Admitted, Delivery } from "@opencode-ai/schema/session-input"
6: import type { Database } from "../database/database"
7: import { EventV2 } from "../event"
8: import { SessionEvent } from "./event"
9: import { SessionMessage } from "./message"
10: import { Prompt } from "./prompt"
11: import { continuation, message } from "./recovery-id"
12: import { SessionSchema } from "./schema"
13: import { SessionInputTable, SessionMessageTable, SessionRecoveryTable } from "./sql"
14:
15: type DatabaseService = Database.Interface["db"]
16:
17: export { Admitted, Delivery }
18:
19: const decodePrompt = Schema.decodeUnknownSync(Prompt)
20: const encodePrompt = Schema.encodeSync(Prompt)
21: const continuePrompt = ...
<path>/home/byk/Code/opencode/packages/core/src/session/input.ts</path>
<type>file</type>
<content>
521: .orderBy(asc(SessionInputTable.admitted_seq))
522: .all()
523: .pipe(Effect.orDie)
524: return yield* publish(
525: db,
526: events,
527: sessionID,
528: rows.map((row) => row.input),
529: commit,
530: )
531: })
532:
533: export const promoteNextQueued = Effect.fn("SessionInput.promoteNextQueued")(function* (
534: db: DatabaseService,
535: events: EventV2.Interface,
536: sessionID: SessionSchema.ID,
537: commit?: (row: typeof SessionInputTable.$inferSelect) => Effect.Effect<void>,
538: ) {
539: const row = yield* db
540: .select({ input: SessionInputTable })
541: .from(SessionInputTable)
542: .leftJoin(
543: SessionRecoveryTable,
544: eq(SessionRecoveryTable.continuation_message_id, SessionInputTable.id),
545: )
546: .where(
547: and(
548: eq(SessionInputTable.session_id, sessionID),
549: ...
<path>/home/byk/Code/opencode/packages/core/src/session/execution/claim.ts</path>
<type>file</type>
<content>
1: export * as SessionExecutionClaim from "./claim"
2:
3: import { and, eq, gt } from "drizzle-orm"
4: import { Clock, Effect } from "effect"
5: import type { Database } from "../../database/database"
6: import type { SessionSchema } from "../schema"
7: import { SESSION_EXECUTION_LEASE_MS, SessionExecutionTable } from "../sql"
8:
9: type DatabaseService = Database.Interface["db"]
10:
11: export type Claim = {
12: readonly sessionID: SessionSchema.ID
13: readonly executionID: string
14: readonly ownerID: string
15: }
16:
17: export type Values = Partial<Pick<typeof SessionExecutionTable.$inferInsert, "assistant_message_id" | "phase">>
18:
19: export class Lost extends Error {
20: constructor(readonly claim: Claim) {
21: super(`Session execution claim lost: ${claim.executionID}`)
22: }
23: }
24:
25: const where = (claim: Claim, now: number) =>
26: and(
27: ...
<path>/home/byk/Code/opencode/packages/core/src/session/execution/local.ts</path>
<type>file</type>
<content>
1: import { and, asc, eq, isNull } from "drizzle-orm"
2: import { Cause, Clock, Duration, Effect, Exit, Layer, Schedule } from "effect"
3: import { Database } from "../../database/database"
4: import { LocationServiceMap } from "../../location-service-map"
5: import { makeGlobalNode } from "../../effect/app-node"
6: import { SessionRunCoordinator } from "../run-coordinator"
7: import { SessionRunner } from "../runner"
8: import { SessionMessage } from "../message"
9: import { SessionSchema } from "../schema"
10: import { SessionStore } from "../store"
11: import { SessionExecution } from "../execution"
12: import { SessionExecutionClaim } from "./claim"
13: import { SESSION_EXECUTION_LEASE_MS, SessionExecutionTable, SessionRecoveryTable } from "../sql"
14:
15: /** Current-process routing for implicit-local Locations. Future remote placement belongs here. ...
<path>/home/byk/Code/opencode/packages/core/src/session/run-coordinator.ts</path>
<type>file</type>
<content>
1: export * as SessionRunCoordinator from "./run-coordinator"
2:
3: import { Deferred, Effect, Exit, Fiber, FiberSet, Scope } from "effect"
4:
5: /** Serializes execution for each key while allowing different keys to run concurrently. */
6: export interface Coordinator<Key, E> {
7: /** Snapshots keys with an execution owned by this coordinator. */
8: readonly active: Effect.Effect<ReadonlySet<Key>>
9: /** Starts execution while idle or joins the active execution. */
10: readonly run: (key: Key) => Effect.Effect<void, E>
11: /** Registers one coalesced follow-up after newly recorded work. */
12: readonly wake: (key: Key) => Effect.Effect<void>
13: /** Stops active execution, runs cleanup, then starts work that arrived while stopping. */
14: readonly interrupt: (key: Key, cleanup?: Effect.Effect<void>) => Effect.Effect<void>
15: }
16:
17: type Entry<E> = {
18: ...
<path>/home/byk/Code/opencode/packages/core/src/session/projector.ts</path>
<type>file</type>
<content>
1: export * as SessionProjector from "./projector"
2:
3: import { and, desc, eq, gt, or, sql } from "drizzle-orm"
4: import { DateTime, Effect, Layer, Schema } from "effect"
5: import { Database } from "../database/database"
6: import { EventV2 } from "../event"
7: import { makeGlobalNode } from "../effect/app-node"
8: import { SessionEvent } from "./event"
9: import { SessionV1 } from "../v1/session"
10: import { WorkspaceTable } from "../control-plane/workspace.sql"
11: import { SessionMessage } from "./message"
12: import { SessionMessageUpdater } from "./message-updater"
13: import { SessionInput } from "./input"
14: import { Prompt } from "./prompt"
15: import { continuation } from "./recovery-id"
16: import { WorkspaceV2 } from "../workspace"
17: import {
18: MessageTable,
19: PartTable,
20: SessionInputTable,
21: SessionMessageTable,
22: SessionRecoveryTable,
23: ...
<path>/home/byk/Code/opencode/packages/core/src/session/projector.ts</path>
<type>file</type>
<content>
341: .set({ agent: event.data.agent, time_updated: DateTime.toEpochMillis(event.data.timestamp) })
342: .where(eq(SessionTable.id, event.data.sessionID))
343: .run()
344: .pipe(Effect.orDie, Effect.andThen(run(db, event))),
345: )
346: yield* events.project(SessionEvent.ModelSwitched, (event) =>
347: Effect.gen(function* () {
348: yield* db
349: .update(SessionTable)
350: .set({ model: event.data.model, time_updated: DateTime.toEpochMillis(event.data.timestamp) })
351: .where(eq(SessionTable.id, event.data.sessionID))
352: .run()
353: .pipe(Effect.orDie)
354: yield* run(db, event)
355: }),
356: )
357: yield* events.project(SessionEvent.Prompted, (event) =>
358: Effect.gen(function* () {
359: if (event.durable === undefined) return yield* ...
<path>/home/byk/Code/opencode/packages/core/src/session/message-updater.ts</path>
<type>file</type>
<content>
1: import { castDraft, produce, type WritableDraft } from "immer"
2: import { Effect } from "effect"
3: import { SessionEvent } from "./event"
4: import { SessionMessage } from "./message"
5: import { message } from "./recovery-id"
6:
7: export type MemoryState = {
8: messages: SessionMessage.Message[]
9: }
10:
11: export interface Adapter {
12: readonly getCurrentAssistant: () => Effect.Effect<SessionMessage.Assistant | undefined>
13: readonly getAssistant: (messageID: SessionMessage.ID) => Effect.Effect<SessionMessage.Assistant | undefined>
14: readonly getCurrentShell: (callID: string) => Effect.Effect<SessionMessage.Shell | undefined>
15: readonly updateAssistant: (assistant: SessionMessage.Assistant) => Effect.Effect<void>
16: readonly updateShell: (shell: SessionMessage.Shell) => Effect.Effect<void>
17: readonly appendMessage: (message: ...
<path>/home/byk/Code/opencode/packages/core/src/session/message-updater.ts</path>
<type>file</type>
<content>
281: }),
282: ),
283: )
284: for (const tool of draft.content) {
285: if (tool.type !== "tool" || (tool.state.status !== "pending" && tool.state.status !== "running")) continue
286: tool.time.completed = event.data.timestamp
287: tool.state = castDraft(
288: SessionMessage.ToolStateError.make({
289: status: "error",
290: error: { type: "unknown", message: "Tool execution outcome unknown after server restart" },
291: input: typeof tool.state.input === "string" ? {} : tool.state.input,
292: structured: tool.state.status === "running" ? tool.state.structured : {},
293: content: tool.state.status === "running" ? tool.state.content : [],
294: }),
295: )
296: }
297: ...
<path>/home/byk/Code/opencode/packages/core/src/session/runner/index.ts</path>
<type>file</type>
<content>
1: export * as SessionRunner from "./index"
2:
3: import type { LLMError } from "@opencode-ai/llm"
4: import { Context, Effect } from "effect"
5: import { SessionSchema } from "../schema"
6: import type { SessionMessage } from "../message"
7: import type { ContextSnapshotDecodeError, MessageDecodeError } from "../error"
8: import { SessionRunnerModel } from "./model"
9: import type { SystemContext } from "../../system-context/index"
10: import type { ToolOutputStore } from "../../tool-output-store"
11:
12: export type RunError =
13: | LLMError
14: | SessionRunnerModel.Error
15: | MessageDecodeError
16: | ContextSnapshotDecodeError
17: | SystemContext.InitializationBlocked
18: | ToolOutputStore.Error
19:
20: /** Runs one local continuation from already-recorded Session history. */
21: export interface Interface {
22: /** Drains eligible durable work. ...
<path>/home/byk/Code/opencode/packages/core/src/session/runner/llm.ts</path>
<type>file</type>
<content>
1: import {
2: LLM,
3: LLMClient,
4: LLMError,
5: LLMEvent,
6: Message,
7: SystemPart,
8: isContextOverflowFailure,
9: type ProviderErrorEvent,
10: } from "@opencode-ai/llm"
11: import { Cause, DateTime, Effect, Exit, Fiber, Layer, Option, Semaphore, Stream } from "effect"
12: import { and, eq } from "drizzle-orm"
13: import { AgentV2 } from "../../agent"
14: import { Config } from "../../config"
15: import { Database } from "../../database/database"
16: import { EventV2 } from "../../event"
17: import { Location } from "../../location"
18: import { ModelV2 } from "../../model"
19: import { PermissionV2 } from "../../permission"
20: import { ProviderV2 } from "../../provider"
21: import { QuestionV2 } from "../../question"
22: import { SystemContext } from "../../system-context/index"
23: import { SystemContextRegistry } from "../../system-context/registry"
24: ...
<path>/home/byk/Code/opencode/packages/core/src/session/runner/llm.ts</path>
<type>file</type>
<content>
501: yield* Effect.yieldNow
502: return yield* runAfterOverflowCompaction(
503: sessionID,
504: executionID,
505: undefined,
506: defect.transition.step,
507: ownerID,
508: )
509: }),
510: ),
511: )
512: },
513: )
514:
515: const runTurn: RunTurn = Effect.fnUntraced(function* (sessionID, executionID, promotion, step, ownerID) {
516: return yield* runTurnAttempt(
517: sessionID,
518: executionID,
519: promotion,
520: step,
521: ownerID,
522: compaction.compactAfterOverflow,
523: ).pipe(
524: Effect.catchDefect(
525: Effect.fnUntraced(function* (defect) {
526: if (!(defect instanceof TurnTransitionError)) return yield* ...
<path>/home/byk/Code/opencode/packages/core/src/session/runner/publish-llm-event.ts</path>
<type>file</type>
<content>
1: import { ToolOutput, type LLMEvent, type ProviderMetadata, type ToolResultValue, type Usage } from "@opencode-ai/llm"
2: import { DateTime, Effect } from "effect"
3: import { EventV2 } from "../../event"
4: import { ModelV2 } from "../../model"
5: import { SessionEvent } from "../event"
6: import { SessionMessage } from "../message"
7: import { SessionSchema } from "../schema"
8:
9: type Input = {
10: readonly sessionID: SessionSchema.ID
11: readonly agent: string
12: readonly model: ModelV2.Ref
13: readonly snapshot?: string
14: readonly startCommit: (assistantMessageID: SessionMessage.ID) => Effect.Effect<void>
15: readonly eventCommit: () => Effect.Effect<void>
16: }
17:
18: const safe = (value: number | undefined) => Math.max(0, Number.isFinite(value) ? (value ?? ...
<path>/home/byk/Code/opencode/packages/core/src/session/context-epoch.ts</path>
<type>file</type>
<content>
1: export * as SessionContextEpoch from "./context-epoch"
2:
3: import { eq } from "drizzle-orm"
4: import { DateTime, Effect, Schema } from "effect"
5: import type { Database } from "../database/database"
6: import { EventV2 } from "../event"
7: import { SystemContext } from "../system-context/index"
8: import { ContextSnapshotDecodeError } from "./error"
9: import { SessionEvent } from "./event"
10: import { SessionHistory } from "./history"
11: import { SessionInput } from "./input"
12: import { SessionMessage } from "./message"
13: import { SessionSchema } from "./schema"
14: import { SessionContextEpochTable } from "./sql"
15: import { SessionExecutionClaim } from "./execution/claim"
16:
17: type DatabaseService = Database.Interface["db"]
18:
19: interface Prepared {
20: readonly baseline: string
21: readonly baselineSeq: number
22: }
23:
24: export function ...
<path>/home/byk/Code/opencode/packages/core/src/session/compaction.ts</path>
<type>file</type>
<content>
1: export * as SessionCompaction from "./compaction"
2:
3: import { LLM, LLMError, LLMEvent, Message, type LLMRequest, type Model } from "@opencode-ai/llm"
4: import { DateTime, Effect, Stream } from "effect"
5: import type { Config } from "../config"
6: import type { EventV2 } from "../event"
7: import { SessionEvent } from "./event"
8: import { SessionMessage } from "./message"
9: import { SessionSchema } from "./schema"
10: import { Token } from "../util/token"
11:
12: const DEFAULT_BUFFER = 20_000
13: const DEFAULT_KEEP_TOKENS = 8_000
14: const TOOL_OUTPUT_MAX_CHARS = 2_000
15: const SUMMARY_OUTPUT_TOKENS = 4_096
16: const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response. ...
<path>/home/byk/Code/opencode/packages/core/src/session.ts</path> <type>file</type> <content> 320: const boundary = anchor 321: ? order === "asc" 322: ? gt(SessionMessageTable.seq, anchor.seq) 323: : lt(SessionMessageTable.seq, anchor.seq) 324: : undefined 325: const where = boundary 326: ? and(eq(SessionMessageTable.session_id, input.sessionID), boundary) 327: : eq(SessionMessageTable.session_id, input.sessionID) 328: const query = db 329: .select() 330: .from(SessionMessageTable) 331: .where(where) 332: .orderBy(order === "asc" ? asc(SessionMessageTable.seq) : desc(SessionMessageTable.seq)) 333: const rows = yield* (input.limit === undefined ? query.all() : query.limit(input.limit).all()).pipe( 334: Effect.orDie, 335: ) 336: return yield* Effect.forEach(direction === "previous" ? rows.toReversed() : rows, decode) 337: ...
<path>/home/byk/Code/opencode/packages/core/src/session/sql.ts</path>
<type>file</type>
<content>
1: import { sql } from "drizzle-orm"
2: import { check, sqliteTable, text, integer, index, primaryKey, real, uniqueIndex } from "drizzle-orm/sqlite-core"
3: import * as DatabasePath from "../database/path"
4: import { ProjectTable } from "../project/sql"
5: import type { SessionMessage } from "./message"
6: import type { Prompt } from "./prompt"
7: import type { SessionInput } from "./input"
8: import type { Snapshot } from "../snapshot"
9: import { PermissionV1 } from "../v1/permission"
10: import { ProjectV2 } from "../project"
11: import type { SessionSchema } from "./schema"
12: import type { MessageID, PartID, SessionV1 } from "../v1/session"
13: import { WorkspaceV2 } from "../workspace"
14: import { Timestamps } from "../database/schema.sql"
15: import type { SystemContext } from "../system-context/index"
16: import { AgentV2 } from "../agent"
17: import type { Revert } from ...
<path>/home/byk/Code/opencode/packages/core/src/database/migration/20260914170650_session-recovery.ts</path>
<type>file</type>
<content>
1: import { Effect } from "effect"
2: import type { DatabaseMigration } from "../migration"
3:
4: export default {
5: id: "20260914170650_session-recovery",
6: up(tx) {
7: return Effect.gen(function* () {
8: yield* tx.run(`ALTER TABLE \`session_input\` ADD \`wake_pending\` integer DEFAULT false NOT NULL;`)
9: yield* tx.run(
10: `ALTER TABLE \`session\` ADD \`wake_attempts\` integer DEFAULT 0 NOT NULL CONSTRAINT \`session_wake_attempts_check\` CHECK(\`wake_attempts\` >= 0 AND \`wake_attempts\` <= 9007199254740991);`,
11: )
12: yield* tx.run(`
13: CREATE TABLE \`session_execution\` (
14: \`id\` text NOT NULL,
15: \`session_id\` text NOT NULL,
16: \`owner_id\` text NOT NULL,
17: \`assistant_message_id\` text,
18: \`phase\` text NOT NULL,
19: ...
<path>/home/byk/Code/opencode/packages/core/src/database/migration.gen.ts</path>
<type>file</type>
<content>
30: import("./migration/20260603001617_session_message_projection_indexes"),
31: import("./migration/20260603040000_session_message_projection_order"),
32: import("./migration/20260603141458_session_input_inbox"),
33: import("./migration/20260603160727_jittery_ezekiel_stane"),
34: import("./migration/20260604172448_event_sourced_session_input"),
35: import("./migration/20260605003541_add_session_context_snapshot"),
36: import("./migration/20260605042240_add_context_epoch_agent"),
37: import("./migration/20260611035744_credential"),
38: import("./migration/20260611192811_lush_chimera"),
39: import("./migration/20260612174303_project_dir_strategy"),
40: import("./migration/20260622142730_simplify_session_context_epoch"),
41: import("./migration/20260622170816_reset_v2_session_state"),
42: ...
Found 74 matches /home/byk/Code/opencode/packages/core/schema.json: Line 66: "name": "session_execution", Line 78: "name": "session_recovery", Line 941: "table": "session_execution" Line 951: "table": "session_execution" Line 961: "table": "session_execution" Line 971: "table": "session_execution" Line 981: "table": "session_execution" Line 991: "table": "session_execution" Line 1001: "table": "session_execution" Line 1011: "table": "session_execution" Line 1079: "name": "wake_pending", Line 1171: "table": "session_recovery" Line 1181: "table": "session_recovery" Line 1191: "table": "session_recovery" Line 1201: "table": "session_recovery" Line 1211: "table": "session_recovery" Line 1219: "name": "wake_pending", Line 1221: "table": "session_recovery" Line 1229: "name": "wake_attempts", Line 1231: "table": ...
<path>/home/byk/Code/opencode/packages/core/src/database/schema.gen.ts</path> <type>file</type> <content> 140: \`message_id\` text NOT NULL, 141: \`session_id\` text NOT NULL, 142: \`time_created\` integer NOT NULL, 143: \`time_updated\` integer NOT NULL, 144: \`data\` text NOT NULL, 145: CONSTRAINT \`fk_part_message_id_message_id_fk\` FOREIGN KEY (\`message_id\`) REFERENCES \`message\`(\`id\`) ON DELETE CASCADE 146: ); 147: `) 148: yield* tx.run(` 149: CREATE TABLE \`session_context_epoch\` ( 150: \`session_id\` text PRIMARY KEY, 151: \`baseline\` text NOT NULL, 152: \`snapshot\` text NOT NULL, 153: \`baseline_seq\` integer NOT NULL, 154: CONSTRAINT \`fk_session_context_epoch_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE 155: ); 156: `) 157: yield* tx.run(` 158: ...
<path>/home/byk/Code/opencode/packages/schema/src/session-event.ts</path>
<type>file</type>
<content>
1: export * as SessionEvent from "./session-event"
2:
3: import { Schema } from "effect"
4: import { optional } from "./schema"
5: import { Event } from "./event"
6: import { ProviderMetadata, ToolContent } from "./llm"
7: import { Delivery } from "./session-delivery"
8: import { Model } from "./model"
9: import { DateTimeUtcFromMillis, NonNegativeInt, RelativePath } from "./schema"
10: import { FileAttachment, Prompt } from "./prompt"
11: import { SessionID } from "./session-id"
12: import { Location } from "./location"
13: import { SessionMessage } from "./session-message"
14: import { Revert } from "./revert"
15:
16: export { FileAttachment }
17:
18: export const Source = Schema.Struct({
19: start: NonNegativeInt,
20: end: NonNegativeInt,
21: text: Schema.String,
22: }).annotate({
23: identifier: "session.next.event.source",
24: })
25: export interface Source extends ...
<path>/home/byk/Code/opencode/packages/schema/src/session-event.ts</path>
<type>file</type>
<content>
220: export const Recovered = Event.define({
221: type: "session.next.step.recovered",
222: ...options,
223: schema: {
224: ...Base,
225: executionID: Schema.String,
226: assistantMessageID: SessionMessage.ID.pipe(optional),
227: phase: Schema.Literals(["ready", "unknown", "continue"]),
228: },
229: })
230: export type Recovered = typeof Recovered.Type
231: }
232:
233: export namespace Text {
234: export const Started = Event.define({
235: type: "session.next.text.started",
236: ...options,
237: schema: {
238: ...Base,
239: assistantMessageID: SessionMessage.ID,
240: textID: Schema.String,
241: },
242: })
243: export type Started = typeof Started.Type
244:
245: // Stream fragments are live-only; Text.Ended is the replayable full-value boundary.
246: export const Delta = Event.define({
247: ...
<path>/home/byk/Code/opencode/packages/schema/src/session-event.ts</path>
<type>file</type>
<content>
480: schema: { ...Base, messageID: SessionMessage.ID },
481: })
482: }
483:
484: export const DurableDefinitions = Event.inventory(
485: AgentSwitched,
486: ModelSwitched,
487: Moved,
488: Prompted,
489: PromptAdmitted,
490: PromptExecutionRequested,
491: ContextUpdated,
492: Synthetic,
493: Shell.Started,
494: Shell.Ended,
495: Step.Started,
496: Step.Ended,
497: Step.Failed,
498: Step.Interrupted,
499: Step.Recovered,
500: Text.Started,
501: Text.Ended,
502: Tool.Input.Started,
503: Tool.Input.Ended,
504: Tool.Called,
505: Tool.Progress,
506: Tool.Success,
507: Tool.Failed,
508: Reasoning.Started,
509: Reasoning.Ended,
510: Retried,
511: Compaction.Started,
512: Compaction.Ended,
513: RevertEvent.Staged,
514: RevertEvent.Cleared,
515: RevertEvent.Committed,
516: )
517:
518: export const Definitions = ...
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts</path>
<type>file</type>
<content>
50: import { Worktree } from "@/worktree"
51: import { RuntimeFlags } from "@/effect/runtime-flags"
52: import { MoveSession } from "@opencode-ai/core/control-plane/move-session"
53: import { Database } from "@opencode-ai/core/database/database"
54: import { AppNodeBuilderV1 } from "@/effect/app-node-builder-v1"
55: import { LayerNode } from "@opencode-ai/core/effect/layer-node"
56: import { httpClient } from "@opencode-ai/core/effect/app-node-platform"
57: import { EventV2 } from "@opencode-ai/core/event"
58: import { ModelsDev } from "@opencode-ai/core/models-dev"
59: import { Npm } from "@opencode-ai/core/npm"
60: import { PermissionSaved } from "@opencode-ai/core/permission/saved"
61: import { ProjectV2 } from "@opencode-ai/core/project"
62: import { ProjectCopy } from "@opencode-ai/core/project/copy"
63: import { PtyTicket } from ...
Found 3 matches /home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts: Line 237: SessionProjector.node, Line 300: AppNodeBuilderV1.build(LayerNode.group([SessionV2.node, SessionRecovery.node]), [ Line 302: [SessionExecution.node, SessionExecutionLocal.node],
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts</path> <type>file</type> <content> 220: Env.node, 221: Git.node, 222: Ripgrep.node, 223: Storage.node, 224: Snapshot.node, 225: Plugin.node, 226: ModelsDev.node, 227: Provider.node, 228: ProviderAuth.node, 229: Agent.node, 230: Skill.node, 231: Discovery.node, 232: Question.node, 233: Permission.node, 234: PermissionSaved.node, 235: Todo.node, 236: Session.node, 237: SessionProjector.node, 238: SessionStatus.node, 239: BackgroundJob.node, 240: RuntimeFlags.node, 241: EventV2Bridge.node, 242: SessionRunState.node, 243: SessionProcessor.node, 244: SessionCompaction.node, 245: SessionRevert.node, 246: SessionSummary.node, 247: SessionPrompt.node, 248: Instruction.node, 249: LLM.node, 250: LSP.node, 251: MCP.node, 252: McpAuth.node, 253: Command.node, 254: Truncate.node, 255: ToolRegistry.node, 256: Format.node, 257: ...
<path>/home/byk/Code/opencode/packages/server/src/routes.ts</path>
<type>file</type>
<content>
1: import { Database } from "@opencode-ai/core/database/database"
2: import { LayerNode } from "@opencode-ai/core/effect/layer-node"
3: import { httpClient } from "@opencode-ai/core/effect/app-node-platform"
4: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
5: import { EventV2 } from "@opencode-ai/core/event"
6: import { Credential } from "@opencode-ai/core/credential"
7: import { PermissionSaved } from "@opencode-ai/core/permission/saved"
8: import { PtyTicket } from "@opencode-ai/core/pty/ticket"
9: import { SessionV2 } from "@opencode-ai/core/session"
10: import { SessionExecution } from "@opencode-ai/core/session/execution"
11: import { LocationServiceMap } from "@opencode-ai/core/location-service-map"
12: import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
13: import { SessionRecovery } from ...
Found 1 matches
/home/byk/Code/opencode/packages/core/test/session-projector.test.ts:
Line 1251: it.effect("quarantines an execution claim blocked by a conflicting recovery identity", () =>
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1160: .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
1161: .run()
1162: yield* db
1163: .insert(SessionTable)
1164: .values(
1165: [sessionID, other].map((id) => ({
1166: id,
1167: project_id: Project.ID.global,
1168: slug: id,
1169: directory: "/project",
1170: title: "test",
1171: version: "test",
1172: })),
1173: )
1174: .run()
1175: yield* events.publish(SessionEvent.Step.Started, {
1176: sessionID: other,
1177: timestamp: created,
1178: assistantMessageID: assistantID,
1179: agent: "build",
1180: model,
1181: })
1182:
1183: const recovered = yield* events
1184: .publish(SessionEvent.Step.Recovered, {
1185: ...
Found 16 matches
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
Line 710: wake_pending: false,
Line 746: wake_pending: false,
/home/byk/Code/opencode/packages/core/test/session-projector.test.ts:
Line 186: expect(yield* db.select().from(SessionRecoveryTable).get()).toMatchObject({ wake_pending: false })
Line 208: expect(yield* db.select().from(SessionRecoveryTable).get()).toMatchObject({ wake_pending: false })
Line 863: wake_pending: false,
Line 868: ).toMatchObject({ wake_pending: false })
Line 1292: recovery_error: "Recovery projection failed",
Line 1376: { wake_pending: false },
Line 1763: .where(eq(SessionExecutionTable.recovery_error, "Recovery projection failed"))
/home/byk/Code/opencode/packages/core/test/session-prompt.test.ts:
Line 362: wake_pending: false,
Line 368: wake_pending: false,
Line 431: wake_pending: false,
...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1660: version: "test",
1661: })
1662: .run()
1663: yield* db
1664: .insert(SessionExecutionTable)
1665: .values({
1666: id: "expired-cancellation",
1667: session_id: sessionID,
1668: owner_id: "cancelled-owner",
1669: phase: "cancelling",
1670: time_created: 0,
1671: expires_at: 0,
1672: })
1673: .run()
1674: const wakes: string[] = []
1675:
1676: yield* SessionRecovery.recover().pipe(
1677: Effect.provideService(
1678: SessionExecution.Service,
1679: SessionExecution.Service.of({
1680: ...recoveryExecution,
1681: wake: (id) => Effect.sync(() => wakes.push(id)).pipe(Effect.asVoid),
1682: }),
1683: ),
1684: )
1685:
1686: expect(wakes).toEqual([])
1687: ...
Found 100 matches (more matches available)
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
Line 619: it.effect("redrives only the synthetic recovery continuation", () =>
Line 688: it.effect("clears durable execution intent when ordinary input is promoted", () =>
Line 715: it.effect("redrives an ordinary prompt after its advisory wake is lost", () =>
Line 752: it.effect("never dispatches a provider after ownership changes", () =>
Line 778: it.effect("never publishes streamed output after ownership changes", () =>
Line 805: it.effect("never starts a local tool after ownership changes", () =>
Line 845: it.effect("never fails interrupted tools after ownership changes", () =>
Line 902: it.effect("advertises and executes a globally attached application tool", () =>
Line 958: it.effect("starts a real runner turn after default prompt recording", () =>
Line 977: it.effect("streams one request with registry definitions from ...
<path>/home/byk/Code/opencode/packages/core/test/session-execution-local.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect } from "bun:test"
2: import { Deferred, Effect, Exit, Fiber, Scope } from "effect"
3: import { TestClock } from "effect/testing"
4: import { eq } from "drizzle-orm"
5: import { Database } from "@opencode-ai/core/database/database"
6: import { Project } from "@opencode-ai/core/project"
7: import { ProjectTable } from "@opencode-ai/core/project/sql"
8: import { AbsolutePath } from "@opencode-ai/core/schema"
9: import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
10: import { SessionSchema } from "@opencode-ai/core/session/schema"
11: import { SessionExecutionTable, SessionTable } from "@opencode-ai/core/session/sql"
12: import { testEffect } from "./lib/effect"
13:
14: const sessionID = SessionSchema.ID.make("ses_execution_local")
15: const it = testEffect(Database.layerFromPath(":memory:"))
16:
17: const ...
Found 42 matches
/home/byk/Code/opencode/packages/core/test/session-projector.test.ts:
Line 68: it.effect("atomically reconciles one abandoned execution into one continuation", () =>
Line 222: it.effect("records a recovery continuation without forging an assistant identity", () =>
Line 258: it.effect("recovers each abandoned execution once across repeated startup scans", () =>
Line 304: it.effect("never recovers a live execution lease", () =>
Line 342: it.effect("never recovers an execution superseded by a newer assistant", () =>
Line 389: it.effect("redrives a durable recovery wake after admission", () =>
Line 440: it.effect("quarantines a malformed recovery row without blocking a valid wake", () =>
Line 494: it.effect("quarantines a full batch of poison recovery rows before redriving valid work", () =>
Line 559: it.effect("fairly redrives recovery and ordinary work beyond a full bounded batch", () =>
Line 695: it.effect("rejects ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
930: Effect.gen(function* () {
931: const { db } = yield* Database.Service
932: const events = yield* EventV2.Service
933: yield* db
934: .insert(ProjectTable)
935: .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
936: .run()
937: yield* db
938: .insert(SessionTable)
939: .values({
940: id: sessionID,
941: project_id: Project.ID.global,
942: slug: "test",
943: directory: "/project",
944: title: "test",
945: version: "test",
946: })
947: .run()
948: yield* SessionInput.admit(db, events, {
949: id: SessionMessage.ID.make("msg_lost_ordinary_wake"),
950: sessionID,
951: prompt: Prompt.make({ text: "recover ordinary input" }),
952: delivery: "steer",
953: ...
Found 2 matches /home/byk/Code/opencode/packages/core/test/session-projector.test.ts: Line 1292: recovery_error: "Recovery projection failed", Line 1763: .where(eq(SessionExecutionTable.recovery_error, "Recovery projection failed"))
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect } from "bun:test"
2: import { DateTime, Deferred, Effect, Exit, Fiber, Schema } from "effect"
3: import { TestClock } from "effect/testing"
4: import { asc, eq, sql } from "drizzle-orm"
5: import { Database } from "@opencode-ai/core/database/database"
6: import { LayerNode } from "@opencode-ai/core/effect/layer-node"
7: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
8: import { EventV2 } from "@opencode-ai/core/event"
9: import { EventTable } from "@opencode-ai/core/event/sql"
10: import { ModelV2 } from "@opencode-ai/core/model"
11: import { Project } from "@opencode-ai/core/project"
12: import { ProjectTable } from "@opencode-ai/core/project/sql"
13: import { ProviderV2 } from "@opencode-ai/core/provider"
14: import { AbsolutePath } from "@opencode-ai/core/schema"
15: import { SessionV2 } from ...
<path>/home/byk/Code/opencode/packages/core/schema.json</path>
<type>file</type>
<content>
1: {
2: "version": "7",
3: "dialect": "sqlite",
4: "id": "9e445a5f-9a7b-4d00-97f6-f80fee3c9383",
5: "prevIds": [
6: "682548a8-b895-4f9b-a6af-fdd4b14a5fc9"
7: ],
8: "ddl": [
9: {
10: "name": "workspace",
11: "entityType": "tables"
12: },
13: {
14: "name": "data_migration",
15: "entityType": "tables"
16: },
17: {
18: "name": "account_state",
19: "entityType": "tables"
20: },
21: {
22: "name": "account",
23: "entityType": "tables"
24: },
25: {
26: "name": "control_account",
27: "entityType": "tables"
28: },
29: {
30: "name": "credential",
31: "entityType": "tables"
32: },
33: {
34: "name": "event_sequence",
35: "entityType": "tables"
36: },
37: {
38: "name": "event",
39: "entityType": "tables"
40: },
41: {
42: ...
<path>/home/byk/Code/opencode/packages/core/schema.json</path>
<type>file</type>
<content>
920: "entityType": "columns",
921: "table": "session_context_epoch"
922: },
923: {
924: "type": "integer",
925: "notNull": true,
926: "autoincrement": false,
927: "default": null,
928: "generated": null,
929: "name": "baseline_seq",
930: "entityType": "columns",
931: "table": "session_context_epoch"
932: },
933: {
934: "type": "text",
935: "notNull": true,
936: "autoincrement": false,
937: "default": null,
938: "generated": null,
939: "name": "id",
940: "entityType": "columns",
941: "table": "session_execution"
942: },
943: {
944: "type": "text",
945: "notNull": true,
946: "autoincrement": false,
947: "default": null,
948: "generated": null,
949: "name": "session_id",
950: "entityType": "columns",
951: "table": ...
<path>/home/byk/Code/opencode/packages/core/schema.json</path>
<type>file</type>
<content>
1800: "entityType": "fks",
1801: "table": "session_context_epoch"
1802: },
1803: {
1804: "columns": [
1805: "session_id"
1806: ],
1807: "tableTo": "session",
1808: "columnsTo": [
1809: "id"
1810: ],
1811: "onUpdate": "NO ACTION",
1812: "onDelete": "CASCADE",
1813: "nameExplicit": false,
1814: "name": "fk_session_execution_session_id_session_id_fk",
1815: "entityType": "fks",
1816: "table": "session_execution"
1817: },
1818: {
1819: "columns": [
1820: "session_id"
1821: ],
1822: "tableTo": "session",
1823: "columnsTo": [
1824: "id"
1825: ],
1826: "onUpdate": "NO ACTION",
1827: "onDelete": "CASCADE",
1828: "nameExplicit": false,
1829: "name": "fk_session_input_session_id_session_id_fk",
1830: "entityType": ...
<path>/home/byk/Code/opencode/packages/core/schema.json</path>
<type>file</type>
<content>
1940: "email",
1941: "url"
1942: ],
1943: "nameExplicit": false,
1944: "name": "control_account_pk",
1945: "entityType": "pks",
1946: "table": "control_account"
1947: },
1948: {
1949: "columns": [
1950: "project_id",
1951: "directory"
1952: ],
1953: "nameExplicit": false,
1954: "name": "project_directory_pk",
1955: "entityType": "pks",
1956: "table": "project_directory"
1957: },
1958: {
1959: "columns": [
1960: "id"
1961: ],
1962: "nameExplicit": true,
1963: "name": "session_execution_pk",
1964: "entityType": "pks",
1965: "table": "session_execution"
1966: },
1967: {
1968: "columns": [
1969: "execution_id"
1970: ],
1971: "nameExplicit": true,
1972: "name": "session_recovery_pk",
1973: ...
<path>/home/byk/Code/opencode/packages/core/schema.json</path>
<type>file</type>
<content>
2230: "table": "part"
2231: },
2232: {
2233: "columns": [
2234: {
2235: "value": "session_id",
2236: "isExpression": false
2237: }
2238: ],
2239: "isUnique": false,
2240: "where": null,
2241: "origin": "manual",
2242: "name": "part_session_idx",
2243: "entityType": "indexes",
2244: "table": "part"
2245: },
2246: {
2247: "columns": [
2248: {
2249: "value": "session_id",
2250: "isExpression": false
2251: }
2252: ],
2253: "isUnique": true,
2254: "where": null,
2255: "origin": "manual",
2256: "name": "session_execution_session_idx",
2257: "entityType": "indexes",
2258: "table": "session_execution"
2259: },
2260: {
2261: "columns": [
2262: {
2263: "value": "owner_id",
2264: ...
<path>/home/byk/Code/opencode/packages/core/src/session/recovery-id.ts</path>
<type>file</type>
<content>
1: import { SessionMessage } from "./message"
2:
3: export const message = (executionID: string, assistantMessageID?: SessionMessage.ID) =>
4: assistantMessageID ?? SessionMessage.ID.make(`msg_recovery_${executionID}`)
5:
6: export const continuation = (executionID: string) => SessionMessage.ID.make(`msg_recovery_continue_${executionID}`)
(End of file - total 6 lines)
</content>
Found 29 matches
/home/byk/Code/opencode/packages/core/test/session-prompt.test.ts:
Line 140: it.effect("exposes the execution registry", () =>
Line 147: it.effect("delegates execution continuation through SessionExecution", () =>
Line 159: it.effect("delegates process-local interruption through SessionExecution", () =>
Line 171: it.effect("delegates interruption without requiring a recorded Session", () =>
Line 181: it.effect("durably admits one user message before transcript promotion", () =>
Line 203: it.effect("resolves attachment MIME before admission", () =>
Line 224: it.effect("streams durable Session events after an aggregate sequence", () =>
Line 254: it.effect("resumes through a recorded message without appending another prompt", () =>
Line 275: it.effect("records distinct messages when the ID is omitted", () =>
Line 290: it.effect("returns the original recorded message when the ID is retried", () =>
Line 310: ...
<path>/home/byk/Code/opencode/packages/core/test/session-prompt.test.ts</path>
<type>file</type>
<content>
250: ).toEqual([[1, "session.next.prompt.admitted"]])
251: }),
252: )
253:
254: it.effect("resumes through a recorded message without appending another prompt", () =>
255: Effect.gen(function* () {
256: yield* setup
257: const session = yield* SessionV2.Service
258: const message = yield* session.prompt({
259: sessionID,
260: prompt: Prompt.make({ text: "Fix the failing tests" }),
261: resume: false,
262: })
263:
264: executionCalls.length = 0
265: wakeCalls.length = 0
266: yield* session.resume(sessionID)
267:
268: expect(yield* session.messages({ sessionID })).toEqual([])
269: expect(yield* admitted(message.id)).not.toHaveProperty("promotedSeq")
270: expect(executionCalls).toEqual([sessionID])
271: expect(wakeCalls).toEqual([])
272: }),
273: )
274:
275: ...
<path>/home/byk/Code/opencode/packages/core/test/database-migration.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect, test } from "bun:test"
2: import { $ } from "bun"
3: import { fileURLToPath } from "url"
4: import path from "path"
5: import { SqliteClient } from "@effect/sql-sqlite-bun"
6: import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
7: import { Effect, Exit, Layer } from "effect"
8: import { eq, inArray, sql } from "drizzle-orm"
9: import { DatabaseMigration } from "@opencode-ai/core/database/migration"
10: import { migrations } from "@opencode-ai/core/database/migration.gen"
11: import workspaceNameMigration from "@opencode-ai/core/database/migration/20260410174513_workspace-name"
12: import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510033149_session_usage"
13: import normalizeStoragePathsMigration from "@opencode-ai/core/database/migration/20260601010001_normalize_storage_paths"
14: import ...
<path>/home/byk/Code/opencode/packages/schema/test/event-manifest.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect, test } from "bun:test"
2: import { FileSystem, Integration, Permission, Project, Reference, Session, Workspace } from "../src"
3: import { EventManifest } from "../src/event-manifest"
4: import { IdeEvent } from "../src/ide-event"
5: import { SessionEvent } from "../src/session-event"
6: import { SessionTodo } from "../src/session-todo"
7: import { SessionV1 } from "../src/session-v1"
8: import { WorkspaceEvent } from "../src/workspace-event"
9:
10: describe("public event manifest", () => {
11: test("owns the complete public event surface", () => {
12: expect(EventManifest.ServerDefinitions.length).toBe(61)
13: expect(EventManifest.Definitions.length).toBe(91)
14: expect(SessionV1.Event.Definitions).toEqual([
15: SessionV1.Event.Created,
16: SessionV1.Event.Updated,
17: SessionV1.Event.Deleted,
18: ...
diff --git a/packages/schema/src/session-event.ts b/packages/schema/src/session-event.ts
index 3a559c3e38..2599bbb78f 100644
--- a/packages/schema/src/session-event.ts
+++ b/packages/schema/src/session-event.ts
@@ -57,84 +57,97 @@ export const AgentSwitched = Event.define({
schema: {
...Base,
messageID: SessionMessage.ID,
agent: Schema.String,
},
})
export type AgentSwitched = typeof AgentSwitched.Type
export const ModelSwitched = Event.define({
type: "session.next.model.switched",
...options,
schema: {
...Base,
messageID: SessionMessage.ID,
model: Model.Ref,
},
})
export type ModelSwitched = typeof ModelSwitched.Type
export const Moved = Event.define({
type: "session.next.moved",
...options,
schema: {
...Base,
location: Location.Ref,
subdirectory: RelativePath.pipe(optional),
},
})
export type Moved = typeof Moved.Type
export const Prompted = Event.define({
type: "session.next.prompted",
...
Found 16 matches
/home/byk/Code/opencode/packages/sdk/js/src/v2/gen/types.gen.ts:
Line 24: | EventSessionNextPromptExecutionRequested
Line 32: | EventSessionNextStepInterrupted
Line 33: | EventSessionNextStepRecovered
Line 874: requestExecution?: boolean
Line 1650: | SyncEventSessionNextPromptExecutionRequested
Line 1658: | SyncEventSessionNextStepInterrupted
Line 1659: | SyncEventSessionNextStepRecovered
Line 3430: requestExecution?: boolean
Line 3435: export type SyncEventSessionNextPromptExecutionRequested = {
Line 3585: export type SyncEventSessionNextStepInterrupted = {
Line 3602: export type SyncEventSessionNextStepRecovered = {
Line 4362: requestExecution?: boolean
Line 6464: requestExecution?: boolean
Line 6468: export type EventSessionNextPromptExecutionRequested = {
Line 6570: export type EventSessionNextStepInterrupted = {
Line 6581: export type EventSessionNextStepRecovered = {
<path>/home/byk/Code/opencode/packages/sdk/js/src/v2/gen/types.gen.ts</path>
<type>file</type>
<content>
6400: export type EventMessagePartRemoved = {
6401: id: string
6402: type: "message.part.removed"
6403: properties: {
6404: sessionID: string
6405: messageID: string
6406: partID: string
6407: }
6408: }
6409:
6410: export type EventSessionNextAgentSwitched = {
6411: id: string
6412: type: "session.next.agent.switched"
6413: properties: {
6414: timestamp: number
6415: sessionID: string
6416: messageID: string
6417: agent: string
6418: }
6419: }
6420:
6421: export type EventSessionNextModelSwitched = {
6422: id: string
6423: type: "session.next.model.switched"
6424: properties: {
6425: timestamp: number
6426: sessionID: string
6427: messageID: string
6428: model: ModelRef
6429: }
6430: }
6431:
6432: export type EventSessionNextMoved = {
6433: id: string
6434: type: "session.next.moved"
6435: properties: ...
Found 12 matches
/home/byk/Code/opencode/packages/sdk/openapi.json:
Line 15336: "$ref": "#/components/schemas/EventSessionNextStepInterrupted"
Line 15339: "$ref": "#/components/schemas/EventSessionNextStepRecovered"
Line 17899: "requestExecution": {
Line 20604: "$ref": "#/components/schemas/SyncEventSessionNextStepInterrupted"
Line 20607: "$ref": "#/components/schemas/SyncEventSessionNextStepRecovered"
Line 25709: "requestExecution": {
Line 26226: "SyncEventSessionNextStepInterrupted": {
Line 26283: "SyncEventSessionNextStepRecovered": {
Line 28599: "requestExecution": {
Line 34811: "requestExecution": {
Line 35156: "EventSessionNextStepInterrupted": {
Line 35192: "EventSessionNextStepRecovered": {
Found 4 matches /home/byk/Code/opencode/packages/sdk/openapi.json: Line 17919: "enum": ["session.next.prompt.execution_requested"] Line 25740: "enum": ["session.next.prompt.execution_requested.1"] Line 28622: "enum": ["session.next.prompt.execution_requested"] Line 34831: "enum": ["session.next.prompt.execution_requested"]
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
580: responseStream = Stream.concat(
581: Stream.fromIterable(fixture.partialEvents),
582: Stream.fromEffect(Deferred.succeed(streamed, undefined)).pipe(Stream.flatMap(() => Stream.never)),
583: )
584:
585: const runner = yield* SessionRunner.Service
586: yield* insertExecution(sessionID)
587: const fiber = yield* runner.run({ sessionID, force: true, executionID, ownerID: "test" }).pipe(Effect.forkChild)
588: yield* Deferred.await(streamed)
589: yield* Fiber.interrupt(fiber)
590: const { db } = yield* Database.Service
591: expect(
592: yield* db
593: .select({ id: SessionExecutionTable.id, phase: SessionExecutionTable.phase })
594: .from(SessionExecutionTable)
595: .where(eq(SessionExecutionTable.session_id, sessionID))
596: .get(),
597: ).toEqual({ id: executionID, phase: "unknown" })
598: ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
1940: LLMEvent.stepFinish({ index: 0, reason: "stop" }),
1941: LLMEvent.finish({ reason: "stop" }),
1942: ],
1943: ]
1944:
1945: yield* session.resume(sessionID)
1946:
1947: expect(requests).toHaveLength(2)
1948: expect(requests[1]?.messages.map((message) => message.role)).toEqual(["user", "assistant", "tool"])
1949: expect(authorizations).toMatchObject([{ sessionID, toolCallID: "call-echo" }])
1950: expect(executions).toEqual(["hello"])
1951: expect(yield* session.context(sessionID)).toMatchObject([
1952: { type: "user", text: "Echo this" },
1953: {
1954: type: "assistant",
1955: finish: "tool-calls",
1956: content: [
1957: {
1958: type: "tool",
1959: id: "call-echo",
1960: name: "echo",
1961: ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
3500: [
3501: LLMEvent.stepStart({ index: 0 }),
3502: LLMEvent.toolCall({ id: "call-blocked", name: "blocked", input: {} }),
3503: LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }),
3504: LLMEvent.finish({ reason: "tool-calls" }),
3505: ],
3506: [
3507: LLMEvent.stepStart({ index: 0 }),
3508: LLMEvent.stepFinish({ index: 0, reason: "stop" }),
3509: LLMEvent.finish({ reason: "stop" }),
3510: ],
3511: ]
3512:
3513: yield* session.resume(sessionID)
3514:
3515: expect(requests).toHaveLength(2)
3516: expect(yield* session.context(sessionID)).toMatchObject([
3517: { type: "user", text: "Call blocked" },
3518: {
3519: type: "assistant",
3520: content: [
3521: { type: "tool", id: "call-blocked", state: { ...
<path>/home/byk/Code/opencode/packages/core/test/session-run-coordinator.test.ts</path>
<type>file</type>
<content>
280: expect(runs).toBe(2)
281: }),
282: ),
283: )
284:
285: it.effect("finalizes interruption when cleanup dies", () =>
286: Effect.scoped(
287: Effect.gen(function* () {
288: const firstStarted = yield* Deferred.make<void>()
289: const cleanupStarted = yield* Deferred.make<void>()
290: const cleanupGate = yield* Deferred.make<void>()
291: const secondStarted = yield* Deferred.make<void>()
292: const defect = new Error("cleanup defect")
293: let runs = 0
294: const coordinator = yield* SessionRunCoordinator.make({
295: drain: () =>
296: Effect.sync(() => ++runs).pipe(
297: Effect.flatMap((run) =>
298: run === 1
299: ? Deferred.succeed(firstStarted, undefined).pipe(Effect.andThen(Effect.never))
300: ...
diff --git a/packages/core/test/session-runner-recorded.test.ts b/packages/core/test/session-runner-recorded.test.ts
index d45cc8c734..6f1b69f9a5 100644
--- a/packages/core/test/session-runner-recorded.test.ts
+++ b/packages/core/test/session-runner-recorded.test.ts
@@ -1,52 +1,52 @@
import { HttpRecorder } from "@opencode-ai/http-recorder"
import { HttpRecorderInternal } from "@opencode-ai/http-recorder/internal"
import * as OpenAIChat from "@opencode-ai/llm/protocols/openai-chat"
import { Auth, LLMClient, RequestExecutor } from "@opencode-ai/llm/route"
import { Database } from "@opencode-ai/core/database/database"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { EventV2 } from "@opencode-ai/core/event"
import { EventTable } from "@opencode-ai/core/event/sql"
import { PermissionV2 } from ...
<path>/home/byk/Code/opencode/packages/core/src/session/execution.ts</path>
<type>file</type>
<content>
1: export * as SessionExecution from "./execution"
2:
3: import { Context, Effect, Layer } from "effect"
4: import { LayerNode } from "../effect/layer-node"
5: import { Node } from "../effect/app-node"
6: import { SessionRunner } from "./runner/index"
7: import { SessionSchema } from "./schema"
8:
9: export interface Interface {
10: /** Snapshots active execution owned by this process. */
11: readonly active: Effect.Effect<ReadonlySet<SessionSchema.ID>>
12: /** Starts execution while idle or joins the active execution. */
13: readonly resume: (sessionID: SessionSchema.ID) => Effect.Effect<void, SessionRunner.RunError>
14: /** Registers newly recorded work. Repeated wakeups may coalesce. */
15: readonly wake: (sessionID: SessionSchema.ID) => Effect.Effect<void>
16: /** Interrupt active work owned by this process. Idle interruption is a no-op. ...
<path>/home/byk/Code/opencode/packages/sdk/openapi.json</path>
<type>file</type>
<content>
15290: {
15291: "$ref": "#/components/schemas/EventMessagePartUpdated"
15292: },
15293: {
15294: "$ref": "#/components/schemas/EventMessagePartRemoved"
15295: },
15296: {
15297: "$ref": "#/components/schemas/EventSessionNextAgentSwitched"
15298: },
15299: {
15300: "$ref": "#/components/schemas/EventSessionNextModelSwitched"
15301: },
15302: {
15303: "$ref": "#/components/schemas/EventSessionNextMoved"
15304: },
15305: {
15306: "$ref": "#/components/schemas/EventSessionNextPrompted"
15307: },
15308: {
15309: "$ref": "#/components/schemas/EventSessionNextPromptAdmitted"
15310: },
15311: {
15312: "$ref": ...
Found 2 matches
/home/byk/Code/opencode/packages/sdk/openapi.json:
Line 25724: "SyncEventSessionNextPromptExecution_requested": {
Line 34822: "EventSessionNextPromptExecution_requested": {
<path>/home/byk/Code/opencode/packages/sdk/openapi.json</path>
<type>file</type>
<content>
34780: "type": "object",
34781: "properties": {
34782: "id": {
34783: "type": "string",
34784: "pattern": "^evt_"
34785: },
34786: "type": {
34787: "type": "string",
34788: "enum": ["session.next.prompt.admitted"]
34789: },
34790: "properties": {
34791: "type": "object",
34792: "properties": {
34793: "timestamp": {
34794: "type": "number"
34795: },
34796: "sessionID": {
34797: "type": "string",
34798: "pattern": "^ses"
34799: },
34800: "messageID": {
34801: "type": "string",
34802: "pattern": "^msg_"
34803: },
34804: "prompt": {
34805: "$ref": ...
No files found
/home/byk/Code/opencode/packages/core/src/event.ts
Found 30 matches
/home/byk/Code/opencode/packages/core/src/event.ts:
Line 122: /** Local operational projection committed atomically with a new durable event. Not replayed or serialized. */
Line 123: readonly commit?: (seq: number) => Effect.Effect<void>
Line 179: const projectors = new Map<string, Subscriber[]>()
Line 180: // TODO: Bind durable projectors to exact type+version before supporting incompatible historical payloads.
Line 205: function commitDurableEvent(
Line 214: commit?: (seq: number) => Effect.Effect<void>,
Line 236: const list = projectors.get(event.type) ?? []
Line 239: const committed = yield* db
Line 240: .transaction(
Line 316: const committed = {
Line 321: yield* projector(committed)
Line 323: if (commit) yield* commit(seq)
Line 354: if (committed) ...
<path>/home/byk/Code/opencode/packages/core/src/event.ts</path>
<type>file</type>
<content>
200: )
201: yield* Effect.forEach(pubsub.typed.values(), PubSub.shutdown, { discard: true })
202: }),
203: )
204:
205: function commitDurableEvent(
206: definition: Definition,
207: event: Payload,
208: input?: {
209: readonly seq: number
210: readonly aggregateID: string
211: readonly ownerID?: string
212: readonly strictOwner?: boolean
213: },
214: commit?: (seq: number) => Effect.Effect<void>,
215: ) {
216: return Effect.gen(function* () {
217: const durable = definition?.durable
218: if (durable) {
219: const aggregateID = (event.data as Record<string, unknown>)[durable.aggregate]
220: if (typeof aggregateID !== "string") {
221: yield* Effect.die(
222: new ...
<path>/home/byk/Code/opencode/packages/core/src/event.ts</path>
<type>file</type>
<content>
385: seq: committed.seq,
386: version: definition.durable.version,
387: },
388: }
389: yield* notify(event as Payload, true)
390: return event
391: }
392: }
393: yield* notify(event as Payload, false)
394: return event
395: })
396: }
397:
398: const observe = (event: Payload, observer: (event: Payload) => Effect.Effect<void>) =>
399: Effect.suspend(() => observer(event)).pipe(
400: Effect.catchCauseIf(
401: (cause) => !Cause.hasInterrupts(cause),
402: (cause) => Effect.logError("Event listener failed", { eventID: event.id, eventType: event.type, cause }),
403: ),
404: )
405:
406: function notify(event: Payload, isolateListeners: boolean) {
407: return ...
base schema id: f14a9b18-8207-487e-a3d3-227e629ba9ad v11 prevIds: ['682548a8-b895-4f9b-a6af-fdd4b14a5fc9'] lineage match: False base ddl entries: 208 v11 ddl entries: 243
diff --git a/packages/core/schema.json b/packages/core/schema.json
index d0eeeebd5c..86f650e83d 100644
--- a/packages/core/schema.json
+++ b/packages/core/schema.json
@@ -1,25 +1,27 @@
{
"version": "7",
"dialect": "sqlite",
- "id": "f14a9b18-8207-487e-a3d3-227e629ba9ad",
- "prevIds": ["169a0f0f-d58f-479f-b024-fa1c7b9a09db"],
+ "id": "9e445a5f-9a7b-4d00-97f6-f80fee3c9383",
+ "prevIds": [
+ "682548a8-b895-4f9b-a6af-fdd4b14a5fc9"
+ ],
"ddl": [
{
"name": "workspace",
"entityType": "tables"
},
{
"name": "data_migration",
"entityType": "tables"
},
{
"name": "account_state",
"entityType": "tables"
},
{
"name": "account",
"entityType": "tables"
},
{
"name": "control_account",
"entityType": "tables"
@@ -43,48 +45,56 @@
{
"name": "project_directory",
"entityType": "tables"
},
{
"name": "project",
"entityType": "tables"
...
<path>/home/byk/Code/opencode/packages/core/script/migration.ts</path>
<type>file</type>
<content>
1: #!/usr/bin/env bun
2:
3: import { $ } from "bun"
4: import fs from "fs/promises"
5: import os from "os"
6: import path from "path"
7: import { pathToFileURL } from "url"
8: import { parseArgs } from "util"
9:
10: const root = path.resolve(import.meta.dirname, "../../..")
11: const snapshot = path.join(root, "packages/core/schema.json")
12: const tsDir = path.join(root, "packages/core/src/database/migration")
13: const registry = path.join(root, "packages/core/src/database/migration.gen.ts")
14: const schema = path.join(root, "packages/core/src/database/schema.gen.ts")
15: const args = parseArgs({
16: args: process.argv.slice(2),
17: options: {
18: check: { type: "boolean" },
19: name: { type: "string" },
20: },
21: })
22:
23: if (args.values.check) {
24: await check()
25: process.exit(0)
26: }
27:
28: await generate()
29:
30: async function generate() {
31: ...
ab9408c81cc9916177641ae8e9f08624fc27b5b9 f14a9b18-8207-487e-a3d3-227e629ba9ad ['169a0f0f-d58f-479f-b024-fa1c7b9a09db'] 208 dc661f30cb2055ad6248fdc59b14998d834497be 57591e35-6f8f-4cda-932c-6a08e85caae7 ['9401e0ed-b1e3-4557-bc94-1c9b29e1e09c'] 231 dc661f30cb (HEAD -> byk/cumulative) feat(core): recover interrupted sessions
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0ab933f620016bifWaoLpOu6F5
{
+ const { db } = yield* Database.Service
+ const events = yield* EventV2.Service
+ yield* db
+ .insert(ProjectTable)
+ .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
+ .run()
+ yield* db
+ .insert(SessionTable)
+ .values({
+ id: sessionID,
+ project_id: Project.ID.global,
+ slug: "test",
+ directory: "/project",
+ title: "test",
+ version: "test",
+ })
+ .run()
+ yield* db
+ .insert(SessionExecutionTable)
+ .values({
+ id: "ready-at-admission-boundary",
+ session_id: sessionID,
+ owner_id: "dead",
+ phase: "ready",
+ time_created: 0,
+ expires_at: 0,
+ })
+ .run()
+ const inputID = ...
439358fbe6d556d25917c7152b1073bb0bac951927a9b28be050f89d7fc11f33 /tmp/opencode/session-recovery-final-v11.patch f629bfea3c141ae5b650902371176b605c79bdbb1aa266752c9e4641e1da1795 /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server M .lore.md M AGENTS.md M packages/core/schema.json M packages/core/src/database/migration.gen.ts M packages/core/src/database/migration/20260914170650_session-recovery.ts D packages/core/src/database/migration/20260914214636_session-execution.ts D packages/core/src/database/migration/20260914222526_session-execution-phase.ts M packages/core/src/database/schema.gen.ts M packages/core/src/session.ts M packages/core/src/session/compaction.ts M packages/core/src/session/context-epoch.ts M packages/core/src/session/execution/local.ts M packages/core/src/session/input.ts M packages/core/src/session/message-updater.ts M packages/core/src/session/projector.ts M packages/core/src/session/recovery.ts M ...
(no output)
No correctness blocker found. I inspected all 33 patched files against the frozen base.
ab9408c81cc9916177641ae8e9f08624fc27b5b9 verified.439358fbe6d556d25917c7152b1073bb0bac951927a9b28be050f89d7fc11f33 verified.f629bfea3c141ae5b650902371176b605c79bdbb1aa266752c9e4641e1da1795 verified.git diff --check reported no whitespace errors.None.
hasPendingExecutionIntent excludes only the current recovery execution ID in the recovery-intent branch at packages/core/src/session/recovery.ts:30-62. Ordinary input remains eligible through the separate null-recovery branch at :47, while another recovery execution remains eligible through the inequality at :49-54.packages/core/src/session/recovery.ts:136-141 and :172-180. A current same-ID recovery row therefore never clears the claim before projector validation.Step.Recovered compares the full stored identity—Session, assistant, deterministic continuation, and phase—at packages/core/src/session/projector.ts:399-423. Any mismatch defects instead of returning idempotent success.packages/core/src/session/recovery.ts:187-210. It does not filter on wake_pending, so both true and false rows receive identical treatment.(session_id, execution_id, owner_id) claim at packages/core/src/session/recovery.ts:211-227. ...