DashboardopencodeSession 00IcX4pcUjGJ

Session 00IcX4pcUjGJ

Full ID: 00IcX4pcUjGJUSWPX
Messages: 82
Distillations: 11
Time range: 2026-09-17 09:20:26 — 2026-09-17 09:45:21

Cost Intelligence

Your Spend$8.96
Conversation: $8.14 (81 turns)Overhead: $0.82 (distill: $0.82)
Cache Hit Rate92%
10.87M in, 36.2K out10.02M cache reads
Actual vs Without Lore$8.96 of $8.54
Net overhead: $0.42
Net overhead: $0.42 — Lore overhead exceeds savings this session

Cache Warming

Status
waiting
Warmups
0
Hits
0
P(returns)
38.9%
P(finished)
61.1%
S(t)
3.3%
Decision Details
Idle: 2h 19m
TTL: 5m
Phase: none
Turns: 161
Text-only runs: 1
Break fraction: 32.8%
Threshold: 30.0%
P(session finished): 61.1%
P(returns): 38.9%
Cycles spent: 0 / 0 max
Expected cycles: 0.0
Session weight: 0.00 (0 obs / 20 pseudocount)
Not warming: Sub-agent session (ephemeral)
Circuit breaker: OK (0/3)
Survival Histogram
10s36%
20s5%
30s3%
45s5%
1m5%
1.5m7%
2m2%
3m4%
4m5%
5m2%5m TTL
7m3%
10m3%
15m4%
20m3%
30m4%
45m4%
1.0h1%1h TTL
1.5h2%
2.0h1%
4.0h2%
>4h2%
Session (0 obs)Global (198 obs)Blended

Conversation (82 messages)

Strict READ-ONLY security/pentest review of frozen v22 in /tmp/opencode/session-recovery-v22-review. Never edit, run tests/builds/services, mutate VCS/index/refs, or use recall. Verify immutable inputs first: base ab9408c81cc9916177641ae8e9f08624fc27b5b9; patch /tmp/opencode/session-recovery-final-v22.patch SHA-256 5af96209cef23e735f09dbf2f4b2f5b0f51b208ece9e5b325e4e217b46003643; SEA /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server SHA-256 10a3698a5e3b009f811a1b27eefab9e1083b560e24efee5f537f003d7edd7384; 34 paths. Inspect all changed files and relevant context. Adversarially inspect cancellation/finalizers, terminal failed-Step tool reconciliation, publication rollback, concurrent interrupt single-flight, stale-owner takeover, cross-Session/corrupt rows, replay and wake duplication, DoS/fairness, external side effects/snapshots, migrations, generated clients, auth/layering. Verify real trigger test aborts session.next.step.interrupted.1 and leaves cancelling plus running tool; completed Step.Failed cleanup publishes Tool.Failed after join and clears with final atomic commit while retaining original failure; startup equivalent never creates continue. Verify schema prevIds and explicit resume. Treat final check-call, process-local routing, bounded skew as disclosed residuals. Findings first with exact file:line and MUST-FIX/CONCERN. End exactly MERGE or DO-NOT-MERGE.

gateway · 2026-09-17 09:20:26
result
<path>/tmp/opencode/session-recovery-v22-review</path>
<type>directory</type>
<entries>
.dockerignore
.editorconfig
.git
.gitattributes
.github/
.gitignore
.gitleaksignore
.husky/
.lore.md
.opencode/
.oxlintrc.json
.prettierignore
.vscode/
.zed/
AGENTS.md
artifacts/
bun.lock
bunfig.toml
CONTEXT.md
CONTRIBUTING.md
flake.lock
flake.nix
github/
infra/
install
LICENSE
nix/
package.json
packages/
patches/
perf/
pnpm-lock.yaml
pnpm-workspace.yaml
README.ar.md
README.bn.md
README.br.md
README.bs.md
README.da.md
README.de.md
README.es.md
README.fr.md
README.gr.md
README.it.md
README.ja.md
README.ko.md
README.md
README.no.md
README.pl.md
README.ru.md
README.th.md
README.tr.md
README.uk.md
README.vi.md
README.zh.md
README.zht.md
screenshot-uk.png
script/
sdks/
SECURITY.md
specs/
sst-env.d.ts
sst.config.ts
STATS.md
tsconfig.json
turbo.json

(65 entries)
</entries>
gateway · 2026-09-17 09:20:44
result
5af96209cef23e735f09dbf2f4b2f5b0f51b208ece9e5b325e4e217b46003643  /tmp/opencode/session-recovery-final-v22.patch
10a3698a5e3b009f811a1b27eefab9e1083b560e24efee5f537f003d7edd7384  /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server
gateway · 2026-09-17 09:20:55
result
 M packages/client/src/generated/types.ts
 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.ts
 M packages/core/src/session/compaction.ts
 M packages/core/src/session/context-epoch.ts
 M packages/core/src/session/execution/local.ts
 M packages/core/src/session/input.ts
 M packages/core/src/session/message-updater.ts
 M packages/core/src/session/projector.ts
 M packages/core/src/session/run-coordinator.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/database-migration.test.ts
 M packages/core/test/session-projector.test.ts
 M packages/core/test/session-prompt.test.ts
 M packages/core/test/session-run-coordinator.test.ts
 M packages/core/test/session-runner-recorded.test.ts
 M packages/core/test/session-runner-tool-events.test.ts
 M ...
