DashboardopencodeSession 0zQHdddpHIud

Session 0zQHdddpHIud

Full ID: 0zQHdddpHIudW7XKG
Messages: 62
Distillations: 0
Time range: 2026-09-16 20:04:52 — 2026-09-16 20:28:58

Conversation (62 messages)

Read-only independent security/pentest review of frozen v13. Base ab9408c81cc9916177641ae8e9f08624fc27b5b9. Patch /tmp/opencode/session-recovery-final-v13.patch SHA-256 3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf. SEA /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server SHA-256 c3b8c5d78e473ad4104a5b54617900e28921a283029b8484be7bce4bc3cee85a. Do not mutate/run tests/builds/services. Verify hashes, clean apply, inspect all 33 paths. Threat model stale recovery owners and concurrent/replayed assistant creation. Primary closures: quarantine writes for cancellation and projection failure cannot mutate after lease expiry; successor can take over. Step.Recovered transaction rechecks assistant-less ready/unknown/continue claims against captured assistant_seq so concurrent Step.Started rolls back stale recovery; direct producer tests cover acquisition and Step.Started. Verify no fail-open catch path, no stale claim clear, equality compaction works. Reassess complete recovery identity pending/settled, cross-Session, retry/queue/steer reservation, stale owners, cancellation finalizers, external side effects/tools, fairness/DoS, replay, schema/auth/dependency boundaries. Accepted residuals: final synchronous check-to-external-call, process-local interrupt routing, bounded clock skew. Evidence recovery 241 pass, Core 1178 pass, typecheck/migration/smoke. Return substantive exact file:line PASS/CONCERN/MUST-FIX, end exactly MERGE or DO-NOT-MERGE. Empty invalid; no recall.

gateway · 2026-09-16 20:04:52
result
3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf  /tmp/opencode/session-recovery-final-v13.patch
c3b8c5d78e473ad4104a5b54617900e28921a283029b8484be7bce4bc3cee85a  /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server
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 packages/core/src/session/projector.ts
 M ...
gateway · 2026-09-16 20:05:18
result
Checking patch packages/core/schema.json...
error: while searching for:
{
  "version": "7",
  "dialect": "sqlite",
  "id": "f14a9b18-8207-487e-a3d3-227e629ba9ad",
  "prevIds": ["169a0f0f-d58f-479f-b024-fa1c7b9a09db"],
  "ddl": [
    {
      "name": "workspace",

error: patch failed: packages/core/schema.json:1
error: packages/core/schema.json: patch does not apply
Checking patch packages/core/src/database/migration.gen.ts...
error: while searching for:
    import("./migration/20260622142730_simplify_session_context_epoch"),
    import("./migration/20260622170816_reset_v2_session_state"),
    import("./migration/20260622202450_simplify_session_input"),
  ])
).map((module) => module.default) satisfies DatabaseMigration.Migration[]

