Dashboard › opencode › Session 0NCgbNIryXjH
0NCgbNIryXjHWz8vVPerform a read-only adversarial correctness review of frozen v10. Base commit ab9408c81cc9916177641ae8e9f08624fc27b5b9. Patch /tmp/opencode/session-recovery-final-v10.patch expected SHA-256 07b95915924ccf5dcfdf7ea5eb45d6cf972a6058664518935154dff9934222b2. SEA /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server expected SHA-256 1117176c4cfbf77527216b4161b54f8d3918ca7f7415747117c24f94d8e0b090. Do not mutate source, refs, index, services, or run builds/tests. Verify hashes, clean apply, and inspect all 33 changed files. Explicitly assess the v9 security blocker: if Step.Recovered projection fails while a same-Session/session_recovery row exists with the same execution ID but mismatched assistant/continuation/phase identity, the scanner must quarantine the exact execution+owner unless ownership disappeared/transferred; it must never treat partial identity as successful projection. Inspect the new regression quarantines an execution claim blocked by a conflicting recovery identity. Also reassess all previous invariants: live lease/takeover/renewal, cancellation cancelling quarantine and finalizers, provider/tool uncertainty, no Step.Ended after stream failure, assistant start state ordering, atomic cross-Session collision rollback, pre-promotion boundary, continuation reservation, queue/steer exclusion, replay wake intent, bounded fairness, migration lineage. The final synchronous claim-check-to-physical-external-call race is an explicitly accepted residual; report only a gap larger than that. Exact-source evidence: recovery suite 231/0/683; full Core clean rerun 1168/0/3272 after one isolated coordinator timeout followed by 10/10 stress passes; five package typechecks; Schema manifest 2/0; migration/diff checks; exact SEA health/schema/recovery/idempotence smoke. Return findings by severity with exact candidate file:line evidence, classify PASS/CONCERN/MUST-FIX, state residuals, end exactly MERGE or DO-NOT-MERGE. Empty output invalid. ...
I’ll first verify the frozen inputs and repository guidance. I will stop if any hash, base, scope, or clean-apply check differs. The required repo-setup skill is not available in this session, so I will follow the checked-in guidance directly.
<path>/home/byk/Code/opencode</path> <type>directory</type> <entries> .dockerignore .editorconfig .git/ .gitattributes .github/ .gitignore .gitleaksignore .husky/ .lore.md .node-cache/ .opencode/ .oxlintrc.json .prettierignore .turbo/ .vscode/ .zed/ AGENTS.md artifacts/ bun.lock bunfig.toml CONTEXT.md CONTRIBUTING.md flake.lock flake.nix github/ infra/ install LICENSE nix/ node_modules/ package.json packages/ patches/ perf.data perf.data.old 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 tmp/ tsconfig.json turbo.json (71 entries) </entries>
/home/byk/Code/opencode/packages/core/src/tool/AGENTS.md
/home/byk/Code/opencode/packages/opencode/AGENTS.md /home/byk/Code/opencode/packages/opencode/test/AGENTS.md /home/byk/Code/opencode/packages/opencode/test/server/AGENTS.md /home/byk/Code/opencode/packages/opencode/src/session/llm/AGENTS.md /home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/AGENTS.md
07b95915924ccf5dcfdf7ea5eb45d6cf972a6058664518935154dff9934222b2 /tmp/opencode/session-recovery-final-v10.patch 1117176c4cfbf77527216b4161b54f8d3918ca7f7415747117c24f94d8e0b090 /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 ...
<path>/home/byk/Code/opencode/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`. ...
<path>/home/byk/Code/opencode/packages/opencode/AGENTS.md</path>
<type>file</type>
<content>
1: # opencode database guide
2:
3: ## Database
4:
5: - **Schema**: Drizzle schema lives in `packages/core/src/**/*.sql.ts`.
6: - **Migrations**: database migrations live in `packages/core` and are applied by core.
7:
8: ## Development server
9:
10: - Running `bun dev` from `packages/opencode` starts the live interactive TUI. Do not run it as a blocking foreground command when you need to inspect the result.
11: - Start it in `tmux` instead: `tmux new-session -d -s opencode-dev 'bun dev'`.
12: - Capture the current TUI output with: `tmux capture-pane -pt opencode-dev`.
13: - Stop the session explicitly when done: `tmux kill-session -t opencode-dev`.
14:
15: # Module shape
16:
17: Do not use `export namespace Foo { ... }` for module organization. It is not
18: standard ESM, it prevents tree-shaking, and it breaks Node's native TypeScript
19: runner. ...
<path>/home/byk/Code/opencode/CONTRIBUTING.md</path> <type>file</type> <content> 1: # Contributing to OpenCode 2: 3: We want to make it easy for you to contribute to OpenCode. Here are the most common type of changes that get merged: 4: 5: - Bug fixes 6: - Additional LSPs / Formatters 7: - Improvements to LLM performance 8: - Support for new providers 9: - Fixes for environment-specific quirks 10: - Missing standard behavior 11: - Documentation improvements 12: 13: However, any UI or core product feature must go through a design review with the core team before implementation. 14: 15: If you are unsure if a PR would be accepted, feel free to ask a maintainer or look for issues with any of the following labels: 16: 17: - [`help wanted`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3Ahelp-wanted) 18: - [`good first issue`](https://github.com/anomalyco/opencode/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) 19: - ...
<path>/home/byk/Code/opencode/package.json</path>
<type>file</type>
<content>
1: {
2: "$schema": "https://json.schemastore.org/package.json",
3: "name": "opencode",
4: "description": "AI-powered development tool",
5: "private": true,
6: "type": "module",
7: "packageManager": "bun@1.3.14",
8: "scripts": {
9: "dev": "bun run --cwd packages/opencode src/index.ts",
10: "dev:desktop": "bun --cwd packages/desktop dev",
11: "dev:web": "bun --cwd packages/app dev",
12: "dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
13: "dev:stats": "bun sst shell --stage=production -- bun run --cwd packages/stats/app dev",
14: "dev:storybook": "bun --cwd packages/storybook storybook",
15: "lint": "oxlint",
16: "typecheck": "bun turbo typecheck",
17: "upgrade-opentui": "bun run script/upgrade-opentui.ts",
18: "postinstall": "bun run --cwd packages/core fix-node-pty",
19: "prepare": "husky",
20: "random": "echo ...
base-pipeline-numbered.txt base-test-numbered.txt llm-v5-current.diff local-v5-current.diff lore-ui-search-response oxfmt-opencode.json projection-parent-pipeline.ts publisher-v5-current.diff reference-v2-format restore-projector-v5-labeled.patch restore-projector-v5.patch restore-runner-v5-labeled.patch restore-runner-v5.patch session-recovery-final-v10 session-recovery-final-v10.patch session-recovery-final-v4 session-recovery-final-v4-diag session-recovery-final-v4.index session-recovery-final-v4.patch session-recovery-final-v5 session-recovery-final-v5.index session-recovery-final-v5.patch session-recovery-final-v6 session-recovery-final-v6.patch session-recovery-final-v7 session-recovery-final-v7.patch session-recovery-final-v8 session-recovery-final-v8.patch session-recovery-final-v9 session-recovery-final-v9.patch session-recovery-recheck.f3rLnS session-recovery-review.1VQHG0 session-recovery-review-c2c8afb0 session-recovery-review-v5-49183f9e session-recovery-review-v5-49183f9e...
error: invalid path '/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/schema.json'
33
The patch and SEA hashes match exactly. The base commit resolves exactly, the patch applies cleanly to a fresh archive of that base, and git apply --numstat reports exactly 33 files. I am reviewing the reconstructed candidate under /tmp/opencode/session-recovery-v10-audit.wukNah, not the mutable working tree.
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/AGENTS.md</path>
<type>file</type>
<content>
1: # HttpApi Route Patterns
2:
3: Use `HttpApiBuilder.group(...)` for normal HTTP endpoints, including streaming HTTP responses such as server-sent events. Handlers should yield stable services once while building the handler layer, then close over those services in endpoint implementations.
4:
5: ```ts
6: export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session", (handlers) =>
7: Effect.gen(function* () {
8: const session = yield* Session.Service
9:
10: return handlers.handle("list", () => session.list())
11: }),
12: )
13: ```
14:
15: For SSE endpoints, stay in `HttpApiBuilder.group(...)` and return `HttpServerResponse.stream(...)` from the handler. Annotate the endpoint success schema with `HttpApiSchema.asText({ contentType: "text/event-stream" })` so OpenAPI documents the stream content type. ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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": ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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 ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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 ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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 ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/recovery.ts</path>
<type>file</type>
<content>
1: export * as SessionRecovery from "./recovery"
2:
3: import { and, asc, desc, eq, exists, gt, isNull, lte, or, sql } from "drizzle-orm"
4: import { Clock, DateTime, Effect, Layer, Option, Schedule, Schema } from "effect"
5: import { Database } from "../database/database"
6: import { makeGlobalNode } from "../effect/app-node"
7: import { EventV2 } from "../event"
8: import { SessionEvent } from "./event"
9: import { SessionExecution } from "./execution"
10: import { SessionProjector } from "./projector"
11: import { SessionExecutionClaim } from "./execution/claim"
12: import { SessionInput } from "./input"
13: import { continuation } from "./recovery-id"
14: import { SessionSchema } from "./schema"
15: import {
16: MAX_WAKE_ATTEMPTS,
17: SESSION_EXECUTION_LEASE_MS,
18: SessionExecutionTable,
19: SessionInputTable,
20: SessionRecoveryTable,
21: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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: ...
No files found
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/event.ts
Found 44 matches
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/event.ts:
Line 122: /** Local operational projection committed atomically with a new durable event. Not replayed or serialized. */
Line 123: readonly commit?: (seq: number) => Effect.Effect<void>
Line 127: readonly publish: <D extends Definition>(
Line 137: readonly project: <D extends Definition>(definition: D, projector: Subscriber<D>) => Effect.Effect<void>
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 179: const projectors = new Map<string, Subscriber[]>()
Line 180: // TODO: Bind durable projectors to exact type+version before supporting incompatible historical payloads.
Line 205: function commitDurableEvent(
Line 214: commit?: (seq: number) => ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/event.ts</path>
<type>file</type>
<content>
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: D,
129: data: Data<D>,
130: options?: PublishOptions,
131: ) => Effect.Effect<Payload<D>>
132: readonly subscribe: <D extends ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/input.ts</path>
<type>file</type>
<content>
1: export * as SessionInput from "./input"
2:
3: import { and, asc, desc, eq, isNull, lte } from "drizzle-orm"
4: import { DateTime, Effect, Option, Schema } from "effect"
5: import { Admitted, Delivery } from "@opencode-ai/schema/session-input"
6: import type { Database } from "../database/database"
7: import { EventV2 } from "../event"
8: import { SessionEvent } from "./event"
9: import { SessionMessage } from "./message"
10: import { Prompt } from "./prompt"
11: import { continuation, message } from "./recovery-id"
12: import { SessionSchema } from "./schema"
13: import { SessionInputTable, SessionMessageTable, SessionRecoveryTable } from "./sql"
14:
15: type DatabaseService = Database.Interface["db"]
16:
17: export { Admitted, Delivery }
18:
19: const decodePrompt = Schema.decodeUnknownSync(Prompt)
20: const encodePrompt = Schema.encodeSync(Prompt)
21: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/execution/claim.ts</path>
<type>file</type>
<content>
1: export * as SessionExecutionClaim from "./claim"
2:
3: import { and, eq, gt } from "drizzle-orm"
4: import { Clock, Effect } from "effect"
5: import type { Database } from "../../database/database"
6: import type { SessionSchema } from "../schema"
7: import { SESSION_EXECUTION_LEASE_MS, SessionExecutionTable } from "../sql"
8:
9: type DatabaseService = Database.Interface["db"]
10:
11: export type Claim = {
12: readonly sessionID: SessionSchema.ID
13: readonly executionID: string
14: readonly ownerID: string
15: }
16:
17: export type Values = Partial<Pick<typeof SessionExecutionTable.$inferInsert, "assistant_message_id" | "phase">>
18:
19: export class Lost extends Error {
20: constructor(readonly claim: Claim) {
21: super(`Session execution claim lost: ${claim.executionID}`)
22: }
23: }
24:
25: const where = (claim: Claim, now: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/execution/local.ts</path>
<type>file</type>
<content>
1: import { and, asc, eq, isNull } from "drizzle-orm"
2: import { Cause, Clock, Duration, Effect, Exit, Layer, Schedule } from "effect"
3: import { Database } from "../../database/database"
4: import { LocationServiceMap } from "../../location-service-map"
5: import { makeGlobalNode } from "../../effect/app-node"
6: import { SessionRunCoordinator } from "../run-coordinator"
7: import { SessionRunner } from "../runner"
8: import { SessionMessage } from "../message"
9: import { SessionSchema } from "../schema"
10: import { SessionStore } from "../store"
11: import { SessionExecution } from "../execution"
12: import { SessionExecutionClaim } from "./claim"
13: import { SESSION_EXECUTION_LEASE_MS, SessionExecutionTable, SessionRecoveryTable } from "../sql"
14:
15: /** Current-process routing for implicit-local Locations. ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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. ...
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/runner/to-llm-message.ts /tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/runner/model.ts /tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/runner/publish-llm-event.ts /tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/runner/index.ts /tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/runner/llm.ts /tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/runner/max-steps.ts
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/runner/llm.ts</path>
<type>file</type>
<content>
1: import {
2: LLM,
3: LLMClient,
4: LLMError,
5: LLMEvent,
6: Message,
7: SystemPart,
8: isContextOverflowFailure,
9: type ProviderErrorEvent,
10: } from "@opencode-ai/llm"
11: import { Cause, DateTime, Effect, Exit, Fiber, Layer, Option, Semaphore, Stream } from "effect"
12: import { and, eq } from "drizzle-orm"
13: import { AgentV2 } from "../../agent"
14: import { Config } from "../../config"
15: import { Database } from "../../database/database"
16: import { EventV2 } from "../../event"
17: import { Location } from "../../location"
18: import { ModelV2 } from "../../model"
19: import { PermissionV2 } from "../../permission"
20: import { ProviderV2 } from "../../provider"
21: import { QuestionV2 } from "../../question"
22: import { SystemContext } from "../../system-context/index"
23: import { SystemContextRegistry } from ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/runner/publish-llm-event.ts</path>
<type>file</type>
<content>
1: import { ToolOutput, type LLMEvent, type ProviderMetadata, type ToolResultValue, type Usage } from "@opencode-ai/llm"
2: import { DateTime, Effect } from "effect"
3: import { EventV2 } from "../../event"
4: import { ModelV2 } from "../../model"
5: import { SessionEvent } from "../event"
6: import { SessionMessage } from "../message"
7: import { SessionSchema } from "../schema"
8:
9: type Input = {
10: readonly sessionID: SessionSchema.ID
11: readonly agent: string
12: readonly model: ModelV2.Ref
13: readonly snapshot?: string
14: readonly startCommit: (assistantMessageID: SessionMessage.ID) => Effect.Effect<void>
15: readonly eventCommit: () => Effect.Effect<void>
16: }
17:
18: const safe = (value: number | undefined) => Math.max(0, Number.isFinite(value) ? (value ?? ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/compaction.ts</path>
<type>file</type>
<content>
1: export * as SessionCompaction from "./compaction"
2:
3: import { LLM, LLMError, LLMEvent, Message, type LLMRequest, type Model } from "@opencode-ai/llm"
4: import { DateTime, Effect, Stream } from "effect"
5: import type { Config } from "../config"
6: import type { EventV2 } from "../event"
7: import { SessionEvent } from "./event"
8: import { SessionMessage } from "./message"
9: import { SessionSchema } from "./schema"
10: import { Token } from "../util/token"
11:
12: const DEFAULT_BUFFER = 20_000
13: const DEFAULT_KEEP_TOKENS = 8_000
14: const TOOL_OUTPUT_MAX_CHARS = 2_000
15: const SUMMARY_OUTPUT_TOKENS = 4_096
16: const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response. ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session/context-epoch.ts</path>
<type>file</type>
<content>
1: export * as SessionContextEpoch from "./context-epoch"
2:
3: import { eq } from "drizzle-orm"
4: import { DateTime, Effect, Schema } from "effect"
5: import type { Database } from "../database/database"
6: import { EventV2 } from "../event"
7: import { SystemContext } from "../system-context/index"
8: import { ContextSnapshotDecodeError } from "./error"
9: import { SessionEvent } from "./event"
10: import { SessionHistory } from "./history"
11: import { SessionInput } from "./input"
12: import { SessionMessage } from "./message"
13: import { SessionSchema } from "./schema"
14: import { SessionContextEpochTable } from "./sql"
15: import { SessionExecutionClaim } from "./execution/claim"
16:
17: type DatabaseService = Database.Interface["db"]
18:
19: interface Prepared {
20: readonly baseline: string
21: readonly baselineSeq: number
22: }
23:
24: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/session.ts</path>
<type>file</type>
<content>
1: export * as SessionV2 from "./session"
2: export * from "./session/schema"
3:
4: import { DateTime, Effect, Layer, Schema, Context, Stream } from "effect"
5: import { ListAnchor } from "@opencode-ai/schema/session"
6: import { and, asc, desc, eq, gt, like, lt, or, type SQL } from "drizzle-orm"
7: import { ProjectV2 } from "./project"
8: import { WorkspaceV2 } from "./workspace"
9: import { ModelV2 } from "./model"
10: import { Location } from "./location"
11: import { SessionMessage } from "./session/message"
12: import { Prompt } from "./session/prompt"
13: import { PromptInput } from "@opencode-ai/schema/prompt-input"
14: import { EventV2 } from "./event"
15: import { Database } from "./database/database"
16: import { SessionProjector } from "./session/projector"
17: import { SessionMessageTable, SessionTable } from "./session/sql"
18: import { SessionSchema } ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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>
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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 ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/opencode/src/server/routes/instance/httpapi/server.ts</path>
<type>file</type>
<content>
1: import { Config as EffectConfig, Context, Effect, Layer } from "effect"
2: import { HttpApiBuilder, OpenApi } from "effect/unstable/httpapi"
3: import { HttpClient, HttpMiddleware, HttpRouter, HttpServer, HttpServerResponse } from "effect/unstable/http"
4: import * as Socket from "effect/unstable/socket/Socket"
5: import { FSUtil } from "@opencode-ai/core/fs-util"
6: import * as Observability from "@opencode-ai/core/observability"
7: import { Account } from "@/account/account"
8: import { Agent } from "@/agent/agent"
9: import { Auth } from "@/auth"
10: import { BackgroundJob } from "@/background/job"
11: import { Command } from "@/command"
12: import { Config } from "@/config/config"
13: import { Workspace } from "@/control-plane/workspace"
14: import { Env } from "@/env"
15: import { EventV2Bridge } from "@/event-v2-bridge"
16: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/opencode/src/server/routes/instance/httpapi/server.ts</path> <type>file</type> <content> 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: BackgroundJob.node, 240: RuntimeFlags.node, 241: EventV2Bridge.node, 242: SessionRunState.node, 243: SessionProcessor.node, 244: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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: ...
Found 20 matches
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/database/schema.gen.ts:
Line 158: CREATE TABLE \`session_execution\` (
Line 167: CONSTRAINT \`session_execution_pk\` PRIMARY KEY(\`id\`),
Line 168: CONSTRAINT \`fk_session_execution_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE,
Line 169: CONSTRAINT "session_execution_phase_check" CHECK("phase" IN ('ready', 'safe', 'unknown', 'continue', 'cancelling'))
Line 180: \`wake_pending\` integer DEFAULT false NOT NULL,
Line 198: CREATE TABLE \`session_recovery\` (
Line 204: \`wake_pending\` integer DEFAULT true NOT NULL,
Line 205: \`wake_attempts\` integer DEFAULT 0 NOT NULL,
Line 208: CONSTRAINT \`session_recovery_pk\` PRIMARY KEY(\`execution_id\`),
Line 209: CONSTRAINT \`fk_session_recovery_session_id_session_id_fk\` FOREIGN KEY ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/src/database/schema.gen.ts</path> <type>file</type> <content> 140: \`message_id\` text NOT NULL, 141: \`session_id\` text NOT NULL, 142: \`time_created\` integer NOT NULL, 143: \`time_updated\` integer NOT NULL, 144: \`data\` text NOT NULL, 145: CONSTRAINT \`fk_part_message_id_message_id_fk\` FOREIGN KEY (\`message_id\`) REFERENCES \`message\`(\`id\`) ON DELETE CASCADE 146: ); 147: `) 148: yield* tx.run(` 149: CREATE TABLE \`session_context_epoch\` ( 150: \`session_id\` text PRIMARY KEY, 151: \`baseline\` text NOT NULL, 152: \`snapshot\` text NOT NULL, 153: \`baseline_seq\` integer NOT NULL, 154: CONSTRAINT \`fk_session_context_epoch_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE 155: ); 156: `) 157: ...
Found 45 matches /tmp/opencode/session-recovery-v10-audit.wukNah/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 1171: "table": "session_recovery" Line 1181: "table": "session_recovery" Line 1191: "table": "session_recovery" Line 1201: "table": "session_recovery" Line 1211: "table": "session_recovery" Line 1221: "table": "session_recovery" Line 1231: "table": "session_recovery" Line 1241: "table": "session_recovery" Line 1251: "table": ...
Found 100 matches (more matches available)
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-tool-progress.test.ts:
Line 11: import { ProviderV2 } from "@opencode-ai/core/provider"
Line 22: const model = { id: ModelV2.ID.make("model"), providerID: ProviderV2.ID.make("provider") }
Line 84: provider: { executed: false },
Line 112: provider: { executed: false },
Line 133: provider: { executed: false },
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-runner-tool-events.test.ts:
Line 9: import { ProviderV2 } from "@opencode-ai/core/provider"
Line 38: replay: () => Effect.void,
Line 39: replayAll: () => Effect.succeed(undefined),
Line 50: providerID: ProviderV2.ID.make("provider"),
Line 115: test("provider-executed success retains its compatibility result", async () => {
Line 117: await Effect.runPromise(publisher.publish(LLMEvent.toolCall({ ...call, ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1180: model,
1181: })
1182:
1183: const recovered = yield* events
1184: .publish(SessionEvent.Step.Recovered, {
1185: sessionID,
1186: timestamp: DateTime.makeUnsafe(1),
1187: executionID: "cross-session",
1188: assistantMessageID: assistantID,
1189: phase: "unknown",
1190: })
1191: .pipe(Effect.exit)
1192:
1193: expect(Exit.isFailure(recovered)).toBe(true)
1194: expect(yield* db.select().from(SessionRecoveryTable).all()).toEqual([])
1195: expect(yield* db.select().from(SessionInputTable).all()).toEqual([])
1196: }),
1197: )
1198:
1199: it.effect("rejects a recovery execution identity owned by another Session", () =>
1200: Effect.gen(function* () {
1201: const { db } = yield* Database.Service
1202: const events = ...
Found 42 matches
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-projector.test.ts:
Line 68: it.effect("atomically reconciles one abandoned execution into one continuation", () =>
Line 222: it.effect("records a recovery continuation without forging an assistant identity", () =>
Line 258: it.effect("recovers each abandoned execution once across repeated startup scans", () =>
Line 304: it.effect("never recovers a live execution lease", () =>
Line 342: it.effect("never recovers an execution superseded by a newer assistant", () =>
Line 389: it.effect("redrives a durable recovery wake after admission", () =>
Line 440: it.effect("quarantines a malformed recovery row without blocking a valid wake", () =>
Line 494: it.effect("quarantines a full batch of poison recovery rows before redriving valid work", () =>
Line 559: it.effect("fairly redrives recovery and ordinary work beyond a full bounded batch", () =>
Line 695: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
960:
961: yield* SessionRecovery.recover().pipe(Effect.provideService(SessionExecution.Service, execution))
962: yield* SessionRecovery.recover().pipe(Effect.provideService(SessionExecution.Service, execution))
963:
964: expect(wakes).toBe(2)
965: expect(yield* db.select().from(SessionInputTable).all()).toEqual([
966: expect.objectContaining({ wake_pending: true, promoted_seq: null }),
967: ])
968: }),
969: )
970:
971: it.effect("redrives ordinary input without recovering a claim that crashed before promotion", () =>
972: Effect.gen(function* () {
973: const { db } = yield* Database.Service
974: const events = yield* EventV2.Service
975: yield* db
976: .insert(ProjectTable)
977: .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1410: expect(yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, continuationID)).get()).toMatchObject(
1411: { promoted_seq: null },
1412: )
1413: expect(yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, ordinaryID)).get()).toMatchObject({
1414: promoted_seq: expect.any(Number),
1415: })
1416: expect(yield* SessionInput.hasPending(db, sessionID, "queue")).toBe(false)
1417: }),
1418: )
1419:
1420: it.effect("never promotes a recovery superseded before scanner validation", () =>
1421: Effect.gen(function* () {
1422: const { db } = yield* Database.Service
1423: const events = yield* EventV2.Service
1424: const recoveredID = SessionMessage.ID.make("msg_recovery_exact_stale")
1425: yield* db
1426: ...
Found 10 matches
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-execution-local.test.ts:
Line 43: it.effect("clears the claim after a successful drain", () =>
Line 52: it.effect("preserves the claim when its scope closes", () =>
Line 68: it.effect("clears the claim after explicit interruption", () =>
Line 82: it.effect("never starts a second owner while the lease is live", () =>
Line 105: it.effect("never overwrites an expired lease", () =>
Line 132: it.effect("never re-enters its own uncertain live lease", () =>
Line 159: it.effect("renews heartbeat expiry from the current clock", () =>
Line 182: it.effect("never revives an expired claim on heartbeat", () =>
Line 200: it.effect("settles cancellation before starting a pending successor", () =>
Line 244: it.effect("fails cancellation when cleanup loses execution ownership", () =>
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-execution-local.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect } from "bun:test"
2: import { Deferred, Effect, Exit, Fiber, Scope } from "effect"
3: import { TestClock } from "effect/testing"
4: import { eq } from "drizzle-orm"
5: import { Database } from "@opencode-ai/core/database/database"
6: import { Project } from "@opencode-ai/core/project"
7: import { ProjectTable } from "@opencode-ai/core/project/sql"
8: import { AbsolutePath } from "@opencode-ai/core/schema"
9: import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
10: import { SessionSchema } from "@opencode-ai/core/session/schema"
11: import { SessionExecutionTable, SessionTable } from "@opencode-ai/core/session/sql"
12: import { testEffect } from "./lib/effect"
13:
14: const sessionID = SessionSchema.ID.make("ses_execution_local")
15: const it = ...
Found 98 matches
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-runner.test.ts:
Line 619: it.effect("redrives only the synthetic recovery continuation", () =>
Line 688: it.effect("clears durable execution intent when ordinary input is promoted", () =>
Line 715: it.effect("redrives an ordinary prompt after its advisory wake is lost", () =>
Line 752: it.effect("never dispatches a provider after ownership changes", () =>
Line 778: it.effect("never publishes streamed output after ownership changes", () =>
Line 805: it.effect("never starts a local tool after ownership changes", () =>
Line 845: it.effect("never fails interrupted tools after ownership changes", () =>
Line 902: it.effect("advertises and executes a globally attached application tool", () =>
Line 958: it.effect("starts a real runner turn after default prompt recording", () =>
Line 977: it.effect("streams one request with registry definitions from ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
600: event.type.startsWith(SessionEvent.Step.Interrupted.type),
601: ),
602: ).toBe(true)
603: expect(yield* session.context(sessionID)).toMatchObject([
604: { type: "user", text: prompt },
605: {
606: type: "assistant",
607: finish: "error",
608: error: { type: "unknown", message: "Provider Step outcome unknown after interruption" },
609: content: [
610: kind === "tool input"
611: ? { type: "tool", id: fragmentID(kind, "interrupted"), state: { status: "error" } }
612: : fixture.expectedContent,
613: ],
614: },
615: ])
616: })
617:
618: describe("SessionRunnerLLM", () => {
619: it.effect("redrives only the synthetic recovery continuation", () =>
620: Effect.gen(function* () {
621: yield* setup
622: const session = ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
1950: expect(executions).toEqual(["hello"])
1951: expect(yield* session.context(sessionID)).toMatchObject([
1952: { type: "user", text: "Echo this" },
1953: {
1954: type: "assistant",
1955: finish: "tool-calls",
1956: content: [
1957: {
1958: type: "tool",
1959: id: "call-echo",
1960: name: "echo",
1961: state: {
1962: status: "completed",
1963: input: { text: "hello" },
1964: structured: { text: "hello" },
1965: content: [{ type: "text", text: "hello" }],
1966: },
1967: },
1968: ],
1969: },
1970: { type: "assistant", finish: "stop", content: [{ type: "text", id: "text-final", text: "Done" }] },
1971: ])
1972: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
2230: )
2231:
2232: it.effect("recovers the continuation committed with a settled tool step", () =>
2233: Effect.gen(function* () {
2234: yield* setup
2235: const session = yield* SessionV2.Service
2236: const { db } = yield* Database.Service
2237: yield* session.prompt({
2238: sessionID,
2239: prompt: Prompt.make({ text: "Recover settled tool continuation" }),
2240: resume: false,
2241: })
2242: responses = [
2243: [
2244: LLMEvent.stepStart({ index: 0 }),
2245: LLMEvent.toolCall({ id: "call-recovery", name: "echo", input: { text: "settled" } }),
2246: LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }),
2247: LLMEvent.finish({ reason: "tool-calls" }),
2248: ],
2249: ]
2250: modelResolveHook = Effect.gen(function* () ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/database-migration.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect, test } from "bun:test"
2: import { $ } from "bun"
3: import { fileURLToPath } from "url"
4: import path from "path"
5: import { SqliteClient } from "@effect/sql-sqlite-bun"
6: import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
7: import { Effect, Exit, Layer } from "effect"
8: import { eq, inArray, sql } from "drizzle-orm"
9: import { DatabaseMigration } from "@opencode-ai/core/database/migration"
10: import { migrations } from "@opencode-ai/core/database/migration.gen"
11: import workspaceNameMigration from "@opencode-ai/core/database/migration/20260410174513_workspace-name"
12: import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510033149_session_usage"
13: import normalizeStoragePathsMigration from ...
Found 29 matches
/tmp/opencode/session-recovery-v10-audit.wukNah/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", () =>
...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-prompt.test.ts</path>
<type>file</type>
<content>
280:
281: const first = yield* session.prompt(input)
282: const second = yield* session.prompt(input)
283:
284: expect(second.id).not.toBe(first.id)
285: expect(yield* session.messages({ sessionID })).toEqual([])
286: expect(yield* admittedCount).toBe(2)
287: }),
288: )
289:
290: it.effect("returns the original recorded message when the ID is retried", () =>
291: Effect.gen(function* () {
292: yield* setup
293: const session = yield* SessionV2.Service
294: const input = {
295: sessionID,
296: id: messageID,
297: prompt: Prompt.make({ text: "Fix the failing tests" }),
298: resume: false,
299: }
300:
301: const first = yield* session.prompt(input)
302: const retried = yield* session.prompt(input)
303:
304: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-prompt.test.ts</path>
<type>file</type>
<content>
600:
601: it.effect("promotes one message once under concurrent promotion attempts", () =>
602: Effect.gen(function* () {
603: yield* setup
604: const { db } = yield* Database.Service
605: const session = yield* SessionV2.Service
606: const events = yield* EventV2.Service
607: yield* session.prompt({ id: messageID, sessionID, prompt: Prompt.make({ text: "Promote once" }), resume: false })
608:
609: yield* Effect.all(
610: [
611: SessionInput.promoteSteers(db, events, sessionID, Number.MAX_SAFE_INTEGER),
612: SessionInput.promoteSteers(db, events, sessionID, Number.MAX_SAFE_INTEGER),
613: ],
614: { concurrency: "unbounded" },
615: )
616:
617: expect(yield* eventCount(EventV2.versionedType(SessionEvent.Prompted.type, 1))).toBe(1)
618: expect(yield* ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-run-coordinator.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect } from "bun:test"
2: import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect"
3: import { SessionRunCoordinator } from "@opencode-ai/core/session/run-coordinator"
4: import { testEffect } from "./lib/effect"
5:
6: const it = testEffect(Layer.empty)
7:
8: describe("SessionRunCoordinator", () => {
9: it.effect("joins concurrent resumes for one key", () =>
10: Effect.scoped(
11: Effect.gen(function* () {
12: const gate = yield* Deferred.make<void>()
13: let runs = 0
14: const coordinator = yield* SessionRunCoordinator.make({
15: drain: () => Effect.sync(() => runs++).pipe(Effect.andThen(Deferred.await(gate))),
16: })
17:
18: const first = yield* coordinator.run("session").pipe(Effect.forkChild)
19: yield* Effect.yieldNow
20: const ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-run-coordinator.test.ts</path>
<type>file</type>
<content>
280: expect(runs).toBe(2)
281: }),
282: ),
283: )
284:
285: it.effect("finalizes interruption when cleanup dies", () =>
286: Effect.scoped(
287: Effect.gen(function* () {
288: const firstStarted = yield* Deferred.make<void>()
289: const cleanupStarted = yield* Deferred.make<void>()
290: const cleanupGate = yield* Deferred.make<void>()
291: const secondStarted = yield* Deferred.make<void>()
292: const defect = new Error("cleanup defect")
293: let runs = 0
294: const coordinator = yield* SessionRunCoordinator.make({
295: drain: () =>
296: Effect.sync(() => ++runs).pipe(
297: Effect.flatMap((run) =>
298: run === 1
299: ? Deferred.succeed(firstStarted, ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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 ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-runner-tool-events.test.ts</path>
<type>file</type>
<content>
1: import { expect, test } from "bun:test"
2: import { Effect, Schema, Stream } from "effect"
3: import { LLMEvent } from "@opencode-ai/llm"
4: import { EventV2 } from "@opencode-ai/core/event"
5: import { SessionEvent } from "@opencode-ai/core/session/event"
6: import { SessionMessage } from "@opencode-ai/core/session/message"
7: import { SessionV2 } from "@opencode-ai/core/session"
8: import { ModelV2 } from "@opencode-ai/core/model"
9: import { ProviderV2 } from "@opencode-ai/core/provider"
10: import { createLLMEventPublisher } from "@opencode-ai/core/session/runner/publish-llm-event"
11:
12: const sessionID = SessionV2.ID.make("ses_tool_event_test")
13: const base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"
14:
15: const capture = () => {
16: const published: Array<{ readonly type: string; readonly data: unknown }> = []
17: const events = ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/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: ...
Found 26 matches
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/sdk/js/src/v2/gen/types.gen.ts:
Line 24: | EventSessionNextPromptExecutionRequested
Line 33: | EventSessionNextStepRecovered
Line 874: requestExecution?: boolean
Line 879: type: "session.next.prompt.execution_requested"
Line 983: type: "session.next.step.recovered"
Line 1650: | SyncEventSessionNextPromptExecutionRequested
Line 1659: | SyncEventSessionNextStepRecovered
Line 2785: | SessionNextPromptExecutionRequested
Line 2794: | SessionNextStepRecovered
Line 2915: | SessionNextPromptExecutionRequested
Line 2924: | SessionNextStepRecovered
Line 3430: requestExecution?: boolean
Line 3435: export type SyncEventSessionNextPromptExecutionRequested = {
Line 3439: type: "session.next.prompt.execution_requested.1"
Line 3602: export type SyncEventSessionNextStepRecovered = {
Line 3606: type: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/sdk/js/src/v2/gen/types.gen.ts</path>
<type>file</type>
<content>
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 SyncEventSessionNextPromptExecutionRequested = {
3436: type: "sync"
3437: id: string
3438: syncEvent: {
3439: type: "session.next.prompt.execution_requested.1"
3440: id: string
3441: seq: number
3442: aggregateID: string
3443: ...
Found 12 matches
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/sdk/openapi.json:
Line 17899: "requestExecution": {
Line 17919: "enum": ["session.next.prompt.execution_requested"]
Line 18289: "enum": ["session.next.step.recovered"]
Line 25709: "requestExecution": {
Line 25740: "enum": ["session.next.prompt.execution_requested.1"]
Line 26299: "enum": ["session.next.step.recovered.1"]
Line 28599: "requestExecution": {
Line 28622: "enum": ["session.next.prompt.execution_requested"]
Line 29190: "enum": ["session.next.step.recovered"]
Line 34811: "requestExecution": {
Line 34831: "enum": ["session.next.prompt.execution_requested"]
Line 35201: "enum": ["session.next.step.recovered"]
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/sdk/openapi.json</path>
<type>file</type>
<content>
17850: "pattern": "^msg_"
17851: },
17852: "prompt": {
17853: "$ref": "#/components/schemas/Prompt"
17854: },
17855: "delivery": {
17856: "type": "string",
17857: "enum": ["steer", "queue"]
17858: }
17859: },
17860: "required": ["timestamp", "sessionID", "messageID", "prompt", "delivery"],
17861: "additionalProperties": false
17862: }
17863: },
17864: "required": ["id", "type", "properties"],
17865: "additionalProperties": false
17866: },
17867: {
17868: "type": "object",
17869: ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/sdk/openapi.json</path>
<type>file</type>
<content>
18255: "properties": {
18256: "type": "object",
18257: "properties": {
18258: "timestamp": {
18259: "type": "number"
18260: },
18261: "sessionID": {
18262: "type": "string",
18263: "pattern": "^ses"
18264: },
18265: "assistantMessageID": {
18266: "type": "string",
18267: "pattern": "^msg_"
18268: },
18269: "error": {
18270: "$ref": "#/components/schemas/SessionErrorUnknown"
18271: }
18272: },
18273: "required": ["timestamp", "sessionID", "assistantMessageID", ...
<path>/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
3500: [
3501: LLMEvent.stepStart({ index: 0 }),
3502: LLMEvent.toolCall({ id: "call-blocked", name: "blocked", input: {} }),
3503: LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }),
3504: LLMEvent.finish({ reason: "tool-calls" }),
3505: ],
3506: [
3507: LLMEvent.stepStart({ index: 0 }),
3508: LLMEvent.stepFinish({ index: 0, reason: "stop" }),
3509: LLMEvent.finish({ reason: "stop" }),
3510: ],
3511: ]
3512:
3513: yield* session.resume(sessionID)
3514:
3515: expect(requests).toHaveLength(2)
3516: expect(yield* session.context(sessionID)).toMatchObject([
3517: { type: "user", text: "Call blocked" },
3518: {
3519: type: "assistant",
3520: content: [
3521: { type: "tool", id: ...
Found 33 matches /tmp/opencode/session-recovery-final-v10.patch: Line 1: diff --git a/packages/core/schema.json b/packages/core/schema.json Line 881: diff --git a/packages/core/src/database/migration.gen.ts b/packages/core/src/database/migration.gen.ts Line 892: diff --git a/packages/core/src/database/migration/20260914170650_session-recovery.ts b/packages/core/src/database/migration/20260914170650_session-recovery.ts Line 956: diff --git a/packages/core/src/database/schema.gen.ts b/packages/core/src/database/schema.gen.ts Line 1058: diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts Line 1090: diff --git a/packages/core/src/session/compaction.ts b/packages/core/src/session/compaction.ts Line 1179: diff --git a/packages/core/src/session/context-epoch.ts b/packages/core/src/session/context-epoch.ts Line 1355: diff --git a/packages/core/src/session/execution/local.ts b/packages/core/src/session/execution/local.ts Line 1542: diff --git ...
Found 6 matches
/tmp/opencode/session-recovery-v10-audit.wukNah/packages/core/test/session-projector.test.ts:
Line 1052: executionID: "existing-recovery",
Line 1273: executionID: "conflicting-recovery-identity",
Line 1279: .where(eq(SessionRecoveryTable.execution_id, "conflicting-recovery-identity"))
Line 1284: id: "conflicting-recovery-identity",
Line 1296: id: "conflicting-recovery-identity",
Line 1300: expect.objectContaining({ execution_id: "conflicting-recovery-identity", phase: "continue" }),
I found a candidate-blocking branch gap in the v9 fix and am completing the end-state identity checks before issuing the verdict. I did not run any build, test, server, or executable.
07b95915924ccf5dcfdf7ea5eb45d6cf972a6058664518935154dff9934222b2 /tmp/opencode/session-recovery-final-v10.patch 1117176c4cfbf77527216b4161b54f8d3918ca7f7415747117c24f94d8e0b090 /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server ab9408c81cc9916177641ae8e9f08624fc27b5b9 33 M .lore.md M AGENTS.md M packages/core/schema.json M packages/core/src/database/migration.gen.ts M packages/core/src/database/migration/20260914170650_session-recovery.ts D packages/core/src/database/migration/20260914214636_session-execution.ts D packages/core/src/database/migration/20260914222526_session-execution-phase.ts M packages/core/src/database/schema.gen.ts M packages/core/src/session.ts M packages/core/src/session/compaction.ts M packages/core/src/session/context-epoch.ts M packages/core/src/session/execution/local.ts M packages/core/src/session/input.ts M packages/core/src/session/message-updater.ts M packages/core/src/session/projector.ts M ...
packages/core/src/session/recovery.ts:136-140 clears an expired ready claim when hasPendingExecutionIntent(...) finds pending work. That query treats any pending recovery row as execution intent without checking or excluding the current execution ID (packages/core/src/session/recovery.ts:30-60).
Therefore, when a same-Session session_recovery row has the same execution ID but mismatched assistant, continuation, or phase identity—and its normal default wake_pending=true remains set—the scanner:
Step.Recovered.packages/core/src/session/projector.ts:414-423.packages/core/src/session/recovery.ts:203-247.packages/core/src/session/recovery.ts:341-371.The new regression does not cover this route. It explicitly changes the conflicting recovery row to wake_pending=false at packages/core/test/session-projector.test.ts:1276-1279, forcing projection to run. With the default true, the phase-mismatched continue row created at lines 1270-1275 is treated as pending intent, the execution claim is deleted, and no "Recovery projection failed" quarantine occurs.
This leaves the v9 blocker open for the normal persisted state. The initial pending-intent check must exclude the current execution ID or validate its complete identity. Extend the regression with the default pending wake and assert that the exact execution+owner is quarantined without wake or promotion.