Dashboard › opencode › Session 1CxDy5cg6FRj
1CxDy5cg6FRjzgYnOPerform a fresh independent frozen, read-only security/pentest review of the complete V2 interrupted-session recovery candidate in /home/byk/Code/opencode. A previous reviewer failed to return evidence; you replace it. Do not edit files, run formatters, mutate Git, touch production data, or start services.
Immutable scope:
ab9408c81cc9916177641ae8e9f08624fc27b5b9dc661f30cb2055ad6248fdc59b14998d834497be plus an uncommitted validation delta/tmp/opencode/session-recovery-full.patchf051a53df476dd924bda0a14c4cd91a63c9e7eafff53bbe71f0460571335fcbdpackages/opencode/dist/server/opencode-server1ecc9afe36c12b31a8892cdbfb8a1560ceec97255c3537c073b795351233be40
Verify both hashes first. Review every file in the canonical patch, using that patch as immutable scope and current files only for exact line evidence.Threat model and focus:
continue, Session/project isolation, unauthorized wake/recovery, startup scan denial of service, migration upgrade/downgrade.Hard invariants:
SessionPrompt.continue Step.No files found
f051a53df476dd924bda0a14c4cd91a63c9e7eafff53bbe71f0460571335fcbd /tmp/opencode/session-recovery-full.patch 1ecc9afe36c12b31a8892cdbfb8a1560ceec97255c3537c073b795351233be40 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/schema.gen.ts M packages/core/src/session/compaction.ts M packages/core/src/session/execution/local.ts M packages/core/src/session/message-updater.ts M packages/core/src/session/projector.ts M packages/core/src/session/recovery.ts M packages/core/src/session/runner/index.ts M packages/core/src/session/runner/llm.ts M packages/core/src/session/runner/publish-llm-event.ts M packages/core/src/session/sql.ts M packages/core/test/session-execution-local.test.ts M packages/core/test/session-projector.test.ts M ...
Found 24 matches /tmp/opencode/session-recovery-full.patch: Line 1: diff --git a/packages/core/schema.json b/packages/core/schema.json Line 732: diff --git a/packages/core/src/database/migration.gen.ts b/packages/core/src/database/migration.gen.ts Line 746: diff --git a/packages/core/src/database/migration/20260914170650_session-recovery.ts b/packages/core/src/database/migration/20260914170650_session-recovery.ts Line 777: diff --git a/packages/core/src/database/migration/20260914214636_session-execution.ts b/packages/core/src/database/migration/20260914214636_session-execution.ts Line 809: diff --git a/packages/core/src/database/migration/20260914222526_session-execution-phase.ts b/packages/core/src/database/migration/20260914222526_session-execution-phase.ts Line 826: diff --git a/packages/core/src/database/schema.gen.ts b/packages/core/src/database/schema.gen.ts Line 891: diff --git a/packages/core/src/session/compaction.ts ...
<path>/tmp/opencode/session-recovery-full.patch</path>
<type>file</type>
<content>
1: diff --git a/packages/core/schema.json b/packages/core/schema.json
2: index d0eeeebd5c..25475f6d34 100644
3: --- a/packages/core/schema.json
4: +++ b/packages/core/schema.json
5: @@ -1,8 +1,10 @@
6: {
7: "version": "7",
8: "dialect": "sqlite",
9: - "id": "f14a9b18-8207-487e-a3d3-227e629ba9ad",
10: - "prevIds": ["169a0f0f-d58f-479f-b024-fa1c7b9a09db"],
11: + "id": "ab874548-0fd0-4748-9f7d-762a6e5e7479",
12: + "prevIds": [
13: + "57591e35-6f8f-4cda-932c-6a08e85caae7"
14: + ],
15: "ddl": [
16: {
17: "name": "workspace",
18: @@ -60,6 +62,10 @@
19: "name": "session_context_epoch",
20: "entityType": "tables"
21: },
22: + {
23: + "name": "session_execution",
24: + "entityType": "tables"
25: + },
26: {
27: "name": "session_input",
28: "entityType": "tables"
29: @@ -68,6 +74,10 @@
30: "name": "session_message",
31: ...
<path>/tmp/opencode/session-recovery-full.patch</path>
<type>file</type>
<content>
1001: + ),
1002: + )
1003: + .run()
1004: + .pipe(Effect.orDie, Effect.asVoid)
1005: + const renew = (executionID: string) =>
1006: + db
1007: + .update(SessionExecutionTable)
1008: + .set({ expires_at: Date.now() + leaseDuration })
1009: + .where(and(eq(SessionExecutionTable.id, executionID), eq(SessionExecutionTable.owner_id, ownerID)))
1010: + .returning({ id: SessionExecutionTable.id })
1011: + .get()
1012: + .pipe(
1013: + Effect.orDie,
1014: + Effect.flatMap((claim) => (claim ? Effect.void : Effect.die(`Session execution claim lost: ${executionID}`))),
1015: + )
1016: + const coordinator = yield* SessionRunCoordinator.make<SessionSchema.ID, SessionRunner.RunError>({
1017: + drain: Effect.fnUntraced(function* (sessionID: SessionSchema.ID, force) {
1018: + const executionID = crypto.randomUUID()
1019: + const ...
<path>/tmp/opencode/session-recovery-full.patch</path>
<type>file</type>
<content>
2001: + const execution = yield* SessionExecutionLocal.make({
2002: + run: () => Deferred.succeed(started, undefined).pipe(Effect.andThen(Effect.never)),
2003: + }).pipe(Effect.provideService(Scope.Scope, child))
2004: + yield* execution.wake(sessionID)
2005: + yield* Deferred.await(started)
2006: + expect(yield* claims).toHaveLength(1)
2007: + yield* Scope.close(child, Exit.void)
2008: + expect(yield* claims).toHaveLength(1)
2009: + }),
2010: + )
2011: +
2012: + it.effect("clears the claim after explicit interruption", () =>
2013: + Effect.gen(function* () {
2014: + yield* insertSession
2015: + const started = Deferred.makeUnsafe<void>()
2016: + const execution = yield* SessionExecutionLocal.make({
2017: + run: () => Deferred.succeed(started, undefined).pipe(Effect.andThen(Effect.never)),
2018: + })
2019: + yield* ...
<path>/home/byk/Code/opencode/packages/core/src/session/execution/local.ts</path>
<type>file</type>
<content>
1: import { and, eq, inArray, lt, or } from "drizzle-orm"
2: import { Cause, 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 { SessionSchema } from "../schema"
9: import { SessionStore } from "../store"
10: import { SessionExecution } from "../execution"
11: import { SESSION_EXECUTION_LEASE_MS, SessionExecutionTable } from "../sql"
12:
13: /** Current-process routing for implicit-local Locations. Future remote placement belongs here. */
14: export const make = Effect.fnUntraced(function* (input: {
15: readonly run: (
16: sessionID: SessionSchema.ID,
17: force: ...
<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, desc, eq, isNull, lt } from "drizzle-orm"
4: import { DateTime, Effect, Layer, Schedule } 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 { SessionMessage } from "./message"
11: import { SessionProjector } from "./projector"
12: import { SessionExecutionTable, SessionRecoveryTable, SessionMessageTable } from "./sql"
13:
14: export const recover = Effect.fn("SessionRecovery.recover")(function* () {
15: const { db } = yield* Database.Service
16: const events = yield* EventV2.Service
17: const execution = yield* SessionExecution.Service
18: const claims = yield* db
19: .select()
20: ...
<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 terminalCommit: (assistantMessageID: SessionMessage.ID) => Effect.Effect<void>
16: readonly eventCommit: () => Effect.Effect<void>
17: }
18:
19: const safe = (value: number | undefined) => Math.max(0, Number.isFinite(value) ? ...
<path>/home/byk/Code/opencode/packages/core/src/session/runner/llm.ts</path>
<type>file</type>
<content>
150: Cause.isDieReason(reason) &&
151: (reason.defect instanceof PermissionV2.DeclinedError || reason.defect instanceof QuestionV2.RejectedError),
152: )
153:
154: type TurnTransition =
155: // Automatic compaction completed; rebuild the request from compacted history.
156: | { readonly _tag: "ContinueAfterCompaction"; readonly step: number }
157: // Overflow compaction completed; rebuild once through the path without overflow recovery. ...
/home/byk/Code/opencode/packages/core/src/event/sql.ts
/home/byk/Code/opencode/packages/core/src/event.ts
<path>/home/byk/Code/opencode/packages/core/src/event.ts</path>
<type>file</type>
<content>
1: export * as EventV2 from "./event"
2:
3: import { Cause, Context, Effect, Layer, Option, PubSub, Queue, Schema, Stream } from "effect"
4: import { Event } from "@opencode-ai/schema/event"
5: import type { Data, Definition, Payload } from "@opencode-ai/schema/event"
6: import { and, asc, eq, gt, inArray } from "drizzle-orm"
7: import { Database } from "./database/database"
8: import { EventSequenceTable, EventTable } from "./event/sql"
9: import { Location } from "./location"
10: import { makeGlobalNode } from "./effect/app-node"
11: import { isDeepStrictEqual } from "node:util"
12: import { Durable } from "@opencode-ai/schema/durable-event-manifest"
13:
14: export const ID = Event.ID
15: export type ID = import("@opencode-ai/schema/event").ID
16: export type { Data, Definition, Payload } from "@opencode-ai/schema/event"
17:
18: export type Subscriber<D extends Definition = Definition> = ...
<path>/home/byk/Code/opencode/packages/core/src/event.ts</path>
<type>file</type>
<content>
419: function publish<D extends Definition>(definition: D, data: Data<D>, options?: PublishOptions) {
420: return Effect.gen(function* () {
421: const serviceLocation = Option.getOrUndefined(yield* Effect.serviceOption(Location.Service))
422: const location =
423: options?.location ??
424: (serviceLocation
425: ? { directory: serviceLocation.directory, workspaceID: serviceLocation.workspaceID }
426: : undefined)
427: return yield* publishEvent(
428: definition,
429: {
430: id: options?.id ?? ID.create(),
431: ...(options?.metadata ? { metadata: options.metadata } : {}),
432: type: definition.type,
433: ...(location ? { location } : {}),
434: data,
435: } as Payload<D>,
436: ...
<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 { WorkspaceV2 } from "../workspace"
16: import {
17: MessageTable,
18: PartTable,
19: SessionInputTable,
20: SessionMessageTable,
21: SessionRecoveryTable,
22: SessionTable,
23: } from "./sql"
24: import type ...
<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/input.ts</path>
<type>file</type>
<content>
1: export * as SessionInput from "./input"
2:
3: import { and, asc, eq, isNull, lte } from "drizzle-orm"
4: import { DateTime, Effect, Schema } from "effect"
5: import { Admitted, Delivery } from "@opencode-ai/schema/session-input"
6: import type { Database } from "../database/database"
7: import type { EventV2 } from "../event"
8: import { SessionEvent } from "./event"
9: import { SessionMessage } from "./message"
10: import { Prompt } from "./prompt"
11: import { SessionSchema } from "./schema"
12: import { SessionInputTable, SessionMessageTable } from "./sql"
13:
14: type DatabaseService = Database.Interface["db"]
15:
16: export { Admitted, Delivery }
17:
18: const decodePrompt = Schema.decodeUnknownSync(Prompt)
19: const encodePrompt = Schema.encodeSync(Prompt)
20:
21: const fromRow = (row: typeof SessionInputTable.$inferSelect): Admitted =>
22: Admitted.make({
23: ...
Found 1 matches /home/byk/Code/opencode/packages/core/src/session/context-epoch.ts: Line 72: yield* events.publish(
<path>/home/byk/Code/opencode/packages/core/src/session/context-epoch.ts</path>
<type>file</type>
<content>
40: const prepareOnce = Effect.fnUntraced(function* (
41: db: DatabaseService,
42: events: EventV2.Interface,
43: context: Effect.Effect<SystemContext.SystemContext>,
44: sessionID: SessionSchema.ID,
45: ) {
46: const [value, stored, compaction] = yield* Effect.all(
47: [context, find(db, sessionID), SessionHistory.latestCompaction(db, sessionID)],
48: { concurrency: "unbounded" },
49: )
50: if (!stored) {
51: const generation = yield* SystemContext.initialize(value)
52: const baselineSeq = yield* insert(db, sessionID, generation)
53: return { baseline: generation.baseline, baselineSeq }
54: }
55:
56: const snapshot = yield* Schema.decodeUnknownEffect(SystemContext.Snapshot)(stored.snapshot).pipe(
57: Effect.mapError((error) => new ContextSnapshotDecodeError({ sessionID, details: String(error) })),
58: )
59: const replacementSeq = ...
Found 3 matches
/home/byk/Code/opencode/packages/core/src/session/recovery.ts:
Line 102: if (recovered) yield* execution.wake(claim.session_id)
/home/byk/Code/opencode/packages/core/src/session/projector.ts:
Line 370: yield* events.project(SessionEvent.PromptAdmitted, (event) =>
/home/byk/Code/opencode/packages/core/src/session/input.ts:
Line 55: .publish(SessionEvent.PromptAdmitted, {
/home/byk/Code/opencode/packages/core/src/session.ts
<path>/home/byk/Code/opencode/packages/core/src/session.ts</path>
<type>file</type>
<content>
1: export * as SessionV2 from "./session"
2: export * from "./session/schema"
3:
4: import { DateTime, Effect, Layer, Schema, Context, Stream } from "effect"
5: import { ListAnchor } from "@opencode-ai/schema/session"
6: import { and, asc, desc, eq, gt, like, lt, or, type SQL } from "drizzle-orm"
7: import { ProjectV2 } from "./project"
8: import { WorkspaceV2 } from "./workspace"
9: import { ModelV2 } from "./model"
10: import { Location } from "./location"
11: import { SessionMessage } from "./session/message"
12: import { Prompt } from "./session/prompt"
13: import { PromptInput } from "@opencode-ai/schema/prompt-input"
14: import { EventV2 } from "./event"
15: import { Database } from "./database/database"
16: import { SessionProjector } from "./session/projector"
17: import { SessionMessageTable, SessionTable } from "./session/sql"
18: import { SessionSchema } from ...
<path>/home/byk/Code/opencode/packages/core/src/session.ts</path>
<type>file</type>
<content>
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: }),
338: message: Effect.fn("V2Session.message")(function* (input) {
339: const stored = yield* store.message(input.messageID)
340: return stored?.sessionID === input.sessionID ? stored.message : undefined
341: }),
342: context: Effect.fn("V2Session.context")(function* (sessionID) {
343: yield* result.get(sessionID)
344: return yield* store.context(sessionID)
345: }),
346: events: (input) ...
<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(`
9: CREATE TABLE \`session_recovery\` (
10: \`session_id\` text NOT NULL,
11: \`assistant_message_id\` text NOT NULL,
12: \`continuation_message_id\` text NOT NULL,
13: \`time_created\` integer NOT NULL,
14: CONSTRAINT \`session_recovery_pk\` PRIMARY KEY(\`session_id\`, \`assistant_message_id\`),
15: CONSTRAINT \`fk_session_recovery_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE,
16: CONSTRAINT \`fk_session_recovery_assistant_message_id_session_message_id_fk\` FOREIGN KEY ...
<path>/home/byk/Code/opencode/packages/core/src/database/migration/20260914214636_session-execution.ts</path>
<type>file</type>
<content>
1: import { Effect } from "effect"
2: import type { DatabaseMigration } from "../migration"
3:
4: export default {
5: id: "20260914214636_session-execution",
6: up(tx) {
7: return Effect.gen(function* () {
8: yield* tx.run(`
9: CREATE TABLE \`session_execution\` (
10: \`id\` text PRIMARY KEY,
11: \`session_id\` text NOT NULL,
12: \`owner_id\` text NOT NULL,
13: \`assistant_message_id\` text,
14: \`time_created\` integer NOT NULL,
15: CONSTRAINT \`fk_session_execution_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE
16: );
17: `)
18: yield* tx.run(`ALTER TABLE \`session_recovery\` ADD \`execution_id\` text NOT NULL;`)
19: yield* tx.run(`CREATE UNIQUE INDEX ...
<path>/home/byk/Code/opencode/packages/core/src/database/migration/20260914222526_session-execution-phase.ts</path>
<type>file</type>
<content>
1: import { Effect } from "effect"
2: import type { DatabaseMigration } from "../migration"
3:
4: export default {
5: id: "20260914222526_session-execution-phase",
6: up(tx) {
7: return Effect.gen(function* () {
8: yield* tx.run(`ALTER TABLE \`session_execution\` ADD \`phase\` text NOT NULL;`)
9: })
10: },
11: } satisfies DatabaseMigration.Migration
(End of file - total 11 lines)
</content>
<path>/home/byk/Code/opencode/packages/core/src/database/migration/20260914235657_session-execution-lease.ts</path>
<type>file</type>
<content>
1: import { Effect } from "effect"
2: import type { DatabaseMigration } from "../migration"
3:
4: export default {
5: id: "20260914235657_session-execution-lease",
6: up(tx) {
7: return Effect.gen(function* () {
8: yield* tx.run(`ALTER TABLE \`session_execution\` ADD \`expires_at\` integer NOT NULL;`)
9: })
10: },
11: } satisfies DatabaseMigration.Migration
(End of file - total 11 lines)
</content>
<path>/home/byk/Code/opencode/packages/core/src/database/migration.ts</path>
<type>file</type>
<content>
1: export * as DatabaseMigration from "./migration"
2:
3: import { sql } from "drizzle-orm"
4: import { Effect, Semaphore } from "effect"
5: import type { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
6: import { migrations } from "./migration.gen"
7: import schema from "./schema.gen"
8:
9: type Database = EffectDrizzleSqlite.EffectSQLiteDatabase
10: type Transaction = Parameters<Parameters<Database["transaction"]>[0]>[0]
11: const lock = Semaphore.makeUnsafe(1)
12:
13: export type Migration = {
14: id: string
15: up: (tx: Transaction) => Effect.Effect<void, unknown>
16: }
17:
18: export function apply(db: Database) {
19: return lock.withPermit(
20: Effect.gen(function* () {
21: const tables = yield* db.all<{ name: string }>(
22: sql`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'`,
23: )
24: ...
Continue your fresh replacement frozen, read-only security/pentest review after the server restart. The exact evidence is present and unchanged: /tmp/opencode/session-recovery-full.patch SHA-256 f051a53df476dd924bda0a14c4cd91a63c9e7eafff53bbe71f0460571335fcbd; base ab9408c81cc9916177641ae8e9f08624fc27b5b9; HEAD dc661f30cb2055ad6248fdc59b14998d834497be plus the frozen delta; SEA SHA-256 1ecc9afe36c12b31a8892cdbfb8a1560ceec97255c3537c073b795351233be40. Verify hashes, remain read-only, and finish the complete review. Return substantive PASS/CONCERN/MUST-FIX findings with exact file:line evidence. End exactly MERGE or DO-NOT-MERGE. If blocked, quote the exact blocker; never return empty or progress-only output.
f051a53df476dd924bda0a14c4cd91a63c9e7eafff53bbe71f0460571335fcbd /tmp/opencode/session-recovery-full.patch 1ecc9afe36c12b31a8892cdbfb8a1560ceec97255c3537c073b795351233be40 packages/opencode/dist/server/opencode-server dc661f30cb2055ad6248fdc59b14998d834497be ab9408c81cc9916177641ae8e9f08624fc27b5b9
<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 and waits for its cleanup. */
14: readonly interrupt: (key: Key) => Effect.Effect<void>
15: }
16:
17: type Entry<E> = {
18: readonly done: Deferred.Deferred<void, E>
19: owner?: ...
Found 41 matches
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
Line 409: yield* events.replayAll(
/home/byk/Code/opencode/packages/core/test/session-projector.test.ts:
Line 150: yield* events.replayAll(
/home/byk/Code/opencode/packages/core/test/session-prompt.test.ts:
Line 430: yield* events.replayAll(
/home/byk/Code/opencode/packages/core/test/session-create.test.ts:
Line 258: expect(yield* events.replayAll(serialized.slice(0, 2))).toBe(created.id)
Line 268: expect(yield* events.replayAll(serialized.slice(2))).toBe(created.id)
/home/byk/Code/opencode/packages/core/test/event.test.ts:
Line 551: yield* events.replay({
Line 570: yield* events.replay({
Line 606: .replay({
Line 639: yield* events.replay({
Line 647: .replay({
Line 671: yield* events.replay({
Line 687: .replay({
Line 704: const source = yield* events.replayAll([
Line 731: ...
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/sync.ts</path>
<type>file</type>
<content>
1: import { Workspace } from "@/control-plane/workspace"
2: import * as InstanceState from "@/effect/instance-state"
3: import { Session } from "@/session/session"
4: import { Database } from "@opencode-ai/core/database/database"
5: import { EventV2 } from "@opencode-ai/core/event"
6: import { EventV2Bridge } from "@/event-v2-bridge"
7: import { EventTable } from "@opencode-ai/core/event/sql"
8: import { asc } from "drizzle-orm"
9: import { and } from "drizzle-orm"
10: import { eq } from "drizzle-orm"
11: import { lte } from "drizzle-orm"
12: import { not } from "drizzle-orm"
13: import { or } from "drizzle-orm"
14: import { Effect, Scope } from "effect"
15: import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi"
16: import { InstanceHttpApi } from "../api"
17: import { HistoryPayload, ReplayPayload, SessionPayload } from ...
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/groups/sync.ts</path>
<type>file</type>
<content>
1: import { NonNegativeInt } from "@opencode-ai/core/schema"
2: import { EventV2 } from "@opencode-ai/core/event"
3: import { SessionID } from "@/session/schema"
4: import { Schema } from "effect"
5: import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
6: import { Authorization } from "../middleware/authorization"
7: import { InstanceContextMiddleware } from "../middleware/instance-context"
8: import { WorkspaceRoutingMiddleware, WorkspaceRoutingQuery } from "../middleware/workspace-routing"
9: import { described } from "./metadata"
10:
11: const root = "/sync"
12: export const ReplayEvent = Schema.Struct({
13: id: EventV2.ID,
14: aggregateID: Schema.String,
15: seq: NonNegativeInt,
16: type: Schema.String,
17: data: Schema.Record(Schema.String, Schema.Unknown),
18: })
19: export const ...
<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, Scope } from "effect"
3: import { eq } from "drizzle-orm"
4: import { Database } from "@opencode-ai/core/database/database"
5: import { Project } from "@opencode-ai/core/project"
6: import { ProjectTable } from "@opencode-ai/core/project/sql"
7: import { AbsolutePath } from "@opencode-ai/core/schema"
8: import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
9: import { SessionSchema } from "@opencode-ai/core/session/schema"
10: import { SessionExecutionTable, SessionTable } from "@opencode-ai/core/session/sql"
11: import { testEffect } from "./lib/effect"
12:
13: const sessionID = SessionSchema.ID.make("ses_execution_local")
14: const it = testEffect(Database.layerFromPath(":memory:"))
15:
16: const insertSession = Effect.gen(function* () {
17: const { db } = ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
40: wake: () => Effect.void,
41: })
42: const sessionID = SessionV2.ID.make("ses_projector_test")
43: const created = DateTime.makeUnsafe(0)
44: const model = { id: ModelV2.ID.make("model"), providerID: ProviderV2.ID.make("provider") }
45: const encodeMessage = Schema.encodeSync(SessionMessage.Message)
46:
47: const assistantRow = (
48: id: SessionMessage.ID,
49: seq: number,
50: time: { created: DateTime.Utc; completed?: DateTime.Utc } = { created },
51: ) => {
52: const {
53: id: _,
54: type,
55: ...data
56: } = encodeMessage(SessionMessage.Assistant.make({ id, type: "assistant", agent: "build", model, content: [], time }))
57: return { id, session_id: sessionID, type, seq, time_created: DateTime.toEpochMillis(time.created), data }
58: }
59:
60: describe("SessionProjector", () => {
61: it.effect("atomically reconciles one abandoned execution ...
<path>/home/byk/Code/opencode/packages/schema/src/session-event.ts</path>
<type>file</type>
<content>
170: tokens: Schema.Struct({
171: input: Schema.Finite,
172: output: Schema.Finite,
173: reasoning: Schema.Finite,
174: cache: Schema.Struct({
175: read: Schema.Finite,
176: write: Schema.Finite,
177: }),
178: }),
179: snapshot: Schema.String.pipe(optional),
180: files: Schema.Array(RelativePath).pipe(optional),
181: },
182: })
183: export type Ended = typeof Ended.Type
184:
185: export const Failed = Event.define({
186: type: "session.next.step.failed",
187: ...stepSettlementOptions,
188: schema: {
189: ...Base,
190: assistantMessageID: SessionMessage.ID,
191: error: UnknownError,
192: },
193: })
194: export type Failed = typeof Failed.Type
195:
196: export const Interrupted = Event.define({
197: type: "session.next.step.interrupted",
198: ...
<path>/home/byk/Code/opencode/packages/core/src/session/message-updater.ts</path>
<type>file</type>
<content>
190: yield* adapter.updateAssistant(
191: produce(currentAssistant, (draft) => {
192: draft.time.completed = event.data.timestamp
193: }),
194: )
195: }
196: yield* adapter.appendMessage(
197: SessionMessage.Assistant.make({
198: id: event.data.assistantMessageID,
199: type: "assistant",
200: agent: event.data.agent,
201: model: event.data.model,
202: time: { created: event.data.timestamp },
203: content: [],
204: snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined,
205: }),
206: )
207: })
208: },
209: "session.next.step.ended": (event) => {
210: return updateOwnedAssistant(event.data.assistantMessageID, ...
<path>/home/byk/Code/opencode/packages/core/src/session/sql.ts</path>
<type>file</type>
<content>
140: export const SessionInputTable = sqliteTable(
141: "session_input",
142: {
143: id: text().$type<SessionMessage.ID>().primaryKey(),
144: session_id: text()
145: .$type<SessionSchema.ID>()
146: .notNull()
147: .references(() => SessionTable.id, { onDelete: "cascade" }),
148: prompt: text({ mode: "json" }).notNull().$type<Prompt>(),
149: delivery: text().$type<SessionInput.Delivery>().notNull(),
150: admitted_seq: integer().notNull(),
151: promoted_seq: integer(),
152: time_created: integer()
153: .notNull()
154: .$default(() => Date.now()),
155: },
156: (table) => [
157: index("session_input_session_pending_delivery_seq_idx").on(
158: table.session_id,
159: table.promoted_seq,
160: table.delivery,
161: table.admitted_seq,
162: ),
163: ...
<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: ...
<path>/home/byk/Code/opencode/packages/core/src/database/schema.gen.ts</path> <type>file</type> <content> 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: CREATE TABLE \`session_execution\` ( 159: \`id\` text PRIMARY KEY, 160: \`session_id\` text NOT NULL, 161: \`owner_id\` text NOT NULL, 162: \`assistant_message_id\` text, 163: ...
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts</path>
<type>file</type>
<content>
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 "@opencode-ai/core/pty/ticket"
64: import { Ripgrep } from "@opencode-ai/core/ripgrep"
65: import { SessionProjector } from "@opencode-ai/core/session/projector"
66: import { SessionV2 } from "@opencode-ai/core/session"
67: import { SessionExecution } from "@opencode-ai/core/session/execution"
68: import * as SessionExecutionLocal from ...
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts</path> <type>file</type> <content> 275: const locationServiceMapV2 = buildLocationServiceMap() 276: 277: return Layer.mergeAll( 278: rootApiRoutes, 279: eventApiRoutes, 280: ptyConnectApiRoutes, 281: instanceRoutes, 282: serverRoutes, 283: docRoute, 284: uiRoute, 285: ).pipe( 286: Layer.provide([ 287: errorLayer, 288: compressionLayer, 289: corsVaryFix, 290: fenceLayer, 291: cors(corsOptions), 292: AppNodeBuilderV1.build(MoveSession.node, [[LocationServiceMap.node, locationServiceMapV2]]), 293: HttpServer.layerServices, 294: ]), 295: Layer.provide(Layer.succeed(CorsConfig)(corsOptions)), 296: Layer.provide(sessionLocationLayer), 297: Layer.provide(locationLayer), 298: Layer.provide(PtyEnvironment.layer), 299: Layer.provide( 300: AppNodeBuilderV1.build(LayerNode.group([SessionV2.node, ...
<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 2 matches
/home/byk/Code/opencode/packages/core/src/v1/config/config.ts:
Line 17: import { ConfigServerV1 } from "./server"
/home/byk/Code/opencode/packages/core/src/v1/config/server.ts:
Line 1: export * as ConfigServerV1 from "./server"
No files found
Found 4 matches /home/byk/Code/opencode/packages/core/src/session/compaction.ts: Line 209: .stream( /home/byk/Code/opencode/packages/core/src/session/runner/llm.ts: Line 66: * - [x] Stream exactly one `llm.stream(request)` provider turn. Line 87: * Use `llm.stream(request)` for each provider turn. Keep tool execution and continuation here. Line 269: const providerStream = Stream.suspend(() => llm.stream(request)).pipe(
Found 11 matches
/home/byk/Code/opencode/packages/core/src/tool/registry.ts:
Line 12: import { definition, permission, settle, validateName, type AnyTool, type RegistrationError } from "./tool"
Line 24: readonly materialize: (permissions?: PermissionV2.Ruleset) => Effect.Effect<Materialization>
Line 31: readonly settle: (input: ExecuteInput) => Effect.Effect<Settlement, ToolOutputStore.Error>
Line 50: const settleWith = Effect.fn("ToolRegistry.settle")(function* (input: ExecuteInput, advertised?: object) {
Line 62: const pending = yield* settle(registration.tool, input.call, {
Line 106: materialize: Effect.fn("ToolRegistry.materialize")(function* (permissions = []) {
Line 116: settle: (input) => {
Line 118: if (registration) return settleWith(input, registration.identity)
/home/byk/Code/opencode/packages/core/src/tool/tool.ts:
Line 66: readonly settle: (call: ToolCall, context: Context) => Effect.Effect<ToolOutput, ...
<path>/home/byk/Code/opencode/packages/core/src/tool/registry.ts</path>
<type>file</type>
<content>
1: export * as ToolRegistry from "./registry"
2:
3: import { ToolOutput, type ToolCall, type ToolDefinition, type ToolResultValue } from "@opencode-ai/llm"
4: import { Context, Effect, Layer, Scope } from "effect"
5: import { AgentV2 } from "../agent"
6: import { PermissionV2 } from "../permission"
7: import { SessionMessage } from "../session/message"
8: import { SessionSchema } from "../session/schema"
9: import { ToolOutputStore } from "../tool-output-store"
10: import { Wildcard } from "../util/wildcard"
11: import { ApplicationTools } from "./application-tools"
12: import { definition, permission, settle, validateName, type AnyTool, type RegistrationError } from "./tool"
13: import { Tools } from "./tools"
14: import { makeLocationNode } from "../effect/app-node"
15:
16: export type ExecuteInput = {
17: readonly sessionID: SessionSchema.ID
18: readonly agent: AgentV2.ID
19: ...
<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, FiberSet, 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: import ...
Found 78 matches
/home/byk/Code/opencode/packages/core/test/session-execution-local.test.ts:
Line 81: it.effect("never starts a second owner while the lease is live", () =>
Line 104: it.effect("takes over an expired lease and fences the stale owner", () =>
Line 120: const stale = (yield* claims)[0]
Line 121: if (!stale) return yield* Effect.die("Execution claim not found")
Line 125: .where(eq(SessionExecutionTable.id, stale.id))
Line 135: it.effect("never re-enters its own uncertain live lease", () =>
Line 146: .set({ phase: "unknown" })
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
Line 42: import { ConfigCompaction } from "@opencode-ai/core/config/compaction"
Line 108: const recoveryModel = Model.make({
Line 109: id: "recovery",
Line 221: compaction: new ConfigCompaction.Info({
Line 317: phase: "ready",
Line 326: phase: "ready",
Line 382: ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
1220: fragmentFixture("text", "text-summary", ["## Objective\n- Preserve the task"]).completeEvents,
1221: fragmentFixture("text", "text-final", ["Continued"]).completeEvents,
1222: ]
1223: yield* session.prompt({ sessionID, prompt: Prompt.make({ text: oversized }), resume: false })
1224: yield* session.resume(sessionID)
1225:
1226: expect(requests).toHaveLength(2)
1227: const summary = userTexts(requests[0])[0]
1228: const continuation = userTexts(requests[1])[0]
1229: expect(summary.match(/OVERSIZED_BOUNDARY/g)).toHaveLength(1)
1230: expect(summary).toContain(oversized)
1231: expect(continuation).not.toContain("OVERSIZED_BOUNDARY")
1232: expect(continuation).not.toContain("OVERSIZED_END")
1233: expect(continuation).toContain("<recent-context>\n\n</recent-context>")
1234: }),
1235: )
1236: ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
2950:
2951: const run = yield* session.resume(sessionID).pipe(Effect.forkChild)
2952: while (executions.length === 0) yield* Effect.yieldNow
2953: yield* Effect.yieldNow
2954: yield* Deferred.succeed(toolExecutionGate, undefined)
2955: expect(yield* Fiber.join(run).pipe(Effect.flip)).toBe(failure)
2956: toolExecutionGate = undefined
2957:
2958: expect(yield* session.context(sessionID)).toMatchObject([
2959: { type: "user", text: "Settle before failing" },
2960: {
2961: type: "assistant",
2962: content: [
2963: { type: "tool", id: "call-before-failure", state: { status: "completed", structured: { text: "settle" } } },
2964: ],
2965: },
2966: ])
2967: }),
2968: )
2969:
2970: it.effect("durably fails blocked local tools when a provider turn is interrupted", () ...
Found 7 matches
/home/byk/Code/opencode/packages/core/src/database/database.ts:
Line 31: yield* db.run("PRAGMA foreign_keys = ON")
/home/byk/Code/opencode/packages/core/src/database/migration/20260410174513_workspace-name.ts:
Line 11: yield* tx.run(`PRAGMA foreign_keys=OFF;`)
Line 29: yield* tx.run(`PRAGMA foreign_keys=ON;`)
/home/byk/Code/opencode/packages/core/src/database/migration/20260612174303_project_dir_strategy.ts:
Line 9: yield* tx.run(`PRAGMA foreign_keys=OFF;`)
Line 26: yield* tx.run(`PRAGMA foreign_keys=ON;`)
/home/byk/Code/opencode/packages/core/src/database/migration/20260604172448_event_sourced_session_input.ts:
Line 20: yield* tx.run(`PRAGMA foreign_keys=OFF;`)
Line 35: yield* tx.run(`PRAGMA foreign_keys=ON;`)
<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:
6: export type MemoryState = {
7: messages: SessionMessage.Message[]
8: }
9:
10: export interface Adapter {
11: readonly getCurrentAssistant: () => Effect.Effect<SessionMessage.Assistant | undefined>
12: readonly getAssistant: (messageID: SessionMessage.ID) => Effect.Effect<SessionMessage.Assistant | undefined>
13: readonly getCurrentShell: (callID: string) => Effect.Effect<SessionMessage.Shell | undefined>
14: readonly updateAssistant: (assistant: SessionMessage.Assistant) => Effect.Effect<void>
15: readonly updateShell: (shell: SessionMessage.Shell) => Effect.Effect<void>
16: readonly appendMessage: (message: SessionMessage.Message) => Effect.Effect<void>
17: }
18:
19: ...
<path>/home/byk/Code/opencode/packages/core/src/session/message-updater.ts</path>
<type>file</type>
<content>
90: assistant?.content.findLast((item): item is DraftText => item.type === "text" && item.id === textID)
91:
92: const latestReasoning = (assistant: DraftAssistant | undefined, reasoningID: string) =>
93: assistant?.content.findLast((item): item is DraftReasoning => item.type === "reasoning" && item.id === reasoningID)
94:
95: const updateOwnedAssistant = (messageID: SessionMessage.ID, recipe: (draft: DraftAssistant) => void) =>
96: Effect.gen(function* () {
97: const assistant = yield* adapter.getAssistant(messageID)
98: if (assistant) yield* adapter.updateAssistant(produce(assistant, recipe))
99: })
100:
101: return Effect.gen(function* () {
102: yield* SessionEvent.All.match(event, {
103: "session.next.agent.switched": (event) => {
104: return adapter.appendMessage(
105: ...
<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 { ContextSnapshotDecodeError, MessageDecodeError } from "../error"
7: import { SessionRunnerModel } from "./model"
8: import type { SystemContext } from "../../system-context/index"
9: import type { ToolOutputStore } from "../../tool-output-store"
10:
11: export type RunError =
12: | LLMError
13: | SessionRunnerModel.Error
14: | MessageDecodeError
15: | ContextSnapshotDecodeError
16: | SystemContext.InitializationBlocked
17: | ToolOutputStore.Error
18:
19: /** Runs one local continuation from already-recorded Session history. */
20: export interface Interface {
21: /** Drains eligible durable work. ...
<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": false,
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/src/database/database.ts</path>
<type>file</type>
<content>
1: export * as Database from "./database"
2:
3: import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
4: import { layer as sqliteLayer } from "#sqlite"
5: import { Context, Effect, Layer } from "effect"
6: import { Global } from "../global"
7: import { Flag } from "../flag/flag"
8: import { isAbsolute, join } from "path"
9: import { DatabaseMigration } from "./migration"
10: import { InstallationChannel } from "../installation/version"
11: import { makeGlobalNode } from "../effect/app-node"
12:
13: const makeDatabase = EffectDrizzleSqlite.makeWithDefaults()
14: type DatabaseShape = Effect.Success<typeof makeDatabase>
15:
16: export interface Interface {
17: db: DatabaseShape
18: }
19:
20: export class Service extends Context.Service<Service, Interface>()("@opencode/v2/storage/Database") {}
21:
22: const layer = Layer.effect(
23: Service,
24: ...
<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/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
25: SessionInputTable,
26: SessionMessageTable,
27: SessionRecoveryTable,
28: SessionTable,
29: } from "@opencode-ai/core/session/sql"
30: import { testEffect } from "./lib/effect"
31: import { Snapshot } from "@opencode-ai/core/snapshot"
32: import { Location } from "@opencode-ai/core/location"
33:
34: const it = testEffect(AppNodeBuilder.build(LayerNode.group([Database.node, EventV2.node, SessionProjector.node])))
35: const sessionsLayer = AppNodeBuilder.build(SessionV2.node, [[SessionExecution.node, SessionExecution.noopLayer]])
36: const recoveryExecution = SessionExecution.Service.of({
37: active: Effect.succeed(new Set()),
38: interrupt: () => Effect.void,
39: resume: () => Effect.void,
40: wake: () => Effect.void,
41: })
42: const sessionID = SessionV2.ID.make("ses_projector_test")
43: const created = DateTime.makeUnsafe(0)
44: const model = { id: ...
f051a53df476dd924bda0a14c4cd91a63c9e7eafff53bbe71f0460571335fcbd /tmp/opencode/session-recovery-full.patch 1ecc9afe36c12b31a8892cdbfb8a1560ceec97255c3537c073b795351233be40 packages/opencode/dist/server/opencode-server dc661f30cb2055ad6248fdc59b14998d834497be M .lore.md M AGENTS.md M packages/core/schema.json M packages/core/src/database/migration.gen.ts M packages/core/src/database/schema.gen.ts M packages/core/src/session/compaction.ts M packages/core/src/session/execution/local.ts M packages/core/src/session/message-updater.ts M packages/core/src/session/projector.ts M packages/core/src/session/recovery.ts M packages/core/src/session/runner/index.ts M packages/core/src/session/runner/llm.ts M packages/core/src/session/runner/publish-llm-event.ts M packages/core/src/session/sql.ts M packages/core/test/session-execution-local.test.ts M packages/core/test/session-projector.test.ts M packages/core/test/session-runner-recorded.test.ts M ...
f051a53df476dd924bda0a14c4cd91a63c9e7eafff53bbe71f0460571335fcbd.1ecc9afe36c12b31a8892cdbfb8a1560ceec97255c3537c073b795351233be40.dc661f30cb2055ad6248fdc59b14998d834497be.ab9408c81cc9916177641ae8e9f08624fc27b5b9.repo-setup skill was not available. I used the repository and nested AGENTS.md instructions directly.renew() evaluates Date.now() while constructing the repeated Effect at packages/core/src/session/execution/local.ts:36-46. The caller constructs it once and repeats that same database operation at packages/core/src/session/execution/local.ts:82-85. Every heartbeat therefore writes the original expiry rather than a new now + leaseDuration.
The lease is only 30 seconds (packages/core/src/session/sql.ts:213). A healthy provider, compaction, or tool call that emits no event for 30 seconds becomes claimable. Another process may take over through the expired-row branch at packages/core/src/session/execution/local.ts:60-76.
The stale owner may also revive an expired claim: neither renewal nor runner phase updates require an unexpired lease. Renewal checks only execution and owner IDs (packages/core/src/session/execution/local.ts:36-45); phase updates check only Session and execution IDs (packages/core/src/session/runner/llm.ts:185-197).
The takeover test never lets the stale owner resume: its first runner is permanently Effect.never, and its scope closes only after takeover finishes (packages/core/test/session-execution-local.test.ts:104-132). It cannot detect this defect. ...
Date: September 15, 2026 * 🟡 (06:17) Initial file search returned “No files found.” * 🟡 (06:17) Recovery patch artifact `/tmp/opencode/session-recovery-full.patch` had SHA-256 `f051a53df476dd924bda0a14c4cd91a63c9e7eafff53bbe71f0460571335fcbd`. * 🟡 (06:17) Built server artifact `packages/opencode/dist/server/opencode-server` had SHA-256 `1ecc9afe36c12b31a8892cdbfb8a1560ceec97255c3537c073b795351…