error: patch failed: packages/core/src/database/migration.gen.ts:40
error: packages/core/src/database/migration.gen.ts: patch does not apply
Checking patch packages/core/src/database/migration/20260914170650_session-recovery.ts... ...
gateway · 2026-09-16 20:05:33
result
drwxr-xr-x 25 byk byk 4096 Sep 16 20:03 /tmp/opencode
dc661f30cb2055ad6248fdc59b14998d834497be
.lore.md
AGENTS.md
packages/core/schema.json
packages/core/src/database/migration.gen.ts
packages/core/src/database/migration/20260914170650_session-recovery.ts
packages/core/src/database/schema.gen.ts
packages/core/src/session.ts
packages/core/src/session/compaction.ts
packages/core/src/session/context-epoch.ts
packages/core/src/session/execution/local.ts
packages/core/src/session/input.ts
packages/core/src/session/message-updater.ts
packages/core/src/session/projector.ts
packages/core/src/session/recovery.ts
packages/core/src/session/run-coordinator.ts
packages/core/src/session/runner/index.ts
packages/core/src/session/runner/llm.ts
packages/core/src/session/runner/publish-llm-event.ts
packages/core/src/session/sql.ts
packages/core/test/database-migration.test.ts
packages/core/test/session-execution-local.test.ts
packages/core/test/session-projector.test.ts
packages/core/test/session-prompt...
gateway · 2026-09-16 20:05:47
result
Checking patch packages/core/schema.json...
Checking patch packages/core/src/database/migration.gen.ts...
Checking patch packages/core/src/database/migration/20260914170650_session-recovery.ts...
Checking patch packages/core/src/database/schema.gen.ts...
Checking patch packages/core/src/session.ts...
Checking patch packages/core/src/session/compaction.ts...
Checking patch packages/core/src/session/context-epoch.ts...
Checking patch packages/core/src/session/execution/local.ts...
Checking patch packages/core/src/session/input.ts...
Checking patch packages/core/src/session/message-updater.ts...
Checking patch packages/core/src/session/projector.ts...
Checking patch packages/core/src/session/recovery.ts...
Checking patch packages/core/src/session/run-coordinator.ts...
Checking patch packages/core/src/session/runner/index.ts...
Checking patch packages/core/src/session/runner/llm.ts...
Checking patch packages/core/src/session/runner/publish-llm-event.ts... ...
gateway · 2026-09-16 20:05:57
result
<path>/tmp/opencode/session-recovery-final-v13.patch</path>
<type>file</type>
<content>
1: diff --git a/packages/core/schema.json b/packages/core/schema.json
2: index d0eeeebd5c..dedf0cc600 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": "3a5ec6d4-a60d-47da-a347-3c72396a13c1",
12: +  "prevIds": [
13: +    "f14a9b18-8207-487e-a3d3-227e629ba9ad"
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: ...
gateway · 2026-09-16 20:06:11
result
<path>/tmp/opencode/session-recovery-final-v13.patch</path>
<type>file</type>
<content>
1565:  export const node = makeGlobalNode({
1566:    service: SessionExecution.Service,
1567:    layer,
1568: -  deps: [SessionStore.node, LocationServiceMap.node],
1569: +  deps: [Database.node, SessionStore.node, LocationServiceMap.node],
1570:  })
1571:  
1572:  export * as SessionExecutionLocal from "./local"
1573: diff --git a/packages/core/src/session/input.ts b/packages/core/src/session/input.ts
1574: index 14b613678d..0b38eb2519 100644
1575: --- a/packages/core/src/session/input.ts
1576: +++ b/packages/core/src/session/input.ts
1577: @@ -1,15 +1,16 @@
1578:  export * as SessionInput from "./input"
1579:  
1580: -import { and, asc, eq, isNull, lte } from "drizzle-orm"
1581: -import { DateTime, Effect, Schema } from "effect"
1582: +import { and, asc, desc, eq, isNull, lte } from "drizzle-orm"
1583: +import { DateTime, Effect, Option, Schema } from "effect"
1584:  import { Admitted, Delivery } ...
gateway · 2026-09-16 20:06:24
result
<path>/tmp/opencode/session-recovery-final-v13.patch</path>
<type>file</type>
<content>
2806: +                    isNull(SessionRecoveryTable.execution_id),
2807: +                  ),
2808: +                )
2809: +                .limit(1),
2810: +            ),
2811: +          ),
2812: +        )
2813: +        .returning({ id: SessionTable.id })
2814: +        .get()
2815: +        .pipe(
2816: +          Effect.orDie,
2817: +          Effect.flatMap((updated) => (updated ? execution.wake(row.sessionID) : Effect.void)),
2818: +          Effect.catchCause((cause) =>
2819: +            Effect.logError("Failed to redrive Session input", cause).pipe(
2820: +              Effect.annotateLogs({ sessionID: row.sessionID }),
2821: +            ),
2822: +          ),
2823: +        ),
2824: +    { discard: true, concurrency: 1 },
2825: +  )
2826: +})
2827: +
2828: +const layer = Layer.effectDiscard(
2829: +  recover().pipe(
2830: +    Effect.catchCause((cause) => Effect.logError("Failed ...
gateway · 2026-09-16 20:06:49
result
2f1b5ee3b250604baa6f809b664776c5889266390d24b65b4a0ddae8f2374c8f  -
3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf  /tmp/opencode/session-recovery-final-v13.patch
31
gateway · 2026-09-16 20:07:01
result
3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf  -
gateway · 2026-09-16 20:07:20
result
<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<
18:   Pick<typeof SessionExecutionTable.$inferInsert, "assistant_message_id" | "assistant_seq" | "phase">
19: >
20: 
21: export class Lost extends Error {
22:   constructor(readonly claim: Claim) {
23:     super(`Session execution claim lost: ${claim.executionID}`)
24:   }
25: }
26: 
27: const where = (claim: Claim, ...
gateway · 2026-09-16 20:07:40
result
<path>/home/byk/Code/opencode/packages/core/src/session/execution/local.ts</path>
<type>file</type>
<content>
1: import { and, asc, desc, 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 {
14:   SESSION_EXECUTION_LEASE_MS,
15:   SessionExecutionTable,
16:   SessionMessageTable,
17:   SessionRecoveryTable,
18: } from "../sql"
19: 
20: /** Current-process routing for implicit-local Locations. ...
gateway · 2026-09-16 20:07:51
result
Found 1 matches
/home/byk/Code/opencode/packages/core/src/event.ts:
  Line 123:   readonly commit?: (seq: number) => Effect.Effect<void>
gateway · 2026-09-16 20:08:19
result
<path>/home/byk/Code/opencode/packages/core/src/event.ts</path>
<type>file</type>
<content>
100:       },
101:       data: event.data,
102:     }),
103:   )
104:   return {
105:     events,
106:     hasMore: rows.length > input.limit,
107:   }
108: })
109: 
110: export class SubscriberOverflowError extends Schema.TaggedErrorClass<SubscriberOverflowError>()(
111:   "EventV2.SubscriberOverflow",
112:   { capacity: Schema.Int },
113: ) {}
114: 
115: export const define = Event.define
116: export const versionedType = Event.versionedType
117: 
118: export interface PublishOptions {
119:   readonly id?: ID
120:   readonly metadata?: Record<string, unknown>
121:   readonly location?: Location.Ref
122:   /** Local operational projection committed atomically with a new durable event. Not replayed or serialized. */
123:   readonly commit?: (seq: number) => Effect.Effect<void>
124: }
125: 
126: export interface Interface {
127:   readonly publish: <D extends Definition>(
128:     definition: ...
gateway · 2026-09-16 20:08:39
result
<path>/home/byk/Code/opencode/packages/core/src/event.ts</path>
<type>file</type>
<content>
236:               const list = projectors.get(event.type) ?? []
237:               return yield* Effect.uninterruptible(
238:                 Effect.gen(function* () {
239:                   const committed = yield* db
240:                     .transaction(
241:                       () =>
242:                         Effect.gen(function* () {
243:                           const row = yield* db
244:                             .select({ seq: EventSequenceTable.seq, ownerID: EventSequenceTable.owner_id })
245:                             .from(EventSequenceTable)
246:                             .where(eq(EventSequenceTable.aggregate_id, aggregateID))
247:                             .get()
248:                             .pipe(Effect.orDie)
249:                           const latest = row?.seq ?? -1
250:                           const encoded = ...
gateway · 2026-09-16 20:09:12
result
<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>
gateway · 2026-09-16 20:09:19
result
Found 63 matches
/home/byk/Code/opencode/packages/core/src/session/recovery.ts:
  Line 48:           and(isNull(SessionRecoveryTable.execution_id), eq(SessionInputTable.wake_pending, true)),

  Line 50:             eq(SessionRecoveryTable.wake_pending, true),

  Line 51:             isNull(SessionRecoveryTable.recovery_error),

  Line 74:     .where(and(lte(SessionExecutionTable.expires_at, scanTime), isNull(SessionExecutionTable.recovery_error)))

  Line 93:               isNull(SessionExecutionTable.recovery_error),

  Line 106:             .set({ recovery_error: `Invalid execution phase: ${String(claim.phase)}` })

  Line 125:             .set({ recovery_error: "Cancellation outcome unknown after owner loss" })

  Line 132:                 isNull(SessionExecutionTable.recovery_error),

  Line 143:             yield* SessionExecutionClaim.clear(db, recoveryClaim)

  Line 161:           yield* SessionExecutionClaim.clear(db, recoveryClaim)

  Line 203:                   yield* ...
gateway · 2026-09-16 20:09:58
result
Found 100 matches (more matches available)
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
  Line 322:         owner_id: "test",

  Line 331:           owner_id: "test",

  Line 587:     const fiber = yield* runner.run({ sessionID, force: true, executionID, ownerID: "test" }).pipe(Effect.forkChild)

  Line 752:   it.effect("never dispatches a provider after ownership changes", () =>

  Line 767:         .set({ id: "provider-successor", owner_id: "successor", expires_at: 60_000 })

  Line 778:   it.effect("never publishes streamed output after ownership changes", () =>

  Line 785:       response = fragmentFixture("text", "text-fenced", ["stale output"]).completeEvents

  Line 793:         .set({ id: "publication-successor", owner_id: "successor", expires_at: 60_000 })

  Line 805:   it.effect("never starts a local tool after ownership changes", () =>

  Line 823:               .set({ id: "tool-successor", owner_id: "successor", expires_at: 60_000 })

  Line 845:   ...
gateway · 2026-09-16 20:11:07
result
<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 ...
gateway · 2026-09-16 20:11:18
result
<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 ...
gateway · 2026-09-16 20:11:25
result
Found 2 matches
/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts:
  Line 213: const app = LayerNode.group([

  Line 300:       AppNodeBuilderV1.build(LayerNode.group([SessionV2.node, SessionRecovery.node]), [
gateway · 2026-09-16 20:11:30
result
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts</path>
<type>file</type>
<content>
205: 
206: type RouteRequirements =
207:   | HttpRouter.HttpRouter
208:   | HttpRouter.Request<"Error", unknown>
209:   | HttpRouter.Request<"GlobalError", unknown>
210:   | HttpRouter.Request<"Requires", unknown>
211:   | HttpRouter.Request<"GlobalRequires", never>
212: 
213: const app = LayerNode.group([
214:   Npm.node,
215:   FSUtil.node,
216:   Database.node,
217:   Auth.node,
218:   Account.node,
219:   Config.node,
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:   ...
gateway · 2026-09-16 20:11:41
result
<path>/home/byk/Code/opencode/packages/schema/src/session-event.ts</path>
<type>file</type>
<content>
70:     messageID: SessionMessage.ID,
71:     model: Model.Ref,
72:   },
73: })
74: export type ModelSwitched = typeof ModelSwitched.Type
75: 
76: export const Moved = Event.define({
77:   type: "session.next.moved",
78:   ...options,
79:   schema: {
80:     ...Base,
81:     location: Location.Ref,
82:     subdirectory: RelativePath.pipe(optional),
83:   },
84: })
85: export type Moved = typeof Moved.Type
86: 
87: export const Prompted = Event.define({
88:   type: "session.next.prompted",
89:   ...options,
90:   schema: PromptFields,
91: })
92: export type Prompted = typeof Prompted.Type
93: 
94: export const PromptAdmitted = Event.define({
95:   type: "session.next.prompt.admitted",
96:   ...options,
97:   schema: {
98:     ...PromptFields,
99:     requestExecution: Schema.Boolean.pipe(optional),
100:   },
101: })
102: export type PromptAdmitted = typeof PromptAdmitted.Type
103: ...
gateway · 2026-09-16 20:12:35
result
<path>/home/byk/Code/opencode/packages/core/src/session/recovery.ts</path>
<type>file</type>
<content>
60:     .get()
61:     .pipe(Effect.orDie)
62:   return pending !== undefined
63: })
64: 
65: export const recover = Effect.fn("SessionRecovery.recover")(function* () {
66:   const { db } = yield* Database.Service
67:   const events = yield* EventV2.Service
68:   const execution = yield* SessionExecution.Service
69:   const ownerID = crypto.randomUUID()
70:   const scanTime = yield* Clock.currentTimeMillis
71:   const claims = yield* db
72:     .select()
73:     .from(SessionExecutionTable)
74:     .where(and(lte(SessionExecutionTable.expires_at, scanTime), isNull(SessionExecutionTable.recovery_error)))
75:     .orderBy(asc(SessionExecutionTable.time_created), asc(SessionExecutionTable.id))
76:     .limit(BATCH_SIZE)
77:     .all()
78:     .pipe(Effect.orDie)
79:   yield* Effect.forEach(
80:     claims,
81:     (claim) =>
82:       Effect.gen(function* () {
83:         const now = ...
gateway · 2026-09-16 20:13:16
result
<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: ...
gateway · 2026-09-16 20:14:07
result
Found 45 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 504:   it.effect("recovers assistant-less continuation at its current assistant boundary", () =>

  Line 555:   it.effect("redrives a durable recovery wake after admission", () =>

  Line 606:   it.effect("quarantines a malformed recovery row without blocking a valid wake", () =>

  Line 660:   it.effect("quarantines a full batch of poison recovery rows before redriving valid work", () =>

  Line 725:   it.effect("fairly ...
gateway · 2026-09-16 20:14:38
result
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
330:           expires_at: Date.now() + 60_000,
331:         })
332:         .run()
333: 
334:       yield* SessionRecovery.recover().pipe(Effect.provideService(SessionExecution.Service, recoveryExecution))
335: 
336:       expect(yield* db.select().from(SessionExecutionTable).all()).toHaveLength(1)
337:       expect(yield* db.select().from(SessionRecoveryTable).all()).toEqual([])
338:       expect(yield* db.select().from(SessionInputTable).all()).toEqual([])
339:     }),
340:   )
341: 
342:   it.effect("never recovers an execution superseded by a newer assistant", () =>
343:     Effect.gen(function* () {
344:       const { db } = yield* Database.Service
345:       const abandonedID = SessionMessage.ID.make("msg_recovery_abandoned")
346:       yield* db
347:         .insert(ProjectTable)
348:         .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), ...
gateway · 2026-09-16 20:15:22
result
<path>/home/byk/Code/opencode/packages/core/test/database-migration.test.ts</path>
<type>file</type>
<content>
150:             sql`SELECT name FROM sqlite_master WHERE type = 'index' AND name IN ('event_aggregate_seq_idx', 'event_aggregate_type_seq_idx', 'session_input_session_pending_seq_idx', 'session_input_session_pending_delivery_seq_idx', 'session_input_session_admitted_seq_idx', 'session_input_session_promoted_seq_idx', 'session_message_session_idx', 'session_message_session_type_idx', 'session_message_session_seq_idx', 'session_message_session_type_seq_idx', 'session_message_session_time_created_id_idx') ORDER BY name`,
151:           ),
152:         ).toEqual([
153:           { name: "event_aggregate_seq_idx" },
154:           { name: "event_aggregate_type_seq_idx" },
155:           { name: "session_input_session_admitted_seq_idx" },
156:           { name: "session_input_session_pending_delivery_seq_idx" },
157:           { name: "session_input_session_promoted_seq_idx" ...
gateway · 2026-09-16 20:16:04
result
<path>/home/byk/Code/opencode/packages/core/test/session-execution-local.test.ts</path>
<type>file</type>
<content>
100:   it.effect("clears the claim after explicit interruption", () =>
101:     Effect.gen(function* () {
102:       yield* insertSession
103:       const started = Deferred.makeUnsafe<void>()
104:       const execution = yield* SessionExecutionLocal.make({
105:         run: () => Deferred.succeed(started, undefined).pipe(Effect.andThen(Effect.never)),
106:       })
107:       yield* execution.wake(sessionID)
108:       yield* Deferred.await(started)
109:       yield* execution.interrupt(sessionID)
110:       expect(yield* claims).toEqual([])
111:     }),
112:   )
113: 
114:   it.effect("never starts a second owner while the lease is live", () =>
115:     Effect.gen(function* () {
116:       yield* insertSession
117:       const firstStarted = Deferred.makeUnsafe<void>()
118:       const firstScope = yield* Scope.make()
119:       const first = yield* ...
gateway · 2026-09-16 20:16:27
result
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1790:           phase: "ready",
1791:           time_created: 1,
1792:           expires_at: 0,
1793:         })
1794:         .run()
1795: 
1796:       yield* SessionRecovery.recover().pipe(Effect.provideService(SessionExecution.Service, recoveryExecution))
1797: 
1798:       expect(
1799:         yield* db
1800:           .select({ error: SessionExecutionTable.recovery_error })
1801:           .from(SessionExecutionTable)
1802:           .where(eq(SessionExecutionTable.id, "malformed"))
1803:           .get(),
1804:       ).toEqual({ error: "Invalid execution phase: invalid" })
1805:       expect(yield* db.select().from(SessionRecoveryTable).all()).toEqual([
1806:         expect.objectContaining({ execution_id: "valid" }),
1807:       ])
1808:     }),
1809:   )
1810: 
1811:   it.effect("never recovers an expired cancellation claim", () =>
1812:     Effect.gen(function* () ...
gateway · 2026-09-16 20:16:51
result
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1235:         Effect.provideService(
1236:           SessionExecution.Service,
1237:           SessionExecution.Service.of({
1238:             ...recoveryExecution,
1239:             wake: (id) => Effect.sync(() => wakes.push(id)).pipe(Effect.asVoid),
1240:           }),
1241:         ),
1242:       )
1243: 
1244:       expect(wakes).toEqual([sessionID])
1245:       expect(yield* db.select().from(SessionExecutionTable).all()).toEqual([])
1246:       expect(yield* db.select().from(SessionRecoveryTable).all()).toHaveLength(1)
1247:       expect(yield* db.select().from(SessionInputTable).all()).toHaveLength(1)
1248:     }),
1249:   )
1250: 
1251:   it.effect("never recovers a pre-promotion claim when input commits at the recovery boundary", () =>
1252:     Effect.gen(function* () {
1253:       const { db } = yield* Database.Service
1254:       const events = yield* ...
gateway · 2026-09-16 20:17:15
result
<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, sql } 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 ...
gateway · 2026-09-16 20:17:27
result
Found 35 matches
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
  Line 43: import { ConfigCompaction } from "@opencode-ai/core/config/compaction"

  Line 224:             compaction: new ConfigCompaction.Info({

  Line 1383:   it.effect("rebuilds the baseline directly after completed compaction", () =>

  Line 1393:       const compactionID = SessionMessage.ID.create()

  Line 1396:         messageID: compactionID,

  Line 1402:         messageID: compactionID,

  Line 1442:         prompt: Prompt.make({ text: "Recent exact request ".repeat(180) }),

  Line 1461:       expect(userTexts(requests[1])[0]).toContain(`[User]: ${"Recent exact request ".repeat(180)}`)

  Line 1464:       expect(context.map((message) => message.type)).toEqual(["compaction", "assistant"])

  Line 1466:         type: "compaction",

  Line 1478:         prompt: Prompt.make({ text: "Newest exact request ".repeat(180) }),

  Line 1487:       expect(userTexts(requests[0])[0]).toContain("Recent ...
gateway · 2026-09-16 20:17:36
result
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
1480:       })
1481:       yield* session.resume(sessionID)
1482: 
1483:       expect(requests).toHaveLength(2)
1484:       expect(userTexts(requests[0])[0]).toContain(
1485:         "<prior-summary>\n## Objective\n- Preserve the task\n</prior-summary>",
1486:       )
1487:       expect(userTexts(requests[0])[0]).toContain("Recent exact request")
1488:       expect((yield* (yield* SessionStore.Service).context(sessionID))[0]).toMatchObject({
1489:         type: "compaction",
1490:         summary: "## Objective\n- Preserve the updated task",
1491:       })
1492:     }),
1493:   )
1494: 
1495:   it.effect("keeps automatic compaction unknown until Compaction.Ended commits", () =>
1496:     Effect.gen(function* () {
1497:       yield* setup
1498:       const session = yield* SessionV2.Service
1499:       const { db } = yield* Database.Service
1500:       response = ...
gateway · 2026-09-16 20:17:49
result
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:   ...
gateway · 2026-09-16 20:18:03
result
Found 25 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 2785:   | SessionNextPromptExecutionRequested

  Line 2793:   | SessionNextStepInterrupted

  Line 2794:   | SessionNextStepRecovered

  Line 2915:   | SessionNextPromptExecutionRequested

  Line 2923:   | SessionNextStepInterrupted

  Line 2924:   | SessionNextStepRecovered

  Line 3430:       requestExecution?: boolean

  Line 3435: export type SyncEventSessionNextPromptExecutionRequested = {

  Line 3585: export type SyncEventSessionNextStepInterrupted = {

  Line 3602: export type SyncEventSessionNextStepRecovered = {

  ...
gateway · 2026-09-16 20:18:27
result
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": {
gateway · 2026-09-16 20:18:48
result
<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:       ...
gateway · 2026-09-16 20:19:08
result
Found 12 matches
/home/byk/Code/opencode/packages/schema/src/session-event.ts:
  Line 104: export const PromptExecutionRequested = Event.define({

  Line 112: export type PromptExecutionRequested = typeof PromptExecutionRequested.Type

  Line 209:   export const Interrupted = Event.define({

  Line 218:   export type Interrupted = typeof Interrupted.Type

  Line 220:   export const Recovered = Event.define({

  Line 230:   export type Recovered = typeof Recovered.Type

  Line 490:   PromptExecutionRequested,

  Line 498:   Step.Interrupted,

  Line 499:   Step.Recovered,

  Line 524:   PromptExecutionRequested,

  Line 532:   Step.Interrupted,

  Line 533:   Step.Recovered,
gateway · 2026-09-16 20:19:30
result
<path>/home/byk/Code/opencode/packages/schema/src/session-event.ts</path>
<type>file</type>
<content>
470: export namespace RevertEvent {
471:   export const Staged = Event.define({
472:     type: "session.next.revert.staged",
473:     ...options,
474:     schema: { ...Base, revert: Revert.State },
475:   })
476:   export const Cleared = Event.define({ type: "session.next.revert.cleared", ...options, schema: Base })
477:   export const Committed = Event.define({
478:     type: "session.next.revert.committed",
479:     ...options,
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:   ...
gateway · 2026-09-16 20:19:44
result
<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 ...
gateway · 2026-09-16 20:20:06
result
Found 100 matches (more matches available)
/home/byk/Code/opencode/packages/server/src/handlers/event.ts:
  Line 22:     const events = yield* EventV2.Service


/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
  Line 404:     const events = yield* EventV2.Service

  Line 416:     yield* events.replayAll(

  Line 523:     const events = yield* EventV2.Service

  Line 623:       const events = yield* EventV2.Service

  Line 719:       const events = yield* EventV2.Service

  Line 809:       const events = yield* EventV2.Service

  Line 848:       const events = yield* EventV2.Service

  Line 1038:       const events = yield* EventV2.Service

  Line 1200:       const events = yield* EventV2.Service

  Line 1229:       const events = yield* EventV2.Service

  Line 1261:       const events = yield* EventV2.Service

  Line 1310:       const events = yield* EventV2.Service

  Line 1355:       const events = yield* EventV2.Service

  Line 1387:       const events = yield* ...
gateway · 2026-09-16 20:20:17
result
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
60:     id: _,
61:     type,
62:     ...data
63:   } = encodeMessage(SessionMessage.Assistant.make({ id, type: "assistant", agent: "build", model, content: [], time }))
64:   return { id, session_id: sessionID, type, seq, time_created: DateTime.toEpochMillis(time.created), data }
65: }
66: 
67: describe("SessionProjector", () => {
68:   it.effect("atomically reconciles one abandoned execution into one continuation", () =>
69:     Effect.gen(function* () {
70:       const { db } = yield* Database.Service
71:       const events = yield* EventV2.Service
72:       const assistantID = SessionMessage.ID.make("msg_recovery_assistant")
73:       const continuationID = continuation("execution")
74:       yield* db
75:         .insert(ProjectTable)
76:         .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
77:         .run()
78:       yield* ...
gateway · 2026-09-16 20:21:16
result
Found 1 matches
/home/byk/Code/opencode/packages/core/src/session/message-updater.ts:
  Line 96:   const updateOwnedAssistant = (messageID: SessionMessage.ID, recipe: (draft: DraftAssistant) => void) =>
gateway · 2026-09-16 20:21:28
result
<path>/home/byk/Code/opencode/packages/core/src/session/message-updater.ts</path>
<type>file</type>
<content>
80:   type DraftAssistant = WritableDraft<SessionMessage.Assistant>
81:   type DraftTool = WritableDraft<SessionMessage.AssistantTool>
82:   type DraftText = WritableDraft<SessionMessage.AssistantText>
83:   type DraftReasoning = WritableDraft<SessionMessage.AssistantReasoning>
84: 
85:   const latestTool = (assistant: DraftAssistant | undefined, callID?: string) =>
86:     assistant?.content.findLast(
87:       (item): item is DraftTool => item.type === "tool" && (callID === undefined || item.id === callID),
88:     )
89: 
90:   const latestText = (assistant: DraftAssistant | undefined, textID: string) =>
91:     assistant?.content.findLast((item): item is DraftText => item.type === "text" && item.id === textID)
92: 
93:   const latestReasoning = (assistant: DraftAssistant | undefined, reasoningID: string) =>
94:     assistant?.content.findLast((item): item is DraftReasoning ...
gateway · 2026-09-16 20:21:47
result
Found 1 matches
/home/byk/Code/opencode/packages/core/src/session/projector.ts:
  Line 515:           .delete(SessionMessageTable)
gateway · 2026-09-16 20:21:57
result
<path>/home/byk/Code/opencode/packages/core/src/session/projector.ts</path>
<type>file</type>
<content>
480:     yield* events.project(SessionEvent.Compaction.Ended, (event) => run(db, event))
481:     yield* events.project(SessionEvent.RevertEvent.Staged, (event) =>
482:       db
483:         .update(SessionTable)
484:         .set({
485:           revert: { ...event.data.revert, files: event.data.revert.files ? [...event.data.revert.files] : undefined },
486:           time_updated: DateTime.toEpochMillis(event.data.timestamp),
487:         })
488:         .where(eq(SessionTable.id, event.data.sessionID))
489:         .run()
490:         .pipe(Effect.orDie, Effect.asVoid),
491:     )
492:     yield* events.project(SessionEvent.RevertEvent.Cleared, (event) =>
493:       db
494:         .update(SessionTable)
495:         .set({ revert: null, time_updated: DateTime.toEpochMillis(event.data.timestamp) })
496:         .where(eq(SessionTable.id, event.data.sessionID))
497:         ...
gateway · 2026-09-16 20:22:40
result
Found 4 matches
/home/byk/Code/opencode/packages/core/src/session.ts:
  Line 169:   readonly resume: (sessionID: SessionSchema.ID) => Effect.Effect<void, NotFoundError | SessionRunner.RunError>

  Line 170:   readonly interrupt: (sessionID: SessionSchema.ID) => Effect.Effect<void>

  Line 427:       resume: Effect.fn("V2Session.resume")(function* (sessionID) {

  Line 431:       interrupt: Effect.fn("V2Session.interrupt")((sessionID) =>
gateway · 2026-09-16 20:22:51
result
<path>/home/byk/Code/opencode/packages/core/src/session.ts</path>
<type>file</type>
<content>
415:           model: input.model,
416:         })
417:       }),
418:       compact: Effect.fn("V2Session.compact")(function* (input) {
419:         yield* result.get(input.sessionID)
420:         return yield* new OperationUnavailableError({ operation: "compact" })
421:       }),
422:       wait: Effect.fn("V2Session.wait")(function* (sessionID) {
423:         yield* result.get(sessionID)
424:         return yield* new OperationUnavailableError({ operation: "wait" })
425:       }),
426:       active: execution.active,
427:       resume: Effect.fn("V2Session.resume")(function* (sessionID) {
428:         yield* result.get(sessionID)
429:         yield* execution.resume(sessionID)
430:       }),
431:       interrupt: Effect.fn("V2Session.interrupt")((sessionID) =>
432:         Effect.uninterruptible(execution.interrupt(sessionID)),
433:       ),
434:       revert: {
435:         stage: ...
gateway · 2026-09-16 20:23:11
result
Found 1 matches
/home/byk/Code/opencode/packages/core/test/session-runner-recorded.test.ts:
  Line 132:   it.effect("executes one recorded V2 prompt through the recorded HTTP transport", () =>
gateway · 2026-09-16 20:23:26
result
Found 5 matches
/home/byk/Code/opencode/packages/core/test/session-runner-tool-events.test.ts:
  Line 92: test("local tool success serializes media base64 once and reconstructs from structured content", async () => {

  Line 115: test("provider-executed success retains its compatibility result", async () => {

  Line 123: test("binary failure emits no success event", async () => {

  Line 139: test("old success event data containing result still decodes", () => {

  Line 168: test("step finish records settlement without publishing step ended", async () => {
gateway · 2026-09-16 20:23:36
result
Found 17 matches
/home/byk/Code/opencode/packages/core/test/session-run-coordinator.test.ts:
  Line 9:   it.effect("joins concurrent resumes for one key", () =>

  Line 31:   it.effect("joins a wake-started execution without forcing a successor", () =>

  Line 57:   it.effect("starts execution when woken while idle", () =>

  Line 69:   it.effect("snapshots only active executions", () =>

  Line 102:   it.effect("cleans active executions after failure and defect", () =>

  Line 122:   it.effect("cleans active executions when its scope closes", () =>

  Line 141:   it.effect("coalesces wakes received during active execution", () =>

  Line 173:   it.effect("runs again when woken during the follow-up", () =>

  Line 209:   it.effect("does nothing when interrupted while idle", () =>

  Line 218:   it.effect("interrupts active execution and clears its pending wake", () =>

  Line 247:   it.effect("runs a wake registered during interruption cleanup", () =>

  Line 285:   ...
gateway · 2026-09-16 20:24:02
result
Found 34 matches
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
  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 1034:   it.effect("interrupts a source Location runner after a Session moves", () =>

  Line 1060:   it.effect("fails gracefully when a stored context snapshot cannot be decoded", () =>

  Line 1225:   it.effect("keeps the sampled agent when selection changes during observation", () =>

  Line 1257:   it.effect("keeps the sampled model when selection changes during model resolution", () =>

  Line 1306:   it.effect("keeps the baseline and chronological System updates after a model switch", () =>

  Line 1351:   it.effect("preserves the baseline while ...
gateway · 2026-09-16 20:24:12
result
Found 7 matches
/home/byk/Code/opencode/packages/core/test/session-projector.test.ts:
  Line 160:       yield* events.replayAll(

  Line 173:       const replayedContinuation = yield* db

  Line 178:       if (!replayedContinuation) return yield* Effect.die("Replayed recovery continuation not found")

  Line 199:       yield* events.replayAll(

  Line 435:     it.effect(`never commits assistant-less ${phase} recovery after concurrent assistant supersession`, () =>

  Line 2813:   it.effect("rejects distinct creator events that reuse one projected message ID", () =>

  Line 2834:       const id = SessionMessage.ID.make("msg_creator_collision")
gateway · 2026-09-16 20:24:24
result
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
2790:         "model-switched",
2791:         "synthetic",
2792:         "shell",
2793:         "compaction",
2794:       ])
2795:       expect(messages.find((message) => message.type === "shell")).toMatchObject({
2796:         output: "/project",
2797:         time: { completed: DateTime.makeUnsafe(1) },
2798:       })
2799:       expect(messages.find((message) => message.type === "compaction")).toMatchObject({
2800:         summary: "summary",
2801:         recent: "recent context",
2802:       })
2803:       expect(
2804:         yield* db.select().from(SessionTable).where(eq(SessionTable.id, sessionID)).get().pipe(Effect.orDie),
2805:       ).toMatchObject({
2806:         agent: "build",
2807:         model,
2808:         time_updated: DateTime.toEpochMillis(created),
2809:       })
2810:     }),
2811:   )
2812: 
2813:   it.effect("rejects distinct creator events that ...
gateway · 2026-09-16 20:24:41
result
<path>/home/byk/Code/opencode/packages/core/src/session/input.ts</path>
<type>file</type>
<content>
250:     readonly delivery: Delivery
251:     readonly timeCreated: DateTime.Utc
252:     readonly promotedSeq: number
253:   },
254: ) {
255:   const validation = yield* validateRecovery(db, input.id)
256:   if (validation && (validation.input === undefined || validation.input.promoted_seq === null) && validation.error)
257:     return yield* Effect.die(
258:       new RecoveryConflict({ executionID: validation.recovery.execution_id, reason: validation.error }),
259:     )
260:   const updated = yield* db
261:     .update(SessionInputTable)
262:     .set({ promoted_seq: input.promotedSeq, wake_pending: false })
263:     .where(
264:       and(
265:         eq(SessionInputTable.id, input.id),
266:         eq(SessionInputTable.session_id, input.sessionID),
267:         isNull(SessionInputTable.promoted_seq),
268:       ),
269:     )
270:     .returning()
271:     .get()
272:     ...
gateway · 2026-09-16 20:25:23
result
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1440:         phase: "continue",
1441:       })
1442:       yield* db
1443:         .insert(SessionExecutionTable)
1444:         .values({
1445:           id: "conflicting-recovery-identity",
1446:           session_id: sessionID,
1447:           owner_id: "dead-owner",
1448:           phase: "ready",
1449:           time_created: 0,
1450:           expires_at: 0,
1451:         })
1452:         .run()
1453: 
1454:       yield* SessionRecovery.recover().pipe(Effect.provideService(SessionExecution.Service, recoveryExecution))
1455: 
1456:       expect(yield* db.select().from(SessionExecutionTable).get()).toMatchObject({
1457:         id: "conflicting-recovery-identity",
1458:         recovery_error: "Recovery projection failed",
1459:       })
1460:       expect(yield* db.select().from(SessionRecoveryTable).all()).toEqual([
1461:         expect.objectContaining({ execution_id: ...
gateway · 2026-09-16 20:26:07
result
<path>/home/byk/Code/opencode/packages/core/test/session-runner-recorded.test.ts</path>
<type>file</type>
<content>
1: import { HttpRecorder } from "@opencode-ai/http-recorder"
2: import { HttpRecorderInternal } from "@opencode-ai/http-recorder/internal"
3: import * as OpenAIChat from "@opencode-ai/llm/protocols/openai-chat"
4: import { Auth, LLMClient, RequestExecutor } from "@opencode-ai/llm/route"
5: import { Database } from "@opencode-ai/core/database/database"
6: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
7: import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
8: import { LayerNode } from "@opencode-ai/core/effect/layer-node"
9: import { EventV2 } from "@opencode-ai/core/event"
10: import { EventTable } from "@opencode-ai/core/event/sql"
11: import { PermissionV2 } from "@opencode-ai/core/permission"
12: import { AgentV2 } from "@opencode-ai/core/agent"
13: import { Config } from "@opencode-ai/core/config"
14: import { ...
gateway · 2026-09-16 20:26:31
result
<path>/home/byk/Code/opencode/packages/core/src/session/runner/llm.ts</path>
<type>file</type>
<content>
180:         _tag: "ContinueAfterOverflowCompaction",
181:         step,
182:       })
183: 
184:     const loadSystemContext = (agent: AgentV2.Selection) =>
185:       Effect.all([systemContext.load(), skillGuidance.load(agent), referenceGuidance.load()], {
186:         concurrency: "unbounded",
187:       }).pipe(Effect.map(SystemContext.combine))
188: 
189:     const runTurnAttempt = Effect.fn("SessionRunner.runTurn")(function* (
190:       sessionID: SessionSchema.ID,
191:       executionID: string,
192:       promotion: Promotion | undefined,
193:       step: number,
194:       ownerID: string,
195:       recoverOverflow?: typeof compaction.compactAfterOverflow,
196:     ) {
197:       const session = yield* getSession(sessionID)
198:       if (session.location.directory !== location.directory || session.location.workspaceID !== location.workspaceID)
199:         return yield* ...
gateway · 2026-09-16 20:26:58
result
<path>/home/byk/Code/opencode/packages/server/src/handlers/event.ts</path>
<type>file</type>
<content>
1: import { EventV2 } from "@opencode-ai/core/event"
2: import { OpenCodeEvent } from "@opencode-ai/protocol/groups/event"
3: import { Effect, Schema, Stream } from "effect"
4: import { HttpServerResponse } from "effect/unstable/http"
5: import { HttpApiBuilder } from "effect/unstable/httpapi"
6: import * as Sse from "effect/unstable/encoding/Sse"
7: import { Api } from "../api"
8: 
9: const subscriberCapacity = 256
10: 
11: function eventData(data: unknown): Sse.Event {
12:   return {
13:     _tag: "Event",
14:     event: "message",
15:     id: undefined,
16:     data: JSON.stringify(Schema.encodeUnknownSync(OpenCodeEvent)(data)),
17:   }
18: }
19: 
20: export const EventHandler = HttpApiBuilder.group(Api, "server.event", (handlers) =>
21:   Effect.gen(function* () {
22:     const events = yield* EventV2.Service
23:     return handlers.handleRaw("event.subscribe", () =>
24:       ...
gateway · 2026-09-16 20:27:54
result
3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf  /tmp/opencode/session-recovery-final-v13.patch
c3b8c5d78e473ad4104a5b54617900e28921a283029b8484be7bce4bc3cee85a  /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server
ab9408c81cc9916177641ae8e9f08624fc27b5b9
33
3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf  -
gateway · 2026-09-16 20:28:58

MUST-FIX

  • A stale runner can quarantine recovery work after losing its lease.
    The production caller supplies an ownership guard to promoteExact at packages/core/src/session/runner/llm.ts:235-239. Normal promotion invokes that guard inside the durable event transaction through packages/core/src/session/input.ts:473-484.
    However, when projection raises RecoveryConflict, the catch path writes session_recovery.recovery_error directly at packages/core/src/session/input.ts:582-596. Its predicate checks only execution identity, wake state, and existing error. It never checks the active Session execution ID, owner ID, or unexpired lease, and it never invokes the supplied guard.

    Therefore, a runner paused across lease expiry or takeover can still mutate durable recovery state and quarantine work owned by its successor. This violates the stale-owner fencing invariant even though most validation errors are monotonic.

    Move the quarantine update into an immediate transaction that first validates the exact live claim, then write the error, and only afterward propagate RecoveryConflict. Add a regression that transfers ownership between conflict detection and quarantine and asserts that the stale owner cannot set recovery_error.

    Existing coverage at packages/core/test/session-projector.test.ts:1581-1642 calls promoteExact with Effect.void; it does not exercise ownership loss.

PASS

  • Patch hash, SEA hash, and base commit match the supplied values.
  • The patch applies cleanly to base ab9408c81cc9916177641ae8e9f08624fc27b5b9 through a base-backed temporary index.
  • The patch contains exactly 33 paths. Reconstructing those paths from the reviewed source reproduces SHA-256 3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf.
  • Cancellation quarantine checks exact execution, Session, owner, unexpired lease, and null error at packages/core/src/session/recovery.ts:121-138. ...
gpt-5.6-sol · 2026-09-16 20:28:58 · ~1.1K tokens