@effect-agent/storage-cloudflare 0.1.0-beta.110 → 0.1.0-beta.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"do-journal-BeHu8_Bh.mjs","names":[],"sources":["../src/internal/do-journal.ts"],"sourcesContent":["import { SqliteMigrator } from \"@effect/sql-sqlite-do\";\nimport { Effect, Schema, Stream } from \"effect\";\nimport { EMPTY_TAIL_DIGEST } from \"effect-agent/digest\";\nimport { CanonicalRecord, CanonicalSequence, ProducerEpoch } from \"effect-agent/records\";\nimport { createMessageDeliveryPendingIndex } from \"effect-agent/sql-message-delivery-store\";\nimport { checkV2ThreadLayout } from \"effect-agent/sql-storage-v2-upgrade\";\nimport {\n createNativeReadIndexes,\n seedNativeReadIndexes,\n indexCanonicalRecord,\n} from \"effect-agent/sql-thread-native-reads\";\nimport {\n MAX_THREAD_EXPORT_RECORDS,\n CheckpointRejected,\n FenceRejected,\n ThreadNotMaterialized,\n ThreadStoreDiagnostic,\n type SaveRecoveryCheckpointRequest,\n} from \"effect-agent/thread-store\";\nimport * as SqlClient from \"effect/unstable/sql/SqlClient\";\nimport { SqlError } from \"effect/unstable/sql/SqlError\";\n\nimport {\n type DoStorageFailpointError,\n DoAppendConflict,\n DoCheckpointConflict,\n DoFenceRejected,\n DoStorageCompatibilityError,\n DoStorageCorruptionError,\n DoStorageError,\n DoValueBoundExceeded,\n type DoStorageFailpointLocation,\n} from \"../DoStorageError.ts\";\nimport { createMessageDeliveryTables } from \"./message-delivery-schema.ts\";\nimport { CurrentDoStorageVersion, createNonterminalIndex, doMigrations } from \"./migrations.ts\";\nimport { createRecoveryCheckpointTable } from \"./recovery-checkpoint-schema.ts\";\n\n/**\n * Static schema ceiling for stored text columns. Writes are bounded in BYTES by the\n * configured `maxStoredValueBytes` (always ≤ 2,000,000); UTF-8 byte length is never smaller\n * than UTF-16 string length, so any value that passed the byte bound also passes this\n * decode-side character ceiling.\n */\nconst BoundedStoredText = Schema.String.check(Schema.isMaxLength(2_000_000));\nconst BoundedIdentifier = Schema.NonEmptyString.check(Schema.isMaxLength(1024));\nconst MAX_RECORDS_PER_THREAD = MAX_THREAD_EXPORT_RECORDS;\nconst ZERO_SEQUENCE = Schema.decodeSync(CanonicalSequence)(0);\nconst MAX_IDENTIFIER_LENGTH = 1_024;\nconst MAX_READ_PAGE_JSON_BYTES = 4 * 1024 * 1024;\n/** Durable Object SQL storage allows at most 100 bound parameters per statement. */\nconst MAX_BOUND_PARAMETERS = 100;\nconst isSqlError = Schema.is(SqlError);\n\nconst storedTextBytes = (value: string): number => new TextEncoder().encode(value).byteLength;\n\nconst chunked = <A>(values: ReadonlyArray<A>, size: number): Array<ReadonlyArray<A>> => {\n const chunks: Array<ReadonlyArray<A>> = [];\n\n for (let index = 0; index < values.length; index += size) {\n chunks.push(values.slice(index, index + size));\n }\n\n return chunks;\n};\n\nclass DoMetaRow extends Schema.Class<DoMetaRow>(\"DoMetaRow\")({\n value: Schema.NonEmptyString.check(Schema.isMaxLength(128)),\n}) {}\n\nclass DoNameRow extends Schema.Class<DoNameRow>(\"DoNameRow\")({\n name: BoundedIdentifier,\n}) {}\n\nclass ThreadRow extends Schema.Class<ThreadRow>(\"ThreadRow\")({\n thread_id: BoundedIdentifier,\n created_at: Schema.NonEmptyString.check(Schema.isMaxLength(128)),\n producer_epoch: ProducerEpoch,\n tail_digest: BoundedStoredText,\n tail_sequence: CanonicalSequence,\n}) {}\n\nclass BatchRow extends Schema.Class<BatchRow>(\"BatchRow\")({\n batch_digest: BoundedStoredText,\n batch_id: BoundedIdentifier,\n batch_json: BoundedStoredText,\n thread_id: BoundedIdentifier,\n first_sequence: CanonicalSequence,\n last_sequence: CanonicalSequence,\n tail_digest: BoundedStoredText,\n}) {}\n\nclass RecordRow extends Schema.Class<RecordRow>(\"RecordRow\")({\n batch_id: BoundedIdentifier,\n thread_id: BoundedIdentifier,\n record_id: BoundedIdentifier,\n record_json: BoundedStoredText,\n sequence: CanonicalSequence,\n}) {}\n\nconst ReadPlanRow = Schema.Struct({\n sequence: CanonicalSequence,\n record_json_bytes: Schema.Natural.check(Schema.isLessThanOrEqualTo(MAX_READ_PAGE_JSON_BYTES)),\n});\n\ntype ReadPage = [typeof ReadPlanRow.Type, ...Array<typeof ReadPlanRow.Type>];\n\nclass CheckpointRow extends Schema.Class<CheckpointRow>(\"CheckpointRow\")({\n checkpoint_json: BoundedStoredText,\n thread_id: BoundedIdentifier,\n tail_digest: BoundedStoredText,\n through_sequence: CanonicalSequence,\n}) {}\n\nexport class RawRecord extends Schema.Class<RawRecord>(\n \"@effect-agent/storage-cloudflare/RawRecord\",\n)({\n recordId: BoundedIdentifier,\n recordJson: BoundedStoredText,\n}) {}\n\nexport class RawAppendRequest extends Schema.Class<RawAppendRequest>(\n \"@effect-agent/storage-cloudflare/RawAppendRequest\",\n)({\n batchDigest: BoundedStoredText,\n batchId: BoundedIdentifier,\n batchJson: BoundedStoredText,\n threadId: BoundedIdentifier,\n expectedTailDigest: BoundedStoredText,\n expectedTailSequence: CanonicalSequence,\n producerEpoch: ProducerEpoch,\n records: Schema.NonEmptyArray(RawRecord).check(Schema.isMaxLength(256)),\n tailDigest: BoundedStoredText,\n}) {}\n\nexport class RawAppendResult extends Schema.Class<RawAppendResult>(\n \"@effect-agent/storage-cloudflare/RawAppendResult\",\n)({\n firstSequence: CanonicalSequence,\n lastSequence: CanonicalSequence,\n replayed: Schema.Boolean,\n tailDigest: BoundedStoredText,\n}) {}\n\nexport class RawReadRequest extends Schema.Class<RawReadRequest>(\n \"@effect-agent/storage-cloudflare/RawReadRequest\",\n)({\n threadId: BoundedIdentifier,\n fromSequenceExclusive: CanonicalSequence,\n limit: Schema.Int.check(Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(1_024)),\n}) {}\n\nexport class RawCheckpoint extends Schema.Class<RawCheckpoint>(\n \"@effect-agent/storage-cloudflare/RawCheckpoint\",\n)({\n checkpointJson: BoundedStoredText,\n threadId: BoundedIdentifier,\n tailDigest: BoundedStoredText,\n throughSequence: CanonicalSequence,\n}) {}\n\nexport class RawThreadExport extends Schema.Class<RawThreadExport>(\n \"@effect-agent/storage-cloudflare/RawThreadExport\",\n)({\n thread: ThreadRow,\n records: Schema.Array(RecordRow),\n}) {}\n\ntype AppendError =\n | DoAppendConflict\n | DoFenceRejected\n | DoStorageCorruptionError\n | DoStorageError\n | DoStorageFailpointError\n | DoValueBoundExceeded;\n\ntype CheckpointError =\n | DoCheckpointConflict\n | DoStorageCorruptionError\n | DoStorageError\n | DoValueBoundExceeded;\n\ntype DoJournalFailpoint = (\n location: DoStorageFailpointLocation,\n) => Effect.Effect<void, DoStorageFailpointError>;\n\nconst noFailpoint: DoJournalFailpoint = () => Effect.void;\n\nconst storageError =\n (operation: string) =>\n (error: SqlError): DoStorageError =>\n DoStorageError.make({\n cause: error,\n operation,\n message: error.message,\n diagnostic: ThreadStoreDiagnostic.make({\n causeTag: error._tag,\n operation,\n issueTag: error.reason._tag,\n }),\n });\n\n/** Decode raw Durable Object SQLite rows against a Schema, reporting failures as typed corruption. */\nexport const decodeRows = Effect.fn(\n <A, I>(\n schema: Schema.Codec<ReadonlyArray<A>, ReadonlyArray<I>>,\n table: string,\n rowKey: string,\n rows: unknown,\n decoder: string = table,\n ): Effect.Effect<ReadonlyArray<A>, DoStorageCorruptionError> =>\n Schema.decodeUnknownEffect(schema)(rows).pipe(\n Effect.mapError((error) =>\n DoStorageCorruptionError.make({\n table,\n rowKey,\n message: \"Stored rows do not satisfy the storage schema\",\n diagnostic: ThreadStoreDiagnostic.make({\n causeTag: error._tag,\n operation: \"decode storage rows\",\n decoder,\n issueTag: error.issue._tag,\n }),\n }),\n ),\n Effect.tapError((error) =>\n Effect.annotateCurrentSpan({\n \"storage.failure.operation\": error.diagnostic?.operation,\n \"storage.failure.cause\": error.diagnostic?.causeTag,\n \"storage.failure.decoder\": decoder,\n \"storage.failure.issue\": error.diagnostic?.issueTag,\n }),\n ),\n ),\n);\n\n/** Decode exactly one raw row against a Schema, reporting failures as typed corruption. */\nexport const decodeSingleRow = Effect.fn(\n <A, I>(\n schema: Schema.Codec<ReadonlyArray<A>, ReadonlyArray<I>>,\n table: string,\n rowKey: string,\n rows: unknown,\n ): Effect.Effect<A, DoStorageCorruptionError> =>\n decodeRows(schema, table, rowKey, rows).pipe(\n Effect.flatMap((decoded) =>\n decoded.length === 1\n ? Effect.succeed(decoded[0])\n : Effect.fail(\n DoStorageCorruptionError.make({\n table,\n rowKey,\n message: `Expected exactly one row but found ${decoded.length}.`,\n }),\n ),\n ),\n ),\n);\n\n/** Column inventory of the supported v3 predecessor, independent of physical column order. */\nconst predecessorColumns = {\n effect_agent_threads: [\n \"thread_id\",\n \"created_at\",\n \"tail_sequence\",\n \"tail_digest\",\n \"producer_epoch\",\n ],\n effect_agent_canonical_batches: [\n \"thread_id\",\n \"batch_id\",\n \"first_sequence\",\n \"last_sequence\",\n \"batch_digest\",\n \"tail_digest\",\n \"batch_json\",\n ],\n effect_agent_canonical_records: [\"thread_id\", \"sequence\", \"record_id\", \"batch_id\", \"record_json\"],\n effect_agent_checkpoints: [\"thread_id\", \"through_sequence\", \"tail_digest\", \"checkpoint_json\"],\n effect_agent_submissions: [\n \"submission_id\",\n \"thread_id\",\n \"queue_sequence\",\n \"principal\",\n \"idempotency_key\",\n \"agent_id\",\n \"agent_digests_json\",\n \"deployment_id\",\n \"input_json\",\n \"input_digest\",\n \"receipt_id\",\n \"state\",\n \"settled_outcome\",\n \"created_at\",\n \"ready_at\",\n \"input_applied_record_id\",\n \"input_applied_sequence\",\n \"joined_host_submission_id\",\n \"suspended_reason_json\",\n \"suspended_at\",\n \"unknown_reason\",\n \"unknown_tool_call_ids_json\",\n \"parent_submission_id\",\n \"parent_tool_call_id\",\n \"admission_group\",\n \"admission_fence_json\",\n ],\n effect_agent_submission_ownership: [\n \"submission_id\",\n \"attempt_id\",\n \"ownership_token\",\n \"producer_epoch\",\n \"owner_producer_id\",\n \"lease_expires_at\",\n ],\n effect_agent_attempts: [\n \"attempt_id\",\n \"submission_id\",\n \"thread_id\",\n \"owner_producer_id\",\n \"producer_epoch\",\n \"claimed_at\",\n ],\n effect_agent_settlement_reservations: [\n \"submission_id\",\n \"settlement_id\",\n \"outcome\",\n \"record_id\",\n \"record_json\",\n \"record_digest\",\n \"reserved_at\",\n \"finalized_at\",\n ],\n effect_agent_abort_intents: [\n \"submission_id\",\n \"author\",\n \"reason\",\n \"requested_at\",\n \"canonical_record_id\",\n ],\n effect_agent_approval_decisions: [\n \"submission_id\",\n \"tool_call_id\",\n \"decision\",\n \"resolver\",\n \"reason\",\n \"decided_at\",\n ],\n effect_agent_unknown_resolutions: [\n \"submission_id\",\n \"tool_call_id\",\n \"author\",\n \"reason\",\n \"resolution_json\",\n \"resolved_at\",\n ],\n effect_agent_child_reservations: [\n \"reservation_id\",\n \"parent_submission_id\",\n \"parent_tool_call_id\",\n \"child_submission_id\",\n \"status\",\n \"allocation_json\",\n \"allocation_digest\",\n \"accounting_json\",\n \"reserved_at\",\n \"release_began_at\",\n \"released_at\",\n ],\n effect_agent_meta: [\"key\", \"value\"],\n effect_agent_child_settlements: [\n \"parent_submission_id\",\n \"child_submission_id\",\n \"child_outcome\",\n \"recorded_at\",\n ],\n} as const;\n\nconst checkPredecessorLayout = Effect.fn(\"DoJournal.checkPredecessorLayout\")(function* (\n version: 3 | 4 | 5,\n) {\n const sql = yield* SqlClient.SqlClient;\n\n const messageColumns =\n version === 3\n ? predecessorColumns\n : {\n ...predecessorColumns,\n effect_agent_submissions: [\n ...predecessorColumns.effect_agent_submissions,\n \"worker_admission_json\",\n \"message_admission_json\",\n ],\n effect_agent_message_deliveries: [\n \"owner_thread_id\",\n \"message_id\",\n \"version\",\n \"state\",\n \"deadline_at_millis\",\n \"record_json\",\n ],\n };\n\n const expectedColumns = {\n ...messageColumns,\n ...(version === 5\n ? {\n effect_agent_recovery_checkpoints: [\n \"thread_id\",\n \"through_sequence\",\n \"tail_digest\",\n \"checkpoint_json\",\n ],\n }\n : {}),\n };\n\n for (const [table, expected] of Object.entries(expectedColumns)) {\n const columns = yield* decodeRows(\n Schema.Array(Schema.Struct({ name: BoundedIdentifier })),\n table,\n \"schema\",\n yield* sql.unsafe(`PRAGMA table_info(${table})`),\n );\n\n const names = new Set<string>(expected);\n\n if (columns.length !== names.size || columns.some((column) => !names.has(column.name)))\n return yield* DoStorageCompatibilityError.make({\n actualVersion: version,\n supportedVersion: CurrentDoStorageVersion,\n message: `The v${version} ${table} columns do not match the supported predecessor; no upgrade was committed.`,\n });\n }\n});\n\nconst REQUIRED_TABLES = [\n \"effect_agent_abort_intents\",\n \"effect_agent_approval_decisions\",\n \"effect_agent_attempts\",\n \"effect_agent_canonical_batches\",\n \"effect_agent_canonical_records\",\n \"effect_agent_checkpoints\",\n \"effect_agent_child_reservations\",\n \"effect_agent_child_settlements\",\n \"effect_agent_threads\",\n \"effect_agent_meta\",\n \"effect_agent_settlement_reservations\",\n \"effect_agent_submission_ownership\",\n \"effect_agent_submissions\",\n \"effect_agent_unknown_resolutions\",\n] as const;\n\n/**\n * Supported-predecessor or fresh storage gate (DEPLOY-008) over `effect_agent_meta` instead of\n * `PRAGMA user_version` (unverified on Durable Object SQL storage; a meta table is portable\n * regardless). No WAL check (Durable Object storage owns durability and confirms writes\n * through output gates) and no busy timeout (a Durable Object has exactly one writer): the\n * Node machinery those served has no DC analogue and is deliberately absent.\n */\nconst ensureCurrentStorage = Effect.fn(\"DoJournal.ensureCurrentStorage\")(function* (\n sql: SqlClient.SqlClient,\n failpoint: DoJournalFailpoint = noFailpoint,\n maxStoredValueBytes: number,\n) {\n const metaTableRows = yield* sql<Record<string, unknown>>`\n SELECT name\n FROM sqlite_master\n WHERE type = 'table'\n AND name = 'effect_agent_meta'\n `.pipe(Effect.mapError(storageError(\"read storage version table\")));\n\n const metaTables = yield* decodeRows(\n Schema.Array(DoNameRow),\n \"sqlite_master\",\n \"effect_agent_meta\",\n metaTableRows,\n );\n\n if (metaTables.length === 0) {\n const existingRows = yield* sql<Record<string, unknown>>`\n SELECT name\n FROM sqlite_master\n WHERE type = 'table'\n AND name LIKE 'effect_agent_%'\n ORDER BY name\n `.pipe(Effect.mapError(storageError(\"inspect unversioned storage\")));\n\n const existing = yield* decodeRows(\n Schema.Array(DoNameRow),\n \"sqlite_master\",\n \"effect_agent_%\",\n existingRows,\n );\n\n if (existing.length > 0) {\n return yield* DoStorageCompatibilityError.make({\n actualVersion: 0,\n supportedVersion: CurrentDoStorageVersion,\n message:\n \"The Durable Object contains unversioned Effect Agent tables. Refusing to mutate ambiguous stored data; retain it for inspection with its original writer.\",\n });\n }\n\n yield* SqliteMigrator.run({\n loader: doMigrations,\n // An application can share this SQL client and own its own migration history.\n // Keep bookkeeping outside effect_agent_% so interrupted unversioned schemas\n // still fail the ambiguity check above.\n table: \"effect_sql_migrations_agent_threads\",\n }).pipe(\n // SqliteMigrator depends on the generic client supplied by this adapter. The concrete\n // Durable Object client is kept at the outer Layer boundary.\n Effect.provideService(SqlClient.SqlClient, sql),\n Effect.mapError((error) =>\n DoStorageError.make({\n cause: error,\n operation: \"initialize current storage\",\n message: error.message,\n }),\n ),\n );\n } else {\n const versionRows = yield* sql<Record<string, unknown>>`\n SELECT value\n FROM effect_agent_meta\n WHERE key = 'storage_version'\n `.pipe(Effect.mapError(storageError(\"read storage version\")));\n\n const version = yield* decodeSingleRow(\n Schema.Array(DoMetaRow),\n \"effect_agent_meta\",\n \"storage_version\",\n versionRows,\n );\n\n if (\n version.value === \"2\" ||\n version.value === \"3\" ||\n version.value === \"4\" ||\n version.value === \"5\"\n ) {\n yield* sql\n .withTransaction(\n Effect.gen(function* () {\n const current = yield* sql<{\n value: string;\n }>`SELECT value FROM effect_agent_meta WHERE key='storage_version'`;\n\n if (current.length === 1 && current[0].value === String(CurrentDoStorageVersion))\n return;\n if (\n current.length !== 1 ||\n (current[0].value !== \"2\" &&\n current[0].value !== \"3\" &&\n current[0].value !== \"4\" &&\n current[0].value !== \"5\")\n )\n return yield* DoStorageCompatibilityError.make({\n actualVersion: -1,\n supportedVersion: CurrentDoStorageVersion,\n message: \"Storage version changed while acquiring the upgrade transaction.\",\n });\n\n const required = yield* decodeRows(\n Schema.Array(DoNameRow),\n \"sqlite_master\",\n \"required_tables\",\n yield* sql`SELECT name FROM sqlite_master WHERE type='table' AND name IN ${sql.in([...REQUIRED_TABLES])}`,\n );\n\n if (required.length !== REQUIRED_TABLES.length)\n return yield* DoStorageCompatibilityError.make({\n actualVersion: Number(current[0].value),\n supportedVersion: CurrentDoStorageVersion,\n message:\n \"The predecessor store is missing required tables. Retain the original store for inspection; no upgrade was committed.\",\n });\n\n const recoveryTables =\n yield* sql`SELECT name FROM sqlite_master WHERE type='table' AND name='effect_agent_recovery_checkpoints'`;\n\n if (recoveryTables.length !== (current[0].value === \"5\" ? 1 : 0))\n return yield* DoStorageCompatibilityError.make({\n actualVersion: Number(current[0].value),\n supportedVersion: CurrentDoStorageVersion,\n message:\n \"The predecessor recovery checkpoint storage does not match its version; refusing ambiguous data without mutation.\",\n });\n\n const indexes =\n yield* sql`SELECT name FROM sqlite_master WHERE name='effect_agent_submissions_nonterminal'`;\n\n if (indexes.length !== 0)\n return yield* DoStorageCompatibilityError.make({\n actualVersion: Number(current[0].value),\n supportedVersion: CurrentDoStorageVersion,\n message:\n \"The predecessor already contains the nonterminal index; refusing ambiguous storage without mutation.\",\n });\n if (current[0].value === \"2\") {\n yield* checkV2ThreadLayout();\n for (const statement of [\n sql`ALTER TABLE effect_agent_submissions ADD COLUMN admission_group TEXT`,\n sql`ALTER TABLE effect_agent_submissions ADD COLUMN admission_fence_json TEXT`,\n sql`CREATE INDEX effect_agent_submissions_group ON effect_agent_submissions (thread_id, admission_group, state)`,\n ]) {\n yield* failpoint(\"upgrade:before-mutation\");\n yield* statement;\n yield* failpoint(\"upgrade:after-mutation\");\n }\n }\n if (current[0].value === \"3\") yield* checkPredecessorLayout(3);\n if (current[0].value === \"4\") yield* checkPredecessorLayout(4);\n if (current[0].value === \"5\") yield* checkPredecessorLayout(5);\n if (current[0].value === \"2\" || current[0].value === \"3\") {\n yield* failpoint(\"upgrade:before-mutation\");\n yield* sql`ALTER TABLE effect_agent_submissions ADD COLUMN worker_admission_json TEXT`;\n yield* failpoint(\"upgrade:after-mutation\");\n yield* failpoint(\"upgrade:before-mutation\");\n yield* sql`ALTER TABLE effect_agent_submissions ADD COLUMN message_admission_json TEXT`;\n yield* failpoint(\"upgrade:after-mutation\");\n yield* failpoint(\"upgrade:before-mutation\");\n yield* createMessageDeliveryTables;\n yield* failpoint(\"upgrade:after-mutation\");\n }\n if (current[0].value !== \"5\") {\n yield* failpoint(\"upgrade:before-mutation\");\n yield* createRecoveryCheckpointTable;\n yield* failpoint(\"upgrade:after-mutation\");\n }\n yield* failpoint(\"upgrade:before-mutation\");\n yield* createNonterminalIndex;\n yield* failpoint(\"upgrade:after-mutation\");\n yield* failpoint(\"upgrade:before-version\");\n yield* createNativeReadIndexes;\n yield* createMessageDeliveryPendingIndex;\n yield* seedNativeReadIndexes.pipe(\n Effect.catchTag(\"ThreadStoreError\", (error) =>\n DoStorageCorruptionError.make({\n table: \"effect_agent_canonical_records\",\n rowKey: \"upgrade\",\n message: error.message,\n }),\n ),\n );\n yield* sql`UPDATE effect_agent_meta SET value='7' WHERE key='storage_version'`;\n yield* failpoint(\"upgrade:after-version\");\n }),\n )\n .pipe(\n Effect.catchTag(\"DoStorageFailpointError\", (error) =>\n DoStorageError.make({\n cause: error,\n operation: \"upgrade storage\",\n message: error.message,\n }),\n ),\n Effect.catchTag(\"StorageUpgradeError\", (error) =>\n DoStorageCorruptionError.make({\n table: error.table,\n rowKey: error.rowKey,\n message: error.message,\n }),\n ),\n Effect.catchTag(\"SchemaError\", (error) =>\n DoStorageCorruptionError.make({\n table: \"effect_agent_canonical_records\",\n rowKey: \"upgrade\",\n message: error.message,\n }),\n ),\n Effect.catchTag(\"SqlError\", storageError(\"upgrade supported thread storage\")),\n );\n } else if (version.value === \"6\") {\n yield* sql\n .withTransaction(\n Effect.gen(function* () {\n const current = yield* sql<{\n value: string;\n }>`SELECT value FROM effect_agent_meta WHERE key = 'storage_version'`;\n\n if (current[0]?.value === \"7\") return;\n if (current[0]?.value !== \"6\")\n return yield* DoStorageCompatibilityError.make({\n actualVersion: -1,\n supportedVersion: 7,\n message: \"Storage version changed during native index upgrade\",\n });\n yield* checkPredecessorLayout(5);\n\n const requiredIndex =\n yield* sql`SELECT name FROM sqlite_master WHERE type = 'index' AND name = 'effect_agent_submissions_nonterminal'`;\n\n if (requiredIndex.length !== 1)\n return yield* DoStorageCompatibilityError.make({\n actualVersion: 6,\n supportedVersion: 7,\n message: \"Predecessor storage is missing its required nonterminal index\",\n });\n yield* failpoint(\"upgrade:before-mutation\");\n yield* createNativeReadIndexes;\n yield* createMessageDeliveryPendingIndex;\n yield* seedNativeReadIndexes.pipe(\n Effect.catchTag(\"ThreadStoreError\", (error) =>\n DoStorageCorruptionError.make({\n table: \"effect_agent_canonical_records\",\n rowKey: \"upgrade\",\n message: error.message,\n }),\n ),\n );\n yield* failpoint(\"upgrade:after-mutation\");\n yield* failpoint(\"upgrade:before-version\");\n yield* sql`UPDATE effect_agent_meta SET value='7' WHERE key='storage_version'`;\n yield* failpoint(\"upgrade:after-version\");\n }),\n )\n .pipe(\n Effect.mapError((error) =>\n DoStorageError.make({\n operation: \"upgrade native indexes\",\n message: error.message,\n cause: error,\n }),\n ),\n );\n } else if (version.value !== String(CurrentDoStorageVersion)) {\n const actualVersion = Number.parseInt(version.value, 10);\n\n return yield* DoStorageCompatibilityError.make({\n actualVersion: Number.isSafeInteger(actualVersion) ? actualVersion : -1,\n supportedVersion: CurrentDoStorageVersion,\n message:\n `The Durable Object uses unsupported storage version ${version.value}; ` +\n `this build supports exactly version ${CurrentDoStorageVersion}. ` +\n \"Only supported v2, v3, v4, v5 and v6 can be upgraded automatically. Keep the original store and use a compatible library version.\",\n });\n }\n }\n\n const requiredRows = yield* sql<Record<string, unknown>>`\n SELECT name\n FROM sqlite_master\n WHERE (type = 'table'\n AND name IN ${sql.in([...REQUIRED_TABLES, \"effect_agent_message_deliveries\", \"effect_agent_recovery_checkpoints\"])}\n ) OR (type = 'index' AND name IN ('effect_agent_submissions_nonterminal', 'effect_agent_records_subtree', 'effect_agent_message_deliveries_pending', 'effect_agent_records_outstanding', 'effect_agent_records_call', 'effect_agent_records_run_input', 'effect_agent_records_worker_input'))\n ORDER BY name\n `.pipe(Effect.mapError(storageError(\"verify storage tables\")));\n\n const required = yield* decodeRows(\n Schema.Array(DoNameRow),\n \"sqlite_master\",\n \"required_tables\",\n requiredRows,\n );\n\n if (required.length !== REQUIRED_TABLES.length + 9) {\n return yield* DoStorageCompatibilityError.make({\n actualVersion: CurrentDoStorageVersion,\n supportedVersion: CurrentDoStorageVersion,\n message:\n \"The Durable Object claims the current format but is missing required tables or its nonterminal index. Retain the original store for inspection.\",\n });\n }\n\n return makeJournal(sql, failpoint, maxStoredValueBytes);\n});\n\nconst makeJournal = (\n sql: SqlClient.SqlClient,\n failpoint: DoJournalFailpoint,\n maxStoredValueBytes: number,\n) => {\n /** Typed pre-write refusal for any single value over the configured byte bound. */\n const checkValueBound = (\n operation: string,\n value: string,\n ): Effect.Effect<void, DoValueBoundExceeded> => {\n const actualBytes = storedTextBytes(value);\n\n return actualBytes > maxStoredValueBytes\n ? Effect.fail(\n DoValueBoundExceeded.make({\n actualBytes,\n maxBytes: maxStoredValueBytes,\n operation,\n }),\n )\n : Effect.void;\n };\n\n /**\n * Runs one journal write transaction on the Durable Object storage-backed\n * `withTransaction` (`ctx.storage.transaction()` under the hood). Within one Durable\n * Object there is exactly ONE writer, so the Node `BEGIN IMMEDIATE` + busy-retry +\n * `SqliteWriteContention` machinery has no analogue here and is deliberately absent.\n * Ownership-token and epoch checks still run INSIDE the transaction, so fencing atomicity\n * (DUR-006) is preserved identically.\n *\n * Journal write transactions are always top level: the Durable Object client rejects\n * nested transactions, so new journal operations must not wrap this helper inside another\n * transaction.\n */\n const withWriteTransaction =\n (operation: string) =>\n <A, E>(effect: Effect.Effect<A, E>): Effect.Effect<A, E | DoStorageError> =>\n sql.withTransaction(effect).pipe(\n Effect.mapError((error) => (isSqlError(error) ? storageError(operation)(error) : error)),\n Effect.withSpan(\"DoJournal.withWriteTransaction\", { attributes: { operation } }),\n );\n\n const materialize = Effect.fn(\"DoJournal.materialize\")(function* (\n threadId: string,\n createdAt: string,\n emptyTailDigest: string,\n producerEpoch: ProducerEpoch,\n ): Effect.fn.Return<\n void,\n DoFenceRejected | DoStorageCorruptionError | DoStorageError | DoValueBoundExceeded\n > {\n if (threadId.length > MAX_IDENTIFIER_LENGTH) {\n return yield* DoStorageError.make({\n operation: \"materialize thread\",\n message: \"Thread identity exceeds the Durable Object storage bounds.\",\n });\n }\n yield* checkValueBound(\"materialize thread\", emptyTailDigest);\n yield* withWriteTransaction(\"materialize transaction\")(\n Effect.gen(function* () {\n const existingRows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n created_at,\n tail_sequence,\n tail_digest,\n producer_epoch\n FROM effect_agent_threads\n WHERE thread_id = ${threadId}\n `.pipe(Effect.mapError(storageError(\"read materialized thread\")));\n\n const existing = yield* decodeRows(\n Schema.Array(ThreadRow),\n \"effect_agent_threads\",\n threadId,\n existingRows,\n );\n\n if (existing.length > 1) {\n return yield* DoStorageCorruptionError.make({\n table: \"effect_agent_threads\",\n rowKey: threadId,\n message: \"A thread primary key returned more than one row.\",\n });\n }\n if (existing.length === 0) {\n yield* sql`\n INSERT INTO effect_agent_threads (\n thread_id,\n created_at,\n tail_sequence,\n tail_digest,\n producer_epoch\n ) VALUES (\n ${threadId},\n ${createdAt},\n 0,\n ${emptyTailDigest},\n ${producerEpoch}\n )\n `.pipe(Effect.mapError(storageError(\"materialize thread\")));\n\n return;\n }\n if (producerEpoch < existing[0].producer_epoch) {\n return yield* DoFenceRejected.make({\n producerEpoch,\n actualEpoch: existing[0].producer_epoch,\n message: `Producer epoch ${producerEpoch} is stale; current epoch is ${existing[0].producer_epoch}.`,\n });\n }\n if (producerEpoch > existing[0].producer_epoch) {\n yield* sql`\n UPDATE effect_agent_threads\n SET producer_epoch = ${producerEpoch}\n WHERE thread_id = ${threadId}\n `.pipe(Effect.mapError(storageError(\"advance materialization epoch\")));\n }\n }),\n );\n });\n\n const getThread = Effect.fn(\"DoJournal.getThread\")(function* (threadId: string) {\n const rows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n created_at,\n tail_sequence,\n tail_digest,\n producer_epoch\n FROM effect_agent_threads\n WHERE thread_id = ${threadId}\n `.pipe(Effect.mapError(storageError(\"read thread\")));\n\n return yield* decodeRows(Schema.Array(ThreadRow), \"effect_agent_threads\", threadId, rows);\n });\n\n const append = Effect.fn(\"DoJournal.append\")(function* (\n request: RawAppendRequest,\n ): Effect.fn.Return<RawAppendResult, AppendError> {\n if (\n request.threadId.length > MAX_IDENTIFIER_LENGTH ||\n request.batchId.length > MAX_IDENTIFIER_LENGTH ||\n request.records.some((record) => record.recordId.length > MAX_IDENTIFIER_LENGTH)\n ) {\n return yield* DoStorageError.make({\n operation: \"append canonical batch\",\n message: \"Canonical identifiers exceed the Durable Object storage bounds.\",\n });\n }\n // The platform's ~2 MB per-value limit, enforced typed BEFORE any write (plan §1.2).\n yield* checkValueBound(\"append canonical batch\", request.batchJson);\n yield* checkValueBound(\"append canonical batch\", request.batchDigest);\n yield* checkValueBound(\"append canonical batch\", request.tailDigest);\n yield* Effect.forEach(\n request.records,\n (record) => checkValueBound(\"append canonical record\", record.recordJson),\n { discard: true },\n );\n\n return yield* withWriteTransaction(\"append transaction\")(\n Effect.gen(function* () {\n const recordIds = request.records.map((record) => record.recordId);\n\n if (new Set(recordIds).size !== recordIds.length) {\n return yield* DoAppendConflict.make({\n message: `Batch ${request.batchId} contains duplicate canonical record IDs.`,\n reason: \"record-identity\",\n });\n }\n\n const threadRows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n created_at,\n tail_sequence,\n tail_digest,\n producer_epoch\n FROM effect_agent_threads\n WHERE thread_id = ${request.threadId}\n `.pipe(Effect.mapError(storageError(\"read append tail\")));\n\n const thread = yield* decodeSingleRow(\n Schema.Array(ThreadRow),\n \"effect_agent_threads\",\n request.threadId,\n threadRows,\n );\n\n if (request.producerEpoch !== thread.producer_epoch) {\n return yield* DoFenceRejected.make({\n producerEpoch: request.producerEpoch,\n actualEpoch: thread.producer_epoch,\n message: `Producer epoch ${request.producerEpoch} is not the current epoch ${thread.producer_epoch}.`,\n });\n }\n\n const batchRows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n batch_id,\n first_sequence,\n last_sequence,\n batch_digest,\n tail_digest,\n batch_json\n FROM effect_agent_canonical_batches\n WHERE thread_id = ${request.threadId}\n AND batch_id = ${request.batchId}\n `.pipe(Effect.mapError(storageError(\"read idempotent batch\")));\n\n const batches = yield* decodeRows(\n Schema.Array(BatchRow),\n \"effect_agent_canonical_batches\",\n `${request.threadId}/${request.batchId}`,\n batchRows,\n );\n\n if (batches.length > 1) {\n return yield* DoStorageCorruptionError.make({\n table: \"effect_agent_canonical_batches\",\n rowKey: `${request.threadId}/${request.batchId}`,\n message: \"A canonical batch primary key returned more than one row.\",\n });\n }\n if (batches.length === 1) {\n const existing = batches[0];\n\n if (existing.batch_digest !== request.batchDigest) {\n return yield* DoAppendConflict.make({\n message: `Batch ${request.batchId} already exists with different canonical content.`,\n reason: \"batch-digest\",\n });\n }\n\n return RawAppendResult.make({\n firstSequence: existing.first_sequence,\n lastSequence: existing.last_sequence,\n replayed: true,\n tailDigest: existing.tail_digest,\n });\n }\n\n if (\n request.expectedTailSequence !== thread.tail_sequence ||\n request.expectedTailDigest !== thread.tail_digest\n ) {\n return yield* DoAppendConflict.make({\n message:\n `Expected tail ${request.expectedTailSequence}/${request.expectedTailDigest} ` +\n `but found ${thread.tail_sequence}/${thread.tail_digest}.`,\n reason: \"tail\",\n actualTailSequence: thread.tail_sequence,\n actualTailDigest: thread.tail_digest,\n });\n }\n if (thread.tail_sequence + request.records.length > MAX_RECORDS_PER_THREAD) {\n return yield* DoStorageError.make({\n operation: \"append canonical batch\",\n message: `Thread record limit ${MAX_RECORDS_PER_THREAD} would be exceeded.`,\n });\n }\n\n // Chunked to respect the Durable Object platform's 100-bound-parameter statement\n // limit: a batch may carry up to 256 records.\n const existingRecords: Array<RecordRow> = [];\n\n for (const chunk of chunked(recordIds, MAX_BOUND_PARAMETERS - 10)) {\n const existingRecordRows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n sequence,\n record_id,\n batch_id,\n record_json\n FROM effect_agent_canonical_records\n WHERE thread_id = ${request.threadId}\n AND record_id IN ${sql.in([...chunk])}\n ORDER BY sequence\n `.pipe(Effect.mapError(storageError(\"check canonical record identities\")));\n\n existingRecords.push(\n ...(yield* decodeRows(\n Schema.Array(RecordRow),\n \"effect_agent_canonical_records\",\n `${request.threadId}/record_ids`,\n existingRecordRows,\n )),\n );\n }\n if (existingRecords.length > 0) {\n return yield* DoAppendConflict.make({\n message: `Canonical record ID ${existingRecords[0].record_id} already exists.`,\n reason: \"record-identity\",\n });\n }\n\n const firstSequence = yield* Schema.decodeEffect(CanonicalSequence)(\n thread.tail_sequence + 1,\n ).pipe(\n Effect.mapError((error) =>\n DoStorageError.make({\n cause: error,\n operation: \"append canonical batch\",\n message: error.message,\n }),\n ),\n );\n\n const lastSequence = yield* Schema.decodeEffect(CanonicalSequence)(\n firstSequence + request.records.length - 1,\n ).pipe(\n Effect.mapError((error) =>\n DoStorageError.make({\n cause: error,\n operation: \"append canonical batch\",\n message: error.message,\n }),\n ),\n );\n\n yield* sql`\n INSERT INTO effect_agent_canonical_batches (\n thread_id,\n batch_id,\n first_sequence,\n last_sequence,\n batch_digest,\n tail_digest,\n batch_json\n ) VALUES (\n ${request.threadId},\n ${request.batchId},\n ${firstSequence},\n ${lastSequence},\n ${request.batchDigest},\n ${request.tailDigest},\n ${request.batchJson}\n )\n `.pipe(Effect.mapError(storageError(\"insert canonical batch\")));\n yield* failpoint(\"append:after-batch-insert\");\n\n yield* Effect.forEach(\n request.records,\n (record, index) =>\n Effect.gen(function* () {\n yield* sql`\n INSERT INTO effect_agent_canonical_records (\n thread_id,\n sequence,\n record_id,\n batch_id,\n record_json\n ) VALUES (\n ${request.threadId},\n ${firstSequence + index},\n ${record.recordId},\n ${request.batchId},\n ${record.recordJson}\n )\n `.pipe(Effect.mapError(storageError(\"insert canonical record\")));\n\n const canonical = yield* Schema.decodeEffect(Schema.fromJsonString(CanonicalRecord))(\n record.recordJson,\n ).pipe(\n Effect.mapError((error) =>\n DoStorageCorruptionError.make({\n table: \"effect_agent_canonical_records\",\n rowKey: record.recordId,\n message: error.message,\n }),\n ),\n );\n\n yield* indexCanonicalRecord(request.threadId, canonical).pipe(\n Effect.provideService(SqlClient.SqlClient, sql),\n Effect.mapError(storageError(\"index canonical record\")),\n );\n yield* failpoint(\"append:after-record-insert\");\n }),\n { discard: true },\n );\n\n yield* sql`\n UPDATE effect_agent_threads\n SET\n tail_sequence = ${lastSequence},\n tail_digest = ${request.tailDigest},\n producer_epoch = ${request.producerEpoch}\n WHERE thread_id = ${request.threadId}\n `.pipe(Effect.mapError(storageError(\"advance thread tail\")));\n yield* failpoint(\"append:after-tail-update\");\n\n return RawAppendResult.make({\n firstSequence,\n lastSequence,\n replayed: false,\n tailDigest: request.tailDigest,\n });\n }),\n );\n });\n\n const read = Effect.fnUntraced(function* (request: RawReadRequest) {\n // Capture membership without retaining payloads. Append-only sequences keep each later\n // payload query inside this snapshot, even when new records arrive during consumption.\n const planRows = yield* sql<Record<string, unknown>>`\n SELECT\n sequence,\n length(CAST(record_json AS BLOB)) AS record_json_bytes\n FROM effect_agent_canonical_records\n WHERE thread_id = ${request.threadId}\n AND sequence > ${request.fromSequenceExclusive}\n ORDER BY sequence\n LIMIT ${request.limit}\n `.pipe(Effect.mapError(storageError(\"read canonical records\")));\n\n const plan = yield* decodeRows(\n Schema.Array(ReadPlanRow),\n \"effect_agent_canonical_records\",\n `${request.threadId}>${request.fromSequenceExclusive}`,\n planRows,\n \"ReadPlanRow\",\n );\n\n const mismatch = () =>\n DoStorageCorruptionError.make({\n table: \"effect_agent_canonical_records\",\n rowKey: `${request.threadId}>${request.fromSequenceExclusive}`,\n message: \"Canonical read membership, sequence, or payload size changed from its read plan.\",\n });\n\n if (plan.length > request.limit) return yield* mismatch();\n\n const pages: Array<ReadPage> = [];\n let page: ReadPage | undefined;\n let pageBytes = 0;\n let previousSequence = request.fromSequenceExclusive;\n\n for (const row of plan) {\n if (row.sequence !== previousSequence + 1) return yield* mismatch();\n previousSequence = row.sequence;\n if (page === undefined || pageBytes + row.record_json_bytes > MAX_READ_PAGE_JSON_BYTES) {\n page = [row];\n pages.push(page);\n pageBytes = row.record_json_bytes;\n } else {\n page.push(row);\n pageBytes += row.record_json_bytes;\n }\n }\n\n const readPage = Effect.fn(\"DoJournal.readPage\")(function* (page: ReadPage) {\n const rows = yield* sql<Record<string, unknown>>`\n SELECT thread_id, sequence, record_id, batch_id, record_json\n FROM effect_agent_canonical_records\n WHERE thread_id = ${request.threadId}\n AND sequence >= ${page[0].sequence}\n AND sequence <= ${page[page.length - 1].sequence}\n ORDER BY sequence\n `.pipe(Effect.mapError(storageError(\"read canonical records\")));\n\n const decoded = yield* decodeRows(\n Schema.Array(RecordRow),\n \"effect_agent_canonical_records\",\n `${request.threadId}/${page[0].sequence}`,\n rows,\n \"RecordRow\",\n );\n\n if (\n decoded.length !== page.length ||\n decoded.some(\n (row, index) =>\n row.thread_id !== request.threadId ||\n row.sequence !== page[index].sequence ||\n storedTextBytes(row.record_json) !== page[index].record_json_bytes,\n )\n ) {\n return yield* mismatch();\n }\n\n return decoded;\n });\n\n return {\n count: plan.length,\n records: Stream.fromIterable(pages).pipe(\n Stream.flatMap((page) => Stream.fromIterableEffect(readPage(page))),\n ),\n };\n });\n\n const exportThread = Effect.fn(\"DoJournal.exportThread\")(function* (threadId: string) {\n return yield* sql\n .withTransaction(\n Effect.gen(function* () {\n const threadRows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n created_at,\n tail_sequence,\n tail_digest,\n producer_epoch\n FROM effect_agent_threads\n WHERE thread_id = ${threadId}\n `.pipe(Effect.mapError(storageError(\"export thread\")));\n\n const thread = yield* decodeSingleRow(\n Schema.Array(ThreadRow),\n \"effect_agent_threads\",\n threadId,\n threadRows,\n );\n\n yield* failpoint(\"export:after-thread-read\");\n\n if (thread.tail_sequence > MAX_RECORDS_PER_THREAD)\n return yield* DoStorageError.make({\n operation: \"export thread\",\n message: \"The thread exceeds the current export record limit.\",\n });\n const records: Array<RecordRow> = [];\n let afterSequence = ZERO_SEQUENCE;\n\n while (afterSequence < thread.tail_sequence) {\n const limit = Math.min(1_024, thread.tail_sequence - afterSequence);\n\n const request = RawReadRequest.make({\n threadId,\n fromSequenceExclusive: afterSequence,\n limit,\n });\n\n const plan = yield* read(request);\n const page = yield* Stream.runCollect(plan.records);\n\n if (\n page.length !== limit ||\n page.some((record, index) => record.sequence !== afterSequence + index + 1)\n ) {\n return yield* DoStorageCorruptionError.make({\n table: \"effect_agent_canonical_records\",\n rowKey: threadId,\n message:\n \"The exported canonical prefix is not contiguous through its captured tail.\",\n });\n }\n records.push(...page);\n afterSequence = page[page.length - 1].sequence;\n }\n\n const beyondTail =\n yield* sql`SELECT sequence FROM effect_agent_canonical_records WHERE thread_id=${threadId} AND sequence > ${thread.tail_sequence} LIMIT 1`.pipe(\n Effect.mapError(storageError(\"verify export tail\")),\n );\n\n if (beyondTail.length !== 0)\n return yield* DoStorageCorruptionError.make({\n table: \"effect_agent_canonical_records\",\n rowKey: threadId,\n message: \"Canonical records exist beyond the captured thread tail.\",\n });\n\n return RawThreadExport.make({ thread, records });\n }),\n )\n .pipe(\n Effect.catchTag(\"SqlError\", (error) =>\n Effect.fail(storageError(\"export transaction\")(error)),\n ),\n );\n });\n\n const saveCheckpoint = Effect.fn(\"DoJournal.saveCheckpoint\")(function* (\n checkpoint: RawCheckpoint,\n ): Effect.fn.Return<void, CheckpointError> {\n if (checkpoint.threadId.length > MAX_IDENTIFIER_LENGTH) {\n return yield* DoStorageError.make({\n operation: \"save checkpoint\",\n message: \"Checkpoint identity exceeds the Durable Object storage bounds.\",\n });\n }\n yield* checkValueBound(\"save checkpoint\", checkpoint.checkpointJson);\n yield* withWriteTransaction(\"checkpoint transaction\")(\n Effect.gen(function* () {\n const threadRows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n created_at,\n tail_sequence,\n tail_digest,\n producer_epoch\n FROM effect_agent_threads\n WHERE thread_id = ${checkpoint.threadId}\n `.pipe(Effect.mapError(storageError(\"read checkpoint tail\")));\n\n const thread = yield* decodeSingleRow(\n Schema.Array(ThreadRow),\n \"effect_agent_threads\",\n checkpoint.threadId,\n threadRows,\n );\n\n if (checkpoint.throughSequence > thread.tail_sequence) {\n return yield* DoCheckpointConflict.make({\n message:\n `Checkpoint sequence ${checkpoint.throughSequence} is after canonical tail ` +\n `${thread.tail_sequence}.`,\n });\n }\n\n const checkpointRows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n through_sequence,\n tail_digest,\n checkpoint_json\n FROM effect_agent_checkpoints\n WHERE thread_id = ${checkpoint.threadId}\n AND through_sequence = ${checkpoint.throughSequence}\n `.pipe(Effect.mapError(storageError(\"read idempotent checkpoint\")));\n\n const existing = yield* decodeRows(\n Schema.Array(CheckpointRow),\n \"effect_agent_checkpoints\",\n `${checkpoint.threadId}/${checkpoint.throughSequence}`,\n checkpointRows,\n );\n\n if (existing.length > 1) {\n return yield* DoStorageCorruptionError.make({\n table: \"effect_agent_checkpoints\",\n rowKey: `${checkpoint.threadId}/${checkpoint.throughSequence}`,\n message: \"A checkpoint primary key returned more than one row.\",\n });\n }\n if (existing.length === 1) {\n if (\n existing[0].tail_digest !== checkpoint.tailDigest ||\n existing[0].checkpoint_json !== checkpoint.checkpointJson\n ) {\n return yield* DoCheckpointConflict.make({\n message: \"A different checkpoint already exists at this canonical sequence.\",\n });\n }\n\n return;\n }\n\n yield* sql`\n INSERT INTO effect_agent_checkpoints (\n thread_id,\n through_sequence,\n tail_digest,\n checkpoint_json\n ) VALUES (\n ${checkpoint.threadId},\n ${checkpoint.throughSequence},\n ${checkpoint.tailDigest},\n ${checkpoint.checkpointJson}\n )\n `.pipe(Effect.mapError(storageError(\"insert checkpoint\")));\n }),\n );\n });\n\n const saveRecoveryCheckpoint = Effect.fn(\"DoJournal.saveRecoveryCheckpoint\")(function* (\n request: SaveRecoveryCheckpointRequest,\n checkpointJson: string,\n ) {\n const { checkpoint } = request;\n\n if (checkpoint.threadId.length > MAX_IDENTIFIER_LENGTH) {\n return yield* DoStorageError.make({\n operation: \"save recovery checkpoint\",\n message: \"Checkpoint identity exceeds the Durable Object storage bounds.\",\n });\n }\n yield* checkValueBound(\"save recovery checkpoint\", checkpointJson);\n // Keep injected waits outside the storage-backed transaction callback.\n yield* failpoint(\"save-recovery-checkpoint:before\");\n yield* withWriteTransaction(\"recovery checkpoint transaction\")(\n Effect.gen(function* () {\n const threads = yield* getThread(checkpoint.threadId);\n const thread = threads[0];\n\n if (thread === undefined)\n return yield* ThreadNotMaterialized.make({ threadId: checkpoint.threadId });\n if (request.producerEpoch !== thread.producer_epoch)\n return yield* FenceRejected.make({\n threadId: checkpoint.threadId,\n actualEpoch: thread.producer_epoch,\n attemptedEpoch: request.producerEpoch,\n });\n if (checkpoint.throughSequence > thread.tail_sequence)\n return yield* CheckpointRejected.make({\n threadId: checkpoint.threadId,\n reason: \"ahead-of-tail\",\n });\n\n const digests =\n checkpoint.throughSequence === 0\n ? [EMPTY_TAIL_DIGEST]\n : yield* getTailDigestAt(checkpoint.threadId, checkpoint.throughSequence);\n\n if (digests.length !== 1 || digests[0] !== checkpoint.tailDigest)\n return yield* CheckpointRejected.make({\n threadId: checkpoint.threadId,\n reason: \"digest-mismatch\",\n });\n\n yield* sql`\n INSERT INTO effect_agent_recovery_checkpoints (thread_id, through_sequence, tail_digest, checkpoint_json)\n VALUES (${checkpoint.threadId}, ${checkpoint.throughSequence}, ${checkpoint.tailDigest}, ${checkpointJson})\n ON CONFLICT (thread_id) DO UPDATE SET\n through_sequence = excluded.through_sequence,\n tail_digest = excluded.tail_digest,\n checkpoint_json = excluded.checkpoint_json\n WHERE excluded.through_sequence >= effect_agent_recovery_checkpoints.through_sequence\n `.pipe(Effect.mapError(storageError(\"save recovery checkpoint\")));\n }),\n );\n yield* failpoint(\"save-recovery-checkpoint:after\");\n });\n\n const loadRecoveryCheckpoint = Effect.fn(\"DoJournal.loadRecoveryCheckpoint\")(function* (\n threadId: string,\n ) {\n const rows = yield* sql<Record<string, unknown>>`\n SELECT thread_id, through_sequence, tail_digest, checkpoint_json\n FROM effect_agent_recovery_checkpoints\n WHERE thread_id = ${threadId}\n `.pipe(Effect.mapError(storageError(\"load recovery checkpoint\")));\n\n return yield* decodeRows(\n Schema.Array(CheckpointRow),\n \"effect_agent_recovery_checkpoints\",\n threadId,\n rows,\n );\n });\n\n const loadCheckpoint = Effect.fn(\"DoJournal.loadCheckpoint\")(function* (\n threadId: string,\n atOrBeforeSequence: CanonicalSequence,\n ) {\n const rows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n through_sequence,\n tail_digest,\n checkpoint_json\n FROM effect_agent_checkpoints\n WHERE thread_id = ${threadId}\n AND through_sequence <= ${atOrBeforeSequence}\n ORDER BY through_sequence DESC\n LIMIT 1\n `.pipe(Effect.mapError(storageError(\"load checkpoint\")));\n\n return yield* decodeRows(\n Schema.Array(CheckpointRow),\n \"effect_agent_checkpoints\",\n `${threadId}<=${atOrBeforeSequence}`,\n rows,\n );\n });\n\n const getTailDigestAt = Effect.fn(\"DoJournal.getTailDigestAt\")(function* (\n threadId: string,\n sequence: CanonicalSequence,\n ) {\n if (sequence === 0) {\n const threads = yield* getThread(threadId);\n\n return threads.length === 0\n ? []\n : [threads[0].tail_sequence === 0 ? threads[0].tail_digest : undefined].filter(\n (value): value is string => value !== undefined,\n );\n }\n\n const rows = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n batch_id,\n first_sequence,\n last_sequence,\n batch_digest,\n tail_digest,\n batch_json\n FROM effect_agent_canonical_batches\n WHERE thread_id = ${threadId}\n AND last_sequence = ${sequence}\n `.pipe(Effect.mapError(storageError(\"read canonical digest at sequence\")));\n\n const batches = yield* decodeRows(\n Schema.Array(BatchRow),\n \"effect_agent_canonical_batches\",\n `${threadId}/${sequence}`,\n rows,\n );\n\n return batches.map((batch) => batch.tail_digest);\n });\n\n const scanStoredPayloads = Effect.fn(\"DoJournal.scanStoredPayloads\")(function* () {\n return yield* sql\n .withTransaction(\n Effect.gen(function* () {\n const threads = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n created_at,\n tail_sequence,\n tail_digest,\n producer_epoch\n FROM effect_agent_threads\n ORDER BY thread_id\n `.pipe(Effect.mapError(storageError(\"scan threads\")));\n\n const batches = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n batch_id,\n first_sequence,\n last_sequence,\n batch_digest,\n tail_digest,\n batch_json\n FROM effect_agent_canonical_batches\n ORDER BY thread_id, first_sequence\n `.pipe(Effect.mapError(storageError(\"scan canonical batches\")));\n\n const records = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n sequence,\n record_id,\n batch_id,\n record_json\n FROM effect_agent_canonical_records\n ORDER BY thread_id, sequence\n `.pipe(Effect.mapError(storageError(\"scan canonical records\")));\n\n const checkpoints = yield* sql<Record<string, unknown>>`\n SELECT\n thread_id,\n through_sequence,\n tail_digest,\n checkpoint_json\n FROM effect_agent_checkpoints\n ORDER BY thread_id, through_sequence\n `.pipe(Effect.mapError(storageError(\"scan checkpoints\")));\n\n return {\n threads: yield* decodeRows(\n Schema.Array(ThreadRow),\n \"effect_agent_threads\",\n \"startup_scan\",\n threads,\n ),\n batches: yield* decodeRows(\n Schema.Array(BatchRow),\n \"effect_agent_canonical_batches\",\n \"startup_scan\",\n batches,\n ),\n records: yield* decodeRows(\n Schema.Array(RecordRow),\n \"effect_agent_canonical_records\",\n \"startup_scan\",\n records,\n ),\n checkpoints: yield* decodeRows(\n Schema.Array(CheckpointRow),\n \"effect_agent_checkpoints\",\n \"startup_scan\",\n checkpoints,\n ),\n };\n }),\n )\n .pipe(\n Effect.catchTag(\"SqlError\", (error) =>\n Effect.fail(storageError(\"startup scan transaction\")(error)),\n ),\n );\n });\n\n return {\n append,\n checkValueBound,\n exportThread,\n getThread,\n getTailDigestAt,\n loadCheckpoint,\n loadRecoveryCheckpoint,\n saveRecoveryCheckpoint,\n materialize,\n read,\n saveCheckpoint,\n scanStoredPayloads,\n withWriteTransaction,\n } as const;\n};\n\nexport type DoJournal = ReturnType<typeof makeJournal>;\n\nexport const initializeDoJournal = ensureCurrentStorage;\n"],"mappings":"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AAO,oBAAoB,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,KACvC,OAAO,UAAU,UACf,yBAAyB,KAAK;CAC5B;CACA;CACA,SAAS;CACT,YAAY,sBAAsB,KAAK;EACrC,UAAU,MAAM;EAChB,WAAW;EACX;EACA,UAAU,MAAM,MAAM;CACxB,CAAC;AACH,CAAC,CACH,GACA,OAAO,UAAU,UACf,OAAO,oBAAoB;CACzB,6BAA6B,MAAM,YAAY;CAC/C,yBAAyB,MAAM,YAAY;CAC3C,2BAA2B;CAC3B,yBAAyB,MAAM,YAAY;AAC7C,CAAC,CACH,CACF,CACJ;;AAGA,MAAa,kBAAkB,OAAO,IAElC,QACA,OACA,QACA,SAEA,WAAW,QAAQ,OAAO,QAAQ,IAAI,CAAC,CAAC,KACtC,OAAO,SAAS,YACd,QAAQ,WAAW,IACf,OAAO,QAAQ,QAAQ,EAAE,IACzB,OAAO,KACL,yBAAyB,KAAK;CAC5B;CACA;CACA,SAAS,sCAAsC,QAAQ,OAAO;AAChE,CAAC,CACH,CACN,CACF,CACJ;;AAGA,MAAM,qBAAqB;CACzB,sBAAsB;EACpB;EACA;EACA;EACA;EACA;CACF;CACA,gCAAgC;EAC9B;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,gCAAgC;EAAC;EAAa;EAAY;EAAa;EAAY;CAAa;CAChG,0BAA0B;EAAC;EAAa;EAAoB;EAAe;CAAiB;CAC5F,0BAA0B;EACxB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,mCAAmC;EACjC;EACA;EACA;EACA;EACA;EACA;CACF;CACA,uBAAuB;EACrB;EACA;EACA;EACA;EACA;EACA;CACF;CACA,sCAAsC;EACpC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,4BAA4B;EAC1B;EACA;EACA;EACA;EACA;CACF;CACA,iCAAiC;EAC/B;EACA;EACA;EACA;EACA;EACA;CACF;CACA,kCAAkC;EAChC;EACA;EACA;EACA;EACA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"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as DoMemoryStore_d_exports } from "./DoMemoryStore.mjs";
2
- import { c as DoStorageError_d_exports } from "./DoStorageError-Cmhvl4TQ.mjs";
2
+ import { c as DoStorageError_d_exports } from "./DoStorageError-DjpuLHmX.mjs";
3
3
  import { t as DoStorageConfig_d_exports } from "./DoStorageConfig.mjs";
