Dashboard › opencode › Session 0167D9N66z91
0167D9N66z91MADxHRead-only adversarial correctness review of frozen v13. Base ab9408c81cc9916177641ae8e9f08624fc27b5b9. Patch /tmp/opencode/session-recovery-final-v13.patch SHA-256 3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf. SEA /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server SHA-256 c3b8c5d78e473ad4104a5b54617900e28921a283029b8484be7bce4bc3cee85a. Do not mutate/run tests/builds/services. Verify hashes, clean apply, inspect all 33 paths. Primary closures from v12: cancellation and projection-failure quarantine writes now require exact owner plus unexpired lease and stop on zero rows; deterministic tests expire old owner and show successor recovery. Assistant-less ready/unknown/continue supersession is revalidated inside Step.Recovered immediate transaction against captured claim.assistant_seq; concurrent Step.Started must roll back projection and exact claim clear. Direct tests cover acquisition snapshot and Step.Started advancement; equality permits current-boundary continue. Inspect these regressions and mutation sensitivity. Reassess complete identity quarantine, stale ownership, cancellation, tools/providers, replay/reservation/fairness, migration/schema, dependency direction. Accepted residuals: final synchronous check-to-external-call race, process-local interrupts, bounded clock skew. Evidence: recovery 241/0/712; Core 1178/0/3301; Core typecheck after fixes; prior unchanged four package typechecks and Schema manifest; migration checks; v13 smoke. Return findings with exact file:line, PASS/CONCERN/MUST-FIX, end exactly MERGE or DO-NOT-MERGE. Empty invalid; no recall.
<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>
3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf /tmp/opencode/session-recovery-final-v13.patch c3b8c5d78e473ad4104a5b54617900e28921a283029b8484be7bce4bc3cee85a /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server dc661f30cb2055ad6248fdc59b14998d834497be commit 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>/tmp/opencode</path> <type>directory</type> <entries> .oxfmtrc.json 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.patch session-recovery-final-v10/ session-recovery-final-v11.patch session-recovery-final-v11/ session-recovery-final-v12.patch session-recovery-final-v12/ session-recovery-final-v13.patch session-recovery-final-v13/ session-recovery-final-v4-diag/ session-recovery-final-v4.index session-recovery-final-v4.patch session-recovery-final-v4/ session-recovery-final-v5.index session-recovery-final-v5.patch session-recovery-final-v5/ session-recovery-final-v6.patch session-recovery-final-v6/ session-recovery-final-v7.patch session-recovery-final-v7/ session-recovery-fi...
<path>/tmp/opencode/session-recovery-final-v13</path> <type>directory</type> <entries> cache/ config/ data/ home/ opencode.db state/ workspace/ (7 entries) </entries>
<path>/tmp/opencode/session-recovery-final-v13.patch</path>
<type>file</type>
<content>
1: diff --git a/packages/core/schema.json b/packages/core/schema.json
2: index d0eeeebd5c..dedf0cc600 100644
3: --- a/packages/core/schema.json
4: +++ b/packages/core/schema.json
5: @@ -1,8 +1,10 @@
6: {
7: "version": "7",
8: "dialect": "sqlite",
9: - "id": "f14a9b18-8207-487e-a3d3-227e629ba9ad",
10: - "prevIds": ["169a0f0f-d58f-479f-b024-fa1c7b9a09db"],
11: + "id": "3a5ec6d4-a60d-47da-a347-3c72396a13c1",
12: + "prevIds": [
13: + "f14a9b18-8207-487e-a3d3-227e629ba9ad"
14: + ],
15: "ddl": [
16: {
17: "name": "workspace",
18: @@ -60,6 +62,10 @@
19: "name": "session_context_epoch",
20: "entityType": "tables"
21: },
22: + {
23: + "name": "session_execution",
24: + "entityType": "tables"
25: + },
26: {
27: "name": "session_input",
28: "entityType": "tables"
29: @@ -68,6 +74,10 @@
30: "name": "session_message",
31: ...
paths=33 unique=33 worktree_diff_sha256=2f1b5ee3b250604baa6f809b664776c5889266390d24b65b4a0ddae8f2374c8f patch_sha256=3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf byte_equal=False
patch_lines=10147 diff_lines=10061 common_prefix_all_shorter; extra=86
<path>/tmp/opencode/session-recovery-final-v13.patch</path>
<type>file</type>
<content>
10045: +++ b/packages/server/src/routes.ts
10046: @@ -10,6 +10,7 @@ import { SessionV2 } from "@opencode-ai/core/session"
10047: import { SessionExecution } from "@opencode-ai/core/session/execution"
10048: import { LocationServiceMap } from "@opencode-ai/core/location-service-map"
10049: import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
10050: +import { SessionRecovery } from "@opencode-ai/core/session/recovery"
10051: import { ToolOutputStore } from "@opencode-ai/core/tool-output-store"
10052: import { HttpRouter, HttpServer } from "effect/unstable/http"
10053: import { HttpApiBuilder } from "effect/unstable/httpapi"
10054: @@ -29,6 +30,7 @@ const applicationServices = LayerNode.group([
10055: httpClient,
10056: ToolOutputStore.cleanupNode,
10057: SessionV2.node,
10058: + SessionRecovery.node,
10059: PermissionSaved.node,
10060: ...
tracked_patch_exact=False packages/core/src/database/migration/20260914170650_session-recovery.ts_exact=True packages/core/src/session/recovery.ts_exact=True packages/core/test/session-execution-local.test.ts_exact=True packages/core/src/session/execution/claim.ts_exact=True packages/core/src/session/recovery-id.ts_exact=True tracked=28 new=5 total=33
Found 100 matches (more matches available) /tmp/opencode/session-recovery-final-v13.patch: Line 1: diff --git a/packages/core/schema.json b/packages/core/schema.json Line 5: @@ -1,8 +1,10 @@ Line 18: @@ -60,6 +62,10 @@ Line 29: @@ -68,6 +74,10 @@ Line 40: @@ -920,6 +930,96 @@ Line 137: @@ -980,6 +1080,16 @@ Line 154: @@ -1060,6 +1170,96 @@ Line 251: @@ -1350,6 +1550,16 @@ Line 268: @@ -1481,9 +1691,13 @@ Line 284: @@ -1492,9 +1706,13 @@ Line 300: @@ -1503,9 +1721,13 @@ Line 316: @@ -1514,9 +1736,13 @@ Line 332: @@ -1525,9 +1751,13 @@ Line 348: @@ -1536,9 +1766,13 @@ Line 364: @@ -1547,9 +1781,13 @@ Line 380: @@ -1558,9 +1796,13 @@ Line 396: @@ -1569,9 +1811,28 @@ Line 427: @@ -1580,9 +1841,13 @@ Line 443: @@ -1591,9 +1856,58 @@ Line 504: @@ -1602,9 +1916,13 @@ Line 520: @@ -1613,9 +1931,13 @@ Line 536: @@ -1624,133 +1946,192 @@ Line 748: @@ -1872,6 +2253,34 @@ Line 783: @@ -1934,6 +2343,28 @@ Line 812: @@ ...
1: packages/core/schema.json 888: packages/core/src/database/migration.gen.ts 899: packages/core/src/database/migration/20260914170650_session-recovery.ts 964: packages/core/src/database/schema.gen.ts 1067: packages/core/src/session.ts 1099: packages/core/src/session/compaction.ts 1188: packages/core/src/session/context-epoch.ts 1364: packages/core/src/session/execution/local.ts 1573: packages/core/src/session/input.ts 2119: packages/core/src/session/message-updater.ts 2213: packages/core/src/session/projector.ts 2332: packages/core/src/session/recovery.ts 2842: packages/core/src/session/run-coordinator.ts 2911: packages/core/src/session/runner/index.ts 2933: packages/core/src/session/runner/llm.ts 3490: packages/core/src/session/runner/publish-llm-event.ts 3810: packages/core/src/session/sql.ts 3934: packages/core/test/database-migration.test.ts 4026: packages/core/test/session-execution-local.test.ts 4347: packages/core/test/session-projector.test.ts 6835: ...
<path>/home/byk/Code/opencode/packages/core/src/session/execution/claim.ts</path>
<type>file</type>
<content>
1: export * as SessionExecutionClaim from "./claim"
2:
3: import { and, eq, gt } from "drizzle-orm"
4: import { Clock, Effect } from "effect"
5: import type { Database } from "../../database/database"
6: import type { SessionSchema } from "../schema"
7: import { SESSION_EXECUTION_LEASE_MS, SessionExecutionTable } from "../sql"
8:
9: type DatabaseService = Database.Interface["db"]
10:
11: export type Claim = {
12: readonly sessionID: SessionSchema.ID
13: readonly executionID: string
14: readonly ownerID: string
15: }
16:
17: export type Values = Partial<
18: Pick<typeof SessionExecutionTable.$inferInsert, "assistant_message_id" | "assistant_seq" | "phase">
19: >
20:
21: export class Lost extends Error {
22: constructor(readonly claim: Claim) {
23: super(`Session execution claim lost: ${claim.executionID}`)
24: }
25: }
26:
27: const where = (claim: Claim, ...
<path>/home/byk/Code/opencode/packages/core/src/session/execution/local.ts</path>
<type>file</type>
<content>
1: import { and, asc, desc, eq, isNull } from "drizzle-orm"
2: import { Cause, Clock, Duration, Effect, Exit, Layer, Schedule } from "effect"
3: import { Database } from "../../database/database"
4: import { LocationServiceMap } from "../../location-service-map"
5: import { makeGlobalNode } from "../../effect/app-node"
6: import { SessionRunCoordinator } from "../run-coordinator"
7: import { SessionRunner } from "../runner"
8: import { SessionMessage } from "../message"
9: import { SessionSchema } from "../schema"
10: import { SessionStore } from "../store"
11: import { SessionExecution } from "../execution"
12: import { SessionExecutionClaim } from "./claim"
13: import {
14: SESSION_EXECUTION_LEASE_MS,
15: SessionExecutionTable,
16: SessionMessageTable,
17: SessionRecoveryTable,
18: } from "../sql"
19:
20: /** Current-process routing for implicit-local Locations. ...
<path>/home/byk/Code/opencode/packages/core/src/session/recovery.ts</path>
<type>file</type>
<content>
1: export * as SessionRecovery from "./recovery"
2:
3: import { and, asc, desc, eq, exists, gt, isNull, lte, or, sql } from "drizzle-orm"
4: import { Clock, DateTime, Effect, Layer, Option, Schedule, Schema } from "effect"
5: import { Database } from "../database/database"
6: import { makeGlobalNode } from "../effect/app-node"
7: import { EventV2 } from "../event"
8: import { SessionEvent } from "./event"
9: import { SessionExecution } from "./execution"
10: import { SessionProjector } from "./projector"
11: import { SessionExecutionClaim } from "./execution/claim"
12: import { SessionInput } from "./input"
13: import { continuation } from "./recovery-id"
14: import { SessionSchema } from "./schema"
15: import {
16: MAX_WAKE_ATTEMPTS,
17: SESSION_EXECUTION_LEASE_MS,
18: SessionExecutionTable,
19: SessionInputTable,
20: SessionRecoveryTable,
21: SessionMessageTable,
22: ...
<path>/home/byk/Code/opencode/packages/core/src/session/input.ts</path>
<type>file</type>
<content>
1: export * as SessionInput from "./input"
2:
3: import { and, asc, desc, eq, isNull, lte } from "drizzle-orm"
4: import { DateTime, Effect, Option, Schema } from "effect"
5: import { Admitted, Delivery } from "@opencode-ai/schema/session-input"
6: import type { Database } from "../database/database"
7: import { EventV2 } from "../event"
8: import { SessionEvent } from "./event"
9: import { SessionMessage } from "./message"
10: import { Prompt } from "./prompt"
11: import { continuation, message } from "./recovery-id"
12: import { SessionSchema } from "./schema"
13: import { SessionInputTable, SessionMessageTable, SessionRecoveryTable } from "./sql"
14:
15: type DatabaseService = Database.Interface["db"]
16:
17: export { Admitted, Delivery }
18:
19: const decodePrompt = Schema.decodeUnknownSync(Prompt)
20: const encodePrompt = Schema.encodeSync(Prompt)
21: const continuePrompt = ...
<path>/home/byk/Code/opencode/packages/core/src/session/projector.ts</path>
<type>file</type>
<content>
1: export * as SessionProjector from "./projector"
2:
3: import { and, desc, eq, gt, or, sql } from "drizzle-orm"
4: import { DateTime, Effect, Layer, Schema } from "effect"
5: import { Database } from "../database/database"
6: import { EventV2 } from "../event"
7: import { makeGlobalNode } from "../effect/app-node"
8: import { SessionEvent } from "./event"
9: import { SessionV1 } from "../v1/session"
10: import { WorkspaceTable } from "../control-plane/workspace.sql"
11: import { SessionMessage } from "./message"
12: import { SessionMessageUpdater } from "./message-updater"
13: import { SessionInput } from "./input"
14: import { Prompt } from "./prompt"
15: import { continuation } from "./recovery-id"
16: import { WorkspaceV2 } from "../workspace"
17: import {
18: MessageTable,
19: PartTable,
20: SessionInputTable,
21: SessionMessageTable,
22: SessionRecoveryTable,
23: ...
No files found
/home/byk/Code/opencode/packages/core/src/event.ts /home/byk/Code/opencode/packages/core/src/session/event.ts
Found 23 matches
/home/byk/Code/opencode/packages/core/src/event.ts:
Line 122: /** Local operational projection committed atomically with a new durable event. Not replayed or serialized. */
Line 123: readonly commit?: (seq: number) => Effect.Effect<void>
Line 205: function commitDurableEvent(
Line 214: commit?: (seq: number) => Effect.Effect<void>,
Line 239: const committed = yield* db
Line 316: const committed = {
Line 321: yield* projector(committed)
Line 323: if (commit) yield* commit(seq)
Line 354: if (committed) {
Line 356: pubsub.durable.get(committed.aggregateID) ?? [],
Line 361: return committed
Line 369: function publishEvent<D extends Definition>(definition: D, event: Payload<D>, commit?: PublishOptions["commit"]) {
Line 371: if (!definition?.durable ...
<path>/home/byk/Code/opencode/packages/core/src/event.ts</path>
<type>file</type>
<content>
195: yield* PubSub.shutdown(pubsub.all)
196: yield* Effect.forEach(
197: pubsub.durable.values(),
198: (pubsubs) => Effect.forEach(pubsubs, PubSub.shutdown, { discard: true }),
199: { discard: true },
200: )
201: yield* Effect.forEach(pubsub.typed.values(), PubSub.shutdown, { discard: true })
202: }),
203: )
204:
205: function commitDurableEvent(
206: definition: Definition,
207: event: Payload,
208: input?: {
209: readonly seq: number
210: readonly aggregateID: string
211: readonly ownerID?: string
212: readonly strictOwner?: boolean
213: },
214: commit?: (seq: number) => Effect.Effect<void>,
215: ) {
216: return Effect.gen(function* () {
217: const durable = definition?.durable
218: if ...
<path>/home/byk/Code/opencode/packages/core/src/session/message-updater.ts</path>
<type>file</type>
<content>
200: id: event.data.assistantMessageID,
201: type: "assistant",
202: agent: event.data.agent,
203: model: event.data.model,
204: time: { created: event.data.timestamp },
205: content: [],
206: snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined,
207: }),
208: )
209: })
210: },
211: "session.next.step.ended": (event) => {
212: return updateOwnedAssistant(event.data.assistantMessageID, (draft) => {
213: draft.time.completed = event.data.timestamp
214: draft.finish = event.data.finish
215: draft.cost = event.data.cost
216: draft.tokens = event.data.tokens
217: if (event.data.snapshot || event.data.files)
218: draft.snapshot = {
219: ...
<path>/home/byk/Code/opencode/packages/core/src/session/runner/llm.ts</path>
<type>file</type>
<content>
1: import {
2: LLM,
3: LLMClient,
4: LLMError,
5: LLMEvent,
6: Message,
7: SystemPart,
8: isContextOverflowFailure,
9: type ProviderErrorEvent,
10: } from "@opencode-ai/llm"
11: import { Cause, DateTime, Effect, Exit, Fiber, Layer, Option, Semaphore, Stream } from "effect"
12: import { and, eq } from "drizzle-orm"
13: import { AgentV2 } from "../../agent"
14: import { Config } from "../../config"
15: import { Database } from "../../database/database"
16: import { EventV2 } from "../../event"
17: import { Location } from "../../location"
18: import { ModelV2 } from "../../model"
19: import { PermissionV2 } from "../../permission"
20: import { ProviderV2 } from "../../provider"
21: import { QuestionV2 } from "../../question"
22: import { SystemContext } from "../../system-context/index"
23: import { SystemContextRegistry } from "../../system-context/registry"
24: ...
<path>/home/byk/Code/opencode/packages/core/src/session/runner/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 ?? ...
<path>/home/byk/Code/opencode/packages/core/src/session/context-epoch.ts</path>
<type>file</type>
<content>
1: export * as SessionContextEpoch from "./context-epoch"
2:
3: import { eq } from "drizzle-orm"
4: import { DateTime, Effect, Schema } from "effect"
5: import type { Database } from "../database/database"
6: import { EventV2 } from "../event"
7: import { SystemContext } from "../system-context/index"
8: import { ContextSnapshotDecodeError } from "./error"
9: import { SessionEvent } from "./event"
10: import { SessionHistory } from "./history"
11: import { SessionInput } from "./input"
12: import { SessionMessage } from "./message"
13: import { SessionSchema } from "./schema"
14: import { SessionContextEpochTable } from "./sql"
15: import { SessionExecutionClaim } from "./execution/claim"
16:
17: type DatabaseService = Database.Interface["db"]
18:
19: interface Prepared {
20: readonly baseline: string
21: readonly baselineSeq: number
22: }
23:
24: export function ...
<path>/home/byk/Code/opencode/packages/core/src/session/compaction.ts</path>
<type>file</type>
<content>
1: export * as SessionCompaction from "./compaction"
2:
3: import { LLM, LLMError, LLMEvent, Message, type LLMRequest, type Model } from "@opencode-ai/llm"
4: import { DateTime, Effect, Stream } from "effect"
5: import type { Config } from "../config"
6: import type { EventV2 } from "../event"
7: import { SessionEvent } from "./event"
8: import { SessionMessage } from "./message"
9: import { SessionSchema } from "./schema"
10: import { Token } from "../util/token"
11:
12: const DEFAULT_BUFFER = 20_000
13: const DEFAULT_KEEP_TOKENS = 8_000
14: const TOOL_OUTPUT_MAX_CHARS = 2_000
15: const SUMMARY_OUTPUT_TOKENS = 4_096
16: const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response. ...
<path>/home/byk/Code/opencode/packages/core/src/session.ts</path>
<type>file</type>
<content>
340: return stored?.sessionID === input.sessionID ? stored.message : undefined
341: }),
342: context: Effect.fn("V2Session.context")(function* (sessionID) {
343: yield* result.get(sessionID)
344: return yield* store.context(sessionID)
345: }),
346: events: (input) =>
347: Stream.unwrap(
348: result
349: .get(input.sessionID)
350: .pipe(Effect.as(events.durable({ aggregateID: input.sessionID, after: input.after }))),
351: ).pipe(Stream.filter((event): event is SessionEvent.DurableEvent => isDurableSessionEvent(event))),
352: history: Effect.fn("V2Session.history")(function* (input) {
353: yield* result.get(input.sessionID)
354: return yield* EventV2.readAggregate(db, {
355: ...input,
356: aggregateID: input.sessionID,
357: manifest: ...
<path>/home/byk/Code/opencode/packages/core/src/session/run-coordinator.ts</path>
<type>file</type>
<content>
1: export * as SessionRunCoordinator from "./run-coordinator"
2:
3: import { Deferred, Effect, Exit, Fiber, FiberSet, Scope } from "effect"
4:
5: /** Serializes execution for each key while allowing different keys to run concurrently. */
6: export interface Coordinator<Key, E> {
7: /** Snapshots keys with an execution owned by this coordinator. */
8: readonly active: Effect.Effect<ReadonlySet<Key>>
9: /** Starts execution while idle or joins the active execution. */
10: readonly run: (key: Key) => Effect.Effect<void, E>
11: /** Registers one coalesced follow-up after newly recorded work. */
12: readonly wake: (key: Key) => Effect.Effect<void>
13: /** Stops active execution, runs cleanup, then starts work that arrived while stopping. */
14: readonly interrupt: (key: Key, cleanup?: Effect.Effect<void>) => Effect.Effect<void>
15: }
16:
17: type Entry<E> = {
18: ...
<path>/home/byk/Code/opencode/packages/core/src/session/sql.ts</path>
<type>file</type>
<content>
1: import { sql } from "drizzle-orm"
2: import { check, sqliteTable, text, integer, index, primaryKey, real, uniqueIndex } from "drizzle-orm/sqlite-core"
3: import * as DatabasePath from "../database/path"
4: import { ProjectTable } from "../project/sql"
5: import type { SessionMessage } from "./message"
6: import type { Prompt } from "./prompt"
7: import type { SessionInput } from "./input"
8: import type { Snapshot } from "../snapshot"
9: import { PermissionV1 } from "../v1/permission"
10: import { ProjectV2 } from "../project"
11: import type { SessionSchema } from "./schema"
12: import type { MessageID, PartID, SessionV1 } from "../v1/session"
13: import { WorkspaceV2 } from "../workspace"
14: import { Timestamps } from "../database/schema.sql"
15: import type { SystemContext } from "../system-context/index"
16: import { AgentV2 } from "../agent"
17: import type { Revert } from ...
<path>/home/byk/Code/opencode/packages/core/src/database/migration/20260914170650_session-recovery.ts</path>
<type>file</type>
<content>
1: import { Effect } from "effect"
2: import type { DatabaseMigration } from "../migration"
3:
4: export default {
5: id: "20260914170650_session-recovery",
6: up(tx) {
7: return Effect.gen(function* () {
8: yield* tx.run(`ALTER TABLE \`session_input\` ADD \`wake_pending\` integer DEFAULT false NOT NULL;`)
9: yield* tx.run(
10: `ALTER TABLE \`session\` ADD \`wake_attempts\` integer DEFAULT 0 NOT NULL CONSTRAINT \`session_wake_attempts_check\` CHECK(\`wake_attempts\` >= 0 AND \`wake_attempts\` <= 9007199254740991);`,
11: )
12: yield* tx.run(`
13: CREATE TABLE \`session_execution\` (
14: \`id\` text NOT NULL,
15: \`session_id\` text NOT NULL,
16: \`owner_id\` text NOT NULL,
17: \`assistant_message_id\` text,
18: \`phase\` text NOT NULL,
19: ...
<path>/home/byk/Code/opencode/packages/core/src/database/migration.gen.ts</path>
<type>file</type>
<content>
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: import("./migration/20260413175956_chief_energizer"),
17: ...
<path>/home/byk/Code/opencode/packages/core/src/database/schema.gen.ts</path> <type>file</type> <content> 140: \`message_id\` text NOT NULL, 141: \`session_id\` text NOT NULL, 142: \`time_created\` integer NOT NULL, 143: \`time_updated\` integer NOT NULL, 144: \`data\` text NOT NULL, 145: CONSTRAINT \`fk_part_message_id_message_id_fk\` FOREIGN KEY (\`message_id\`) REFERENCES \`message\`(\`id\`) ON DELETE CASCADE 146: ); 147: `) 148: yield* tx.run(` 149: CREATE TABLE \`session_context_epoch\` ( 150: \`session_id\` text PRIMARY KEY, 151: \`baseline\` text NOT NULL, 152: \`snapshot\` text NOT NULL, 153: \`baseline_seq\` integer NOT NULL, 154: CONSTRAINT \`fk_session_context_epoch_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE 155: ); 156: `) 157: yield* tx.run(` 158: ...
<path>/home/byk/Code/opencode/packages/schema/src/session-event.ts</path>
<type>file</type>
<content>
1: export * as SessionEvent from "./session-event"
2:
3: import { Schema } from "effect"
4: import { optional } from "./schema"
5: import { Event } from "./event"
6: import { ProviderMetadata, ToolContent } from "./llm"
7: import { Delivery } from "./session-delivery"
8: import { Model } from "./model"
9: import { DateTimeUtcFromMillis, NonNegativeInt, RelativePath } from "./schema"
10: import { FileAttachment, Prompt } from "./prompt"
11: import { SessionID } from "./session-id"
12: import { Location } from "./location"
13: import { SessionMessage } from "./session-message"
14: import { Revert } from "./revert"
15:
16: export { FileAttachment }
17:
18: export const Source = Schema.Struct({
19: start: NonNegativeInt,
20: end: NonNegativeInt,
21: text: Schema.String,
22: }).annotate({
23: identifier: "session.next.event.source",
24: })
25: export interface Source extends ...
<path>/home/byk/Code/opencode/packages/core/src/session/runner/index.ts</path>
<type>file</type>
<content>
1: export * as SessionRunner from "./index"
2:
3: import type { LLMError } from "@opencode-ai/llm"
4: import { Context, Effect } from "effect"
5: import { SessionSchema } from "../schema"
6: import type { SessionMessage } from "../message"
7: import type { ContextSnapshotDecodeError, MessageDecodeError } from "../error"
8: import { SessionRunnerModel } from "./model"
9: import type { SystemContext } from "../../system-context/index"
10: import type { ToolOutputStore } from "../../tool-output-store"
11:
12: export type RunError =
13: | LLMError
14: | SessionRunnerModel.Error
15: | MessageDecodeError
16: | ContextSnapshotDecodeError
17: | SystemContext.InitializationBlocked
18: | ToolOutputStore.Error
19:
20: /** Runs one local continuation from already-recorded Session history. */
21: export interface Interface {
22: /** Drains eligible durable work. ...
<path>/home/byk/Code/opencode/packages/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>/home/byk/Code/opencode/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: import { Format } from ...
Found 3 matches
/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts:
Line 69: import { SessionRecovery } from "@opencode-ai/core/session/recovery"
Line 237: SessionProjector.node,
Line 300: AppNodeBuilderV1.build(LayerNode.group([SessionV2.node, SessionRecovery.node]), [
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts</path> <type>file</type> <content> 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: SessionCompaction.node, 245: SessionRevert.node, 246: SessionSummary.node, 247: SessionPrompt.node, 248: Instruction.node, 249: LLM.node, 250: LSP.node, 251: MCP.node, 252: ...
Found 77 matches
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
Line 26: import { Snapshot } from "@opencode-ai/core/snapshot"
Line 767: .set({ id: "provider-successor", owner_id: "successor", expires_at: 60_000 })
Line 793: .set({ id: "publication-successor", owner_id: "successor", expires_at: 60_000 })
Line 823: .set({ id: "tool-successor", owner_id: "successor", expires_at: 60_000 })
Line 852: yield* events.publish(SessionEvent.Step.Started, {
Line 885: .set({ id: "interrupted-tool-successor", owner_id: "successor" })
Line 1060: it.effect("fails gracefully when a stored context snapshot cannot be decoded", () =>
Line 1070: .set({ snapshot: { invalid: { value: "bad" } } })
Line 1516: .set({ id: "compaction-successor", owner_id: "successor", expires_at: 60_000 })
Line 1538: ).toEqual({ id: "compaction-successor", phase: "unknown" })
Line 2016: ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
300: expect(messages[0]?.type).toBe("synthetic")
301: }),
302: )
303:
304: it.effect("never recovers a live execution lease", () =>
305: Effect.gen(function* () {
306: const { db } = yield* Database.Service
307: yield* db
308: .insert(ProjectTable)
309: .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
310: .run()
311: yield* db
312: .insert(SessionTable)
313: .values({
314: id: sessionID,
315: project_id: Project.ID.global,
316: slug: "test",
317: directory: "/project",
318: title: "test",
319: version: "test",
320: })
321: .run()
322: yield* db
323: .insert(SessionExecutionTable)
324: .values({
325: id: "execution",
326: session_id: ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1370: yield* db
1371: .insert(ProjectTable)
1372: .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
1373: .run()
1374: yield* db
1375: .insert(SessionTable)
1376: .values(
1377: [sessionID, other].map((id) => ({
1378: id,
1379: project_id: Project.ID.global,
1380: slug: id,
1381: directory: "/project",
1382: title: "test",
1383: version: "test",
1384: })),
1385: )
1386: .run()
1387: yield* events.publish(SessionEvent.Step.Recovered, {
1388: sessionID: other,
1389: timestamp: created,
1390: executionID: "shared-recovery-identity",
1391: phase: "ready",
1392: })
1393:
1394: const recovered = yield* events
1395: ...
<path>/home/byk/Code/opencode/packages/core/test/session-execution-local.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect } from "bun:test"
2: import { Deferred, Effect, Exit, Fiber, Scope } from "effect"
3: import { TestClock } from "effect/testing"
4: import { eq, sql } from "drizzle-orm"
5: import { Database } from "@opencode-ai/core/database/database"
6: import { Project } from "@opencode-ai/core/project"
7: import { ProjectTable } from "@opencode-ai/core/project/sql"
8: import { AbsolutePath } from "@opencode-ai/core/schema"
9: import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
10: import { SessionSchema } from "@opencode-ai/core/session/schema"
11: import { SessionExecutionTable, SessionTable } from "@opencode-ai/core/session/sql"
12: import { testEffect } from "./lib/effect"
13:
14: const sessionID = SessionSchema.ID.make("ses_execution_local")
15: const it = testEffect(Database.layerFromPath(":memory:"))
16:
17: const ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
2750: text: "partial",
2751: })
2752: expect(
2753: yield* db
2754: .select({ id: EventTable.id })
2755: .from(EventTable)
2756: .where(eq(EventTable.type, SessionEvent.Compaction.Delta.type))
2757: .all()
2758: .pipe(Effect.orDie),
2759: ).toEqual([])
2760: expect(
2761: yield* db
2762: .select({ id: SessionMessageTable.id })
2763: .from(SessionMessageTable)
2764: .where(eq(SessionMessageTable.type, "compaction"))
2765: .all()
2766: .pipe(Effect.orDie),
2767: ).toEqual([])
2768: yield* events.publish(SessionEvent.Compaction.Ended, {
2769: sessionID,
2770: messageID: compactionID,
2771: timestamp: DateTime.makeUnsafe(1),
2772: reason: "manual",
2773: text: "summary",
2774: ...
Found 10 matches
/home/byk/Code/opencode/packages/core/test/session-projector.test.ts:
Line 37: import { Snapshot } from "@opencode-ai/core/snapshot"
Line 419: assistant_seq: -1,
Line 461: assistant_seq: -1,
Line 533: assistant_seq: 0,
Line 2269: .select({ snapshot: SessionContextEpochTable.snapshot })
Line 2305: .select({ snapshot: SessionContextEpochTable.snapshot })
Line 2362: .select({ snapshot: SessionContextEpochTable.snapshot })
Line 2396: .select({ snapshot: SessionContextEpochTable.snapshot })
Line 2547: revert: { messageID: boundary, snapshot: Snapshot.ID.make("tree"), diff: "patch", files: [] },
Line 2551: snapshot: "tree",
Found 29 matches
/home/byk/Code/opencode/packages/core/test/session-prompt.test.ts:
Line 140: it.effect("exposes the execution registry", () =>
Line 147: it.effect("delegates execution continuation through SessionExecution", () =>
Line 159: it.effect("delegates process-local interruption through SessionExecution", () =>
Line 171: it.effect("delegates interruption without requiring a recorded Session", () =>
Line 181: it.effect("durably admits one user message before transcript promotion", () =>
Line 203: it.effect("resolves attachment MIME before admission", () =>
Line 224: it.effect("streams durable Session events after an aggregate sequence", () =>
Line 254: it.effect("resumes through a recorded message without appending another prompt", () =>
Line 275: it.effect("records distinct messages when the ID is omitted", () =>
Line 290: it.effect("returns the original recorded message when the ID is retried", () =>
Line 310: ...
Found 45 matches
/home/byk/Code/opencode/packages/core/test/session-projector.test.ts:
Line 68: it.effect("atomically reconciles one abandoned execution into one continuation", () =>
Line 222: it.effect("records a recovery continuation without forging an assistant identity", () =>
Line 258: it.effect("recovers each abandoned execution once across repeated startup scans", () =>
Line 304: it.effect("never recovers a live execution lease", () =>
Line 342: it.effect("never recovers an execution superseded by a newer assistant", () =>
Line 504: it.effect("recovers assistant-less continuation at its current assistant boundary", () =>
Line 555: it.effect("redrives a durable recovery wake after admission", () =>
Line 606: it.effect("quarantines a malformed recovery row without blocking a valid wake", () =>
Line 660: it.effect("quarantines a full batch of poison recovery rows before redriving valid work", () =>
Line 725: it.effect("fairly ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
50: const created = DateTime.makeUnsafe(0)
51: const model = { id: ModelV2.ID.make("model"), providerID: ProviderV2.ID.make("provider") }
52: const encodeMessage = Schema.encodeSync(SessionMessage.Message)
53:
54: const assistantRow = (
55: id: SessionMessage.ID,
56: seq: number,
57: time: { created: DateTime.Utc; completed?: DateTime.Utc } = { created },
58: ) => {
59: const {
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* ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
600: wake_pending: true,
601: }),
602: ])
603: }),
604: )
605:
606: it.effect("quarantines a malformed recovery row without blocking a valid wake", () =>
607: Effect.gen(function* () {
608: const { db } = yield* Database.Service
609: const events = yield* EventV2.Service
610: const valid = SessionV2.ID.make("ses_recovery_row_valid")
611: yield* db
612: .insert(ProjectTable)
613: .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
614: .run()
615: yield* db
616: .insert(SessionTable)
617: .values(
618: [sessionID, valid].map((id) => ({
619: id,
620: project_id: Project.ID.global,
621: slug: id,
622: directory: "/project",
623: title: "test",
624: ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
680: LLMEvent.finish({ reason: "stop" }),
681: ]
682: yield* session.resume(sessionID)
683: expect(userTexts(requests[1]!)).toContain("ordinary work after replayed recovery")
684: requests.length = 0
685: }),
686: )
687:
688: it.effect("clears durable execution intent when ordinary input is promoted", () =>
689: Effect.gen(function* () {
690: yield* setup
691: const session = yield* SessionV2.Service
692: const message = yield* session.prompt({
693: sessionID,
694: prompt: Prompt.make({ text: "clear durable wake" }),
695: resume: false,
696: })
697: const { db } = yield* Database.Service
698: yield* db.update(SessionInputTable).set({ wake_pending: true }).where(eq(SessionInputTable.id, message.id)).run()
699: response = [
700: LLMEvent.stepStart({ index: 0 ...
<path>/home/byk/Code/opencode/packages/core/test/database-migration.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect, test } from "bun:test"
2: import { $ } from "bun"
3: import { fileURLToPath } from "url"
4: import path from "path"
5: import { SqliteClient } from "@effect/sql-sqlite-bun"
6: import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
7: import { Effect, Exit, Layer } from "effect"
8: import { eq, inArray, sql } from "drizzle-orm"
9: import { DatabaseMigration } from "@opencode-ai/core/database/migration"
10: import { migrations } from "@opencode-ai/core/database/migration.gen"
11: import workspaceNameMigration from "@opencode-ai/core/database/migration/20260410174513_workspace-name"
12: import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510033149_session_usage"
13: import normalizeStoragePathsMigration from "@opencode-ai/core/database/migration/20260601010001_normalize_storage_paths"
14: import ...
<path>/home/byk/Code/opencode/packages/core/test/database-migration.test.ts</path>
<type>file</type>
<content>
166: test("adds final recovery ownership tables to a populated predecessor", async () => {
167: await run(
168: Effect.gen(function* () {
169: const db = yield* makeDb
170: yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY)`)
171: yield* db.run(sql`CREATE TABLE session_message (id text PRIMARY KEY)`)
172: yield* db.run(
173: sql`CREATE TABLE session_input (id text PRIMARY KEY, session_id text NOT NULL, promoted_seq integer, time_created integer NOT NULL)`,
174: )
175: yield* db.run(sql`INSERT INTO session (id) VALUES ('ses_existing')`)
176: yield* db.run(sql`INSERT INTO session_message (id) VALUES ('msg_existing')`)
177: yield* db.run(sql`INSERT INTO session_input (id, session_id, time_created) VALUES ('msg_input', 'ses_existing', 0)`)
178:
179: yield* ...
Found 100 matches (more matches available)
/home/byk/Code/opencode/packages/sdk/openapi.json:
Line 17899: "requestExecution": {
Line 18110: "assistantMessageID": {
Line 18124: "required": ["timestamp", "sessionID", "assistantMessageID", "agent", "model"],
Line 18152: "assistantMessageID": {
Line 18201: "required": ["timestamp", "sessionID", "assistantMessageID", "finish", "cost", "tokens"],
Line 18229: "assistantMessageID": {
Line 18237: "required": ["timestamp", "sessionID", "assistantMessageID", "error"],
Line 18265: "assistantMessageID": {
Line 18273: "required": ["timestamp", "sessionID", "assistantMessageID", "error"],
Line 18289: "enum": ["session.next.step.recovered"]
Line 18301: "executionID": {
Line 18304: ...
Found 19 matches
/home/byk/Code/opencode/packages/sdk/js/src/v2/gen/types.gen.ts:
Line 33: | EventSessionNextStepRecovered
Line 874: requestExecution?: boolean
Line 983: type: "session.next.step.recovered"
Line 987: executionID: string
Line 1659: | SyncEventSessionNextStepRecovered
Line 2794: | SessionNextStepRecovered
Line 2924: | SessionNextStepRecovered
Line 3430: requestExecution?: boolean
Line 3602: export type SyncEventSessionNextStepRecovered = {
Line 3606: type: "session.next.step.recovered.1"
Line 3613: executionID: string
Line 4362: requestExecution?: boolean
Line 4560: export type SessionNextStepRecovered = {
Line 4565: type: "session.next.step.recovered"
Line 4575: executionID: string
Line 6464: requestExecution?: boolean
Line 6581: export type EventSessionNextStepRecovered = {
Line 6583: type: "session.next.step.recovered"
Line 6587: ...
<path>/home/byk/Code/opencode/packages/schema/test/event-manifest.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect, test } from "bun:test"
2: import { FileSystem, Integration, Permission, Project, Reference, Session, Workspace } from "../src"
3: import { EventManifest } from "../src/event-manifest"
4: import { IdeEvent } from "../src/ide-event"
5: import { SessionEvent } from "../src/session-event"
6: import { SessionTodo } from "../src/session-todo"
7: import { SessionV1 } from "../src/session-v1"
8: import { WorkspaceEvent } from "../src/workspace-event"
9:
10: describe("public event manifest", () => {
11: test("owns the complete public event surface", () => {
12: expect(EventManifest.ServerDefinitions.length).toBe(61)
13: expect(EventManifest.Definitions.length).toBe(91)
14: expect(SessionV1.Event.Definitions).toEqual([
15: SessionV1.Event.Created,
16: SessionV1.Event.Updated,
17: SessionV1.Event.Deleted,
18: ...
<path>/tmp/opencode/session-recovery-final-v13.patch</path>
<type>file</type>
<content>
7160: }),
7161: )
7162: diff --git a/packages/core/test/session-run-coordinator.test.ts b/packages/core/test/session-run-coordinator.test.ts
7163: index dfbeda664c..63dfad7a71 100644
7164: --- a/packages/core/test/session-run-coordinator.test.ts
7165: +++ b/packages/core/test/session-run-coordinator.test.ts
7166: @@ -282,6 +282,51 @@ describe("SessionRunCoordinator", () => {
7167: ),
7168: )
7169:
7170: + it.effect("finalizes interruption when cleanup dies", () =>
7171: + Effect.scoped(
7172: + Effect.gen(function* () {
7173: + const firstStarted = yield* Deferred.make<void>()
7174: + const cleanupStarted = yield* Deferred.make<void>()
7175: + const cleanupGate = yield* Deferred.make<void>()
7176: + const secondStarted = yield* Deferred.make<void>()
7177: + const defect = new Error("cleanup defect")
7178: + let runs = 0
7179: + ...
<path>/home/byk/Code/opencode/packages/core/test/session-run-coordinator.test.ts</path>
<type>file</type>
<content>
260: ? Deferred.succeed(firstStarted, undefined).pipe(
261: Effect.andThen(Effect.never),
262: Effect.onInterrupt(() =>
263: Deferred.succeed(cleanupStarted, undefined).pipe(Effect.andThen(Deferred.await(cleanupGate))),
264: ),
265: )
266: : Deferred.succeed(secondStarted, undefined),
267: ),
268: ),
269: })
270:
271: yield* coordinator.wake("session")
272: yield* Deferred.await(firstStarted)
273: const interrupt = yield* coordinator.interrupt("session").pipe(Effect.forkChild)
274: yield* Deferred.await(cleanupStarted)
275: yield* coordinator.wake("session")
276: yield* Deferred.succeed(cleanupGate, undefined)
277: yield* ...
<path>/home/byk/Code/opencode/packages/core/src/session/projector.ts</path>
<type>file</type>
<content>
500: yield* events.project(SessionEvent.RevertEvent.Committed, (event) =>
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: ...
Found 5 matches
/home/byk/Code/opencode/packages/core/test/session-projector.test.ts:
Line 1862: it.effect("never quarantines cancellation after recovery ownership expires", () =>
Line 1914: it.effect("never quarantines projection failure after recovery ownership expires", () =>
Line 2199: owner_id: "successor",
Line 2296: .set({ id: "successor-advance", owner_id: "successor-owner", expires_at: 60_000 })
Line 2469: owner_id: "successor-owner",
<path>/home/byk/Code/opencode/packages/core/src/session/message-updater.ts</path>
<type>file</type>
<content>
1: import { castDraft, produce, type WritableDraft } from "immer"
2: import { Effect } from "effect"
3: import { SessionEvent } from "./event"
4: import { SessionMessage } from "./message"
5: import { message } from "./recovery-id"
6:
7: export type MemoryState = {
8: messages: SessionMessage.Message[]
9: }
10:
11: export interface Adapter {
12: readonly getCurrentAssistant: () => Effect.Effect<SessionMessage.Assistant | undefined>
13: readonly getAssistant: (messageID: SessionMessage.ID) => Effect.Effect<SessionMessage.Assistant | undefined>
14: readonly getCurrentShell: (callID: string) => Effect.Effect<SessionMessage.Shell | undefined>
15: readonly updateAssistant: (assistant: SessionMessage.Assistant) => Effect.Effect<void>
16: readonly updateShell: (shell: SessionMessage.Shell) => Effect.Effect<void>
17: readonly appendMessage: (message: ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0abe4bc6f001ARGqKQzk1YnJJf
+ stored?.sessionID === claim.session_id &&
+ stored.assistantMessageID === (assistantMessageID ?? null) &&
+ stored.continuationMessageID === continuation(claim.id) &&
+ stored.phase === phase.value
+ )
+ return Effect.void
-+ return db
-+ .update(SessionExecutionTable)
-+ .set({ recovery_error: "Recovery projection failed" })
-+ .where(
-+ and(
-+ eq(SessionExecutionTable.id, recoveryClaim.executionID),
-+ eq(SessionExecutionTable.session_id, recoveryClaim.sessionID),
-+ eq(SessionExecutionTable.owner_id, recoveryClaim.ownerID),
-+ ...
<path>/tmp/opencode/session-recovery-final-v13.patch</path>
<type>file</type>
<content>
6835: diff --git a/packages/core/test/session-prompt.test.ts b/packages/core/test/session-prompt.test.ts
6836: index c6bc9430b3..a0d6f26467 100644
6837: --- a/packages/core/test/session-prompt.test.ts
6838: +++ b/packages/core/test/session-prompt.test.ts
6839: @@ -1,6 +1,6 @@
6840: import { describe, expect } from "bun:test"
6841: import { DateTime, Effect, Fiber, Layer, Stream } from "effect"
6842: -import { eq } from "drizzle-orm"
6843: +import { asc, eq } from "drizzle-orm"
6844: import { Database } from "@opencode-ai/core/database/database"
6845: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
6846: import { LayerNode } from "@opencode-ai/core/effect/layer-node"
6847: @@ -16,6 +16,7 @@ import { SessionMessage } from "@opencode-ai/core/session/message"
6848: import { SessionProjector } from "@opencode-ai/core/session/projector"
6849: import { SessionExecution } ...
Found 96 matches
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
Line 619: it.effect("redrives only the synthetic recovery continuation", () =>
Line 688: it.effect("clears durable execution intent when ordinary input is promoted", () =>
Line 715: it.effect("redrives an ordinary prompt after its advisory wake is lost", () =>
Line 752: it.effect("never dispatches a provider after ownership changes", () =>
Line 778: it.effect("never publishes streamed output after ownership changes", () =>
Line 805: it.effect("never starts a local tool after ownership changes", () =>
Line 845: it.effect("never fails interrupted tools after ownership changes", () =>
Line 902: it.effect("advertises and executes a globally attached application tool", () =>
Line 958: it.effect("starts a real runner turn after default prompt recording", () =>
Line 977: it.effect("streams one request with registry definitions from chronological V2 user ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
560: : fixture.expectedContent
561: expect(yield* session.context(sessionID)).toMatchObject([
562: { type: "user", text: prompt },
563: {
564: type: "assistant",
565: finish: "error",
566: error: { type: "unknown", message: "Provider unavailable" },
567: content: [expectedContent],
568: },
569: ])
570: })
571:
572: const verifyPartialFlushOnInterruption = (kind: FragmentKind) =>
573: Effect.gen(function* () {
574: yield* setup
575: const session = yield* SessionV2.Service
576: const prompt = `Interrupt after ${kind}`
577: const fixture = fragmentFixture(kind, fragmentID(kind, "interrupted"), ["Partial"])
578: const streamed = yield* Deferred.make<void>()
579: yield* session.prompt({ sessionID, prompt: Prompt.make({ text: prompt }), resume: false })
580: responseStream = ...
verified_blob_pairs=33 mismatches=0
Found 7 matches
/home/byk/Code/opencode/packages/core/src/tool/registry.ts:
Line 31: readonly settle: (input: ExecuteInput) => Effect.Effect<Settlement, ToolOutputStore.Error>
Line 50: const settleWith = Effect.fn("ToolRegistry.settle")(function* (input: ExecuteInput, advertised?: object) {
Line 62: const pending = yield* settle(registration.tool, input.call, {
Line 116: settle: (input) => {
/home/byk/Code/opencode/packages/core/src/tool/tool.ts:
Line 66: readonly settle: (call: ToolCall, context: Context) => Effect.Effect<ToolOutput, ToolFailure>
Line 91: settle: (call, context) =>
Line 150: export const settle = (tool: AnyTool, call: ToolCall, context: Context) => runtimeOf(tool).settle(call, context)
<path>/home/byk/Code/opencode/packages/core/src/tool/registry.ts</path>
<type>file</type>
<content>
1: export * as ToolRegistry from "./registry"
2:
3: import { ToolOutput, type ToolCall, type ToolDefinition, type ToolResultValue } from "@opencode-ai/llm"
4: import { Context, Effect, Layer, Scope } from "effect"
5: import { AgentV2 } from "../agent"
6: import { PermissionV2 } from "../permission"
7: import { SessionMessage } from "../session/message"
8: import { SessionSchema } from "../session/schema"
9: import { ToolOutputStore } from "../tool-output-store"
10: import { Wildcard } from "../util/wildcard"
11: import { ApplicationTools } from "./application-tools"
12: import { definition, permission, settle, validateName, type AnyTool, type RegistrationError } from "./tool"
13: import { Tools } from "./tools"
14: import { makeLocationNode } from "../effect/app-node"
15:
16: export type ExecuteInput = {
17: readonly sessionID: SessionSchema.ID
18: readonly agent: AgentV2.ID
19: ...
<path>/home/byk/Code/opencode/packages/core/src/tool/tool.ts</path>
<type>file</type>
<content>
50: readonly input: Schema.Schema.Type<Input>
51: readonly output: Output["Encoded"]
52: }) => Schema.Schema.Type<Structured>
53: readonly execute: (
54: input: Schema.Schema.Type<Input>,
55: context: Context,
56: ) => Effect.Effect<Schema.Schema.Type<Output>, ToolFailure>
57: readonly toModelOutput?: (input: {
58: readonly input: Schema.Schema.Type<Input>
59: readonly output: Output["Encoded"]
60: }) => ReadonlyArray<Content>
61: }
62:
63: type Runtime = {
64: readonly permission?: string
65: readonly definition: (name: string) => ToolDefinition
66: readonly settle: (call: ToolCall, context: Context) => Effect.Effect<ToolOutput, ToolFailure>
67: }
68:
69: const runtimes = new WeakMap<AnyTool, Runtime>()
70:
71: export function make<
72: Input extends SchemaType<any>,
73: Output extends SchemaType<any>,
74: Structured extends SchemaType<any> ...
Found 66 matches
/home/byk/Code/opencode/packages/core/src/tool/write.ts:
Line 14: import { PermissionV2 } from "../permission"
Line 52: const permission = yield* PermissionV2.Service
Line 56: [name]: Tool.withPermission(
Line 73: yield* permission.assert({
Line 74: ...LocationMutation.externalDirectoryPermission(external),
Line 79: yield* permission.assert({
Line 100: deps: [ToolRegistry.node, LocationMutation.node, FileMutation.node, PermissionV2.node],
/home/byk/Code/opencode/packages/core/src/tool/glob.ts:
Line 11: import { PermissionV2 } from "../permission"
Line 43: const permission = yield* PermissionV2.Service
Line 62: yield* permission.assert({
Line 104: deps: [ToolRegistry.node, Ripgrep.node, Location.node, PermissionV2.node],
/home/byk/Code/opencode/packages/core/src/tool/edit.ts:
Line 17: import { PermissionV2 } from "../permission"
Line 96: ...
<path>/home/byk/Code/opencode/packages/core/src/tool/bash.ts</path>
<type>file</type>
<content>
100: const mutation = yield* LocationMutation.Service
101: const fs = yield* FSUtil.Service
102: const appProcess = yield* AppProcess.Service
103: const config = yield* Config.Service
104: const permission = yield* PermissionV2.Service
105:
106: yield* tools
107: .register({
108: [name]: Tool.make({
109: description: `Execute one shell command string with the host user's filesystem, process, and network authority. The active Location is the default working directory. Relative workdir values resolve from that Location. External workdir values require external_directory approval; best-effort command-argument path warnings are advisory only. Timeout values are milliseconds (default: ${DEFAULT_TIMEOUT_MS}; maximum: ${MAX_TIMEOUT_MS}). Uses the configured shell when set; otherwise uses /bin/sh on POSIX and COMSPEC or cmd.exe on Windows.`,
110: ...
<path>/home/byk/Code/opencode/packages/core/src/session.ts</path> <type>file</type> <content> 280: order === "asc" 281: ? or( 282: gt(sortColumn, input.anchor.time), 283: and(eq(sortColumn, input.anchor.time), gt(SessionTable.id, input.anchor.id)), 284: )! 285: : or( 286: lt(sortColumn, input.anchor.time), 287: and(eq(sortColumn, input.anchor.time), lt(SessionTable.id, input.anchor.id)), 288: )!, 289: ) 290: } 291: const query = db 292: .select() 293: .from(SessionTable) 294: .where(conditions.length > 0 ? and(...conditions) : undefined) 295: .orderBy( 296: order === "asc" ? asc(sortColumn) : desc(sortColumn), 297: order === "asc" ? asc(SessionTable.id) : desc(SessionTable.id), 298: ) 299: const rows = yield* (input.limit === undefined ? ...
<path>/home/byk/Code/opencode/packages/core/test/session-prompt.test.ts</path>
<type>file</type>
<content>
630: const events = yield* EventV2.Service
631: const first = yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Before cutoff" }), resume: false })
632: const cutoff = first.admittedSeq
633: const second = yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "After cutoff" }), resume: false })
634:
635: yield* SessionInput.promoteSteers(db, events, sessionID, cutoff)
636:
637: expect(yield* admitted(first.id)).toHaveProperty("promotedSeq")
638: expect(yield* admitted(second.id)).not.toHaveProperty("promotedSeq")
639: }),
640: )
641:
642: it.effect("reprojects pending inbox input without scheduling execution", () =>
643: Effect.gen(function* () {
644: yield* setup
645: const { db } = yield* Database.Service
646: const session = yield* SessionV2.Service
647: wakeCalls.length = ...
Found 57 matches /home/byk/Code/opencode/packages/core/schema.json: Line 66: "name": "session_execution", Line 78: "name": "session_recovery", Line 941: "table": "session_execution" Line 951: "table": "session_execution" Line 961: "table": "session_execution" Line 971: "table": "session_execution" Line 981: "table": "session_execution" Line 989: "name": "assistant_seq", 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": ...
No files found
Found 42 matches
/home/byk/Code/opencode/packages/core/src/database/schema.gen.ts:
Line 206: \`wake_attempts\` integer DEFAULT 0 NOT NULL,
Line 213: CONSTRAINT "session_recovery_wake_attempts_check" CHECK("wake_attempts" >= 0 AND "wake_attempts" <= 9007199254740991)
Line 247: \`wake_attempts\` integer DEFAULT 0 NOT NULL,
Line 249: CONSTRAINT "session_wake_attempts_check" CHECK("wake_attempts" >= 0 AND "wake_attempts" <= 9007199254740991)
Line 314: `CREATE INDEX \`session_recovery_wake_pending_error_attempts_created_execution_idx\` ON \`session_recovery\` (\`wake_pending\`,\`recovery_error\`,\`wake_attempts\`,\`time_created\`,\`execution_id\`);`,
/home/byk/Code/opencode/packages/core/src/database/migration/20260914170650_session-recovery.ts:
Line 10: `ALTER TABLE \`session\` ADD \`wake_attempts\` integer DEFAULT 0 NOT NULL CONSTRAINT \`session_wake_attempts_check\` CHECK(\`wake_attempts\` >= 0 AND ...
No files found
ab9408c81cc9916177641ae8e9f08624fc27b5b9 exists as a commit.3e9b20cb5ec05d1065b6bc78bab00b47e8ca9296bc819689a4f3d889eddcb5bf.c3b8c5d78e473ad4104a5b54617900e28921a283029b8484be7bce4bc3cee85a.session.wake_attempts belongs to the Session and only increases:
packages/core/src/session/sql.ts:63packages/core/src/session/recovery.ts:395-445packages/core/src/session/recovery.ts:449-480Prompt promotion clears the input’s wake state but never resets the Session counter:
packages/core/src/session/input.ts:260-301The scheduler reserves only one ordinary retry slot per scan at packages/core/src/session/recovery.ts:395-410, then ranks retries by their persistent attempt count. This leaks retry debt across independent work episodes.
For example:
wake_attempts = 60000.The MAX_WAKE_ATTEMPTS rotation at packages/core/src/session/recovery.ts:403-405 does not help in practice because the maximum is Number.MAX_SAFE_INTEGER.
Existing fairness coverage at packages/core/test/session-projector.test.ts:725-857 starts each work set with comparable counters. It does not cover a new work episode on a Session carrying historical retry debt. ...