Dashboard › opencode › Session 1aKYiw8BMTQ5
1aKYiw8BMTQ57yUl0Read-only adversarial correctness review frozen v14. Base ab9408c81cc9916177641ae8e9f08624fc27b5b9. Patch /tmp/opencode/session-recovery-final-v14.patch SHA-256 5efd9d74f450e9f875790a07c860248cb526f7144a822cf6df51761237f9789b. SEA /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server SHA-256 3c26981f57d37d15549b76657c20a090355911e80ea94d43f957422dc3276796. Verify hashes/base/clean apply/33 paths, inspect all. No mutations/tests/builds/services. v13 closures: promoteExact RecoveryConflict quarantine now runs immediate transaction, first invokes supplied exact live-claim guard, then quarantines exactly one row, raises conflict after commit; transfer regression proves stale owner cannot write. Startup cancellation/projection quarantine have separate expired-lease and live-owner-transfer regressions. Ordinary Session wake_attempts now resets only when projectPrompted freshly changes unpromoted input; duplicate replay leaves later debt unchanged; test starts at 60000 then replay at 7. Assistant seq transaction closure retained. Reassess complete lifecycle, fairness, replay, identity, tools, migration/dependencies. Accepted external-call/process-local/skew residuals. Evidence recovery 245/0/720; Core exact tree 1180 pass and 2 unrelated failures: location isolation timeout alone >5 sec and WebFetch conversion fixture expected error but got text content; five typechecks, manifest, migration/diff, v14 smoke pass. Return exact line PASS/CONCERN/MUST-FIX and end exactly MERGE or DO-NOT-MERGE. Empty invalid; no recall.
5efd9d74f450e9f875790a07c860248cb526f7144a822cf6df51761237f9789b /tmp/opencode/session-recovery-final-v14.patch
3c26981f57d37d15549b76657c20a090355911e80ea94d43f957422dc3276796 /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server
commit
ab9408c81cc9916177641ae8e9f08624fc27b5b9
Checking patch packages/core/schema.json...
error: while searching for:
{
"version": "7",
"dialect": "sqlite",
"id": "f14a9b18-8207-487e-a3d3-227e629ba9ad",
"prevIds": ["169a0f0f-d58f-479f-b024-fa1c7b9a09db"],
"ddl": [
{
"name": "workspace",
error: patch failed: packages/core/schema.json:1
error: packages/core/schema.json: patch does not apply
Checking patch packages/core/src/database/migration.gen.ts...
error: while searching for:
import("./migration/20260622142730_simplify_session_context_epoch"),
import("./migration/20260622170816_reset_v2_session_state"),
import("./migration/20260622202450_simplify_session_input"),
])
).map((module) => module.default) ...
paths 33
540 47 packages/core/schema.json
1 0 packages/core/src/database/migration.gen.ts
59 0 packages/core/src/database/migration/20260914170650_session-recovery.ts
49 1 packages/core/src/database/schema.gen.ts
5 4 packages/core/src/session.ts
31 16 packages/core/src/session/compaction.ts
54 34 packages/core/src/session/context-epoch.ts
158 19 packages/core/src/session/execution/local.ts
364 31 packages/core/src/session/input.ts
69 0 packages/core/src/session/message-updater.ts
88 1 packages/core/src/session/projector.ts
504 0 packages/core/src/session/recovery.ts
30 5 packages/core/src/session/run-coordinator.ts
4 0 packages/core/src/session/runner/index.ts
273 104 packages/core/src/session/runner/llm.ts
89 37 packages/core/src/session/runner/publish-llm-event.ts
79 1 packages/core/src/session/sql.ts
66 1 packages/core/test/database-migration.test.ts
315 0 ...
OK packages/core/schema.json (25 hunks) OK packages/core/src/database/migration.gen.ts (1 hunks) OK packages/core/src/database/migration/20260914170650_session-recovery.ts (1 hunks) OK packages/core/src/database/schema.gen.ts (7 hunks) OK packages/core/src/session.ts (2 hunks) OK packages/core/src/session/compaction.ts (4 hunks) OK packages/core/src/session/context-epoch.ts (10 hunks) OK packages/core/src/session/execution/local.ts (1 hunks) OK packages/core/src/session/input.ts (16 hunks) OK packages/core/src/session/message-updater.ts (3 hunks) OK packages/core/src/session/projector.ts (3 hunks) OK packages/core/src/session/recovery.ts (1 hunks) OK packages/core/src/session/run-coordinator.ts (3 hunks) OK packages/core/src/session/runner/index.ts (2 hunks) OK packages/core/src/session/runner/llm.ts (15 hunks) OK packages/core/src/session/runner/publish-llm-event.ts (25 hunks) OK packages/core/src/session/sql.ts (5 hunks) OK packages/core/test/database-migration.test.ts (3 hunks) OK ...
candidate files matching reconstructed patch: 33 / 33
<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/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, SessionTable } from "./sql"
14:
15: type DatabaseService = Database.Interface["db"]
16:
17: export { Admitted, Delivery }
18:
19: const decodePrompt = Schema.decodeUnknownSync(Prompt)
20: const encodePrompt = Schema.encodeSync(Prompt)
21: const ...
<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/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/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/llm.ts</path>
<type>file</type>
<content>
501: return yield* Effect.die("Post-compaction provider attempt cannot recover another overflow")
502: yield* Effect.yieldNow
503: return yield* runAfterOverflowCompaction(
504: sessionID,
505: executionID,
506: undefined,
507: defect.transition.step,
508: ownerID,
509: )
510: }),
511: ),
512: )
513: },
514: )
515:
516: const runTurn: RunTurn = Effect.fnUntraced(function* (sessionID, executionID, promotion, step, ownerID) {
517: return yield* runTurnAttempt(
518: sessionID,
519: executionID,
520: promotion,
521: step,
522: ownerID,
523: compaction.compactAfterOverflow,
524: ).pipe(
525: Effect.catchDefect(
526: ...
<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/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: ...
Found 20 matches
/home/byk/Code/opencode/packages/core/src/database/schema.gen.ts:
Line 158: CREATE TABLE \`session_execution\` (
Line 168: CONSTRAINT \`session_execution_pk\` PRIMARY KEY(\`id\`),
Line 169: CONSTRAINT \`fk_session_execution_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE,
Line 170: CONSTRAINT "session_execution_phase_check" CHECK("phase" IN ('ready', 'safe', 'unknown', 'continue', 'cancelling'))
Line 181: \`wake_pending\` integer DEFAULT false NOT NULL,
Line 199: CREATE TABLE \`session_recovery\` (
Line 205: \`wake_pending\` integer DEFAULT true NOT NULL,
Line 206: \`wake_attempts\` integer DEFAULT 0 NOT NULL,
Line 209: CONSTRAINT \`session_recovery_pk\` PRIMARY KEY(\`execution_id\`),
Line 210: CONSTRAINT \`fk_session_recovery_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) ...
<path>/home/byk/Code/opencode/packages/core/src/database/schema.gen.ts</path> <type>file</type> <content> 145: CONSTRAINT \`fk_part_message_id_message_id_fk\` FOREIGN KEY (\`message_id\`) REFERENCES \`message\`(\`id\`) ON DELETE CASCADE 146: ); 147: `) 148: yield* tx.run(` 149: CREATE TABLE \`session_context_epoch\` ( 150: \`session_id\` text PRIMARY KEY, 151: \`baseline\` text NOT NULL, 152: \`snapshot\` text NOT NULL, 153: \`baseline_seq\` integer NOT NULL, 154: CONSTRAINT \`fk_session_context_epoch_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE CASCADE 155: ); 156: `) 157: yield* tx.run(` 158: CREATE TABLE \`session_execution\` ( 159: \`id\` text NOT NULL, 160: \`session_id\` text NOT NULL, 161: \`owner_id\` text NOT NULL, 162: \`assistant_message_id\` text, 163: ...
<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: ...
<path>/home/byk/Code/opencode/packages/core/src/session/projector.ts</path>
<type>file</type>
<content>
501: Effect.gen(function* () {
502: const boundary = yield* db
503: .select({ seq: SessionMessageTable.seq })
504: .from(SessionMessageTable)
505: .where(
506: and(
507: eq(SessionMessageTable.session_id, event.data.sessionID),
508: eq(SessionMessageTable.id, event.data.messageID),
509: ),
510: )
511: .get()
512: .pipe(Effect.orDie)
513: if (!boundary) return yield* Effect.die(`Revert boundary message not found: ${event.data.messageID}`)
514: yield* db
515: .delete(SessionMessageTable)
516: .where(
517: and(eq(SessionMessageTable.session_id, event.data.sessionID), gt(SessionMessageTable.seq, boundary.seq)),
518: )
519: .run()
520: .pipe(Effect.orDie)
521: yield* db
522: ...
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>
190: return created
191: })
192:
193: yield* Effect.addFinalizer(() =>
194: Effect.gen(function* () {
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) => ...
<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: ...
<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/schema/src/session-event.ts</path> <type>file</type> <content> 500: Text.Started, 501: Text.Ended, 502: Tool.Input.Started, 503: Tool.Input.Ended, 504: Tool.Called, 505: Tool.Progress, 506: Tool.Success, 507: Tool.Failed, 508: Reasoning.Started, 509: Reasoning.Ended, 510: Retried, 511: Compaction.Started, 512: Compaction.Ended, 513: RevertEvent.Staged, 514: RevertEvent.Cleared, 515: RevertEvent.Committed, 516: ) 517: 518: export const Definitions = Event.inventory( 519: AgentSwitched, 520: ModelSwitched, 521: Moved, 522: Prompted, 523: PromptAdmitted, 524: PromptExecutionRequested, 525: ContextUpdated, 526: Synthetic, 527: Shell.Started, 528: Shell.Ended, 529: Step.Started, 530: Step.Ended, 531: Step.Failed, 532: Step.Interrupted, 533: Step.Recovered, 534: Text.Started, 535: Text.Delta, 536: Text.Ended, 537: Reasoning.Started, 538: Reasoning.Delta, 539: ...
<path>/home/byk/Code/opencode/packages/schema/src/session-event.ts</path>
<type>file</type>
<content>
470: export namespace RevertEvent {
471: export const Staged = Event.define({
472: type: "session.next.revert.staged",
473: ...options,
474: schema: { ...Base, revert: Revert.State },
475: })
476: export const Cleared = Event.define({ type: "session.next.revert.cleared", ...options, schema: Base })
477: export const Committed = Event.define({
478: type: "session.next.revert.committed",
479: ...options,
480: schema: { ...Base, messageID: SessionMessage.ID },
481: })
482: }
483:
484: export const DurableDefinitions = Event.inventory(
485: AgentSwitched,
486: ModelSwitched,
487: Moved,
488: Prompted,
489: PromptAdmitted,
490: PromptExecutionRequested,
491: ContextUpdated,
492: Synthetic,
493: Shell.Started,
494: Shell.Ended,
495: Step.Started,
496: Step.Ended,
497: Step.Failed,
498: Step.Interrupted,
499: ...
<path>/home/byk/Code/opencode/packages/core/src/session.ts</path> <type>file</type> <content> 320: const boundary = anchor 321: ? order === "asc" 322: ? gt(SessionMessageTable.seq, anchor.seq) 323: : lt(SessionMessageTable.seq, anchor.seq) 324: : undefined 325: const where = boundary 326: ? and(eq(SessionMessageTable.session_id, input.sessionID), boundary) 327: : eq(SessionMessageTable.session_id, input.sessionID) 328: const query = db 329: .select() 330: .from(SessionMessageTable) 331: .where(where) 332: .orderBy(order === "asc" ? asc(SessionMessageTable.seq) : desc(SessionMessageTable.seq)) 333: const rows = yield* (input.limit === undefined ? query.all() : query.limit(input.limit).all()).pipe( 334: Effect.orDie, 335: ) 336: return yield* Effect.forEach(direction === "previous" ? rows.toReversed() : rows, decode) 337: ...
<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>
50: import { Worktree } from "@/worktree"
51: import { RuntimeFlags } from "@/effect/runtime-flags"
52: import { MoveSession } from "@opencode-ai/core/control-plane/move-session"
53: import { Database } from "@opencode-ai/core/database/database"
54: import { AppNodeBuilderV1 } from "@/effect/app-node-builder-v1"
55: import { LayerNode } from "@opencode-ai/core/effect/layer-node"
56: import { httpClient } from "@opencode-ai/core/effect/app-node-platform"
57: import { EventV2 } from "@opencode-ai/core/event"
58: import { ModelsDev } from "@opencode-ai/core/models-dev"
59: import { Npm } from "@opencode-ai/core/npm"
60: import { PermissionSaved } from "@opencode-ai/core/permission/saved"
61: import { ProjectV2 } from "@opencode-ai/core/project"
62: import { ProjectCopy } from "@opencode-ai/core/project/copy"
63: import { PtyTicket } from ...
Found 7 matches
/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts:
Line 54: import { AppNodeBuilderV1 } from "@/effect/app-node-builder-v1"
Line 68: import * as SessionExecutionLocal from "@opencode-ai/core/session/execution/local"
Line 69: import { SessionRecovery } from "@opencode-ai/core/session/recovery"
Line 292: AppNodeBuilderV1.build(MoveSession.node, [[LocationServiceMap.node, locationServiceMapV2]]),
Line 300: AppNodeBuilderV1.build(LayerNode.group([SessionV2.node, SessionRecovery.node]), [
Line 302: [SessionExecution.node, SessionExecutionLocal.node],
Line 307: Layer.provide(AppNodeBuilderV1.build(app)),
<path>/home/byk/Code/opencode/packages/opencode/src/server/routes/instance/httpapi/server.ts</path>
<type>file</type>
<content>
270: ])
271:
272: export function createRoutes(
273: corsOptions?: CorsOptions,
274: ): Layer.Layer<never, EffectConfig.ConfigError, RouteRequirements> {
275: const locationServiceMapV2 = buildLocationServiceMap()
276:
277: return Layer.mergeAll(
278: rootApiRoutes,
279: eventApiRoutes,
280: ptyConnectApiRoutes,
281: instanceRoutes,
282: serverRoutes,
283: docRoute,
284: uiRoute,
285: ).pipe(
286: Layer.provide([
287: errorLayer,
288: compressionLayer,
289: corsVaryFix,
290: fenceLayer,
291: cors(corsOptions),
292: AppNodeBuilderV1.build(MoveSession.node, [[LocationServiceMap.node, locationServiceMapV2]]),
293: HttpServer.layerServices,
294: ]),
295: Layer.provide(Layer.succeed(CorsConfig)(corsOptions)),
296: Layer.provide(sessionLocationLayer),
297: ...
<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/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>/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: ...
Found 51 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 390: it.effect(`never recovers an assistant-less ${phase} execution superseded by a newer assistant`, () =>
Line 435: it.effect(`never commits assistant-less ${phase} recovery after concurrent assistant supersession`, () =>
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 ...
Found 100 matches (more matches available)
/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 ...
Found 100 matches (more matches available)
/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 ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
730: LLMEvent.stepFinish({ index: 0, reason: "stop" }),
731: LLMEvent.finish({ reason: "stop" }),
732: ]
733: requests.length = 0
734:
735: yield* SessionRecovery.recover()
736: while (requests.length === 0) yield* Effect.yieldNow
737: const executionService = yield* SessionExecution.Service
738: while ((yield* executionService.active).size > 0) yield* Effect.yieldNow
739:
740: const request = requests[0]
741: if (!request) return yield* Effect.die("Redriven provider request not found")
742: expect(userTexts(request)).toEqual(["run after restart"])
743: expect(
744: yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, message.id)).get(),
745: ).toMatchObject({
746: wake_pending: false,
747: promoted_seq: expect.any(Number),
748: })
749: ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
830: }),
831: { discard: true, concurrency: 1 },
832: )
833: yield* Effect.forEach(
834: ordinarySessions.slice(0, SessionRecovery.BATCH_SIZE),
835: (id, index) =>
836: SessionInput.admit(db, events, {
837: id: SessionMessage.ID.make(`msg_ordinary_fresh_wake_${index.toString().padStart(3, "0")}`),
838: sessionID: id,
839: prompt: Prompt.make({ text: "fresh wake" }),
840: delivery: "steer",
841: requestExecution: true,
842: }),
843: { discard: true, concurrency: 1 },
844: )
845:
846: const restartedWakes: string[] = []
847: const restartedExecution = SessionExecution.Service.of({
848: ...recoveryExecution,
849: wake: (id) => Effect.sync(() => restartedWakes.push(id)).pipe(Effect.asVoid),
850: })
851: ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1440: phase: "ready",
1441: })
1442:
1443: const recovered = yield* events
1444: .publish(SessionEvent.Step.Recovered, {
1445: sessionID,
1446: timestamp: DateTime.makeUnsafe(1),
1447: executionID: "shared-recovery-identity",
1448: phase: "ready",
1449: })
1450: .pipe(Effect.exit)
1451:
1452: expect(Exit.isFailure(recovered)).toBe(true)
1453: expect(yield* db.select().from(SessionRecoveryTable).all()).toEqual([
1454: expect.objectContaining({
1455: session_id: other,
1456: execution_id: "shared-recovery-identity",
1457: continuation_message_id: continuation("shared-recovery-identity"),
1458: }),
1459: ])
1460: expect(yield* db.select().from(SessionInputTable).all()).toEqual([
1461: expect.objectContaining({ ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1880: yield* SessionInput.admit(db, events, {
1881: id: ordinaryID,
1882: sessionID,
1883: prompt: Prompt.make({ text: "later" }),
1884: delivery: "steer",
1885: requestExecution: false,
1886: })
1887: expect(yield* SessionInput.promoteSteers(db, events, sessionID, Number.MAX_SAFE_INTEGER)).toBe(1)
1888: expect(yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, continuationID)).get()).toMatchObject(
1889: { promoted_seq: null },
1890: )
1891: expect(yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, ordinaryID)).get()).toMatchObject({
1892: promoted_seq: expect.any(Number),
1893: })
1894: }),
1895: )
1896:
1897: it.effect("quarantines a malformed claim without blocking a valid claim", () =>
1898: Effect.gen(function* () ...
Found 11 matches
/home/byk/Code/opencode/packages/core/test/session-execution-local.test.ts:
Line 43: it.effect("clears the claim after a successful drain", () =>
Line 52: it.effect("snapshots the latest assistant sequence when acquiring a claim", () =>
Line 84: it.effect("preserves the claim when its scope closes", () =>
Line 100: it.effect("clears the claim after explicit interruption", () =>
Line 114: it.effect("never starts a second owner while the lease is live", () =>
Line 137: it.effect("never overwrites an expired lease", () =>
Line 164: it.effect("never re-enters its own uncertain live lease", () =>
Line 191: it.effect("renews heartbeat expiry from the current clock", () =>
Line 214: it.effect("never revives an expired claim on heartbeat", () =>
Line 232: it.effect("settles cancellation before starting a pending successor", () =>
Line 276: it.effect("fails cancellation when cleanup loses execution ownership", () =>
<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/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 ...
Found 55 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 991: "table": "session_execution" Line 1001: "table": "session_execution" Line 1011: "table": "session_execution" Line 1021: "table": "session_execution" Line 1089: "name": "wake_pending", Line 1181: "table": "session_recovery" Line 1191: "table": "session_recovery" Line 1201: "table": "session_recovery" Line 1211: "table": "session_recovery" Line 1221: "table": "session_recovery" Line 1229: "name": "wake_pending", Line 1231: "table": "session_recovery" Line 1239: "name": ...
<path>/home/byk/Code/opencode/packages/core/schema.json</path>
<type>file</type>
<content>
1: {
2: "version": "7",
3: "dialect": "sqlite",
4: "id": "3a5ec6d4-a60d-47da-a347-3c72396a13c1",
5: "prevIds": [
6: "f14a9b18-8207-487e-a3d3-227e629ba9ad"
7: ],
8: "ddl": [
9: {
10: "name": "workspace",
11: "entityType": "tables"
12: },
13: {
14: "name": "data_migration",
15: "entityType": "tables"
16: },
17: {
18: "name": "account_state",
19: "entityType": "tables"
20: },
21: {
22: "name": "account",
23: "entityType": "tables"
24: },
25: {
26: "name": "control_account",
27: "entityType": "tables"
28: },
29: {
30: "name": "credential",
31: "entityType": "tables"
32: },
33: {
34: "name": "event_sequence",
35: "entityType": "tables"
36: },
37: {
38: "name": "event",
39: "entityType": "tables"
40: },
41: {
42: ...
<path>/home/byk/Code/opencode/packages/core/schema.json</path>
<type>file</type>
<content>
920: "entityType": "columns",
921: "table": "session_context_epoch"
922: },
923: {
924: "type": "integer",
925: "notNull": true,
926: "autoincrement": false,
927: "default": null,
928: "generated": null,
929: "name": "baseline_seq",
930: "entityType": "columns",
931: "table": "session_context_epoch"
932: },
933: {
934: "type": "text",
935: "notNull": true,
936: "autoincrement": false,
937: "default": null,
938: "generated": null,
939: "name": "id",
940: "entityType": "columns",
941: "table": "session_execution"
942: },
943: {
944: "type": "text",
945: "notNull": true,
946: "autoincrement": false,
947: "default": null,
948: "generated": null,
949: "name": "session_id",
950: "entityType": "columns",
951: "table": ...
Found 16 matches
/home/byk/Code/opencode/packages/sdk/js/src/v2/gen/types.gen.ts:
Line 24: | EventSessionNextPromptExecutionRequested
Line 33: | EventSessionNextStepRecovered
Line 879: type: "session.next.prompt.execution_requested"
Line 983: type: "session.next.step.recovered"
Line 1650: | SyncEventSessionNextPromptExecutionRequested
Line 1659: | SyncEventSessionNextStepRecovered
Line 3435: export type SyncEventSessionNextPromptExecutionRequested = {
Line 3439: type: "session.next.prompt.execution_requested.1"
Line 3602: export type SyncEventSessionNextStepRecovered = {
Line 3606: type: "session.next.step.recovered.1"
Line 4371: type: "session.next.prompt.execution_requested"
Line 4565: type: "session.next.step.recovered"
Line 6468: export type EventSessionNextPromptExecutionRequested = {
Line 6470: type: "session.next.prompt.execution_requested"
Line 6581: export type EventSessionNextStepRecovered ...
<path>/home/byk/Code/opencode/packages/sdk/js/src/v2/gen/types.gen.ts</path>
<type>file</type>
<content>
850: location: LocationRef
851: subdirectory?: string
852: }
853: }
854: | {
855: id: string
856: type: "session.next.prompted"
857: properties: {
858: timestamp: number
859: sessionID: string
860: messageID: string
861: prompt: Prompt
862: delivery: "steer" | "queue"
863: }
864: }
865: | {
866: id: string
867: type: "session.next.prompt.admitted"
868: properties: {
869: timestamp: number
870: sessionID: string
871: messageID: string
872: prompt: Prompt
873: delivery: "steer" | "queue"
874: requestExecution?: boolean
875: }
876: }
877: | {
878: id: string
879: type: "session.next.prompt.execution_requested"
880: properties: {
881: ...
<path>/home/byk/Code/opencode/packages/sdk/js/src/v2/gen/types.gen.ts</path>
<type>file</type>
<content>
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: data: {
3444: timestamp: number
3445: sessionID: string
3446: messageID: string
3447: }
3448: }
3449: ...
<path>/home/byk/Code/opencode/packages/sdk/js/src/v2/gen/types.gen.ts</path>
<type>file</type>
<content>
6450: prompt: Prompt
6451: delivery: "steer" | "queue"
6452: }
6453: }
6454:
6455: export type EventSessionNextPromptAdmitted = {
6456: id: string
6457: type: "session.next.prompt.admitted"
6458: properties: {
6459: timestamp: number
6460: sessionID: string
6461: messageID: string
6462: prompt: Prompt
6463: delivery: "steer" | "queue"
6464: requestExecution?: boolean
6465: }
6466: }
6467:
6468: export type EventSessionNextPromptExecutionRequested = {
6469: id: string
6470: type: "session.next.prompt.execution_requested"
6471: properties: {
6472: timestamp: number
6473: sessionID: string
6474: messageID: string
6475: }
6476: }
6477:
6478: export type EventSessionNextContextUpdated = {
6479: id: string
6480: type: "session.next.context.updated"
6481: properties: {
6482: timestamp: number
6483: sessionID: ...
EventSessionNextStepRecovered {"additionalProperties": false, "properties": {"id": {"pattern": "^evt_", "type": "string"}, "properties": {"additionalProperties": false, "properties": {"assistantMessageID": {"pattern": "^msg_", "type": "string"}, "executionID": {"type": "string"}, "phase": {"enum": ["ready", "unknown", "continue"], "type": "string"}, "sessionID": {"pattern": "^ses", "type": "string"}, "timestamp": {"type": "number"}}, "required": ["timestamp", "sessionID", "executionID", "phase"], "type": "object"}, "type": {"enum": ["session.next.step.recovered"], "type": "string"}}, "required": ["id", "type", "properties"], "type": "object"}
SessionNextStepRecovered {"additionalProperties": false, "properties": {"data": {"additionalProperties": false, "properties": {"assistantMessageID": {"pattern": "^msg_", "type": "string"}, "executionID": {"type": "string"}, "phase": {"enum": ["ready", "unknown", "continue"], "type": "string"}, "sessionID": {"pattern": "^ses", "type": "string"}, ...
(no output)
Found 4 matches /home/byk/Code/opencode/packages/sdk/openapi.json: Line 17919: "enum": ["session.next.prompt.execution_requested"] Line 25740: "enum": ["session.next.prompt.execution_requested.1"] Line 28622: "enum": ["session.next.prompt.execution_requested"] Line 34831: "enum": ["session.next.prompt.execution_requested"]
<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: ...
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: ...
<path>/home/byk/Code/opencode/packages/core/test/session-prompt.test.ts</path>
<type>file</type>
<content>
270: expect(executionCalls).toEqual([sessionID])
271: expect(wakeCalls).toEqual([])
272: }),
273: )
274:
275: it.effect("records distinct messages when the ID is omitted", () =>
276: Effect.gen(function* () {
277: yield* setup
278: const session = yield* SessionV2.Service
279: const input = { sessionID, prompt: Prompt.make({ text: "Fix the failing tests" }), resume: false }
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: ...
<path>/home/byk/Code/opencode/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, undefined).pipe(Effect.andThen(Effect.never))
300: ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1120: phase: "ready",
1121: })
1122: yield* db.run(
1123: sql`UPDATE session_input SET session_id = ${other} WHERE id = ${continuation("cross-session-continuation")}`,
1124: )
1125: const wakes: string[] = []
1126:
1127: yield* SessionRecovery.recover().pipe(
1128: Effect.provideService(
1129: SessionExecution.Service,
1130: SessionExecution.Service.of({
1131: ...recoveryExecution,
1132: wake: (id) => Effect.sync(() => wakes.push(id)).pipe(Effect.asVoid),
1133: }),
1134: ),
1135: )
1136:
1137: expect(wakes).toEqual([])
1138: expect(yield* db.select().from(SessionRecoveryTable).get()).toMatchObject({
1139: recovery_error: "Invalid recovery continuation input",
1140: })
1141: }),
1142: )
1143:
1144: it.effect("redrives ...
/home/byk/Code/opencode/packages/client/src/generated/types.ts /home/byk/Code/opencode/packages/client/src/generated/index.ts /home/byk/Code/opencode/packages/client/src/generated/client.ts /home/byk/Code/opencode/packages/client/src/generated/client-error.ts /home/byk/Code/opencode/packages/client/src/generated/.httpapi-codegen.json /home/byk/Code/opencode/packages/client/src/generated-effect/index.ts /home/byk/Code/opencode/packages/client/src/generated-effect/client.ts /home/byk/Code/opencode/packages/client/src/generated-effect/client-error.ts /home/byk/Code/opencode/packages/client/src/generated-effect/.httpapi-codegen.json
No files found
Found 2 matches /home/byk/Code/opencode/packages/client/src/generated/types.ts: Line 756: readonly type: "session.next.prompt.admitted" Line 1214: readonly type: "session.next.prompt.admitted"
<path>/home/byk/Code/opencode/packages/client/src/generated/types.ts</path>
<type>file</type>
<content>
730: readonly durable?: { readonly aggregateID: string; readonly seq: number; readonly version: number }
731: readonly location?: { readonly directory: string; readonly workspaceID?: string }
732: readonly data: {
733: readonly timestamp: number
734: readonly sessionID: string
735: readonly messageID: string
736: readonly prompt: {
737: readonly text: string
738: readonly files?: ReadonlyArray<{
739: readonly uri: string
740: readonly mime: string
741: readonly name?: string
742: readonly description?: string
743: readonly source?: { readonly start: number; readonly end: number; readonly text: string }
744: }>
745: readonly agents?: ReadonlyArray<{
746: readonly name: string
747: ...
Found 4 matches /home/byk/Code/opencode/packages/sdk/openapi.json: Line 18289: "enum": ["session.next.step.recovered"] Line 26299: "enum": ["session.next.step.recovered.1"] Line 29190: "enum": ["session.next.step.recovered"] Line 35201: "enum": ["session.next.step.recovered"]
<path>/home/byk/Code/opencode/packages/core/test/session-runner-recorded.test.ts</path>
<type>file</type>
<content>
1: import { HttpRecorder } from "@opencode-ai/http-recorder"
2: import { HttpRecorderInternal } from "@opencode-ai/http-recorder/internal"
3: import * as OpenAIChat from "@opencode-ai/llm/protocols/openai-chat"
4: import { Auth, LLMClient, RequestExecutor } from "@opencode-ai/llm/route"
5: import { Database } from "@opencode-ai/core/database/database"
6: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
7: import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
8: import { LayerNode } from "@opencode-ai/core/effect/layer-node"
9: import { EventV2 } from "@opencode-ai/core/event"
10: import { EventTable } from "@opencode-ai/core/event/sql"
11: import { PermissionV2 } from "@opencode-ai/core/permission"
12: import { AgentV2 } from "@opencode-ai/core/agent"
13: import { Config } from "@opencode-ai/core/config"
14: import { ...
<path>/home/byk/Code/opencode/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 = ...
packages/core/src/session/input.ts @@ -1,15 +1,16 @@
packages/core/src/session/input.ts @@ -17,6 +18,10 @@ export { Admitted, Delivery }
packages/core/src/session/input.ts @@ -38,6 +43,11 @@ export class LifecycleConflict extends Schema.TaggedErrorClass<LifecycleConflict
packages/core/src/session/input.ts @@ -46,10 +56,16 @@ export const admit = Effect.fn("SessionInput.admit")(function* (
packages/core/src/session/input.ts @@ -58,26 +74,110 @@ export const admit = Effect.fn("SessionInput.admit")(function* (
packages/core/src/session/input.ts @@ -88,6 +188,7 @@ export const projectAdmitted = Effect.fn("SessionInput.projectAdmitted")(functio
packages/core/src/session/input.ts @@ -106,6 +207,7 @@ export const projectAdmitted = Effect.fn("SessionInput.projectAdmitted")(functio
packages/core/src/session/input.ts @@ -115,6 +217,30 @@ export const projectAdmitted = Effect.fn("SessionInput.projectAdmitted")(functio
packages/core/src/session/input.ts @@ -126,9 +252,14 @@ export const ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
1475: ]
1476: yield* session.prompt({
1477: sessionID,
1478: prompt: Prompt.make({ text: "Newest exact request ".repeat(180) }),
1479: resume: false,
1480: })
1481: yield* session.resume(sessionID)
1482:
1483: expect(requests).toHaveLength(2)
1484: expect(userTexts(requests[0])[0]).toContain(
1485: "<prior-summary>\n## Objective\n- Preserve the task\n</prior-summary>",
1486: )
1487: expect(userTexts(requests[0])[0]).toContain("Recent exact request")
1488: expect((yield* (yield* SessionStore.Service).context(sessionID))[0]).toMatchObject({
1489: type: "compaction",
1490: summary: "## Objective\n- Preserve the updated task",
1491: })
1492: }),
1493: )
1494:
1495: it.effect("keeps automatic compaction unknown until Compaction.Ended commits", () =>
1496: ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
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: }),
1973: )
1974:
1975: it.effect("never commits continuation before unresolved hosted tools are reconciled", () =>
1976: Effect.gen(function* () {
1977: yield* setup
1978: const session = yield* SessionV2.Service
1979: const { db } = yield* Database.Service
1980: yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Mixed tools" }), resume: false ...
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
3530: { type: "assistant", finish: "stop" },
3531: ])
3532: }),
3533: )
3534:
3535: it.effect("interrupts runner continuation when permission approval is declined", () =>
3536: Effect.gen(function* () {
3537: yield* setup
3538: const session = yield* SessionV2.Service
3539: const registry = yield* ToolRegistry.Service
3540: yield* registry.register({
3541: declined: Tool.make({
3542: description: "Fail because the user declined approval",
3543: input: Schema.Struct({}),
3544: output: Schema.Struct({}),
3545: execute: () => Effect.die(new PermissionV2.DeclinedError()),
3546: }),
3547: })
3548: yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Call declined" }), resume: false })
3549:
3550: requests.length = 0
3551: response = [
3552: ...
Found 4 matches
/home/byk/Code/opencode/packages/core/test/session-runner.test.ts:
Line 845: it.effect("never fails interrupted tools after ownership changes", () =>
Line 3845: it.effect("durably fails blocked local tools when a provider turn is interrupted", () =>
Line 3984: it.effect("durably fails blocked local tools when interrupted while awaiting settlement", () =>
/home/byk/Code/opencode/packages/core/test/session-runner-tool-registry.test.ts:
Line 157: it.effect("returns model errors without swallowing interruption or defects", () =>
No files found
Found 14 matches /home/byk/Code/opencode/packages/core/test/session-runner.test.ts: Line 401: const replaySessionProjection = (id: SessionV2.ID) => Line 541: yield* replaySessionProjection(sessionID) Line 666: yield* replaySessionProjection(sessionID) Line 1114: yield* replaySessionProjection(sessionID) Line 1344: yield* replaySessionProjection(sessionID) Line 1416: yield* replaySessionProjection(sessionID) Line 1678: yield* replaySessionProjection(sessionID) Line 2373: yield* replaySessionProjection(sessionID) Line 2432: yield* replaySessionProjection(sessionID) Line 2577: yield* replaySessionProjection(sessionID) Line 3892: yield* replaySessionProjection(sessionID) Line 4204: yield* replaySessionProjection(sessionID) Line 4285: yield* replaySessionProjection(sessionID) Line 4324: yield* replaySessionProjection(sessionID)
<path>/home/byk/Code/opencode/packages/core/test/session-runner.test.ts</path>
<type>file</type>
<content>
380: yield* setup
381: const session = yield* SessionV2.Service
382: response = fragmentFixture("text", "text-earlier", ["Earlier answer"]).completeEvents
383: yield* session.prompt({
384: sessionID,
385: prompt: Prompt.make({ text: "Earlier question ".repeat(700) }),
386: resume: false,
387: })
388: yield* session.resume(sessionID)
389: currentModel = recoveryModel
390: requests.length = 0
391: return session
392: })
393:
394: const messageTexts = (request: LLMRequest, role: "user" | "system") =>
395: request.messages.flatMap((message) =>
396: message.role === role ? message.content.flatMap((content) => (content.type === "text" ? [content.text] : [])) : [],
397: )
398: const userTexts = (request: LLMRequest) => messageTexts(request, "user")
399: const systemTexts = (request: LLMRequest) => messageTexts(request, "system")
400:
401: const ...
<path>/home/byk/Code/opencode/packages/client/src/generated/types.ts</path>
<type>file</type>
<content>
840: readonly timestamp: number
841: readonly sessionID: string
842: readonly assistantMessageID: string
843: readonly agent: string
844: readonly model: { readonly id: string; readonly providerID: string; readonly variant?: string }
845: readonly snapshot?: string
846: }
847: }
848: | {
849: readonly id: string
850: readonly metadata?: { readonly [x: string]: JsonValue }
851: readonly type: "session.next.step.ended"
852: readonly durable?: { readonly aggregateID: string; readonly seq: number; readonly version: number }
853: readonly location?: { readonly directory: string; readonly workspaceID?: string }
854: readonly data: {
855: readonly timestamp: number
856: readonly sessionID: string
857: readonly assistantMessageID: ...
Found 21 matches
/tmp/opencode/session-recovery-final-v14.patch:
Line 1712: + SessionEvent.PromptExecutionRequested,
Line 2267: + yield* events.project(SessionEvent.PromptExecutionRequested, (event) =>
Line 5936: + .publish(SessionEvent.PromptExecutionRequested, {
Line 7323: + expect(yield* eventCount(EventV2.versionedType(SessionEvent.PromptExecutionRequested.type, 1))).toBe(1)
Line 7378: + expect(yield* eventCount(EventV2.versionedType(SessionEvent.PromptExecutionRequested.type, 1))).toBe(0)
Line 7386: + expect(yield* eventCount(EventV2.versionedType(SessionEvent.PromptExecutionRequested.type, 1))).toBe(0)
Line 8868: diff --git a/packages/schema/src/session-event.ts b/packages/schema/src/session-event.ts
Line 8884: +export const PromptExecutionRequested = Event.define({
Line 8892: +export type PromptExecutionRequested = typeof PromptExecutionRequested.Type
Line 8902: + export const Interrupted = Event.define({
Line 8913: ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect } from "bun:test"
2: import { DateTime, Deferred, Effect, Exit, Fiber, Schema } from "effect"
3: import { TestClock } from "effect/testing"
4: import { asc, eq, sql } from "drizzle-orm"
5: import { Database } from "@opencode-ai/core/database/database"
6: import { LayerNode } from "@opencode-ai/core/effect/layer-node"
7: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
8: import { EventV2 } from "@opencode-ai/core/event"
9: import { EventTable } from "@opencode-ai/core/event/sql"
10: import { ModelV2 } from "@opencode-ai/core/model"
11: import { Project } from "@opencode-ai/core/project"
12: import { ProjectTable } from "@opencode-ai/core/project/sql"
13: import { ProviderV2 } from "@opencode-ai/core/provider"
14: import { AbsolutePath } from "@opencode-ai/core/schema"
15: import { SessionV2 } from ...
<path>/home/byk/Code/opencode/packages/core/test/session-projector.test.ts</path>
<type>file</type>
<content>
2230:
2231: expect(yield* db.select().from(SessionExecutionTable).get()).toMatchObject({
2232: owner_id: "successor-owner",
2233: recovery_error: null,
2234: })
2235: }),
2236: )
2237:
2238: it.effect("quarantines failed execution conversion before scanning later claims", () =>
2239: Effect.gen(function* () {
2240: const { db } = yield* Database.Service
2241: const events = yield* EventV2.Service
2242: const count = SessionRecovery.BATCH_SIZE + 1
2243: const ids = Array.from({ length: count }, (_, index) =>
2244: SessionV2.ID.make(`ses_recovery_conversion_${index.toString().padStart(3, "0")}`),
2245: )
2246: yield* db
2247: .insert(ProjectTable)
2248: .values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
2249: .run()
2250: ...
<path>/home/byk/Code/opencode/packages/client/src/generated/types.ts</path>
<type>file</type>
<content>
640: readonly content: ReadonlyArray<
641: | { readonly type: "text"; readonly text: string }
642: | { readonly type: "file"; readonly uri: string; readonly mime: string; readonly name?: string }
643: >
644: readonly structured: { readonly [x: string]: JsonValue }
645: readonly error: { readonly type: "unknown"; readonly message: string }
646: readonly result?: JsonValue
647: }
648: readonly time: {
649: readonly created: number
650: readonly ran?: number
651: readonly completed?: number
652: readonly pruned?: number
653: }
654: }
655: >
656: readonly snapshot?: { readonly start?: string; readonly end?: ...
<path>/home/byk/Code/opencode/packages/client/src/generated/types.ts</path>
<type>file</type>
<content>
665: readonly error?: { readonly type: "unknown"; readonly message: string }
666: }
667: | {
668: readonly type: "compaction"
669: readonly reason: "auto" | "manual"
670: readonly summary: string
671: readonly recent: string
672: readonly id: string
673: readonly metadata?: { readonly [x: string]: JsonValue }
674: readonly time: { readonly created: number }
675: }
676: >
677: }["data"]
678:
679: export type SessionsHistoryInput = {
680: readonly sessionID: { readonly sessionID: string }["sessionID"]
681: readonly limit?: { readonly limit?: number | undefined; readonly after?: number | undefined }["limit"]
682: readonly after?: { readonly limit?: number | undefined; readonly after?: number | undefined }["after"]
683: }
684:
685: export type SessionsHistoryOutput = {
686: readonly data: ...
PASS — frozen inputs
ab9408c81cc9916177641ae8e9f08624fc27b5b9.5efd9d74f450e9f875790a07c860248cb526f7144a822cf6df51761237f9789b.3c26981f57d37d15549b76657c20a090355911e80ea94d43f957422dc3276796.packages/core/src/session/execution/local.ts:116-118 preserves a cancelling claim only when the final exit cause still contains an interruption. During interruption, packages/core/src/session/runner/llm.ts:412-417 publishes tool and Step settlement before restoring the original interrupt cause.
If either publication defects, that defect replaces the saved interrupt before Effect.failCause(cause) runs. The local finalizer then sees a non-interrupt failure and clears the cancelling claim because that phase is neither unknown nor continue.
This permits the following sequence:
interrupt() marks the claim cancelling at execution/local.ts:132.execution/local.ts:116-118 clears the claim.execution/local.ts:136-147.A cancelling claim must always survive the runner exit. Only the coordinator’s explicit cleanup may clear it after all finalizers and settlement work finish. Add a publication-defect regression that asserts the claim remains and unresolved tools never become silently unrecoverable. ...