gateway · 2026-09-17 09:21:11
result
72	0	packages/client/src/generated/types.ts
566	47	packages/core/schema.json
1	0	packages/core/src/database/migration.gen.ts
62	0	packages/core/src/database/migration/20260914170650_session-recovery.ts
52	1	packages/core/src/database/schema.gen.ts
6	4	packages/core/src/session.ts
31	16	packages/core/src/session/compaction.ts
54	34	packages/core/src/session/context-epoch.ts
260	19	packages/core/src/session/execution/local.ts
391	31	packages/core/src/session/input.ts
69	0	packages/core/src/session/message-updater.ts
88	1	packages/core/src/session/projector.ts
560	0	packages/core/src/session/recovery.ts
35	5	packages/core/src/session/run-coordinator.ts
4	0	packages/core/src/session/runner/index.ts
289	104	packages/core/src/session/runner/llm.ts
108	48	packages/core/src/session/runner/publish-llm-event.ts
88	1	packages/core/src/session/sql.ts
66	1	packages/core/test/database-migration.test.ts
393	0	packages/core/test/session-execution-local.test.ts
2922	3	packages/core/test/session-project...
gateway · 2026-09-17 09:21:16
result
/tmp/opencode/session-recovery-v22-review/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/ui/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/app/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/codemode/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/desktop/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/app/e2e/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/opencode/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/app/e2e/performance/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/opencode/test/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/session-ui/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/effect-drizzle-sqlite/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/opencode/src/session/llm/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/llm/AGENTS.md
/tmp/opencode/session-recovery-v22-review/packages/opencode/src/server/routes/i...
gateway · 2026-09-17 09:21:24
result
<path>/tmp/opencode/session-recovery-v22-review/AGENTS.md</path>
<type>file</type>
<content>
1: - To regenerate the legacy JavaScript SDK, run `./packages/sdk/js/script/build.ts`.
2: - After changing the public Protocol or Server `HttpApi`, run `bun run generate` from `packages/client`. Do not edit `src/generated` or `src/generated-effect` directly.
3: - Keep runtime dependencies directed from Schema to Core and Protocol, then from Core and Protocol to Server. Client runtime code may depend on Schema and Protocol but never Core or Server; `sdk-next` composes Client, Core, and Server.
4: - The default branch in this repo is `dev`.
5: - Local `main` ref may not exist; use `dev` or `origin/dev` for diffs.
6: 
7: ## Branch Names
8: 
9: Use a short branch name of at most three words, separated by hyphens. Do not use slashes or type prefixes such as `feat/` or `fix/`.
10: 
11: Examples: `session-recovery`, `fix-scroll-state`, `regenerate-sdk`. ...
gateway · 2026-09-17 09:21:31
result
<path>/tmp/opencode/session-recovery-v22-review/CONTRIBUTING.md</path>
<type>file</type>
<content>
1: # Contributing to OpenCode
2: 
3: We want to make it easy for you to contribute to OpenCode. Here are the most common type of changes that get merged:
4: 
5: - Bug fixes
6: - Additional LSPs / Formatters
7: - Improvements to LLM performance
8: - Support for new providers
9: - Fixes for environment-specific quirks
10: - Missing standard behavior
11: - Documentation improvements
12: 
13: However, any UI or core product feature must go through a design review with the core team before implementation.
14: 
15: If you are unsure if a PR would be accepted, feel free to ask a maintainer or look for issues with any of the following labels:
16: 
17: - [`help wanted`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelp-wanted)
18: - [`good first ...
gateway · 2026-09-17 09:21:38
result
<path>/tmp/opencode/session-recovery-v22-review/packages/schema/AGENTS.md</path>
<type>file</type>
<content>
1: # Schema Package Guide
2: 
3: `@opencode-ai/schema` owns browser-safe wire and storage contracts shared by protocol, server, core, and generated SDKs. Keep runtime behavior, service layers, side effects, and host-local implementation details in the domain package that owns them.
4: 
5: ## Package Boundary
6: 
7: - Preserve the dependency direction: `@opencode-ai/schema <- @opencode-ai/protocol <- @opencode-ai/server`.
8: - Schema values should be serializable contract definitions, not service implementations or runtime registries.
9: - A domain may keep a minimal public wire contract here when SDK generation needs it, but do not move the broader runtime model into Schema just because an event is public. `plugin.added` is the current example: Schema may own the minimum browser-safe event payload, while plugin runtime behavior stays outside Schema. ...
gateway · 2026-09-17 09:21:42
result
<path>/tmp/opencode/session-recovery-v22-review/packages/opencode/AGENTS.md</path>
<type>file</type>
<content>
1: # opencode database guide
2: 
3: ## Database
4: 
5: - **Schema**: Drizzle schema lives in `packages/core/src/**/*.sql.ts`.
6: - **Migrations**: database migrations live in `packages/core` and are applied by core.
7: 
8: ## Development server
9: 
10: - Running `bun dev` from `packages/opencode` starts the live interactive TUI. Do not run it as a blocking foreground command when you need to inspect the result.
11: - Start it in `tmux` instead: `tmux new-session -d -s opencode-dev 'bun dev'`.
12: - Capture the current TUI output with: `tmux capture-pane -pt opencode-dev`.
13: - Stop the session explicitly when done: `tmux kill-session -t opencode-dev`.
14: 
15: # Module shape
16: 
17: Do not use `export namespace Foo { ... }` for module organization. It is not
18: standard ESM, it prevents tree-shaking, and it breaks Node's native TypeScript
19: runner. ...
gateway · 2026-09-17 09:21:47
result
<path>/tmp/opencode/session-recovery-v22-review/packages/opencode/src/server/routes/instance/httpapi/AGENTS.md</path>
<type>file</type>
<content>
1: # HttpApi Route Patterns
2: 
3: Use `HttpApiBuilder.group(...)` for normal HTTP endpoints, including streaming HTTP responses such as server-sent events. Handlers should yield stable services once while building the handler layer, then close over those services in endpoint implementations.
4: 
5: ```ts
6: export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session", (handlers) =>
7:   Effect.gen(function* () {
8:     const session = yield* Session.Service
9: 
10:     return handlers.handle("list", () => session.list())
11:   }),
12: )
13: ```
14: 
15: For SSE endpoints, stay in `HttpApiBuilder.group(...)` and return `HttpServerResponse.stream(...)` from the handler. Annotate the endpoint success schema with `HttpApiSchema.asText({ contentType: "text/event-stream" })` so OpenAPI documents the stream content type. ...
gateway · 2026-09-17 09:22:00
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/execution/claim.ts</path>
<type>file</type>
<content>
1: export * as SessionExecutionClaim from "./claim"
2: 
3: import { and, eq, gt, notInArray } 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: ...
gateway · 2026-09-17 09:22:05
result
<path>/tmp/opencode/session-recovery-v22-review/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-17 09:22:13
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/database/migration/20260914170650_session-recovery.ts</path>
<type>file</type>
<content>
1: import { Effect } from "effect"
2: import type { DatabaseMigration } from "../migration"
3: 
4: export default {
5:   id: "20260914170650_session-recovery",
6:   up(tx) {
7:     return Effect.gen(function* () {
8:       yield* tx.run(`ALTER TABLE \`session_input\` ADD \`wake_pending\` integer DEFAULT false NOT NULL;`)
9:       yield* tx.run(
10:         `ALTER TABLE \`session\` ADD \`wake_attempts\` integer DEFAULT 0 NOT NULL CONSTRAINT \`session_wake_attempts_check\` CHECK(\`wake_attempts\` >= 0 AND \`wake_attempts\` <= 9007199254740991);`,
11:       )
12:       yield* tx.run(`
13:         CREATE TABLE \`session_execution\` (
14:           \`id\` text NOT NULL,
15:           \`session_id\` text NOT NULL,
16:           \`owner_id\` text NOT NULL,
17:           \`assistant_message_id\` text,
18:            \`phase\` text NOT ...
gateway · 2026-09-17 09:22:17
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/recovery.ts</path>
<type>file</type>
<content>
1: export * as SessionRecovery from "./recovery"
2: 
3: import { and, asc, desc, eq, exists, gt, isNull, lte, or, sql } from "drizzle-orm"
4: import { Clock, DateTime, Effect, Layer, Option, Schedule, Schema } from "effect"
5: import { Database } from "../database/database"
6: import { makeGlobalNode } from "../effect/app-node"
7: import { EventV2 } from "../event"
8: import { SessionEvent } from "./event"
9: import { SessionExecution } from "./execution"
10: import { SessionProjector } from "./projector"
11: import { SessionExecutionClaim } from "./execution/claim"
12: import { SessionInput } from "./input"
13: import { SessionMessage } from "./message"
14: import { continuation } from "./recovery-id"
15: import { SessionSchema } from "./schema"
16: import {
17:   MAX_WAKE_ATTEMPTS,
18:   SESSION_EXECUTION_LEASE_MS,
19:   SessionExecutionTable,
20:   ...
gateway · 2026-09-17 09:22:25
result
<path>/tmp/opencode/session-recovery-v22-review/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, DateTime, Deferred, Duration, Effect, Exit, Layer, Option, Schedule, Schema } 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 { EventV2 } from "../../event"
13: import { SessionEvent } from "../event"
14: import { SessionExecutionClaim } from "./claim"
15: import {
16:   SESSION_EXECUTION_LEASE_MS,
17:   SessionExecutionTable,
18:   ...
gateway · 2026-09-17 09:22:33
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/input.ts</path>
<type>file</type>
<content>
1: export * as SessionInput from "./input"
2: 
3: import { and, asc, desc, eq, isNull, lte } from "drizzle-orm"
4: import { DateTime, Effect, Option, Schema } from "effect"
5: import { Admitted, Delivery } from "@opencode-ai/schema/session-input"
6: import type { Database } from "../database/database"
7: import { EventV2 } from "../event"
8: import { SessionEvent } from "./event"
9: import { SessionMessage } from "./message"
10: import { Prompt } from "./prompt"
11: import { continuation, message } from "./recovery-id"
12: import { SessionSchema } from "./schema"
13: import { SessionInputTable, SessionMessageTable, SessionRecoveryTable, SessionTable } from "./sql"
14: 
15: type DatabaseService = Database.Interface["db"]
16: 
17: export { Admitted, Delivery }
18: 
19: const decodePrompt = Schema.decodeUnknownSync(Prompt)
20: const encodePrompt = ...
gateway · 2026-09-17 09:22:39
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/input.ts</path>
<type>file</type>
<content>
501:   rows: ReadonlyArray<typeof SessionInputTable.$inferSelect>,
502:   commit?: (row: typeof SessionInputTable.$inferSelect) => Effect.Effect<void>,
503: ) {
504:   for (const row of rows) {
505:     const id = SessionMessage.ID.make(row.id)
506:     yield* events
507:       .publish(
508:         SessionEvent.Prompted,
509:         {
510:           sessionID,
511:           timestamp: DateTime.makeUnsafe(row.time_created),
512:           messageID: id,
513:           prompt: decodePrompt(row.prompt),
514:           delivery: row.delivery,
515:         },
516:         commit ? { commit: () => commit(row) } : undefined,
517:       )
518:       .pipe(
519:         Effect.catchDefect((defect) =>
520:           defect instanceof LifecycleConflict
521:             ? find(db, id).pipe(
522:                 Effect.flatMap((stored) => (stored?.promotedSeq === undefined ? ...
gateway · 2026-09-17 09:22:48
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/projector.ts</path>
<type>file</type>
<content>
1: export * as SessionProjector from "./projector"
2: 
3: import { and, desc, eq, gt, or, sql } from "drizzle-orm"
4: import { DateTime, Effect, Layer, Schema } from "effect"
5: import { Database } from "../database/database"
6: import { EventV2 } from "../event"
7: import { makeGlobalNode } from "../effect/app-node"
8: import { SessionEvent } from "./event"
9: import { SessionV1 } from "../v1/session"
10: import { WorkspaceTable } from "../control-plane/workspace.sql"
11: import { SessionMessage } from "./message"
12: import { SessionMessageUpdater } from "./message-updater"
13: import { SessionInput } from "./input"
14: import { Prompt } from "./prompt"
15: import { continuation } from "./recovery-id"
16: import { WorkspaceV2 } from "../workspace"
17: import {
18:   MessageTable,
19:   PartTable,
20:   SessionInputTable,
21:   SessionMessageTable,
22:   ...
gateway · 2026-09-17 09:23:00
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/projector.ts</path>
<type>file</type>
<content>
501:       Effect.gen(function* () {
502:         const boundary = yield* db
503:           .select({ seq: SessionMessageTable.seq })
504:           .from(SessionMessageTable)
505:           .where(
506:             and(
507:               eq(SessionMessageTable.session_id, event.data.sessionID),
508:               eq(SessionMessageTable.id, event.data.messageID),
509:             ),
510:           )
511:           .get()
512:           .pipe(Effect.orDie)
513:         if (!boundary) return yield* Effect.die(`Revert boundary message not found: ${event.data.messageID}`)
514:         yield* db
515:           .delete(SessionMessageTable)
516:           .where(
517:             and(eq(SessionMessageTable.session_id, event.data.sessionID), gt(SessionMessageTable.seq, boundary.seq)),
518:           )
519:           .run()
520:           .pipe(Effect.orDie)
521:         ...
gateway · 2026-09-17 09:23:08
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/message-updater.ts</path>
<type>file</type>
<content>
1: import { castDraft, produce, type WritableDraft } from "immer"
2: import { Effect } from "effect"
3: import { SessionEvent } from "./event"
4: import { SessionMessage } from "./message"
5: import { message } from "./recovery-id"
6: 
7: export type MemoryState = {
8:   messages: SessionMessage.Message[]
9: }
10: 
11: export interface Adapter {
12:   readonly getCurrentAssistant: () => Effect.Effect<SessionMessage.Assistant | undefined>
13:   readonly getAssistant: (messageID: SessionMessage.ID) => Effect.Effect<SessionMessage.Assistant | undefined>
14:   readonly getCurrentShell: (callID: string) => Effect.Effect<SessionMessage.Shell | undefined>
15:   readonly updateAssistant: (assistant: SessionMessage.Assistant) => Effect.Effect<void>
16:   readonly updateShell: (shell: SessionMessage.Shell) => Effect.Effect<void>
17:   readonly appendMessage: (message: ...
gateway · 2026-09-17 09:23:16
result
Found 17 matches
/tmp/opencode/session-recovery-v22-review/packages/core/test/session-runner.test.ts:
  Line 838:             event.type.startsWith(SessionEvent.Tool.Failed.type),

  Line 3924:         WHEN NEW.type = 'session.next.step.interrupted.1'

  Line 3940:       expect(yield* db.select().from(SessionExecutionTable).get()).toMatchObject({ phase: "cancelling" })

  Line 3960:         event.type !== SessionEvent.Step.Failed.type

  Line 3994:           .where(eq(EventTable.type, "session.next.step.interrupted.1"))

  Line 4063:           .where(eq(EventTable.type, "session.next.step.interrupted.1"))

  Line 4097:           .where(eq(EventTable.type, "session.next.step.interrupted.1"))

  Line 4102:         phase: "cancelling",

  Line 4128:           .where(eq(EventTable.type, "session.next.step.interrupted.1"))


/tmp/opencode/session-recovery-v22-review/packages/core/test/session-tool-progress.test.ts:
  Line 127:       const failed = yield* ...
gateway · 2026-09-17 09:23:25
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
3800:           input: Schema.Struct({}),
3801:           output: Schema.Struct({}),
3802:           execute: () =>
3803:             Deferred.succeed(executionStarted, undefined).pipe(
3804:               Effect.andThen(Effect.never),
3805:               Effect.ensuring(
3806:                 Deferred.succeed(finalizerStarted, undefined).pipe(Effect.andThen(Deferred.await(releaseFinalizer))),
3807:               ),
3808:             ),
3809:         }),
3810:         sibling_defect: Tool.make({
3811:           description: "Fail while another tool remains active",
3812:           input: Schema.Struct({}),
3813:           output: Schema.Struct({}),
3814:           execute: () => Deferred.await(executionStarted).pipe(Effect.andThen(Effect.die("sibling defect"))),
3815:         }),
3816:       })
3817:       yield* session.prompt({ sessionID, prompt: Prompt.make({ ...
gateway · 2026-09-17 09:23:35
result
<path>/tmp/opencode/session-recovery-v22-review/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: ...
gateway · 2026-09-17 09:23:41
result
diff --git a/packages/core/src/session/runner/llm.ts b/packages/core/src/session/runner/llm.ts
index 874086a06b..ed7cc57008 100644
--- a/packages/core/src/session/runner/llm.ts
+++ b/packages/core/src/session/runner/llm.ts
@@ -1,439 +1,624 @@
 import {
   LLM,
   LLMClient,
   LLMError,
   LLMEvent,
   Message,
   SystemPart,
   isContextOverflowFailure,
   type ProviderErrorEvent,
 } from "@opencode-ai/llm"
-import { Cause, DateTime, Effect, FiberSet, Layer, Option, Semaphore, Stream } from "effect"
+import { Cause, DateTime, Effect, Exit, Fiber, Layer, Option, Semaphore, Stream } from "effect"
+import { and, eq } from "drizzle-orm"
 import { AgentV2 } from "../../agent"
 import { Config } from "../../config"
 import { Database } from "../../database/database"
 import { EventV2 } from "../../event"
 import { Location } from "../../location"
 import { ModelV2 } from "../../model"
 import { PermissionV2 } from "../../permission"
 import { ProviderV2 } from "../../provider"
 import { ...
gateway · 2026-09-17 09:23:53
result
No files found
gateway · 2026-09-17 09:24:02
result
/tmp/opencode/session-recovery-v22-review/packages/core/src/event.ts
/tmp/opencode/session-recovery-v22-review/packages/core/src/public-event-manifest.ts
/tmp/opencode/session-recovery-v22-review/packages/core/src/session/event.ts
/tmp/opencode/session-recovery-v22-review/packages/core/src/session/runner/publish-llm-event.ts
/tmp/opencode/session-recovery-v22-review/packages/core/src/database/migration/20260604172448_event_sourced_session_input.ts
/tmp/opencode/session-recovery-v22-review/packages/core/src/database/migration/20260323234822_events.ts
gateway · 2026-09-17 09:24:11
result
Found 13 matches
/tmp/opencode/session-recovery-v22-review/packages/core/src/event.ts:
  Line 127:   readonly publish: <D extends Definition>(

  Line 140:     options?: { readonly publish?: boolean; readonly ownerID?: string; readonly strictOwner?: boolean },

  Line 144:     options?: { readonly publish?: boolean; readonly ownerID?: string; readonly strictOwner?: boolean },

  Line 357:                       (wake) => PubSub.publish(wake, undefined),

  Line 369:       function publishEvent<D extends Definition>(definition: D, event: Payload<D>, commit?: PublishOptions["commit"]) {

  Line 414:           if (typed) yield* PubSub.publish(typed, event)

  Line 415:           yield* PubSub.publish(pubsub.all, event)

  Line 419:       function publish<D extends Definition>(definition: D, data: Data<D>, options?: PublishOptions) {

  Line 427:           return yield* publishEvent(

  Line 443:         options?: { readonly publish?: boolean; readonly ownerID?: string; readonly ...
gateway · 2026-09-17 09:24:15
result
<path>/tmp/opencode/session-recovery-v22-review/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:  ...
gateway · 2026-09-17 09:24:23
result
<path>/tmp/opencode/session-recovery-v22-review/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, seq: number) => Effect.Effect<void>
15:   readonly eventCommit: () => Effect.Effect<void>
16: }
17: 
18: const safe = (value: number | undefined) => Math.max(0, Number.isFinite(value) ? (value ?? ...
gateway · 2026-09-17 09:24:32
result
diff --git a/packages/core/src/session/compaction.ts b/packages/core/src/session/compaction.ts
index f714633295..92a934c44f 100644
--- a/packages/core/src/session/compaction.ts
+++ b/packages/core/src/session/compaction.ts
@@ -1,248 +1,263 @@
 export * as SessionCompaction from "./compaction"
 
 import { LLM, LLMError, LLMEvent, Message, type LLMRequest, type Model } from "@opencode-ai/llm"
 import { DateTime, Effect, Stream } from "effect"
 import type { Config } from "../config"
 import type { EventV2 } from "../event"
 import { SessionEvent } from "./event"
 import { SessionMessage } from "./message"
 import { SessionSchema } from "./schema"
 import { Token } from "../util/token"
 
 const DEFAULT_BUFFER = 20_000
 const DEFAULT_KEEP_TOKENS = 8_000
 const TOOL_OUTPUT_MAX_CHARS = 2_000
 const SUMMARY_OUTPUT_TOKENS = 4_096
 const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. ...
gateway · 2026-09-17 09:24:38
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/sql.ts</path>
<type>file</type>
<content>
1: import { sql } from "drizzle-orm"
2: import { check, sqliteTable, text, integer, index, primaryKey, real, uniqueIndex } from "drizzle-orm/sqlite-core"
3: import * as DatabasePath from "../database/path"
4: import { ProjectTable } from "../project/sql"
5: import type { SessionMessage } from "./message"
6: import type { Prompt } from "./prompt"
7: import type { SessionInput } from "./input"
8: import type { Snapshot } from "../snapshot"
9: import { PermissionV1 } from "../v1/permission"
10: import { ProjectV2 } from "../project"
11: import type { SessionSchema } from "./schema"
12: import type { MessageID, PartID, SessionV1 } from "../v1/session"
13: import { WorkspaceV2 } from "../workspace"
14: import { Timestamps } from "../database/schema.sql"
15: import type { SystemContext } from "../system-context/index"
16: import { AgentV2 } from "../agent"
17: import type { ...
gateway · 2026-09-17 09:24:46
result
diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts
index 2dabfb2d6f..1fbd6285d0 100644
--- a/packages/core/src/session.ts
+++ b/packages/core/src/session.ts
@@ -268,219 +268,221 @@ const layer = Layer.effect(
       list: Effect.fn("V2Session.list")(function* (input = {}) {
         const direction = input.anchor?.direction ?? "next"
         const requestedOrder = input.order ?? "desc"
         const order = direction === "previous" ? (requestedOrder === "asc" ? "desc" : "asc") : requestedOrder
         const sortColumn = SessionTable.time_created
         const conditions: SQL[] = []
         if ("directory" in input) conditions.push(eq(SessionTable.directory, input.directory))
         if (input.workspaceID) conditions.push(eq(SessionTable.workspace_id, input.workspaceID))
         if ("project" in input) conditions.push(eq(SessionTable.project_id, input.project))
         if (input.search) conditions.push(like(SessionTable.title, `%${input.search}%`))
        ...
gateway · 2026-09-17 09:24:57
result
Found 100 matches (more matches available)
/tmp/opencode/session-recovery-v22-review/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 23838:             "$ref": "#/components/schemas/SessionNextStepInterrupted"

  Line 23841:             "$ref": "#/components/schemas/SessionNextStepRecovered"

  Line 24227:             "$ref": "#/components/schemas/SessionNextStepInterrupted"

  Line 24230:             "$ref": "#/components/schemas/SessionNextStepRecovered"

  Line 25709:                   "requestExecution": {

  Line 26226:       "SyncEventSessionNextStepInterrupted": {

  ...
gateway · 2026-09-17 09:25:04
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/schema.json</path>
<type>file</type>
<content>
1: {
2:   "version": "7",
3:   "dialect": "sqlite",
4:   "id": "3bc49670-06d1-4eba-9a45-8c8128bdcf6f",
5:   "prevIds": [
6:     "f14a9b18-8207-487e-a3d3-227e629ba9ad"
7:   ],
8:   "ddl": [
9:     {
10:       "name": "workspace",
11:       "entityType": "tables"
12:     },
13:     {
14:       "name": "data_migration",
15:       "entityType": "tables"
16:     },
17:     {
18:       "name": "account_state",
19:       "entityType": "tables"
20:     },
21:     {
22:       "name": "account",
23:       "entityType": "tables"
24:     },
25:     {
26:       "name": "control_account",
27:       "entityType": "tables"
28:     },
29:     {
30:       "name": "credential",
31:       "entityType": "tables"
32:     },
33:     {
34:       "name": "event_sequence",
35:       "entityType": "tables"
36:     },
37:     {
38:       "name": "event",
39:       "entityType": "tables"
40:     ...
gateway · 2026-09-17 09:25:11
result
abf93718acd0193fa2ca6cfc205cde8714eff23d7df42a4f093a55e9921fd1a3  -
f14a9b18-8207-487e-a3d3-227e629ba9ad ['169a0f0f-d58f-479f-b024-fa1c7b9a09db']
gateway · 2026-09-17 09:25:16
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/database/migration.gen.ts</path>
<type>file</type>
<content>
1: import type { DatabaseMigration } from "./migration"
2: 
3: export const migrations = (
4:   await Promise.all([
5:     import("./migration/20260127222353_familiar_lady_ursula"),
6:     import("./migration/20260211171708_add_project_commands"),
7:     import("./migration/20260213144116_wakeful_the_professor"),
8:     import("./migration/20260225215848_workspace"),
9:     import("./migration/20260227213759_add_session_workspace_id"),
10:     import("./migration/20260228203230_blue_harpoon"),
11:     import("./migration/20260303231226_add_workspace_fields"),
12:     import("./migration/20260309230000_move_org_to_state"),
13:     import("./migration/20260312043431_session_message_cursor"),
14:     import("./migration/20260323234822_events"),
15:     import("./migration/20260410174513_workspace-name"),
16:     ...
gateway · 2026-09-17 09:25:27
result
Found 21 matches
/tmp/opencode/session-recovery-v22-review/packages/core/src/database/schema.gen.ts:
  Line 158:         CREATE TABLE \`session_execution\` (

  Line 168:           CONSTRAINT \`session_execution_pk\` PRIMARY KEY(\`id\`),

  Line 169:           CONSTRAINT \`fk_session_execution_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE,

  Line 170:           CONSTRAINT "session_execution_phase_check" CHECK("phase" IN ('ready', 'safe', 'unknown', 'continue', 'cancelling', 'cancelled'))

  Line 181:           \`wake_pending\` integer DEFAULT false NOT NULL,

  Line 199:         CREATE TABLE \`session_recovery\` (

  Line 205:           \`wake_pending\` integer DEFAULT true NOT NULL,

  Line 206:           \`wake_attempts\` integer DEFAULT 0 NOT NULL,

  Line 209:           CONSTRAINT \`session_recovery_pk\` PRIMARY KEY(\`execution_id\`),

  Line 210:           CONSTRAINT \`fk_session_recovery_session_id_session_id_fk\` ...
gateway · 2026-09-17 09:25:39
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/database/schema.gen.ts</path>
<type>file</type>
<content>
130:           \`session_id\` text NOT NULL,
131:           \`time_created\` integer NOT NULL,
132:           \`time_updated\` integer NOT NULL,
133:           \`data\` text NOT NULL,
134:           CONSTRAINT \`fk_message_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE
135:         );
136:       `)
137:       yield* tx.run(`
138:         CREATE TABLE \`part\` (
139:           \`id\` text PRIMARY KEY,
140:           \`message_id\` text NOT NULL,
141:           \`session_id\` text NOT NULL,
142:           \`time_created\` integer NOT NULL,
143:           \`time_updated\` integer NOT NULL,
144:           \`data\` text NOT NULL,
145:           CONSTRAINT \`fk_part_message_id_message_id_fk\` FOREIGN KEY (\`message_id\`) REFERENCES \`message\`(\`id\`) ON DELETE CASCADE
146:         );
147:       `)
148:       ...
gateway · 2026-09-17 09:25:49
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/runner/index.ts</path>
<type>file</type>
<content>
1: export * as SessionRunner from "./index"
2: 
3: import type { LLMError } from "@opencode-ai/llm"
4: import { Context, Effect } from "effect"
5: import { SessionSchema } from "../schema"
6: import type { SessionMessage } from "../message"
7: import type { ContextSnapshotDecodeError, MessageDecodeError } from "../error"
8: import { SessionRunnerModel } from "./model"
9: import type { SystemContext } from "../../system-context/index"
10: import type { ToolOutputStore } from "../../tool-output-store"
11: 
12: export type RunError =
13:   | LLMError
14:   | SessionRunnerModel.Error
15:   | MessageDecodeError
16:   | ContextSnapshotDecodeError
17:   | SystemContext.InitializationBlocked
18:   | ToolOutputStore.Error
19: 
20: /** Runs one local continuation from already-recorded Session history. ...
gateway · 2026-09-17 09:26:01
result
<path>/tmp/opencode/session-recovery-v22-review/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 { SessionExecutionClaim } from "@opencode-ai/core/session/execution/claim"
11: import { SessionMessage } from "@opencode-ai/core/session/message"
12: import { SessionSchema } from "@opencode-ai/core/session/schema"
13: import { SessionExecutionTable, SessionTable } from "@opencode-ai/core/session/sql"
14: import { testEffect ...
gateway · 2026-09-17 09:26:28
result
Found 42 matches
/tmp/opencode/session-recovery-v22-review/packages/core/test/session-projector.test.ts:
  Line 709:   it.effect("quarantines a malformed recovery row without blocking a valid wake", () =>

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

  Line 767:       const poisonCount = SessionRecovery.BATCH_SIZE + 1

  Line 768:       const sessions = Array.from({ length: poisonCount + 1 }, (_, index) =>

  Line 769:         SessionV2.ID.make(`ses_recovery_poison_${index.toString().padStart(3, "0")}`),

  Line 794:             executionID: `poison-${index.toString().padStart(3, "0")}`,

  Line 799:       yield* db.run(sql`UPDATE session_input SET prompt = '{}' WHERE time_created < ${poisonCount}`)

  Line 814:       ).toHaveLength(SessionRecovery.BATCH_SIZE)

  Line 817:       expect(wakes).toEqual([sessions[poisonCount]])

  Line 824:       ).toHaveLength(poisonCount)

  Line 828:   it.effect("fairly redrives ...
gateway · 2026-09-17 09:26:34
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
650:       expect(yield* db.select().from(SessionInputTable).get()).toMatchObject({
651:         id: continuation("assistant-less-current"),
652:         prompt: { text: "continue" },
653:         delivery: "queue",
654:       })
655:     }),
656:   )
657: 
658:   it.effect("redrives a durable recovery wake after admission", () =>
659:     Effect.gen(function* () {
660:       const { db } = yield* Database.Service
661:       const events = yield* EventV2.Service
662:       yield* db
663:         .insert(ProjectTable)
664:         .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
665:         .run()
666:       yield* db
667:         .insert(SessionTable)
668:         .values({
669:           id: sessionID,
670:           project_id: Project.ID.global,
671:           slug: "test",
672:           directory: ...
gateway · 2026-09-17 09:26:42
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1530:       expect(yield* db.select().from(SessionExecutionTable).all()).toEqual([])
1531:       expect(yield* db.select().from(SessionRecoveryTable).all()).toEqual([])
1532:       expect(yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, inputID)).get()).toMatchObject({
1533:         promoted_seq: null,
1534:         wake_pending: true,
1535:       })
1536:     }),
1537:   )
1538: 
1539:   it.effect("rejects a recovered assistant from another Session", () =>
1540:     Effect.gen(function* () {
1541:       const { db } = yield* Database.Service
1542:       const events = yield* EventV2.Service
1543:       const other = SessionV2.ID.make("ses_recovery_other")
1544:       const assistantID = SessionMessage.ID.make("msg_recovery_other")
1545:       yield* db
1546:         .insert(ProjectTable)
1547:         .values({ id: Project.ID.global, ...
gateway · 2026-09-17 09:26:59
result
Found 5 matches
/tmp/opencode/session-recovery-v22-review/packages/core/test/session-projector.test.ts:
  Line 126:         Schema.decodeUnknownSync(SessionMessage.Message)({ ...message.data, id: message.id, type: message.type }),

  Line 300:         Schema.decodeUnknownSync(SessionMessage.Message)({ ...message.data, id: message.id, type: message.type }),

  Line 3264:         Schema.decodeUnknownSync(SessionMessage.Message)({ ...row.data, id: row.id, type: row.type }),

  Line 3405:         Schema.decodeUnknownSync(SessionMessage.Message)({ ...row.data, id: row.id, type: row.type }),

  Line 3464:         Schema.decodeUnknownSync(SessionMessage.Message)({ ...row.data, id: row.id, type: row.type }),
gateway · 2026-09-17 09:27:17
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-prompt.test.ts</path>
<type>file</type>
<content>
380:       const message = yield* session.prompt({
381:         sessionID,
382:         prompt: Prompt.make({ text: "Recover a lost advisory wake" }),
383:       })
384: 
385:       expect(yield* admitted(message.id)).toMatchObject({ id: message.id })
386:       const { db } = yield* Database.Service
387:       expect(
388:         yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, message.id)).get(),
389:       ).toMatchObject({
390:         wake_pending: true,
391:         promoted_seq: null,
392:       })
393:       expect(wakeCalls).toEqual([sessionID])
394: 
395:       wakeCalls.length = 0
396:       yield* replaySessionProjection()
397:       expect(
398:         yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, message.id)).get(),
399:       ).toMatchObject({
400:         wake_pending: true,
401:         ...
gateway · 2026-09-17 09:27:28
result
Found 7 matches
/tmp/opencode/session-recovery-v22-review/packages/core/test/session-prompt.test.ts:
  Line 153:       yield* session.resume(sessionID)

  Line 266:       yield* session.resume(sessionID)

  Line 408:   it.effect("replays historical prompt admission without execution intent as admit-only", () =>

  Line 424:           prompt: { text: "Historical admit-only prompt" },

  Line 440:   it.effect("turns an admit-only exact retry into durable execution intent", () =>

  Line 775:   it.effect("starts execution when resume is explicitly true", () =>

  Line 784:         prompt: Prompt.make({ text: "Run explicitly" }),
gateway · 2026-09-17 09:27:37
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-prompt.test.ts</path>
<type>file</type>
<content>
120:       type: event.type,
121:       data: event.data,
122:     })),
123:   )
124: })
125: 
126: const recover = () =>
127:   SessionRecovery.recover().pipe(
128:     Effect.provideService(
129:       SessionExecution.Service,
130:       SessionExecution.Service.of({
131:         active: Effect.succeed(new Set()),
132:         resume: () => Effect.void,
133:         interrupt: () => Effect.void,
134:         wake: (id) => Effect.sync(() => wakeCalls.push(id)).pipe(Effect.asVoid),
135:       }),
136:     ),
137:   )
138: 
139: describe("SessionV2.prompt", () => {
140:   it.effect("exposes the execution registry", () =>
141:     Effect.gen(function* () {
142:       activeSessions.add(sessionID)
143:       expect(Array.from(yield* (yield* SessionV2.Service).active)).toEqual([sessionID])
144:     }).pipe(Effect.ensuring(Effect.sync(() => ...
gateway · 2026-09-17 09:27:44
result
Found 27 matches
/tmp/opencode/session-recovery-v22-review/packages/core/test/session-projector.test.ts:
  Line 139:         prompt: { text: "continue" },

  Line 183:         prompt: Prompt.make({ text: "continue" }),

  Line 222:   it.effect("never continues a Step that durably failed before owner loss", () =>

  Line 348:         executionID: "continue-only",

  Line 349:         phase: "continue",

  Line 352:         execution_id: "continue-only",

  Line 354:         phase: "continue",

  Line 361:   it.effect("recovers each abandoned execution once across repeated startup scans", () =>

  Line 492:   for (const phase of ["ready", "unknown", "continue"] as const) {

  Line 537:   for (const phase of ["ready", "unknown", "continue"] as const) {

  Line 635:           phase: "continue",

  Line 648:         phase: "continue",

  Line 652:         prompt: { text: "continue" },

  Line 833:       const recoverySessions = Array.from({ length: failedCount + 1 }, (_, index) =>

  Line ...
gateway · 2026-09-17 09:27:49
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect } from "bun:test"
2: import { DateTime, Deferred, Effect, Exit, Fiber, Schema } from "effect"
3: import { TestClock } from "effect/testing"
4: import { asc, eq, sql } from "drizzle-orm"
5: import { Database } from "@opencode-ai/core/database/database"
6: import { LayerNode } from "@opencode-ai/core/effect/layer-node"
7: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
8: import { EventV2 } from "@opencode-ai/core/event"
9: import { EventTable } from "@opencode-ai/core/event/sql"
10: import { ModelV2 } from "@opencode-ai/core/model"
11: import { Project } from "@opencode-ai/core/project"
12: import { ProjectTable } from "@opencode-ai/core/project/sql"
13: import { ProviderV2 } from "@opencode-ai/core/provider"
14: import { AbsolutePath } from "@opencode-ai/core/schema"
15: import { SessionV2 } from ...
gateway · 2026-09-17 09:28:00
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
2640:       expect(yield* db.select().from(SessionExecutionTable).all()).toEqual([
2641:         expect.objectContaining({ id: `execution-${SessionRecovery.BATCH_SIZE.toString().padStart(3, "0")}` }),
2642:       ])
2643:     }),
2644:   )
2645: 
2646:   it.effect("rolls back promotion and Context Epoch mutation after ownership loss", () =>
2647:     Effect.gen(function* () {
2648:       const { db } = yield* Database.Service
2649:       const events = yield* EventV2.Service
2650:       const inputID = SessionMessage.ID.make("msg_fenced_input")
2651:       yield* db
2652:         .insert(ProjectTable)
2653:         .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
2654:         .run()
2655:       yield* db
2656:         .insert(SessionTable)
2657:         .values({
2658:           id: sessionID,
2659:           ...
gateway · 2026-09-17 09:28:08
result
diff --git a/packages/core/test/database-migration.test.ts b/packages/core/test/database-migration.test.ts
index 464ce2695a..9814b75a11 100644
--- a/packages/core/test/database-migration.test.ts
+++ b/packages/core/test/database-migration.test.ts
@@ -1,58 +1,59 @@
 import { describe, expect, test } from "bun:test"
 import { $ } from "bun"
 import { fileURLToPath } from "url"
 import path from "path"
 import { SqliteClient } from "@effect/sql-sqlite-bun"
 import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
-import { Effect, Layer } from "effect"
+import { Effect, Exit, Layer } from "effect"
 import { eq, inArray, sql } from "drizzle-orm"
 import { DatabaseMigration } from "@opencode-ai/core/database/migration"
 import { migrations } from "@opencode-ai/core/database/migration.gen"
 import workspaceNameMigration from "@opencode-ai/core/database/migration/20260410174513_workspace-name"
 import sessionUsageMigration from ...
gateway · 2026-09-17 09:28:17
result
<path>/tmp/opencode/session-recovery-v22-review/packages/client/src/generated/types.ts</path>
<type>file</type>
<content>
740:               readonly mime: string
741:               readonly name?: string
742:               readonly description?: string
743:               readonly source?: { readonly start: number; readonly end: number; readonly text: string }
744:             }>
745:             readonly agents?: ReadonlyArray<{
746:               readonly name: string
747:               readonly source?: { readonly start: number; readonly end: number; readonly text: string }
748:             }>
749:           }
750:           readonly delivery: "steer" | "queue"
751:         }
752:       }
753:     | {
754:         readonly id: string
755:         readonly metadata?: { readonly [x: string]: JsonValue }
756:         readonly type: "session.next.prompt.admitted"
757:         readonly durable?: { readonly aggregateID: string; readonly seq: number; readonly version: number }
758:        ...
gateway · 2026-09-17 09:28:22
result
<path>/tmp/opencode/session-recovery-v22-review/packages/client/src/generated/types.ts</path>
<type>file</type>
<content>
900:           readonly sessionID: string
901:           readonly assistantMessageID: string
902:           readonly error: { readonly type: "unknown"; readonly message: string }
903:         }
904:       }
905:     | {
906:         readonly id: string
907:         readonly metadata?: { readonly [x: string]: JsonValue }
908:         readonly type: "session.next.step.recovered"
909:         readonly durable?: { readonly aggregateID: string; readonly seq: number; readonly version: number }
910:         readonly location?: { readonly directory: string; readonly workspaceID?: string }
911:         readonly data: {
912:           readonly timestamp: number
913:           readonly sessionID: string
914:           readonly executionID: string
915:           readonly assistantMessageID?: string
916:           readonly phase: "ready" | "unknown" | "continue"
917:         ...
gateway · 2026-09-17 09:28:29
result
<path>/tmp/opencode/session-recovery-v22-review/packages/client/src/generated/types.ts</path>
<type>file</type>
<content>
850:           readonly sessionID: string
851:           readonly assistantMessageID: string
852:           readonly agent: string
853:           readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
854:           readonly snapshot?: string
855:         }
856:       }
857:     | {
858:         readonly id: string
859:         readonly metadata?: { readonly [x: string]: JsonValue }
860:         readonly type: "session.next.step.ended"
861:         readonly durable?: { readonly aggregateID: string; readonly seq: number; readonly version: number }
862:         readonly location?: { readonly directory: string; readonly workspaceID?: string }
863:         readonly data: {
864:           readonly timestamp: number
865:           readonly sessionID: string
866:           readonly assistantMessageID: string
867:           readonly ...
gateway · 2026-09-17 09:28:38
result
Found 57 matches
/tmp/opencode/session-recovery-v22-review/packages/core/schema.json:
  Line 66:       "name": "session_execution",

  Line 78:       "name": "session_recovery",

  Line 941:       "table": "session_execution"

  Line 951:       "table": "session_execution"

  Line 961:       "table": "session_execution"

  Line 971:       "table": "session_execution"

  Line 981:       "table": "session_execution"

  Line 991:       "table": "session_execution"

  Line 1001:       "table": "session_execution"

  Line 1011:       "table": "session_execution"

  Line 1021:       "table": "session_execution"

  Line 1089:       "name": "wake_pending",

  Line 1181:       "table": "session_recovery"

  Line 1191:       "table": "session_recovery"

  Line 1201:       "table": "session_recovery"

  Line 1211:       "table": "session_recovery"

  Line 1221:       "table": "session_recovery"

  Line 1229:       "name": "wake_pending",

  Line 1231:       "table": "session_recovery"

  Line ...
gateway · 2026-09-17 09:30:22
result
<path>/tmp/opencode/session-recovery-v22-review/packages/sdk/js/src/v2/gen/types.gen.ts</path>
<type>file</type>
<content>
3400:   id: string
3401:   syncEvent: {
3402:     type: "session.next.prompted.1"
3403:     id: string
3404:     seq: number
3405:     aggregateID: string
3406:     data: {
3407:       timestamp: number
3408:       sessionID: string
3409:       messageID: string
3410:       prompt: Prompt
3411:       delivery: "steer" | "queue"
3412:     }
3413:   }
3414: }
3415: 
3416: export type SyncEventSessionNextPromptAdmitted = {
3417:   type: "sync"
3418:   id: string
3419:   syncEvent: {
3420:     type: "session.next.prompt.admitted.1"
3421:     id: string
3422:     seq: number
3423:     aggregateID: string
3424:     data: {
3425:       timestamp: number
3426:       sessionID: string
3427:       messageID: string
3428:       prompt: Prompt
3429:       delivery: "steer" | "queue"
3430:       requestExecution?: boolean
3431:     }
3432:   }
3433: }
3434: 
3435: export type ...
gateway · 2026-09-17 09:30:36
result
Found 90 matches
/tmp/opencode/session-recovery-v22-review/packages/sdk/js/src/v2/gen/types.gen.ts:
  Line 3243:   syncEvent: {

  Line 3258:   syncEvent: {

  Line 3273:   syncEvent: {

  Line 3288:   syncEvent: {

  Line 3303:   syncEvent: {

  Line 3318:   syncEvent: {

  Line 3334:   syncEvent: {

  Line 3350:   syncEvent: {

  Line 3367:   syncEvent: {

  Line 3384:   syncEvent: {

  Line 3401:   syncEvent: {

  Line 3419:   syncEvent: {

  Line 3438:   syncEvent: {

  Line 3454:   syncEvent: {

  Line 3471:   syncEvent: {

  Line 3488:   syncEvent: {

  Line 3506:   syncEvent: {

  Line 3523:   syncEvent: {

  Line 3542:   syncEvent: {

  Line 3571:   syncEvent: {

  Line 3588:   syncEvent: {

  Line 3605:   syncEvent: {

  Line 3623:   syncEvent: {

  Line 3640:   syncEvent: {

  Line 3658:   syncEvent: {

  Line 3676:   syncEvent: {

  Line 3695:   syncEvent: {

  Line 3713:   syncEvent: {

  Line 3731:   syncEvent: {

  Line 3756:   syncEvent: {

  Line 3777:   syncEvent: {

 ...
gateway · 2026-09-17 09:30:42
result
<path>/tmp/opencode/session-recovery-v22-review/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 } ...
gateway · 2026-09-17 09:31:43
result
Found 100 matches (more matches available)
/tmp/opencode/session-recovery-v22-review/packages/core/test/session-runner.test.ts:
  Line 26: import { Snapshot } from "@opencode-ai/core/snapshot"

  Line 249:       run: (claim, force, recoveryInputID) =>

  Line 250:         sessionRunner.run({ ...claim, force, recoveryInputID }).pipe(

  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 761:       yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "fence provider" }), resume: false })

  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", ...
gateway · 2026-09-17 09:31:53
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
700:         LLMEvent.stepStart({ index: 0 }),
701:         LLMEvent.stepFinish({ index: 0, reason: "stop" }),
702:         LLMEvent.finish({ reason: "stop" }),
703:       ]
704: 
705:       yield* session.resume(sessionID)
706: 
707:       expect(
708:         yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, message.id)).get(),
709:       ).toMatchObject({
710:         wake_pending: false,
711:       })
712:     }),
713:   )
714: 
715:   it.effect("redrives an ordinary prompt after its advisory wake is lost", () =>
716:     Effect.gen(function* () {
717:       yield* setup
718:       const session = yield* SessionV2.Service
719:       const events = yield* EventV2.Service
720:       const { db } = yield* Database.Service
721:       const { admitted: message } = yield* SessionInput.admit(db, events, {
722:         id: ...
gateway · 2026-09-17 09:32:06
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
1420:   )
1421: 
1422:   it.effect("automatically compacts into a completed summary and retained recent turn", () =>
1423:     Effect.gen(function* () {
1424:       yield* setup
1425:       const session = yield* SessionV2.Service
1426:       response = fragmentFixture("text", "text-first", ["Earlier answer"]).completeEvents
1427:       yield* session.prompt({
1428:         sessionID,
1429:         prompt: Prompt.make({ text: "Earlier question ".repeat(180) }),
1430:         resume: false,
1431:       })
1432:       yield* session.resume(sessionID)
1433: 
1434:       currentModel = compactModel
1435:       requests.length = 0
1436:       responses = [
1437:         fragmentFixture("text", "text-summary", ["## Objective\n- Preserve the task"]).completeEvents,
1438:         fragmentFixture("text", "text-final", ["Continued"]).completeEvents,
1439:       ]
1440:    ...
gateway · 2026-09-17 09:32:12
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
1920:       const session = yield* SessionV2.Service
1921:       yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Echo this" }), resume: false })
1922: 
1923:       requests.length = 0
1924:       authorizations.length = 0
1925:       executions.length = 0
1926:       streamGate = undefined
1927:       streamStarted = undefined
1928:       responses = [
1929:         [
1930:           LLMEvent.stepStart({ index: 0 }),
1931:           LLMEvent.toolCall({ id: "call-echo", name: "echo", input: { text: "hello" } }),
1932:           LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }),
1933:           LLMEvent.finish({ reason: "tool-calls" }),
1934:         ],
1935:         [
1936:           LLMEvent.stepStart({ index: 0 }),
1937:           LLMEvent.textStart({ id: "text-final" }),
1938:           LLMEvent.textDelta({ id: "text-final", text: "Done" ...
gateway · 2026-09-17 09:32:19
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
2190:   it.effect("keeps provider failure unknown until unresolved tools are durable", () =>
2191:     Effect.gen(function* () {
2192:       yield* setup
2193:       const session = yield* SessionV2.Service
2194:       const { db } = yield* Database.Service
2195:       yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Fail with unresolved tool" }), resume: false })
2196:       response = [
2197:         LLMEvent.stepStart({ index: 0 }),
2198:         LLMEvent.toolCall({
2199:           id: "call-provider-failure-pending",
2200:           name: "web_search",
2201:           input: { query: "pending" },
2202:           providerExecuted: true,
2203:         }),
2204:         LLMEvent.providerError({ message: "Provider unavailable" }),
2205:       ]
2206:       yield* db.run(sql`
2207:         CREATE TRIGGER fail_provider_tool_reconciliation
2208:      ...
gateway · 2026-09-17 09:32:34
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
200: const skillGuidance = Layer.mock(SkillGuidance.Service, {
201:   load: (agent) =>
202:     Effect.succeed(
203:       skillBaselines.has(agent.id)
204:         ? SystemContext.make({
205:             key: SystemContext.Key.make("test/skill-guidance"),
206:             codec: Schema.toCodecJson(Schema.String),
207:             load: Effect.succeed(skillBaselines.get(agent.id)!),
208:             baseline: String,
209:             update: (_previous, current) => current,
210:             removed: () => "Skill guidance removed",
211:           })
212:         : SystemContext.empty,
213:     ),
214: })
215: const referenceGuidance = Layer.mock(ReferenceGuidance.Service, { load: () => Effect.succeed(SystemContext.empty) })
216: const config = Layer.succeed(
217:   Config.Service,
218:   Config.Service.of({
219:     entries: () =>
220:       Effect.succeed([
221: ...
gateway · 2026-09-17 09:32:46
result
Found 5 matches
/tmp/opencode/session-recovery-v22-review/packages/core/test/session-runner.test.ts:
  Line 294: const otherSessionID = SessionV2.ID.make("ses_runner_other")

  Line 3265:       yield* insertSession(otherSessionID)

  Line 3268:       yield* session.prompt({ sessionID: otherSessionID, prompt: Prompt.make({ text: "Run second" }), resume: false })

  Line 3278:       const second = yield* session.resume(otherSessionID).pipe(Effect.forkChild)

  Line 3284:         otherSessionID,
gateway · 2026-09-17 09:32:53
result
<path>/tmp/opencode/session-recovery-v22-review/packages/sdk/openapi.json</path>
<type>file</type>
<content>
35080:                   "output": {
35081:                     "type": "number"
35082:                   },
35083:                   "reasoning": {
35084:                     "type": "number"
35085:                   },
35086:                   "cache": {
35087:                     "type": "object",
35088:                     "properties": {
35089:                       "read": {
35090:                         "type": "number"
35091:                       },
35092:                       "write": {
35093:                         "type": "number"
35094:                       }
35095:                     },
35096:                     "required": ["read", "write"],
35097:                     "additionalProperties": false
35098:                   }
35099:                 },
35100:                 "required": ["input", "output", "reasoning", "cache"],
35101:                 ...
gateway · 2026-09-17 09:33:25
result
No files found
gateway · 2026-09-17 09:33:37
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
407:   it.effect("never recovers a live execution lease", () =>
408:     Effect.gen(function* () {
409:       const { db } = yield* Database.Service
410:       yield* db
411:         .insert(ProjectTable)
412:         .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
413:         .run()
414:       yield* db
415:         .insert(SessionTable)
416:         .values({
417:           id: sessionID,
418:           project_id: Project.ID.global,
419:           slug: "test",
420:           directory: "/project",
421:           title: "test",
422:           version: "test",
423:         })
424:         .run()
425:       yield* db
426:         .insert(SessionExecutionTable)
427:         .values({
428:           id: "execution",
429:           session_id: sessionID,
430:           owner_id: "live",
431:           phase: ...
gateway · 2026-09-17 09:33:46
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1009:       expect(yield* db.select({ attempts: SessionTable.wake_attempts }).from(SessionTable).get()).toEqual({ attempts: 7 })
1010:     }),
1011:   )
1012: 
1013:   it.effect("rejects invalid persisted wake attempt counters", () =>
1014:     Effect.gen(function* () {
1015:       const { db } = yield* Database.Service
1016:       const events = yield* EventV2.Service
1017:       yield* db
1018:         .insert(ProjectTable)
1019:         .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
1020:         .run()
1021:       yield* db
1022:         .insert(SessionTable)
1023:         .values({
1024:           id: sessionID,
1025:           project_id: Project.ID.global,
1026:           slug: "test",
1027:           directory: "/project",
1028:           title: "test",
1029:           version: "test",
1030:         })
1031: ...
gateway · 2026-09-17 09:34:11
result
<path>/tmp/opencode/session-recovery-final-v22.patch</path>
<type>file</type>
<content>
1: diff --git a/packages/client/src/generated/types.ts b/packages/client/src/generated/types.ts
2: index 3b3188c874..5130fa9496 100644
3: --- a/packages/client/src/generated/types.ts
4: +++ b/packages/client/src/generated/types.ts
5: @@ -775,8 +775,17 @@ export type SessionsHistoryOutput = {
6:              }>
7:            }
8:            readonly delivery: "steer" | "queue"
9: +          readonly requestExecution?: boolean
10:          }
11:        }
12: +    | {
13: +        readonly id: string
14: +        readonly metadata?: { readonly [x: string]: JsonValue }
15: +        readonly type: "session.next.prompt.execution_requested"
16: +        readonly durable?: { readonly aggregateID: string; readonly seq: number; readonly version: number }
17: +        readonly location?: { readonly directory: string; readonly workspaceID?: string }
18: +        readonly data: { readonly timestamp: number; ...
gateway · 2026-09-17 09:34:33
result
34
fd55a6aa25cf551987e59416058f92589150414fd49b4e2201099eab9675fca7
gateway · 2026-09-17 09:34:41
result
Found 3 matches
/tmp/opencode/session-recovery-final-v20.patch:
  Line 1036: diff --git a/packages/core/src/database/migration/20260914170650_session-recovery.ts b/packages/core/src/database/migration/20260914170650_session-recovery.ts


/tmp/opencode/session-recovery-final-v22.patch:
  Line 1033: diff --git a/packages/core/src/database/migration/20260914170650_session-recovery.ts b/packages/core/src/database/migration/20260914170650_session-recovery.ts


/tmp/opencode/session-recovery-final-v21.patch:
  Line 1033: diff --git a/packages/core/src/database/migration/20260914170650_session-recovery.ts b/packages/core/src/database/migration/20260914170650_session-recovery.ts
gateway · 2026-09-17 09:34:54
result
<path>/tmp/opencode/session-recovery-final-v22.patch</path>
<type>file</type>
<content>
1028:      import("./migration/20260622170816_reset_v2_session_state"),
1029:      import("./migration/20260622202450_simplify_session_input"),
1030: +    import("./migration/20260914170650_session-recovery"),
1031:    ])
1032:  ).map((module) => module.default) satisfies DatabaseMigration.Migration[]
1033: diff --git a/packages/core/src/database/migration/20260914170650_session-recovery.ts b/packages/core/src/database/migration/20260914170650_session-recovery.ts
1034: new file mode 100644
1035: index 0000000000..20eb1a4373
1036: --- /dev/null
1037: +++ b/packages/core/src/database/migration/20260914170650_session-recovery.ts
1038: @@ -0,0 +1,62 @@
1039: +import { Effect } from "effect"
1040: +import type { DatabaseMigration } from "../migration"
1041: +
1042: +export default {
1043: +  id: "20260914170650_session-recovery",
1044: +  up(tx) {
1045: +    return Effect.gen(function* () {
1046: +      ...
gateway · 2026-09-17 09:35:03
result
diff --git a/packages/core/src/session/recovery-id.ts b/packages/core/src/session/recovery-id.ts
new file mode 100644
index 0000000000..b79614fd1e
--- /dev/null
+++ b/packages/core/src/session/recovery-id.ts
@@ -0,0 +1,6 @@
+import { SessionMessage } from "./message"
+
+export const message = (executionID: string, assistantMessageID?: SessionMessage.ID) =>
+  assistantMessageID ?? SessionMessage.ID.make(`msg_recovery_${executionID}`)
+
+export const continuation = (executionID: string) => SessionMessage.ID.make(`msg_recovery_continue_${executionID}`)
gateway · 2026-09-17 09:35:24
result
patch_sections 34 status_paths 34 same_paths True
gateway · 2026-09-17 09:36:04
result
diff --git a/packages/core/test/session-prompt.test.ts b/packages/core/test/session-prompt.test.ts
index c6bc9430b3..a0d6f26467 100644
--- a/packages/core/test/session-prompt.test.ts
+++ b/packages/core/test/session-prompt.test.ts
@@ -1,38 +1,39 @@
 import { describe, expect } from "bun:test"
 import { DateTime, Effect, Fiber, Layer, Stream } from "effect"
-import { eq } from "drizzle-orm"
+import { asc, eq } from "drizzle-orm"
 import { Database } from "@opencode-ai/core/database/database"
 import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
 import { LayerNode } from "@opencode-ai/core/effect/layer-node"
 import { EventV2 } from "@opencode-ai/core/event"
 import { EventTable } from "@opencode-ai/core/event/sql"
 import { SessionEvent } from "@opencode-ai/core/session/event"
 import { Project } from "@opencode-ai/core/project"
 import { ProjectTable } from "@opencode-ai/core/project/sql"
 import { AbsolutePath } from "@opencode-ai/core/schema"
 import { ...
gateway · 2026-09-17 09:37:25
result
packages/client/src/generated/types.ts: @@ -775,8 +775,17 @@ export type SessionsHistoryOutput = {
packages/client/src/generated/types.ts: @@ -880,6 +889,33 @@ export type SessionsHistoryOutput = {
packages/client/src/generated/types.ts: @@ -1233,8 +1269,17 @@ export type SessionsEventsOutput =
packages/client/src/generated/types.ts: @@ -1338,6 +1383,33 @@ export type SessionsEventsOutput =
packages/core/schema.json: @@ -1,8 +1,10 @@
packages/core/schema.json: @@ -60,6 +62,10 @@
packages/core/schema.json: @@ -68,6 +74,10 @@
packages/core/schema.json: @@ -920,6 +930,96 @@
packages/core/schema.json: @@ -980,6 +1080,16 @@
packages/core/schema.json: @@ -1060,6 +1170,96 @@
packages/core/schema.json: @@ -1350,6 +1550,16 @@
packages/core/schema.json: @@ -1481,9 +1691,13 @@
packages/core/schema.json: @@ -1492,9 +1706,13 @@
packages/core/schema.json: @@ -1503,9 +1721,13 @@
packages/core/schema.json: @@ -1514,9 +1736,13 @@
packages/core/schema.json: @@ -1525,9 +1751,13 ...
gateway · 2026-09-17 09:40:50
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/src/session/execution.ts</path>
<type>file</type>
<content>
1: export * as SessionExecution from "./execution"
2: 
3: import { Context, Effect, Layer } from "effect"
4: import { LayerNode } from "../effect/layer-node"
5: import { Node } from "../effect/app-node"
6: import { SessionRunner } from "./runner/index"
7: import { SessionSchema } from "./schema"
8: 
9: export interface Interface {
10:   /** Snapshots active execution owned by this process. */
11:   readonly active: Effect.Effect<ReadonlySet<SessionSchema.ID>>
12:   /** Starts execution while idle or joins the active execution. */
13:   readonly resume: (sessionID: SessionSchema.ID) => Effect.Effect<void, SessionRunner.RunError>
14:   /** Registers newly recorded work. Repeated wakeups may coalesce. */
15:   readonly wake: (sessionID: SessionSchema.ID) => Effect.Effect<void>
16:   /** Interrupt active work owned by this process. Idle interruption is a no-op. ...
gateway · 2026-09-17 09:42:01
result
Found 100 matches (more matches available)
/tmp/opencode/session-recovery-v22-review/packages/server/src/routes.ts:
  Line 13: import { SessionRecovery } from "@opencode-ai/core/session/recovery"

  Line 33:   SessionRecovery.node,


/tmp/opencode/session-recovery-v22-review/packages/core/src/session/input.ts:
  Line 13: import { SessionInputTable, SessionMessageTable, SessionRecoveryTable, SessionTable } from "./sql"

  Line 173:       SessionRecoveryTable,

  Line 174:       eq(SessionRecoveryTable.continuation_message_id, SessionInputTable.id),

  Line 180:         isNull(SessionRecoveryTable.execution_id),

  Line 342:     .update(SessionRecoveryTable)

  Line 346:         eq(SessionRecoveryTable.session_id, input.sessionID),

  Line 347:         eq(SessionRecoveryTable.continuation_message_id, input.id),

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

  Line 360:     .from(SessionRecoveryTable)

  Line 361:     ...
gateway · 2026-09-17 09:42:14
result
<path>/tmp/opencode/session-recovery-v22-review/packages/core/package.json</path>
<type>file</type>
<content>
1: {
2:   "$schema": "https://json.schemastore.org/package.json",
3:   "version": "1.18.30",
4:   "name": "@opencode-ai/core",
5:   "type": "module",
6:   "license": "MIT",
7:   "private": true,
8:   "scripts": {
9:     "db": "bun drizzle-kit",
10:     "migration": "bun run script/migration.ts",
11:     "fix-node-pty": "bun run script/fix-node-pty.ts",
12:     "test": "bun test --only-failures",
13:     "typecheck": "tsgo --noEmit"
14:   },
15:   "bin": {
16:     "opencode": "./bin/opencode"
17:   },
18:   "exports": {
19:     "./effect/layer-node": "./src/effect/layer-node.ts",
20:     "./effect/app-node": "./src/effect/app-node.ts",
21:     "./session/runner": "./src/session/runner/index.ts",
22:     "./system-context": "./src/system-context/index.ts",
23:     "./*": "./src/*.ts"
24:   },
25:   "imports": {
26:     "#sqlite": {
27:       "bun": ...
gateway · 2026-09-17 09:45:21

Findings

  • MUST-FIX — A wake or explicit resume can be lost while interruption enters cancelling. SessionExecutionLocal.interrupt persists cancelling before calling the coordinator (packages/core/src/session/execution/local.ts:218-248). During that database write, the coordinator still treats the entry as running: wake sets pendingWake and run joins the doomed owner (packages/core/src/session/run-coordinator.ts:73-98). When interruption finally begins, it unconditionally resets pendingWake (packages/core/src/session/run-coordinator.ts:100-110). A prompt arriving in this window will not start its successor after cleanup; a concurrent explicit resume can inherit the interrupted exit. Existing tests register work only after coordinator interruption has begun, so they do not cover this gap.

  • MUST-FIX — Corrupt assistant projections can indefinitely starve later recovery claims. Recovery scans only the oldest 100 expired claims (packages/core/src/session/recovery.ts:71-79). After takeover, decoding the latest assistant dies on malformed stored data (packages/core/src/session/recovery.ts:169-177), but the outer handler only logs the failure (packages/core/src/session/recovery.ts:316-323); it does not quarantine the row. The row therefore expires and returns to the head of the scan. Because the lease lasts 30 seconds while scans repeat every 10 seconds (packages/core/src/session/sql.ts:253, packages/core/src/session/recovery.ts:547-551), three or more full corrupt cohorts can recycle forever and prevent later valid claims from being selected. Corrupt projection rows must be quarantined under the exact live execution/owner fence.

  • CONCERN — Completed cancellation does not delete the claim in the final event transaction. For a completed failed Step with unresolved tools, the final Tool.Failed commit atomically changes the claim to cancelled and clears assistant_message_id ...

gpt-5.6-sol · 2026-09-17 09:45:21 · ~1.0K tokens

Distillations (11)

Gen 0 2026-09-17 11:41:19 · 1022 tokens

Date: Sep 17, 2026 * 🔴 (09:27) User specified that recovery must never continue a `Step` that durably failed before owner loss. * 🔴 (09:28) User specified that recovery must never recover a live execution lease. * 🔴 (09:28) User specified that the `Context Epoch` must never advance after ownership changes. * 🟡 (09:27) `packages/core/test/session-prompt.test.ts` lines 126-137 define `recover()…

Gen 0 2026-09-17 11:02:41 · 909 tokens

Date: Sep 17, 2026 * 🔴 (09:26) User specified that recovery superseded before scanner validation must never be promoted. * 🔴 (09:26) User specified that an exact recovery conflict must never be quarantined after ownership transfer. * 🔴 (09:26) User specified that a quarantined recovery continuation must never be promoted as an ordinary steer. * 🔴 (09:26) User specified that an expired cancell…

Gen 0 2026-09-17 10:55:35 · 938 tokens

Date: Sep 17, 2026 * 🔴 (09:26) User specified that local session execution must never start a second owner while the lease is live. * 🔴 (09:26) User specified that local session execution must never overwrite an expired lease. * 🔴 (09:26) User specified that local session execution must never re-enter its own uncertain live lease. * 🔴 (09:26) User specified that a heartbeat must never revive …

Gen 0 2026-09-17 10:39:26 · 258 tokens

Date: Sep 17, 2026 * 🟡 (09:26) `packages/core/src/session/runner/index.ts` exports `SessionRunner` from `"./index"` and defines `RunError` as the union of `LLMError`, `SessionRunnerModel.Error`, `MessageDecodeError`, `ContextSnapshotDecodeError`, `SystemContext.InitializationBlocked`, and `ToolOutputStore.Error`. * 🟡 (09:26) `packages/core/src/session/runner/index.ts` defines `Interface.run(inp…

Gen 0 2026-09-17 10:39:18 · 361 tokens

Date: Sep 17, 2026 * 🟡 (09:25) `packages/core/src/session.ts:373` sets `requestExecution: input.resume !== false`, so admitted prompts request execution by default unless `resume` is explicitly `false`. * 🟡 (09:25) `packages/core/src/database/migration.gen.ts` registers the latest migration as `./migration/20260914170650_session-recovery`. * 🟡 (09:25) `packages/core/schema.json` is SQLite sche…

Gen 0 2026-09-17 10:12:28 · 920 tokens

Date: Sep 17, 2026 * 🟡 (09:24) `packages/core/src/session/compaction.ts` changed automatic compaction to publish durable events through `dependencies.events.publish(...)` before and after summary generation, with both payloads carrying `sessionID: input.sessionID` and `reason: "auto"`. * 🟡 (09:24) Automatic compaction now invokes `input.beforeStream()` after publishing the initial compaction ev…

Gen 0 2026-09-17 09:57:55 · 732 tokens

Date: Sep 17, 2026 * 🔴 (09:23) User directed that local ownership be replaced with durable multi-node ownership when clustered, recorded as an unchecked TODO in `packages/core/src/session/runner/llm.ts`. * 🟡 (09:23) `packages/core/src/session/runner/llm.ts` was expanded from 439 to 624 lines and added `import { and, eq } from "drizzle-orm"` plus durable execution ownership logic involving `Sess…

Gen 0 2026-09-17 09:49:12 · 995 tokens

Date: Sep 17, 2026 * 🟡 (09:23) Search found 17 cancellation/failure-related matches across 4 tests: `packages/core/test/session-runner.test.ts` lines 838 (`SessionEvent.Tool.Failed.type`), 3924 (`session.next.step.interrupted.1` trigger), 3940 (`phase: "cancelling"`), 3960 (`SessionEvent.Step.Failed.type`), 3994, 4063, 4097, and 4128 (`session.next.step.interrupted.1`), and 4102 (`phase: "cancel…

Gen 0 2026-09-17 09:42:06 · 664 tokens

Date: Sep 17, 2026 * 🟡 (09:22) `packages/core/src/session/input.ts:501-529` publishes each input row as `SessionEvent.Prompted` with `sessionID`, `timestamp`, `messageID`, decoded `prompt`, and `delivery`; optional `commit(row)` is passed as the event commit callback. A `LifecycleConflict` is tolerated only when `find(db, id)` shows `stored?.promotedSeq !== undefined`; otherwise the defect is re…

Gen 0 2026-09-17 09:32:20 · 573 tokens

Date: Sep 17, 2026 * 🟡 (09:22) Review inspection continued through `packages/core/src/session/recovery.ts`; the implementation imports `SessionExecution` from `./execution` and uses `SessionExecutionTable`, `SessionRecoveryTable`, and `SessionInputTable`. * 🟡 (09:22) `packages/core/src/session/recovery.ts:76` orders execution-recovery candidates by ascending `SessionExecutionTable.time_created`…

Gen 0 2026-09-17 09:23:25 · 2862 tokens

Date: Sep 17, 2026 * 🔴 [requested-review] (09:20) User requested a strict READ-ONLY security/pentest review of frozen v22 in `/tmp/opencode/session-recovery-v22-review`; prohibited editing, running tests/builds/services, mutating VCS/index/refs, and using recall. * 🔴 (09:20) User specified immutable review inputs: base commit `ab9408c81cc9916177641ae8e9f08624fc27b5b9`; patch `/tmp/opencode/sess…