4
4
  import { t as DoStorageFailpoint_d_exports } from "./DoStorageFailpoint.mjs";
5
5
  import { t as DoMessageDeliveryStore_d_exports } from "./DoMessageDeliveryStore.mjs";
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@effect-agent/storage-cloudflare","version":"0.1.0-beta.110","dependencies":{"@effect/platform-browser":"4.0.0-rc.115","@effect/sql-sqlite-do":"4.0.0-rc.115","effect-agent":"0.1.0-beta.110"},"devDependencies":{"@cloudflare/vitest-pool-workers":"0.21.3","@cloudflare/workers-types":"5.20260825.1","@effect-agent/testing":"0.1.0-beta.110","@effect/vitest":"4.0.0-rc.115","effect":"4.0.0-rc.115","typescript":"7.0.2","vite-plus":"0.3.2","vitest":"4.1.11"},"peerDependencies":{"effect":"^4.0.0-rc.115"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./do-memory-store":{"types":"./dist/DoMemoryStore.d.mts","default":"./dist/DoMemoryStore.mjs"},"./do-schedule-store":{"types":"./dist/DoScheduleStore.d.mts","default":"./dist/DoScheduleStore.mjs"},"./do-storage-config":{"types":"./dist/DoStorageConfig.d.mts","default":"./dist/DoStorageConfig.mjs"},"./do-storage-error":{"types":"./dist/DoStorageError.d.mts","default":"./dist/DoStorageError.mjs"},"./do-storage-failpoint":{"types":"./dist/DoStorageFailpoint.d.mts","default":"./dist/DoStorageFailpoint.mjs"},"./do-storage-version":{"types":"./dist/DoStorageVersion.d.mts","default":"./dist/DoStorageVersion.mjs"},"./do-submission-ledger":{"types":"./dist/DoSubmissionLedger.d.mts","default":"./dist/DoSubmissionLedger.mjs"},"./do-subscription-store":{"types":"./dist/DoSubscriptionStore.d.mts","default":"./dist/DoSubscriptionStore.mjs"},"./do-thread-store":{"types":"./dist/DoThreadStore.d.mts","default":"./dist/DoThreadStore.mjs"},"./memory-protocol":{"types":"./dist/MemoryProtocol.d.mts","default":"./dist/MemoryProtocol.mjs"},"./port-protocol":{"types":"./dist/PortProtocol.d.mts","default":"./dist/PortProtocol.mjs"},"./port-routing":{"types":"./dist/PortRouting.d.mts","default":"./dist/PortRouting.mjs"},"./testing/do-storage-failpoint-testing":{"types":"./dist/DoStorageFailpointTesting.d.mts","default":"./dist/DoStorageFailpointTesting.mjs"},"./do-message-delivery-store":{"types":"./dist/DoMessageDeliveryStore.d.mts","default":"./dist/DoMessageDeliveryStore.mjs"}},"description":"Durable Object SQLite storage adapters and the routed port protocol for Effect Agent on Cloudflare.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/storage-cloudflare"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json"}}
1
+ {"name":"@effect-agent/storage-cloudflare","version":"0.1.0-beta.112","dependencies":{"@effect/platform-browser":"4.0.0-rc.116","@effect/sql-sqlite-do":"4.0.0-rc.116","effect-agent":"0.1.0-beta.112"},"devDependencies":{"@cloudflare/vitest-pool-workers":"0.21.3","@cloudflare/workers-types":"5.20260825.1","@effect-agent/testing":"0.1.0-beta.112","@effect/vitest":"4.0.0-rc.116","effect":"4.0.0-rc.116","typescript":"7.0.2","vite-plus":"0.3.2","vitest":"4.1.11"},"peerDependencies":{"effect":"^4.0.0-rc.116"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./do-memory-store":{"types":"./dist/DoMemoryStore.d.mts","default":"./dist/DoMemoryStore.mjs"},"./do-schedule-store":{"types":"./dist/DoScheduleStore.d.mts","default":"./dist/DoScheduleStore.mjs"},"./do-storage-config":{"types":"./dist/DoStorageConfig.d.mts","default":"./dist/DoStorageConfig.mjs"},"./do-storage-error":{"types":"./dist/DoStorageError.d.mts","default":"./dist/DoStorageError.mjs"},"./do-storage-failpoint":{"types":"./dist/DoStorageFailpoint.d.mts","default":"./dist/DoStorageFailpoint.mjs"},"./do-storage-version":{"types":"./dist/DoStorageVersion.d.mts","default":"./dist/DoStorageVersion.mjs"},"./do-submission-ledger":{"types":"./dist/DoSubmissionLedger.d.mts","default":"./dist/DoSubmissionLedger.mjs"},"./do-subscription-store":{"types":"./dist/DoSubscriptionStore.d.mts","default":"./dist/DoSubscriptionStore.mjs"},"./do-thread-store":{"types":"./dist/DoThreadStore.d.mts","default":"./dist/DoThreadStore.mjs"},"./memory-protocol":{"types":"./dist/MemoryProtocol.d.mts","default":"./dist/MemoryProtocol.mjs"},"./port-protocol":{"types":"./dist/PortProtocol.d.mts","default":"./dist/PortProtocol.mjs"},"./port-routing":{"types":"./dist/PortRouting.d.mts","default":"./dist/PortRouting.mjs"},"./testing/do-storage-failpoint-testing":{"types":"./dist/DoStorageFailpointTesting.d.mts","default":"./dist/DoStorageFailpointTesting.mjs"},"./do-message-delivery-store":{"types":"./dist/DoMessageDeliveryStore.d.mts","default":"./dist/DoMessageDeliveryStore.mjs"}},"description":"Durable Object SQLite storage adapters and the routed port protocol for Effect Agent on Cloudflare.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/storage-cloudflare"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json"}}
@@ -1,5 +1,6 @@
1
1
  import { Schema } from "effect";
2
2
  import { CanonicalSequence, ProducerEpoch } from "effect-agent/records";
3
+ import { ThreadStoreDiagnostic } from "effect-agent/thread-store";
3
4
 
4
5
  /** The Durable Object's SQLite storage uses a private-development format this adapter cannot read. */
5
6
  export class DoStorageCompatibilityError extends Schema.TaggedError<DoStorageCompatibilityError>()(
@@ -18,6 +19,7 @@ export class DoStorageCorruptionError extends Schema.TaggedError<DoStorageCorrup
18
19
  message: Schema.String,
19
20
  rowKey: Schema.String,
20
21
  table: Schema.String,
22
+ diagnostic: Schema.optionalKey(ThreadStoreDiagnostic),
21
23
  },
22
24
  ) {}
23
25
 
@@ -26,6 +28,7 @@ export class DoStorageError extends Schema.TaggedError<DoStorageError>()("DoStor
26
28
  cause: Schema.optionalKey(Schema.Defect()),
27
29
  message: Schema.String,
28
30
  operation: Schema.String,
31
+ diagnostic: Schema.optionalKey(ThreadStoreDiagnostic),
29
32
  }) {}
30
33
 
31
34
  /**
@@ -72,6 +72,7 @@ import {
72
72
  SubmissionLookup,
73
73
  SubmissionLookupByKey,
74
74
  SubmissionSnapshot,
75
+ SubmissionWorkItem,
75
76
  SubmissionState,
76
77
  SettlementReservationSnapshot,
77
78
  settlementFailureFromRecord,
@@ -86,6 +87,7 @@ import {
86
87
  type ChildSettledOutcome,
87
88
  type SuspensionOutcome,
88
89
  } from "effect-agent/submission-ledger";
90
+ import { ThreadStoreDiagnostic } from "effect-agent/thread-store";
89
91
  import * as SqlClientService from "effect/unstable/sql/SqlClient";
90
92
  import type { SqlError } from "effect/unstable/sql/SqlError";
91
93
 
@@ -534,26 +536,27 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
534
536
  operation: string,
535
537
  row: SubmissionRow,
536
538
  ): Effect.fn.Return<SubmissionSnapshot, LedgerError> {
539
+ const decodeFailure = (error: Schema.SchemaError) =>
540
+ internalFailure(operation)(
541
+ DoStorageCorruptionError.make({
542
+ table: "effect_agent_submissions",
543
+ rowKey: row.submission_id,
544
+ message: "Stored submission does not satisfy the ledger schema",
545
+ diagnostic: ThreadStoreDiagnostic.make({
546
+ causeTag: error._tag,
547
+ operation,
548
+ decoder: "SubmissionSnapshot",
549
+ issueTag: error.issue._tag,
550
+ }),
551
+ }),
552
+ );
553
+
537
554
  const agentDigests = yield* parseStoredJsonText(row.agent_digests_json).pipe(
538
- Effect.mapError((error) =>
539
- corruptionFailure(
540
- operation,
541
- "effect_agent_submissions",
542
- row.submission_id,
543
- error.message,
544
- ),
545
- ),
555
+ Effect.mapError(decodeFailure),
546
556
  );
547
557
 
548
558
  const inputPayload = yield* parseStoredJsonText(row.input_json).pipe(
549
- Effect.mapError((error) =>
550
- corruptionFailure(
551
- operation,
552
- "effect_agent_submissions",
553
- row.submission_id,
554
- error.message,
555
- ),
556
- ),
559
+ Effect.mapError(decodeFailure),
557
560
  );
558
561
 
559
562
  if ((row.parent_submission_id === null) !== (row.parent_tool_call_id === null)) {
@@ -584,21 +587,21 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
584
587
  ? {}
585
588
  : {
586
589
  workerAdmission: yield* parseStoredJsonText(row.worker_admission_json).pipe(
587
- Effect.mapError(internalFailure(operation)),
590
+ Effect.mapError(decodeFailure),
588
591
  ),
589
592
  }),
590
593
  ...(row.message_admission_json === null
591
594
  ? {}
592
595
  : {
593
596
  messageAdmission: yield* parseStoredJsonText(row.message_admission_json).pipe(
594
- Effect.mapError(internalFailure(operation)),
597
+ Effect.mapError(decodeFailure),
595
598
  ),
596
599
  }),
597
600
  ...(row.admission_fence_json === null
598
601
  ? {}
599
602
  : {
600
603
  admissionFence: yield* parseStoredJsonText(row.admission_fence_json).pipe(
601
- Effect.mapError(internalFailure(operation)),
604
+ Effect.mapError(decodeFailure),
602
605
  ),
603
606
  }),
604
607
  ...(row.settled_outcome === null ? {} : { settledOutcome: row.settled_outcome }),
@@ -611,16 +614,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
611
614
  parentToolCallId: row.parent_tool_call_id,
612
615
  },
613
616
  }),
614
- }).pipe(
615
- Effect.mapError((error) =>
616
- corruptionFailure(
617
- operation,
618
- "effect_agent_submissions",
619
- row.submission_id,
620
- error.message,
621
- ),
622
- ),
623
- );
617
+ }).pipe(Effect.mapError(decodeFailure));
624
618
  },
625
619
  );
626
620
 
@@ -3235,7 +3229,7 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
3235
3229
  const scanPage = Effect.fn("DoSubmissionLedger.scanPage")(function* (
3236
3230
  cursor: ScanCursor | undefined,
3237
3231
  ): Effect.fn.Return<
3238
- readonly [ReadonlyArray<SubmissionSnapshot>, Option.Option<ScanCursor | undefined>],
3232
+ readonly [ReadonlyArray<SubmissionWorkItem>, Option.Option<ScanCursor | undefined>],
3239
3233
  LedgerError
3240
3234
  > {
3241
3235
  const operation = "ledger scan nonterminal";
@@ -3243,14 +3237,18 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
3243
3237
  const rows = yield* (
3244
3238
  cursor === undefined
3245
3239
  ? sql<Record<string, unknown>>`
3246
- SELECT ${sql.literal(SUBMISSION_COLUMNS)}
3240
+ SELECT submission_id AS "submissionId", thread_id AS "threadId",
3241
+ queue_sequence AS "queueSequence", principal, idempotency_key AS "idempotencyKey",
3242
+ deployment_id AS "deploymentId", receipt_id AS "receiptId", state
3247
3243
  FROM effect_agent_submissions
3248
3244
  WHERE state <> 'settled'
3249
3245
  ORDER BY thread_id ASC, queue_sequence ASC
3250
3246
  LIMIT ${SCAN_PAGE_SIZE}
3251
3247
  `
3252
3248
  : sql<Record<string, unknown>>`
3253
- SELECT ${sql.literal(SUBMISSION_COLUMNS)}
3249
+ SELECT submission_id AS "submissionId", thread_id AS "threadId",
3250
+ queue_sequence AS "queueSequence", principal, idempotency_key AS "idempotencyKey",
3251
+ deployment_id AS "deploymentId", receipt_id AS "receiptId", state
3254
3252
  FROM effect_agent_submissions
3255
3253
  WHERE state <> 'settled'
3256
3254
  AND (thread_id, queue_sequence) > (${cursor.threadId}, ${cursor.queueSequence})
@@ -3259,11 +3257,13 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
3259
3257
  `
3260
3258
  ).pipe(Effect.mapError(sqlFailure(operation)));
3261
3259
 
3262
- const decoded = yield* decodeSubmissionRows(operation, "nonterminal_scan", rows);
3263
-
3264
- const snapshots = yield* Effect.forEach(decoded, (row) =>
3265
- decodeSubmissionSnapshot(operation, row),
3266
- );
3260
+ const decoded = yield* decodeRows(
3261
+ Schema.Array(SubmissionWorkItem),
3262
+ "effect_agent_submissions",
3263
+ "nonterminal_scan",
3264
+ rows,
3265
+ "SubmissionWorkItem",
3266
+ ).pipe(Effect.mapError(internalFailure(operation)));
3267
3267
 
3268
3268
  const last = decoded[decoded.length - 1];
3269
3269
 
@@ -3271,16 +3271,16 @@ const makeServices = Effect.fn("DoSubmissionLedger.makeServices")(function* () {
3271
3271
  last === undefined || decoded.length < SCAN_PAGE_SIZE
3272
3272
  ? Option.none()
3273
3273
  : Option.some({
3274
- threadId: last.thread_id,
3275
- queueSequence: last.queue_sequence,
3274
+ threadId: last.threadId,
3275
+ queueSequence: last.queueSequence,
3276
3276
  });
3277
3277
 
3278
- return [snapshots, next] as const;
3278
+ return [decoded, next] as const;
3279
3279
  });
3280
3280
 
3281
- const scanNonterminal: Stream.Stream<SubmissionSnapshot, LedgerError> = Stream.paginate<
3281
+ const scanNonterminal: Stream.Stream<SubmissionWorkItem, LedgerError> = Stream.paginate<
3282
3282
  ScanCursor | undefined,
3283
- SubmissionSnapshot,
3283
+ SubmissionWorkItem,
3284
3284
  LedgerError
3285
3285
  >(undefined, scanPage);
3286
3286
 
@@ -37,6 +37,7 @@ import {
37
37
  ThreadStore,
38
38
  type ThreadCheckpoints,
39
39
  ThreadStoreError,
40
+ ThreadStoreDiagnostic,
40
41
  ThreadTail,
41
42
  ThreadTailRequest,
42
43
  FenceRejected,
@@ -112,18 +113,26 @@ const isDoFenceRejected = Schema.is(DoFenceRejected);
112
113
  const isDoAppendConflict = Schema.is(DoAppendConflict);
113
114
  const isDoCheckpointConflict = Schema.is(DoCheckpointConflict);
114
115
 
115
- const storeError = (operation: string, error: { readonly message: string }) =>
116
+ const storeError = (
117
+ operation: string,
118
+ error: { readonly message: string; readonly diagnostic?: ThreadStoreDiagnostic },
119
+ ) =>
116
120
  ThreadStoreError.make({
117
121
  cause: error,
118
122
  operation,
119
123
  message: error.message,
124
+ ...(error.diagnostic === undefined ? {} : { diagnostic: error.diagnostic }),
120
125
  });
121
126
 
122
- const schemaStoreError = (operation: string, error: { readonly message: string }) =>
127
+ const schemaStoreError = (operation: string, error: Schema.SchemaError) =>
123
128
  ThreadStoreError.make({
124
- cause: error,
125
129
  operation,
126
- message: error.message,
130
+ message: "A Thread storage value does not satisfy its schema",
131
+ diagnostic: ThreadStoreDiagnostic.make({
132
+ causeTag: error._tag,
133
+ operation,
134
+ issueTag: error.issue._tag,
135
+ }),
127
136
  });
128
137
 
129
138
  const makeOffset = Effect.fn(function* (
@@ -215,7 +224,23 @@ const decodeEnvelope = Effect.fn(function* (row: {
215
224
  Effect.mapError((error) =>
216
225
  ThreadStoreError.make({
217
226
  operation: "decode canonical record",
218
- message: error.message,
227
+ message: "The canonical record does not satisfy its schema",
228
+ diagnostic: ThreadStoreDiagnostic.make({
229
+ causeTag: error._tag,
230
+ operation: "decode canonical record",
231
+ decoder: "CanonicalRecord",
232
+ sequence: row.sequence,
233
+ issueTag: error.issue._tag,
234
+ }),
235
+ }),
236
+ ),
237
+ Effect.tapError((error) =>
238
+ Effect.annotateCurrentSpan({
239
+ "storage.failure.operation": error.diagnostic?.operation,
240
+ "storage.failure.cause": error.diagnostic?.causeTag,
241
+ "storage.failure.decoder": error.diagnostic?.decoder,
242
+ "storage.failure.sequence": row.sequence,
243
+ "storage.failure.issue": error.diagnostic?.issueTag,
219
244
  }),
220
245
  ),
221
246
  );
@@ -14,6 +14,7 @@ import {
14
14
  CheckpointRejected,
15
15
  FenceRejected,
16
16
  ThreadNotMaterialized,
17
+ ThreadStoreDiagnostic,
17
18
  type SaveRecoveryCheckpointRequest,
18
19
  } from "effect-agent/thread-store";
19
20
  import * as SqlClient from "effect/unstable/sql/SqlClient";
@@ -191,6 +192,11 @@ const storageError =
191
192
  cause: error,
192
193
  operation,
193
194
  message: error.message,
195
+ diagnostic: ThreadStoreDiagnostic.make({
196
+ causeTag: error._tag,
197
+ operation,
198
+ issueTag: error.reason._tag,
199
+ }),
194
200
  });
195
201
 
196
202
  /** Decode raw Durable Object SQLite rows against a Schema, reporting failures as typed corruption. */
@@ -200,13 +206,28 @@ export const decodeRows = Effect.fn(
200
206
  table: string,
201
207
  rowKey: string,
202
208
  rows: unknown,
209
+ decoder: string = table,
203
210
  ): Effect.Effect<ReadonlyArray<A>, DoStorageCorruptionError> =>
204
211
  Schema.decodeUnknownEffect(schema)(rows).pipe(
205
212
  Effect.mapError((error) =>
206
213
  DoStorageCorruptionError.make({
207
214
  table,
208
215
  rowKey,
209
- message: String(error),
216
+ message: "Stored rows do not satisfy the storage schema",
217
+ diagnostic: ThreadStoreDiagnostic.make({
218
+ causeTag: error._tag,
219
+ operation: "decode storage rows",
220
+ decoder,
221
+ issueTag: error.issue._tag,
222
+ }),
223
+ }),
224
+ ),
225
+ Effect.tapError((error) =>
226
+ Effect.annotateCurrentSpan({
227
+ "storage.failure.operation": error.diagnostic?.operation,
228
+ "storage.failure.cause": error.diagnostic?.causeTag,
229
+ "storage.failure.decoder": decoder,
230
+ "storage.failure.issue": error.diagnostic?.issueTag,
210
231
  }),
211
232
  ),
212
233
  ),
@@ -1168,6 +1189,7 @@ const makeJournal = (
1168
1189
  "effect_agent_canonical_records",
1169
1190
  `${request.threadId}>${request.fromSequenceExclusive}`,
1170
1191
  planRows,
1192
+ "ReadPlanRow",
1171
1193
  );
1172
1194
 
1173
1195
  const mismatch = () =>
@@ -1212,6 +1234,7 @@ const makeJournal = (
1212
1234
  "effect_agent_canonical_records",
1213
1235
  `${request.threadId}/${page[0].sequence}`,
1214
1236
  rows,
1237
+ "RecordRow",
1215
1238
  );
1216
1239
 
1217
1240
  if (