| author | |
| committer | |
| log | 9fae3ee0e95b036388b597558ee58617b0035494 |
| tree | 1c308d19081f13418191a0188d75f5186559e63a |
| parent | a328f66bfa9f09f2748a5960ed65546dbd8da9c9 |
44 files changed, 765 insertions(+), 280 deletions(-)
.plans/16c-pr89-remediation-checklist.md+6-6| ... | ... | @@ -210,8 +210,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6` |
| 210 | 210 | - Threads: PRRT_kwDORLtfbc5wiLhQ |
| 211 | 211 | - Audit note: Snapshot sequence can under-report due to extra projectors, but replay impact is lower now. |
| 212 | 212 | |
| 213 | - [ ] `C033` Three error classes defined but never instantiated anywhere | |
| 214 | - Status: `TODO` | |
| 213 | - [x] `C033` Three error classes defined but never instantiated anywhere | |
| 214 | - Status: `DONE` | |
| 215 | 215 | - Verdict: `partially-valid` |
| 216 | 216 | - Severity: `Low` |
| 217 | 217 | - Area: `Checkpointing correctness` |
| ... | ... | @@ -219,8 +219,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6` |
| 219 | 219 | - Threads: PRRT_kwDORLtfbc5wlYgo |
| 220 | 220 | - Audit note: Original claim overstated; some errors used, others appear unused. |
| 221 | 221 | |
| 222 | - [ ] `C034` Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type | |
| 223 | - Status: `TODO` | |
| 222 | - [x] `C034` Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type | |
| 223 | - Status: `DONE` | |
| 224 | 224 | - Verdict: `valid` |
| 225 | 225 | - Severity: `Low` |
| 226 | 226 | - Area: `Checkpointing correctness` |
| ... | ... | @@ -228,8 +228,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6` |
| 228 | 228 | - Threads: PRRT_kwDORLtfbc5wj5fn |
| 229 | 229 | - Audit note: CheckpointInvariantError remains redundantly included in service union. |
| 230 | 230 | |
| 231 | - [ ] `C035` Redundant error type in CheckpointServiceError union definition | |
| 232 | - Status: `TODO` | |
| 231 | - [x] `C035` Redundant error type in CheckpointServiceError union definition | |
| 232 | - Status: `DONE` | |
| 233 | 233 | - Verdict: `valid` |
| 234 | 234 | - Severity: `Low` |
| 235 | 235 | - Area: `Checkpointing correctness` |
apps/server/src/checkpointing/Errors.ts+2-38| ... | ... | @@ -19,37 +19,6 @@ export class CheckpointUnavailableError extends Schema.TaggedErrorClass<Checkpoi |
| 19 | 19 | } |
| 20 | 20 | } |
| 21 | 21 | |
| 22 | /** | |
| 23 | * CheckpointRepositoryError - Checkpointing unavailable for cwd/repository. | |
| 24 | */ | |
| 25 | export class CheckpointRepositoryError extends Schema.TaggedErrorClass<CheckpointRepositoryError>()( | |
| 26 | "CheckpointRepositoryError", | |
| 27 | { | |
| 28 | cwd: Schema.String, | |
| 29 | detail: Schema.String, | |
| 30 | cause: Schema.optional(Schema.Defect), | |
| 31 | }, | |
| 32 | ) { | |
| 33 | override get message(): string { | |
| 34 | return `Checkpoint repository error for ${this.cwd}: ${this.detail}`; | |
| 35 | } | |
| 36 | } | |
| 37 | ||
| 38 | /** | |
| 39 | * CheckpointSessionNotFoundError - Session is missing in checkpoint workflow. | |
| 40 | */ | |
| 41 | export class CheckpointSessionNotFoundError extends Schema.TaggedErrorClass<CheckpointSessionNotFoundError>()( | |
| 42 | "CheckpointSessionNotFoundError", | |
| 43 | { | |
| 44 | sessionId: Schema.String, | |
| 45 | cause: Schema.optional(Schema.Defect), | |
| 46 | }, | |
| 47 | ) { | |
| 48 | override get message(): string { | |
| 49 | return `Checkpoint session not found: ${this.sessionId}`; | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | 22 | /** |
| 54 | 23 | * CheckpointInvariantError - Inconsistent provider/filesystem/catalog state. |
| 55 | 24 | */ |
| ... | ... | @@ -69,11 +38,6 @@ export class CheckpointInvariantError extends Schema.TaggedErrorClass<Checkpoint |
| 69 | 38 | export type CheckpointStoreError = |
| 70 | 39 | | GitCommandError |
| 71 | 40 | | CheckpointInvariantError |
| 72 | | CheckpointUnavailableError | |
| 73 | | CheckpointRepositoryError; | |
| 41 | | CheckpointUnavailableError; | |
| 74 | 42 | |
| 75 | export type CheckpointServiceError = | |
| 76 | | CheckpointStoreError | |
| 77 | | ProjectionRepositoryError | |
| 78 | | CheckpointSessionNotFoundError | |
| 79 | | CheckpointInvariantError; | |
| 43 | export type CheckpointServiceError = CheckpointStoreError | ProjectionRepositoryError; |
apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts-1| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | import { |
| 2 | 2 | OrchestrationGetTurnDiffResult, |
| 3 | type ProjectId, | |
| 4 | 3 | type OrchestrationGetFullThreadDiffInput, |
| 5 | 4 | type OrchestrationGetFullThreadDiffResult, |
| 6 | 5 | type OrchestrationGetTurnDiffResult as OrchestrationGetTurnDiffResultType, |
apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts+25| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * CheckpointDiffQuery - Query interface for computed checkpoint diffs. | |
| 3 | * | |
| 4 | * Provides read-only diff operations across checkpoint snapshots used by | |
| 5 | * orchestration APIs. | |
| 6 | * | |
| 7 | * @module CheckpointDiffQuery | |
| 8 | */ | |
| 1 | 9 | import type { |
| 2 | 10 | OrchestrationGetFullThreadDiffInput, |
| 3 | 11 | OrchestrationGetFullThreadDiffResult, |
| ... | ... | @@ -9,15 +17,32 @@ import type { Effect } from "effect"; |
| 9 | 17 | |
| 10 | 18 | import type { CheckpointServiceError } from "../Errors.ts"; |
| 11 | 19 | |
| 20 | /** | |
| 21 | * CheckpointDiffQueryShape - Service API for checkpoint diff queries. | |
| 22 | */ | |
| 12 | 23 | export interface CheckpointDiffQueryShape { |
| 24 | /** | |
| 25 | * Read the patch diff for a single turn checkpoint transition. | |
| 26 | * | |
| 27 | * Verifies checkpoint availability in both projection state and filesystem. | |
| 28 | */ | |
| 13 | 29 | readonly getTurnDiff: ( |
| 14 | 30 | input: OrchestrationGetTurnDiffInput, |
| 15 | 31 | ) => Effect.Effect<OrchestrationGetTurnDiffResult, CheckpointServiceError>; |
| 32 | ||
| 33 | /** | |
| 34 | * Read the full patch diff across a thread range of checkpoints. | |
| 35 | * | |
| 36 | * Delegates to turn diff with `fromTurnCount = 0`. | |
| 37 | */ | |
| 16 | 38 | readonly getFullThreadDiff: ( |
| 17 | 39 | input: OrchestrationGetFullThreadDiffInput, |
| 18 | 40 | ) => Effect.Effect<OrchestrationGetFullThreadDiffResult, CheckpointServiceError>; |
| 19 | 41 | } |
| 20 | 42 | |
| 43 | /** | |
| 44 | * CheckpointDiffQuery - Service tag for checkpoint diff queries. | |
| 45 | */ | |
| 21 | 46 | export class CheckpointDiffQuery extends ServiceMap.Service< |
| 22 | 47 | CheckpointDiffQuery, |
| 23 | 48 | CheckpointDiffQueryShape |
apps/server/src/checkpointing/Services/CheckpointStore.ts+11| ... | ... | @@ -39,6 +39,9 @@ export interface DeleteCheckpointRefsInput { |
| 39 | 39 | readonly checkpointRefs: ReadonlyArray<CheckpointRef>; |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | /** | |
| 43 | * CheckpointStoreShape - Service API for checkpoint capture/restore and diff access. | |
| 44 | */ | |
| 42 | 45 | export interface CheckpointStoreShape { |
| 43 | 46 | /** |
| 44 | 47 | * Check whether cwd is inside a Git worktree. |
| ... | ... | @@ -47,6 +50,8 @@ export interface CheckpointStoreShape { |
| 47 | 50 | |
| 48 | 51 | /** |
| 49 | 52 | * Capture a checkpoint commit and store it at the provided checkpoint ref. |
| 53 | * | |
| 54 | * Uses an isolated temporary Git index and writes a hidden ref. | |
| 50 | 55 | */ |
| 51 | 56 | readonly captureCheckpoint: ( |
| 52 | 57 | input: CaptureCheckpointInput, |
| ... | ... | @@ -61,6 +66,8 @@ export interface CheckpointStoreShape { |
| 61 | 66 | |
| 62 | 67 | /** |
| 63 | 68 | * Restore workspace/staging state to a checkpoint. |
| 69 | * | |
| 70 | * Optionally falls back to current `HEAD` when the checkpoint ref is missing. | |
| 64 | 71 | */ |
| 65 | 72 | readonly restoreCheckpoint: ( |
| 66 | 73 | input: RestoreCheckpointInput, |
| ... | ... | @@ -68,6 +75,8 @@ export interface CheckpointStoreShape { |
| 68 | 75 | |
| 69 | 76 | /** |
| 70 | 77 | * Compute patch diff between two checkpoint refs. |
| 78 | * | |
| 79 | * Can optionally treat missing "from" ref as `HEAD`. | |
| 71 | 80 | */ |
| 72 | 81 | readonly diffCheckpoints: ( |
| 73 | 82 | input: DiffCheckpointsInput, |
| ... | ... | @@ -75,6 +84,8 @@ export interface CheckpointStoreShape { |
| 75 | 84 | |
| 76 | 85 | /** |
| 77 | 86 | * Delete the provided checkpoint refs. |
| 87 | * | |
| 88 | * Best-effort delete: missing refs are tolerated. | |
| 78 | 89 | */ |
| 79 | 90 | readonly deleteCheckpointRefs: ( |
| 80 | 91 | input: DeleteCheckpointRefsInput, |
apps/server/src/config.ts+25| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ServerConfig and NetService - Runtime configuration services. | |
| 3 | * | |
| 4 | * Defines process-level server configuration and networking helpers used by | |
| 5 | * startup and runtime layers. | |
| 6 | * | |
| 7 | * @module ServerConfig | |
| 8 | */ | |
| 1 | 9 | import * as Net from "node:net"; |
| 2 | 10 | import { Data, Effect, FileSystem, Layer, Path, ServiceMap } from "effect"; |
| 3 | 11 | |
| ... | ... | @@ -5,6 +13,9 @@ export const DEFAULT_PORT = 3773; |
| 5 | 13 | |
| 6 | 14 | export type RuntimeMode = "web" | "desktop"; |
| 7 | 15 | |
| 16 | /** | |
| 17 | * ServerConfigShape - Process/runtime configuration required by the server. | |
| 18 | */ | |
| 8 | 19 | export interface ServerConfigShape { |
| 9 | 20 | readonly mode: RuntimeMode; |
| 10 | 21 | readonly port: number; |
| ... | ... | @@ -20,6 +31,9 @@ export interface ServerConfigShape { |
| 20 | 31 | readonly logWebSocketEvents: boolean; |
| 21 | 32 | } |
| 22 | 33 | |
| 34 | /** | |
| 35 | * ServerConfig - Service tag for server runtime configuration. | |
| 36 | */ | |
| 23 | 37 | export class ServerConfig extends ServiceMap.Service<ServerConfig, ServerConfigShape>()( |
| 24 | 38 | "server/ServerConfig", |
| 25 | 39 | ) {} |
| ... | ... | @@ -31,10 +45,21 @@ export class NetError extends Data.TaggedError("NetError")<{ |
| 31 | 45 | readonly cause?: unknown; |
| 32 | 46 | }> {} |
| 33 | 47 | |
| 48 | /** | |
| 49 | * NetServiceShape - Networking helper operations used during startup. | |
| 50 | */ | |
| 34 | 51 | export interface NetServiceShape { |
| 52 | /** | |
| 53 | * Resolve an available listening port, preferring the provided port first. | |
| 54 | * | |
| 55 | * Falls back to an ephemeral OS-assigned port when the preferred port is in use. | |
| 56 | */ | |
| 35 | 57 | readonly findAvailablePort: (preferred: number) => Effect.Effect<number, NetError>; |
| 36 | 58 | } |
| 37 | 59 | |
| 60 | /** | |
| 61 | * NetService - Service tag for startup networking helpers. | |
| 62 | */ | |
| 38 | 63 | export class NetService extends ServiceMap.Service<NetService, NetServiceShape>()( |
| 39 | 64 | "server/NetService", |
| 40 | 65 | ) { |
apps/server/src/git/Errors.ts+3-18| ... | ... | @@ -57,26 +57,11 @@ export class GitManagerError extends Schema.TaggedErrorClass<GitManagerError>()( |
| 57 | 57 | } |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | /** | |
| 61 | * GitCoreError - Errors emitted by low-level Git repository operations. | |
| 62 | */ | |
| 63 | export type GitCoreError = GitCommandError; | |
| 64 | ||
| 65 | /** | |
| 66 | * GitHubCliServiceError - Errors emitted by GitHub CLI service helpers. | |
| 67 | */ | |
| 68 | export type GitHubCliServiceError = GitHubCliError; | |
| 69 | ||
| 70 | /** | |
| 71 | * TextGenerationServiceError - Errors emitted by commit/PR text generation. | |
| 72 | */ | |
| 73 | export type TextGenerationServiceError = TextGenerationError; | |
| 74 | ||
| 75 | 60 | /** |
| 76 | 61 | * GitManagerServiceError - Errors emitted by stacked Git workflow orchestration. |
| 77 | 62 | */ |
| 78 | 63 | export type GitManagerServiceError = |
| 79 | 64 | | GitManagerError |
| 80 | | GitCoreError | |
| 81 | | GitHubCliServiceError | |
| 82 | | TextGenerationServiceError; | |
| 65 | | GitCommandError | |
| 66 | | GitHubCliError | |
| 67 | | TextGenerationError; |
apps/server/src/git/Services/GitService.ts+3| ... | ... | @@ -27,6 +27,9 @@ export interface ExecuteGitResult { |
| 27 | 27 | readonly stderr: string; |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | /** | |
| 31 | * GitServiceShape - Service API for Git command execution. | |
| 32 | */ | |
| 30 | 33 | export interface GitServiceShape { |
| 31 | 34 | /** |
| 32 | 35 | * Execute a Git command. |
apps/server/src/keybindings.ts+26| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * Keybindings - Keybinding configuration service definitions. | |
| 3 | * | |
| 4 | * Owns parsing, validation, merge, and persistence of user keybinding | |
| 5 | * configuration consumed by the server runtime. | |
| 6 | * | |
| 7 | * @module Keybindings | |
| 8 | */ | |
| 1 | 9 | import { |
| 2 | 10 | KeybindingRule, |
| 3 | 11 | KeybindingsConfig, |
| ... | ... | @@ -388,16 +396,34 @@ function mergeWithDefaultKeybindings(custom: ResolvedKeybindingsConfig): Resolve |
| 388 | 396 | return merged.slice(-MAX_KEYBINDINGS_COUNT); |
| 389 | 397 | } |
| 390 | 398 | |
| 399 | /** | |
| 400 | * KeybindingsShape - Service API for keybinding configuration operations. | |
| 401 | */ | |
| 391 | 402 | export interface KeybindingsShape { |
| 403 | /** | |
| 404 | * Load and resolve keybindings from disk merged with defaults. | |
| 405 | * | |
| 406 | * Returns an in-memory cached result after the first successful load. | |
| 407 | */ | |
| 392 | 408 | readonly loadResolvedKeybindingsConfig: Effect.Effect< |
| 393 | 409 | readonly ResolvedKeybindingRule[], |
| 394 | 410 | KeybindingsConfigError |
| 395 | 411 | >; |
| 412 | ||
| 413 | /** | |
| 414 | * Upsert a keybinding rule and persist the resulting configuration. | |
| 415 | * | |
| 416 | * Writes config atomically and enforces the max rule count by truncating | |
| 417 | * oldest entries when needed. | |
| 418 | */ | |
| 396 | 419 | readonly upsertKeybindingRule: ( |
| 397 | 420 | rule: KeybindingRule, |
| 398 | 421 | ) => Effect.Effect<ResolvedKeybindingsConfig, KeybindingsConfigError>; |
| 399 | 422 | } |
| 400 | 423 | |
| 424 | /** | |
| 425 | * Keybindings - Service tag for keybinding configuration operations. | |
| 426 | */ | |
| 401 | 427 | export class Keybindings extends ServiceMap.Service<Keybindings, KeybindingsShape>()( |
| 402 | 428 | "server/Keybindings", |
| 403 | 429 | ) {} |
apps/server/src/main.ts+25| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * CliConfig - CLI/runtime bootstrap service definitions. | |
| 3 | * | |
| 4 | * Defines startup-only service contracts used while resolving process config | |
| 5 | * and constructing server runtime layers. | |
| 6 | * | |
| 7 | * @module CliConfig | |
| 8 | */ | |
| 1 | 9 | import { Config, Data, Effect, FileSystem, Layer, Option, Path, Schema, ServiceMap } from "effect"; |
| 2 | 10 | import { Command, Flag } from "effect/unstable/cli"; |
| 3 | 11 | |
| ... | ... | @@ -32,12 +40,29 @@ interface CliInput { |
| 32 | 40 | readonly logWebSocketEvents: Option.Option<boolean>; |
| 33 | 41 | } |
| 34 | 42 | |
| 43 | /** | |
| 44 | * CliConfigShape - Startup helpers required while building server layers. | |
| 45 | */ | |
| 35 | 46 | export interface CliConfigShape { |
| 47 | /** | |
| 48 | * Current process working directory. | |
| 49 | */ | |
| 36 | 50 | readonly cwd: string; |
| 51 | ||
| 52 | /** | |
| 53 | * Apply OS-specific PATH normalization. | |
| 54 | */ | |
| 37 | 55 | readonly fixPath: Effect.Effect<void>; |
| 56 | ||
| 57 | /** | |
| 58 | * Resolve static web asset directory for server mode. | |
| 59 | */ | |
| 38 | 60 | readonly resolveStaticDir: Effect.Effect<string | undefined>; |
| 39 | 61 | } |
| 40 | 62 | |
| 63 | /** | |
| 64 | * CliConfig - Service tag for startup CLI/runtime helpers. | |
| 65 | */ | |
| 41 | 66 | export class CliConfig extends ServiceMap.Service<CliConfig, CliConfigShape>()("server/CliConfig") { |
| 42 | 67 | static readonly layer = Layer.effect( |
| 43 | 68 | CliConfig, |
apps/server/src/open.ts+23| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * Open - Browser/editor launch service interface. | |
| 3 | * | |
| 4 | * Owns process launch helpers for opening URLs in a browser and workspace | |
| 5 | * paths in a configured editor. | |
| 6 | * | |
| 7 | * @module Open | |
| 8 | */ | |
| 1 | 9 | import { spawn } from "node:child_process"; |
| 2 | 10 | |
| 3 | 11 | import { EDITORS, type EditorId } from "@t3tools/contracts"; |
| ... | ... | @@ -22,11 +30,26 @@ interface EditorLaunch { |
| 22 | 30 | readonly args: ReadonlyArray<string>; |
| 23 | 31 | } |
| 24 | 32 | |
| 33 | /** | |
| 34 | * OpenShape - Service API for browser and editor launch actions. | |
| 35 | */ | |
| 25 | 36 | export interface OpenShape { |
| 37 | /** | |
| 38 | * Open a URL target in the default browser. | |
| 39 | */ | |
| 26 | 40 | readonly openBrowser: (target: string) => Effect.Effect<void, OpenError>; |
| 41 | ||
| 42 | /** | |
| 43 | * Open a workspace path in a selected editor integration. | |
| 44 | * | |
| 45 | * Launches the editor as a detached process so server startup is not blocked. | |
| 46 | */ | |
| 27 | 47 | readonly openInEditor: (input: OpenInEditorInput) => Effect.Effect<void, OpenError>; |
| 28 | 48 | } |
| 29 | 49 | |
| 50 | /** | |
| 51 | * Open - Service tag for browser/editor launch operations. | |
| 52 | */ | |
| 30 | 53 | export class Open extends ServiceMap.Service<Open, OpenShape>()("server/Open") {} |
| 31 | 54 | |
| 32 | 55 | // ============================== |
apps/server/src/orchestration/Errors.ts-4| ... | ... | @@ -1,8 +1,6 @@ |
| 1 | 1 | import { SchemaIssue, Schema } from "effect"; |
| 2 | 2 | |
| 3 | 3 | import type { |
| 4 | OrchestrationCommandReceiptRepositoryError, | |
| 5 | OrchestrationEventStoreError, | |
| 6 | 4 | ProjectionRepositoryError, |
| 7 | 5 | } from "../persistence/Errors.ts"; |
| 8 | 6 | |
| ... | ... | @@ -83,8 +81,6 @@ export class OrchestrationListenerCallbackError extends Schema.TaggedErrorClass< |
| 83 | 81 | } |
| 84 | 82 | |
| 85 | 83 | export type OrchestrationDispatchError = |
| 86 | | OrchestrationEventStoreError | |
| 87 | | OrchestrationCommandReceiptRepositoryError | |
| 88 | 84 | | ProjectionRepositoryError |
| 89 | 85 | | OrchestrationCommandInvariantError |
| 90 | 86 | | OrchestrationCommandPreviouslyRejectedError |
apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts+7-17| ... | ... | @@ -14,9 +14,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; |
| 14 | 14 | import * as SqlSchema from "effect/unstable/sql/SqlSchema"; |
| 15 | 15 | |
| 16 | 16 | import { |
| 17 | PersistenceDecodeError, | |
| 18 | PersistenceSerializationError, | |
| 19 | PersistenceSqlError, | |
| 17 | isPersistenceError, | |
| 20 | 18 | toPersistenceDecodeError, |
| 21 | 19 | toPersistenceSqlError, |
| 22 | 20 | type ProjectionRepositoryError, |
| ... | ... | @@ -106,19 +104,6 @@ function toPersistenceSqlOrDecodeError(sqlOperation: string, decodeOperation: st |
| 106 | 104 | : toPersistenceSqlError(sqlOperation)(cause); |
| 107 | 105 | } |
| 108 | 106 | |
| 109 | function toProjectionRepositoryError(operation: string) { | |
| 110 | return (cause: unknown): ProjectionRepositoryError => { | |
| 111 | if ( | |
| 112 | cause instanceof PersistenceSqlError || | |
| 113 | cause instanceof PersistenceDecodeError || | |
| 114 | cause instanceof PersistenceSerializationError | |
| 115 | ) { | |
| 116 | return cause; | |
| 117 | } | |
| 118 | return toPersistenceSqlError(operation)(cause); | |
| 119 | }; | |
| 120 | } | |
| 121 | ||
| 122 | 107 | const makeProjectionSnapshotQuery = Effect.gen(function* () { |
| 123 | 108 | const sql = yield* SqlClient.SqlClient; |
| 124 | 109 | |
| ... | ... | @@ -441,7 +426,12 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { |
| 441 | 426 | }), |
| 442 | 427 | ) |
| 443 | 428 | .pipe( |
| 444 | Effect.mapError(toProjectionRepositoryError("ProjectionSnapshotQuery.getSnapshot:query")), | |
| 429 | Effect.mapError((error) => { | |
| 430 | if (isPersistenceError(error)) { | |
| 431 | return error; | |
| 432 | } | |
| 433 | return toPersistenceSqlError("ProjectionSnapshotQuery.getSnapshot:query")(error); | |
| 434 | }), | |
| 445 | 435 | ); |
| 446 | 436 | |
| 447 | 437 | return { |
apps/server/src/orchestration/Services/CheckpointReactor.ts+17| ... | ... | @@ -1,16 +1,33 @@ |
| 1 | /** | |
| 2 | * CheckpointReactor - Checkpoint reaction service interface. | |
| 3 | * | |
| 4 | * Owns background workers that react to orchestration checkpoint lifecycle | |
| 5 | * events and apply checkpoint side effects. | |
| 6 | * | |
| 7 | * @module CheckpointReactor | |
| 8 | */ | |
| 1 | 9 | import { ServiceMap } from "effect"; |
| 2 | 10 | import type { Effect, Scope } from "effect"; |
| 3 | 11 | |
| 12 | /** | |
| 13 | * CheckpointReactorShape - Service API for checkpoint reactor lifecycle. | |
| 14 | */ | |
| 4 | 15 | export interface CheckpointReactorShape { |
| 5 | 16 | /** |
| 6 | 17 | * Start the checkpoint reactor. |
| 7 | 18 | * |
| 8 | 19 | * The returned effect must be run in a scope so all worker fibers can be |
| 9 | 20 | * finalized on shutdown. |
| 21 | * | |
| 22 | * Consumes both orchestration-domain and provider-runtime events via an | |
| 23 | * internal queue. | |
| 10 | 24 | */ |
| 11 | 25 | readonly start: Effect.Effect<void, never, Scope.Scope>; |
| 12 | 26 | } |
| 13 | 27 | |
| 28 | /** | |
| 29 | * CheckpointReactor - Service tag for checkpoint reactor workers. | |
| 30 | */ | |
| 14 | 31 | export class CheckpointReactor extends ServiceMap.Service< |
| 15 | 32 | CheckpointReactor, |
| 16 | 33 | CheckpointReactorShape |
apps/server/src/orchestration/Services/OrchestrationEngine.ts+8| ... | ... | @@ -21,6 +21,9 @@ import type { Effect, Stream } from "effect"; |
| 21 | 21 | import type { OrchestrationDispatchError } from "../Errors.ts"; |
| 22 | 22 | import type { OrchestrationEventStoreError } from "../../persistence/Errors.ts"; |
| 23 | 23 | |
| 24 | /** | |
| 25 | * OrchestrationEngineShape - Service API for orchestration command and event flow. | |
| 26 | */ | |
| 24 | 27 | export interface OrchestrationEngineShape { |
| 25 | 28 | /** |
| 26 | 29 | * Read the current in-memory orchestration read model. |
| ... | ... | @@ -44,6 +47,9 @@ export interface OrchestrationEngineShape { |
| 44 | 47 | * |
| 45 | 48 | * @param command - Valid orchestration command. |
| 46 | 49 | * @returns Effect containing the sequence of the persisted event. |
| 50 | * | |
| 51 | * Dispatch is serialized through an internal queue and deduplicated via | |
| 52 | * command receipts. | |
| 47 | 53 | */ |
| 48 | 54 | readonly dispatch: ( |
| 49 | 55 | command: OrchestrationCommand, |
| ... | ... | @@ -51,6 +57,8 @@ export interface OrchestrationEngineShape { |
| 51 | 57 | |
| 52 | 58 | /** |
| 53 | 59 | * Stream persisted domain events in dispatch order. |
| 60 | * | |
| 61 | * This is a hot runtime stream (new events only), not a historical replay. | |
| 54 | 62 | */ |
| 55 | 63 | readonly streamDomainEvents: Stream.Stream<OrchestrationEvent>; |
| 56 | 64 | } |
apps/server/src/orchestration/Services/OrchestrationReactor.ts+14| ... | ... | @@ -1,6 +1,17 @@ |
| 1 | /** | |
| 2 | * OrchestrationReactor - Composite orchestration reactor service interface. | |
| 3 | * | |
| 4 | * Coordinates startup of orchestration runtime reactors that translate domain | |
| 5 | * events into downstream side effects. | |
| 6 | * | |
| 7 | * @module OrchestrationReactor | |
| 8 | */ | |
| 1 | 9 | import { ServiceMap } from "effect"; |
| 2 | 10 | import type { Effect, Scope } from "effect"; |
| 3 | 11 | |
| 12 | /** | |
| 13 | * OrchestrationReactorShape - Service API for orchestration reactor lifecycle. | |
| 14 | */ | |
| 4 | 15 | export interface OrchestrationReactorShape { |
| 5 | 16 | /** |
| 6 | 17 | * Start orchestration-side reactors for provider/runtime/checkpoint flows. |
| ... | ... | @@ -11,6 +22,9 @@ export interface OrchestrationReactorShape { |
| 11 | 22 | readonly start: Effect.Effect<void, never, Scope.Scope>; |
| 12 | 23 | } |
| 13 | 24 | |
| 25 | /** | |
| 26 | * OrchestrationReactor - Service tag for orchestration reactor coordination. | |
| 27 | */ | |
| 14 | 28 | export class OrchestrationReactor extends ServiceMap.Service< |
| 15 | 29 | OrchestrationReactor, |
| 16 | 30 | OrchestrationReactorShape |
apps/server/src/orchestration/Services/ProjectionPipeline.ts+26-9| ... | ... | @@ -1,24 +1,41 @@ |
| 1 | /** | |
| 2 | * OrchestrationProjectionPipeline - Event projection pipeline service interface. | |
| 3 | * | |
| 4 | * Coordinates projection bootstrap/replay and per-event projection updates for | |
| 5 | * orchestration read models. | |
| 6 | * | |
| 7 | * @module OrchestrationProjectionPipeline | |
| 8 | */ | |
| 1 | 9 | import type { OrchestrationEvent } from "@t3tools/contracts"; |
| 2 | 10 | import { ServiceMap } from "effect"; |
| 3 | 11 | import type { Effect } from "effect"; |
| 4 | 12 | |
| 5 | import type { | |
| 6 | OrchestrationEventStoreError, | |
| 7 | ProjectionRepositoryError, | |
| 8 | } from "../../persistence/Errors.ts"; | |
| 9 | ||
| 10 | export type OrchestrationProjectionPipelineError = | |
| 11 | | OrchestrationEventStoreError | |
| 12 | | ProjectionRepositoryError; | |
| 13 | import type { ProjectionRepositoryError } from "../../persistence/Errors.ts"; | |
| 13 | 14 | |
| 15 | /** | |
| 16 | * OrchestrationProjectionPipelineShape - Service API for projection execution. | |
| 17 | */ | |
| 14 | 18 | export interface OrchestrationProjectionPipelineShape { |
| 15 | readonly bootstrap: Effect.Effect<void, OrchestrationProjectionPipelineError>; | |
| 19 | /** | |
| 20 | * Bootstrap projections by replaying persisted events. | |
| 21 | * | |
| 22 | * Resumes each projector from its stored projection-state cursor. | |
| 23 | */ | |
| 24 | readonly bootstrap: Effect.Effect<void, ProjectionRepositoryError>; | |
| 16 | 25 | |
| 26 | /** | |
| 27 | * Project a single orchestration event into projection repositories. | |
| 28 | * | |
| 29 | * Projectors are executed sequentially to preserve deterministic ordering. | |
| 30 | */ | |
| 17 | 31 | readonly projectEvent: ( |
| 18 | 32 | event: OrchestrationEvent, |
| 19 | 33 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 20 | 34 | } |
| 21 | 35 | |
| 36 | /** | |
| 37 | * OrchestrationProjectionPipeline - Service tag for orchestration projections. | |
| 38 | */ | |
| 22 | 39 | export class OrchestrationProjectionPipeline extends ServiceMap.Service< |
| 23 | 40 | OrchestrationProjectionPipeline, |
| 24 | 41 | OrchestrationProjectionPipelineShape |
apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts+20| ... | ... | @@ -1,13 +1,33 @@ |
| 1 | /** | |
| 2 | * ProjectionSnapshotQuery - Read-model snapshot query service interface. | |
| 3 | * | |
| 4 | * Exposes the current orchestration projection snapshot for read-only API | |
| 5 | * access. | |
| 6 | * | |
| 7 | * @module ProjectionSnapshotQuery | |
| 8 | */ | |
| 1 | 9 | import type { OrchestrationReadModel } from "@t3tools/contracts"; |
| 2 | 10 | import { ServiceMap } from "effect"; |
| 3 | 11 | import type { Effect } from "effect"; |
| 4 | 12 | |
| 5 | 13 | import type { ProjectionRepositoryError } from "../../persistence/Errors.ts"; |
| 6 | 14 | |
| 15 | /** | |
| 16 | * ProjectionSnapshotQueryShape - Service API for read-model snapshots. | |
| 17 | */ | |
| 7 | 18 | export interface ProjectionSnapshotQueryShape { |
| 19 | /** | |
| 20 | * Read the latest orchestration projection snapshot. | |
| 21 | * | |
| 22 | * Rehydrates from projection tables and derives snapshot sequence from | |
| 23 | * projector cursor state. | |
| 24 | */ | |
| 8 | 25 | readonly getSnapshot: () => Effect.Effect<OrchestrationReadModel, ProjectionRepositoryError>; |
| 9 | 26 | } |
| 10 | 27 | |
| 28 | /** | |
| 29 | * ProjectionSnapshotQuery - Service tag for projection snapshot queries. | |
| 30 | */ | |
| 11 | 31 | export class ProjectionSnapshotQuery extends ServiceMap.Service< |
| 12 | 32 | ProjectionSnapshotQuery, |
| 13 | 33 | ProjectionSnapshotQueryShape |
apps/server/src/orchestration/Services/ProviderCommandReactor.ts+17| ... | ... | @@ -1,16 +1,33 @@ |
| 1 | /** | |
| 2 | * ProviderCommandReactor - Provider command reaction service interface. | |
| 3 | * | |
| 4 | * Owns background workers that react to orchestration intent events and | |
| 5 | * dispatch provider-side command execution. | |
| 6 | * | |
| 7 | * @module ProviderCommandReactor | |
| 8 | */ | |
| 1 | 9 | import { ServiceMap } from "effect"; |
| 2 | 10 | import type { Effect, Scope } from "effect"; |
| 3 | 11 | |
| 12 | /** | |
| 13 | * ProviderCommandReactorShape - Service API for provider command reactors. | |
| 14 | */ | |
| 4 | 15 | export interface ProviderCommandReactorShape { |
| 5 | 16 | /** |
| 6 | 17 | * Start reacting to provider-intent orchestration domain events. |
| 7 | 18 | * |
| 8 | 19 | * The returned effect must be run in a scope so all worker fibers can be |
| 9 | 20 | * finalized on shutdown. |
| 21 | * | |
| 22 | * Filters orchestration domain events to provider-intent types before | |
| 23 | * processing. | |
| 10 | 24 | */ |
| 11 | 25 | readonly start: Effect.Effect<void, never, Scope.Scope>; |
| 12 | 26 | } |
| 13 | 27 | |
| 28 | /** | |
| 29 | * ProviderCommandReactor - Service tag for provider command reaction workers. | |
| 30 | */ | |
| 14 | 31 | export class ProviderCommandReactor extends ServiceMap.Service< |
| 15 | 32 | ProviderCommandReactor, |
| 16 | 33 | ProviderCommandReactorShape |
apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts+17| ... | ... | @@ -1,16 +1,33 @@ |
| 1 | /** | |
| 2 | * ProviderRuntimeIngestionService - Provider runtime ingestion service interface. | |
| 3 | * | |
| 4 | * Owns background workers that consume provider runtime streams and emit | |
| 5 | * orchestration commands/events. | |
| 6 | * | |
| 7 | * @module ProviderRuntimeIngestionService | |
| 8 | */ | |
| 1 | 9 | import { ServiceMap } from "effect"; |
| 2 | 10 | import type { Effect, Scope } from "effect"; |
| 3 | 11 | |
| 12 | /** | |
| 13 | * ProviderRuntimeIngestionShape - Service API for runtime ingestion lifecycle. | |
| 14 | */ | |
| 4 | 15 | export interface ProviderRuntimeIngestionShape { |
| 5 | 16 | /** |
| 6 | 17 | * Start ingesting provider runtime events into orchestration commands. |
| 7 | 18 | * |
| 8 | 19 | * The returned effect must be run in a scope so all worker fibers can be |
| 9 | 20 | * finalized on shutdown. |
| 21 | * | |
| 22 | * Uses an internal queue and continues after non-interrupt failures by | |
| 23 | * logging warnings. | |
| 10 | 24 | */ |
| 11 | 25 | readonly start: Effect.Effect<void, never, Scope.Scope>; |
| 12 | 26 | } |
| 13 | 27 | |
| 28 | /** | |
| 29 | * ProviderRuntimeIngestionService - Service tag for runtime ingestion workers. | |
| 30 | */ | |
| 14 | 31 | export class ProviderRuntimeIngestionService extends ServiceMap.Service< |
| 15 | 32 | ProviderRuntimeIngestionService, |
| 16 | 33 | ProviderRuntimeIngestionShape |
apps/server/src/persistence/Errors.ts+5-116| ... | ... | @@ -30,19 +30,6 @@ export class PersistenceDecodeError extends Schema.TaggedErrorClass<PersistenceD |
| 30 | 30 | } |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | export class PersistenceSerializationError extends Schema.TaggedErrorClass<PersistenceSerializationError>()( | |
| 34 | "PersistenceSerializationError", | |
| 35 | { | |
| 36 | operation: Schema.String, | |
| 37 | detail: Schema.String, | |
| 38 | cause: Schema.optional(Schema.Defect), | |
| 39 | }, | |
| 40 | ) { | |
| 41 | override get message(): string { | |
| 42 | return `Serialization error in ${this.operation}: ${this.detail}`; | |
| 43 | } | |
| 44 | } | |
| 45 | ||
| 46 | 33 | export function toPersistenceSqlError(operation: string) { |
| 47 | 34 | return (cause: unknown): PersistenceSqlError => |
| 48 | 35 | new PersistenceSqlError({ |
| ... | ... | @@ -70,85 +57,8 @@ export function toPersistenceDecodeCauseError(operation: string) { |
| 70 | 57 | }); |
| 71 | 58 | } |
| 72 | 59 | |
| 73 | export function toPersistenceSerializationError(operation: string) { | |
| 74 | return (cause: unknown): PersistenceSerializationError => | |
| 75 | new PersistenceSerializationError({ | |
| 76 | operation, | |
| 77 | detail: `Failed to execute ${operation}`, | |
| 78 | cause, | |
| 79 | }); | |
| 80 | } | |
| 81 | ||
| 82 | // =============================== | |
| 83 | // Project Repository Errors | |
| 84 | // =============================== | |
| 85 | ||
| 86 | export class ProjectValidationError extends Schema.TaggedErrorClass<ProjectValidationError>()( | |
| 87 | "ProjectValidationError", | |
| 88 | { | |
| 89 | operation: Schema.String, | |
| 90 | issue: Schema.String, | |
| 91 | cause: Schema.optional(Schema.Defect), | |
| 92 | }, | |
| 93 | ) { | |
| 94 | override get message(): string { | |
| 95 | return `Project validation failed in ${this.operation}: ${this.issue}`; | |
| 96 | } | |
| 97 | } | |
| 98 | ||
| 99 | export class ProjectPathMissingError extends Schema.TaggedErrorClass<ProjectPathMissingError>()( | |
| 100 | "ProjectPathMissingError", | |
| 101 | { | |
| 102 | cwd: Schema.String, | |
| 103 | cause: Schema.optional(Schema.Defect), | |
| 104 | }, | |
| 105 | ) { | |
| 106 | override get message(): string { | |
| 107 | return `Project path does not exist: ${this.cwd}`; | |
| 108 | } | |
| 109 | } | |
| 110 | ||
| 111 | export class ProjectNotFoundError extends Schema.TaggedErrorClass<ProjectNotFoundError>()( | |
| 112 | "ProjectNotFoundError", | |
| 113 | { | |
| 114 | projectId: Schema.String, | |
| 115 | cause: Schema.optional(Schema.Defect), | |
| 116 | }, | |
| 117 | ) { | |
| 118 | override get message(): string { | |
| 119 | return `Project not found: ${this.projectId}`; | |
| 120 | } | |
| 121 | } | |
| 122 | ||
| 123 | // =============================== | |
| 124 | // Checkpoint Repository Errors | |
| 125 | // =============================== | |
| 126 | ||
| 127 | export class CheckpointRepositoryValidationError extends Schema.TaggedErrorClass<CheckpointRepositoryValidationError>()( | |
| 128 | "CheckpointRepositoryValidationError", | |
| 129 | { | |
| 130 | operation: Schema.String, | |
| 131 | issue: Schema.String, | |
| 132 | cause: Schema.optional(Schema.Defect), | |
| 133 | }, | |
| 134 | ) { | |
| 135 | override get message(): string { | |
| 136 | return `Checkpoint repository validation failed in ${this.operation}: ${this.issue}`; | |
| 137 | } | |
| 138 | } | |
| 139 | ||
| 140 | export class CheckpointRepositoryPersistenceError extends Schema.TaggedErrorClass<CheckpointRepositoryPersistenceError>()( | |
| 141 | "CheckpointRepositoryPersistenceError", | |
| 142 | { | |
| 143 | operation: Schema.String, | |
| 144 | detail: Schema.String, | |
| 145 | cause: Schema.optional(Schema.Defect), | |
| 146 | }, | |
| 147 | ) { | |
| 148 | override get message(): string { | |
| 149 | return `Checkpoint repository persistence error in ${this.operation}: ${this.detail}`; | |
| 150 | } | |
| 151 | } | |
| 60 | export const isPersistenceError = (u: unknown) => | |
| 61 | Schema.is(PersistenceSqlError)(u) || Schema.is(PersistenceDecodeError)(u); | |
| 152 | 62 | |
| 153 | 63 | // =============================== |
| 154 | 64 | // Provider Session Repository Errors |
| ... | ... | @@ -180,22 +90,7 @@ export class ProviderSessionRepositoryPersistenceError extends Schema.TaggedErro |
| 180 | 90 | } |
| 181 | 91 | } |
| 182 | 92 | |
| 183 | export type ProjectRepositoryError = | |
| 184 | | PersistenceSqlError | |
| 185 | | PersistenceDecodeError | |
| 186 | | PersistenceSerializationError | |
| 187 | | ProjectValidationError | |
| 188 | | ProjectPathMissingError | |
| 189 | | ProjectNotFoundError; | |
| 190 | ||
| 191 | export type OrchestrationEventStoreError = | |
| 192 | | PersistenceSqlError | |
| 193 | | PersistenceDecodeError | |
| 194 | | PersistenceSerializationError; | |
| 195 | ||
| 196 | export type CheckpointRepositoryError = | |
| 197 | | CheckpointRepositoryValidationError | |
| 198 | | CheckpointRepositoryPersistenceError; | |
| 93 | export type OrchestrationEventStoreError = PersistenceSqlError | PersistenceDecodeError; | |
| 199 | 94 | |
| 200 | 95 | export type ProviderSessionRepositoryError = |
| 201 | 96 | | ProviderSessionRepositoryValidationError |
| ... | ... | @@ -205,12 +100,6 @@ export type OrchestrationCommandReceiptRepositoryError = |
| 205 | 100 | | PersistenceSqlError |
| 206 | 101 | | PersistenceDecodeError; |
| 207 | 102 | |
| 208 | export type ProviderSessionRuntimeRepositoryError = | |
| 209 | | PersistenceSqlError | |
| 210 | | PersistenceDecodeError | |
| 211 | | PersistenceSerializationError; | |
| 103 | export type ProviderSessionRuntimeRepositoryError = PersistenceSqlError | PersistenceDecodeError; | |
| 212 | 104 | |
| 213 | export type ProjectionRepositoryError = | |
| 214 | | PersistenceSqlError | |
| 215 | | PersistenceDecodeError | |
| 216 | | PersistenceSerializationError; | |
| 105 | export type ProjectionRepositoryError = PersistenceSqlError | PersistenceDecodeError; |
apps/server/src/persistence/NodeSqliteClient.ts+5| ... | ... | @@ -1,6 +1,8 @@ |
| 1 | 1 | /** |
| 2 | 2 | * Port of `@effect/sql-sqlite-node` that uses the native `node:sqlite` |
| 3 | 3 | * bindings instead of `better-sqlite3`. |
| 4 | * | |
| 5 | * @module SqliteClient | |
| 4 | 6 | */ |
| 5 | 7 | import { DatabaseSync, type StatementSync } from "node:sqlite"; |
| 6 | 8 | |
| ... | ... | @@ -27,6 +29,9 @@ export const TypeId: TypeId = "~local/sqlite-node/SqliteClient"; |
| 27 | 29 | |
| 28 | 30 | export type TypeId = "~local/sqlite-node/SqliteClient"; |
| 29 | 31 | |
| 32 | /** | |
| 33 | * SqliteClient - Effect service tag for the sqlite SQL client. | |
| 34 | */ | |
| 30 | 35 | export const SqliteClient = ServiceMap.Service<Client.SqlClient>("@local/sqlite-node/SqliteClient"); |
| 31 | 36 | |
| 32 | 37 | export interface SqliteClientConfig { |
apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts+22| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * OrchestrationCommandReceiptRepository - Repository interface for command receipts. | |
| 3 | * | |
| 4 | * Owns persistence operations for deduplication and status tracking of | |
| 5 | * orchestration command handling. | |
| 6 | * | |
| 7 | * @module OrchestrationCommandReceiptRepository | |
| 8 | */ | |
| 1 | 9 | import { |
| 2 | 10 | CommandId, |
| 3 | 11 | IsoDateTime, |
| ... | ... | @@ -28,11 +36,22 @@ export const GetByCommandIdInput = Schema.Struct({ |
| 28 | 36 | }); |
| 29 | 37 | export type GetByCommandIdInput = typeof GetByCommandIdInput.Type; |
| 30 | 38 | |
| 39 | /** | |
| 40 | * OrchestrationCommandReceiptRepositoryShape - Service API for command receipts. | |
| 41 | */ | |
| 31 | 42 | export interface OrchestrationCommandReceiptRepositoryShape { |
| 43 | /** | |
| 44 | * Insert or replace a command receipt row. | |
| 45 | * | |
| 46 | * Upserts by `commandId` for idempotent command-result tracking. | |
| 47 | */ | |
| 32 | 48 | readonly upsert: ( |
| 33 | 49 | receipt: OrchestrationCommandReceipt, |
| 34 | 50 | ) => Effect.Effect<void, OrchestrationCommandReceiptRepositoryError>; |
| 35 | 51 | |
| 52 | /** | |
| 53 | * Read a command receipt by command id. | |
| 54 | */ | |
| 36 | 55 | readonly getByCommandId: ( |
| 37 | 56 | input: GetByCommandIdInput, |
| 38 | 57 | ) => Effect.Effect< |
| ... | ... | @@ -41,6 +60,9 @@ export interface OrchestrationCommandReceiptRepositoryShape { |
| 41 | 60 | >; |
| 42 | 61 | } |
| 43 | 62 | |
| 63 | /** | |
| 64 | * OrchestrationCommandReceiptRepository - Service tag for command receipt persistence. | |
| 65 | */ | |
| 44 | 66 | export class OrchestrationCommandReceiptRepository extends ServiceMap.Service< |
| 45 | 67 | OrchestrationCommandReceiptRepository, |
| 46 | 68 | OrchestrationCommandReceiptRepositoryShape |
apps/server/src/persistence/Services/OrchestrationEventStore.ts+7| ... | ... | @@ -15,12 +15,17 @@ import type { Effect, Stream } from "effect"; |
| 15 | 15 | |
| 16 | 16 | import type { OrchestrationEventStoreError } from "../Errors.ts"; |
| 17 | 17 | |
| 18 | /** | |
| 19 | * OrchestrationEventStoreShape - Service API for orchestration event persistence. | |
| 20 | */ | |
| 18 | 21 | export interface OrchestrationEventStoreShape { |
| 19 | 22 | /** |
| 20 | 23 | * Persist a new orchestration event. |
| 21 | 24 | * |
| 22 | 25 | * @param event - Event payload without sequence (assigned by storage). |
| 23 | 26 | * @returns Effect containing the stored event with assigned sequence. |
| 27 | * | |
| 28 | * Actor kind is inferred from command/metadata before persistence. | |
| 24 | 29 | */ |
| 25 | 30 | readonly append: ( |
| 26 | 31 | event: Omit<OrchestrationEvent, "sequence">, |
| ... | ... | @@ -32,6 +37,8 @@ export interface OrchestrationEventStoreShape { |
| 32 | 37 | * @param sequenceExclusive - Sequence cursor (exclusive). |
| 33 | 38 | * @param limit - Maximum number of events to emit. |
| 34 | 39 | * @returns Stream containing ordered events. |
| 40 | * | |
| 41 | * Reads in fixed-size pages and normalizes non-integer/negative limits. | |
| 35 | 42 | */ |
| 36 | 43 | readonly readFromSequence: ( |
| 37 | 44 | sequenceExclusive: number, |
apps/server/src/persistence/Services/ProjectionCheckpoints.ts+30| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProjectionCheckpointRepository - Projection repository interface for checkpoints. | |
| 3 | * | |
| 4 | * Owns persistence operations for projected checkpoint summaries in thread | |
| 5 | * timelines. | |
| 6 | * | |
| 7 | * @module ProjectionCheckpointRepository | |
| 8 | */ | |
| 1 | 9 | import { |
| 2 | 10 | CheckpointRef, |
| 3 | 11 | IsoDateTime, |
| ... | ... | @@ -41,22 +49,44 @@ export const DeleteByThreadIdInput = Schema.Struct({ |
| 41 | 49 | }); |
| 42 | 50 | export type DeleteByThreadIdInput = typeof DeleteByThreadIdInput.Type; |
| 43 | 51 | |
| 52 | /** | |
| 53 | * ProjectionCheckpointRepositoryShape - Service API for projected checkpoints. | |
| 54 | */ | |
| 44 | 55 | export interface ProjectionCheckpointRepositoryShape { |
| 56 | /** | |
| 57 | * Insert or replace a projected checkpoint row. | |
| 58 | * | |
| 59 | * Upserts by composite key `(threadId, checkpointTurnCount)`. | |
| 60 | */ | |
| 45 | 61 | readonly upsert: (row: ProjectionCheckpoint) => Effect.Effect<void, ProjectionRepositoryError>; |
| 46 | 62 | |
| 63 | /** | |
| 64 | * List projected checkpoints for a thread. | |
| 65 | * | |
| 66 | * Returned in ascending checkpoint turn-count order. | |
| 67 | */ | |
| 47 | 68 | readonly listByThreadId: ( |
| 48 | 69 | input: ListByThreadIdInput, |
| 49 | 70 | ) => Effect.Effect<ReadonlyArray<ProjectionCheckpoint>, ProjectionRepositoryError>; |
| 50 | 71 | |
| 72 | /** | |
| 73 | * Read a projected checkpoint by thread and turn-count key. | |
| 74 | */ | |
| 51 | 75 | readonly getByThreadAndTurnCount: ( |
| 52 | 76 | input: GetByThreadAndTurnCountInput, |
| 53 | 77 | ) => Effect.Effect<Option.Option<ProjectionCheckpoint>, ProjectionRepositoryError>; |
| 54 | 78 | |
| 79 | /** | |
| 80 | * Delete projected checkpoint rows by thread. | |
| 81 | */ | |
| 55 | 82 | readonly deleteByThreadId: ( |
| 56 | 83 | input: DeleteByThreadIdInput, |
| 57 | 84 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 58 | 85 | } |
| 59 | 86 | |
| 87 | /** | |
| 88 | * ProjectionCheckpointRepository - Service tag for checkpoint projection persistence. | |
| 89 | */ | |
| 60 | 90 | export class ProjectionCheckpointRepository extends ServiceMap.Service< |
| 61 | 91 | ProjectionCheckpointRepository, |
| 62 | 92 | ProjectionCheckpointRepositoryShape |
apps/server/src/persistence/Services/ProjectionPendingApprovals.ts+30| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProjectionPendingApprovalRepository - Repository interface for pending approvals. | |
| 3 | * | |
| 4 | * Owns persistence operations for projected approval requests awaiting user | |
| 5 | * decisions. | |
| 6 | * | |
| 7 | * @module ProjectionPendingApprovalRepository | |
| 8 | */ | |
| 1 | 9 | import { |
| 2 | 10 | ApprovalRequestId, |
| 3 | 11 | IsoDateTime, |
| ... | ... | @@ -37,24 +45,46 @@ export const DeleteProjectionPendingApprovalInput = Schema.Struct({ |
| 37 | 45 | }); |
| 38 | 46 | export type DeleteProjectionPendingApprovalInput = typeof DeleteProjectionPendingApprovalInput.Type; |
| 39 | 47 | |
| 48 | /** | |
| 49 | * ProjectionPendingApprovalRepositoryShape - Service API for pending approvals. | |
| 50 | */ | |
| 40 | 51 | export interface ProjectionPendingApprovalRepositoryShape { |
| 52 | /** | |
| 53 | * Insert or replace a projected pending approval row. | |
| 54 | * | |
| 55 | * Upserts by `requestId`. | |
| 56 | */ | |
| 41 | 57 | readonly upsert: ( |
| 42 | 58 | row: ProjectionPendingApproval, |
| 43 | 59 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 44 | 60 | |
| 61 | /** | |
| 62 | * List pending approvals for a thread. | |
| 63 | * | |
| 64 | * Returned in ascending creation order. | |
| 65 | */ | |
| 45 | 66 | readonly listByThreadId: ( |
| 46 | 67 | input: ListProjectionPendingApprovalsInput, |
| 47 | 68 | ) => Effect.Effect<ReadonlyArray<ProjectionPendingApproval>, ProjectionRepositoryError>; |
| 48 | 69 | |
| 70 | /** | |
| 71 | * Read a pending approval row by request id. | |
| 72 | */ | |
| 49 | 73 | readonly getByRequestId: ( |
| 50 | 74 | input: GetProjectionPendingApprovalInput, |
| 51 | 75 | ) => Effect.Effect<Option.Option<ProjectionPendingApproval>, ProjectionRepositoryError>; |
| 52 | 76 | |
| 77 | /** | |
| 78 | * Delete a pending approval row by request id. | |
| 79 | */ | |
| 53 | 80 | readonly deleteByRequestId: ( |
| 54 | 81 | input: DeleteProjectionPendingApprovalInput, |
| 55 | 82 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 56 | 83 | } |
| 57 | 84 | |
| 85 | /** | |
| 86 | * ProjectionPendingApprovalRepository - Service tag for pending approval persistence. | |
| 87 | */ | |
| 58 | 88 | export class ProjectionPendingApprovalRepository extends ServiceMap.Service< |
| 59 | 89 | ProjectionPendingApprovalRepository, |
| 60 | 90 | ProjectionPendingApprovalRepositoryShape |
apps/server/src/persistence/Services/ProjectionPendingTurnStarts.ts+25| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProjectionPendingTurnStartRepository - Repository interface for pending turn starts. | |
| 3 | * | |
| 4 | * Owns persistence operations for projected "pending turn start" markers used | |
| 5 | * while creating new turns from queued messages. | |
| 6 | * | |
| 7 | * @module ProjectionPendingTurnStartRepository | |
| 8 | */ | |
| 1 | 9 | import { IsoDateTime, MessageId, ThreadId } from "@t3tools/contracts"; |
| 2 | 10 | import { Option, Schema, ServiceMap } from "effect"; |
| 3 | 11 | import type { Effect } from "effect"; |
| ... | ... | @@ -21,20 +29,37 @@ export const DeleteProjectionPendingTurnStartInput = Schema.Struct({ |
| 21 | 29 | }); |
| 22 | 30 | export type DeleteProjectionPendingTurnStartInput = typeof DeleteProjectionPendingTurnStartInput.Type; |
| 23 | 31 | |
| 32 | /** | |
| 33 | * ProjectionPendingTurnStartRepositoryShape - Service API for pending turn-start markers. | |
| 34 | */ | |
| 24 | 35 | export interface ProjectionPendingTurnStartRepositoryShape { |
| 36 | /** | |
| 37 | * Insert or replace a pending turn-start row. | |
| 38 | * | |
| 39 | * Maintains at most one pending marker per thread. | |
| 40 | */ | |
| 25 | 41 | readonly upsert: ( |
| 26 | 42 | row: ProjectionPendingTurnStart, |
| 27 | 43 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 28 | 44 | |
| 45 | /** | |
| 46 | * Read a pending turn-start marker by thread id. | |
| 47 | */ | |
| 29 | 48 | readonly getByThreadId: ( |
| 30 | 49 | input: GetProjectionPendingTurnStartInput, |
| 31 | 50 | ) => Effect.Effect<Option.Option<ProjectionPendingTurnStart>, ProjectionRepositoryError>; |
| 32 | 51 | |
| 52 | /** | |
| 53 | * Delete a pending turn-start marker by thread id. | |
| 54 | */ | |
| 33 | 55 | readonly deleteByThreadId: ( |
| 34 | 56 | input: DeleteProjectionPendingTurnStartInput, |
| 35 | 57 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 36 | 58 | } |
| 37 | 59 | |
| 60 | /** | |
| 61 | * ProjectionPendingTurnStartRepository - Service tag for pending turn-start persistence. | |
| 62 | */ | |
| 38 | 63 | export class ProjectionPendingTurnStartRepository extends ServiceMap.Service< |
| 39 | 64 | ProjectionPendingTurnStartRepository, |
| 40 | 65 | ProjectionPendingTurnStartRepositoryShape |
apps/server/src/persistence/Services/ProjectionProjects.ts+30| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProjectionProjectRepository - Projection repository interface for projects. | |
| 3 | * | |
| 4 | * Owns persistence operations for project rows in the orchestration projection | |
| 5 | * read model. | |
| 6 | * | |
| 7 | * @module ProjectionProjectRepository | |
| 8 | */ | |
| 1 | 9 | import { IsoDateTime, ProjectId, ProjectScript } from "@t3tools/contracts"; |
| 2 | 10 | import { Option, Schema, ServiceMap } from "effect"; |
| 3 | 11 | import type { Effect } from "effect"; |
| ... | ... | @@ -26,23 +34,45 @@ export const DeleteProjectionProjectInput = Schema.Struct({ |
| 26 | 34 | }); |
| 27 | 35 | export type DeleteProjectionProjectInput = typeof DeleteProjectionProjectInput.Type; |
| 28 | 36 | |
| 37 | /** | |
| 38 | * ProjectionProjectRepositoryShape - Service API for projected project records. | |
| 39 | */ | |
| 29 | 40 | export interface ProjectionProjectRepositoryShape { |
| 41 | /** | |
| 42 | * Insert or replace a projected project row. | |
| 43 | * | |
| 44 | * Upserts by `projectId` and persists scripts through JSON encoding. | |
| 45 | */ | |
| 30 | 46 | readonly upsert: (row: ProjectionProject) => Effect.Effect<void, ProjectionRepositoryError>; |
| 31 | 47 | |
| 48 | /** | |
| 49 | * Read a projected project row by id. | |
| 50 | */ | |
| 32 | 51 | readonly getById: ( |
| 33 | 52 | input: GetProjectionProjectInput, |
| 34 | 53 | ) => Effect.Effect<Option.Option<ProjectionProject>, ProjectionRepositoryError>; |
| 35 | 54 | |
| 55 | /** | |
| 56 | * List all projected project rows. | |
| 57 | * | |
| 58 | * Returned in deterministic creation order. | |
| 59 | */ | |
| 36 | 60 | readonly listAll: () => Effect.Effect< |
| 37 | 61 | ReadonlyArray<ProjectionProject>, |
| 38 | 62 | ProjectionRepositoryError |
| 39 | 63 | >; |
| 40 | 64 | |
| 65 | /** | |
| 66 | * Soft-delete a projected project row by id. | |
| 67 | */ | |
| 41 | 68 | readonly deleteById: ( |
| 42 | 69 | input: DeleteProjectionProjectInput, |
| 43 | 70 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 44 | 71 | } |
| 45 | 72 | |
| 73 | /** | |
| 74 | * ProjectionProjectRepository - Service tag for project projection persistence. | |
| 75 | */ | |
| 46 | 76 | export class ProjectionProjectRepository extends ServiceMap.Service< |
| 47 | 77 | ProjectionProjectRepository, |
| 48 | 78 | ProjectionProjectRepositoryShape |
apps/server/src/persistence/Services/ProjectionState.ts+30| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProjectionStateRepository - Projection repository interface for projector cursors. | |
| 3 | * | |
| 4 | * Owns persistence operations for projection cursor state used to resume | |
| 5 | * incremental event projection. | |
| 6 | * | |
| 7 | * @module ProjectionStateRepository | |
| 8 | */ | |
| 1 | 9 | import { IsoDateTime, NonNegativeInt } from "@t3tools/contracts"; |
| 2 | 10 | import { Option, Schema, ServiceMap } from "effect"; |
| 3 | 11 | import type { Effect } from "effect"; |
| ... | ... | @@ -16,18 +24,40 @@ export const GetProjectionStateInput = Schema.Struct({ |
| 16 | 24 | }); |
| 17 | 25 | export type GetProjectionStateInput = typeof GetProjectionStateInput.Type; |
| 18 | 26 | |
| 27 | /** | |
| 28 | * ProjectionStateRepositoryShape - Service API for projector state records. | |
| 29 | */ | |
| 19 | 30 | export interface ProjectionStateRepositoryShape { |
| 31 | /** | |
| 32 | * Insert or replace a projection cursor row. | |
| 33 | * | |
| 34 | * Upserts by projector name. | |
| 35 | */ | |
| 20 | 36 | readonly upsert: (row: ProjectionState) => Effect.Effect<void, ProjectionRepositoryError>; |
| 21 | 37 | |
| 38 | /** | |
| 39 | * Read projection cursor state for a projector key. | |
| 40 | */ | |
| 22 | 41 | readonly getByProjector: ( |
| 23 | 42 | input: GetProjectionStateInput, |
| 24 | 43 | ) => Effect.Effect<Option.Option<ProjectionState>, ProjectionRepositoryError>; |
| 25 | 44 | |
| 45 | /** | |
| 46 | * List all projector cursor rows. | |
| 47 | */ | |
| 26 | 48 | readonly listAll: () => Effect.Effect<ReadonlyArray<ProjectionState>, ProjectionRepositoryError>; |
| 27 | 49 | |
| 50 | /** | |
| 51 | * Read the minimum applied sequence across all projectors. | |
| 52 | * | |
| 53 | * Returns `null` when no projector state rows exist. | |
| 54 | */ | |
| 28 | 55 | readonly minLastAppliedSequence: () => Effect.Effect<number | null, ProjectionRepositoryError>; |
| 29 | 56 | } |
| 30 | 57 | |
| 58 | /** | |
| 59 | * ProjectionStateRepository - Service tag for projection cursor persistence. | |
| 60 | */ | |
| 31 | 61 | export class ProjectionStateRepository extends ServiceMap.Service< |
| 32 | 62 | ProjectionStateRepository, |
| 33 | 63 | ProjectionStateRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreadActivities.ts+27| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProjectionThreadActivityRepository - Projection repository interface for thread activity. | |
| 3 | * | |
| 4 | * Owns persistence operations for activity timeline entries projected from | |
| 5 | * orchestration events. | |
| 6 | * | |
| 7 | * @module ProjectionThreadActivityRepository | |
| 8 | */ | |
| 1 | 9 | import { |
| 2 | 10 | EventId, |
| 3 | 11 | IsoDateTime, |
| ... | ... | @@ -33,20 +41,39 @@ export const DeleteProjectionThreadActivitiesInput = Schema.Struct({ |
| 33 | 41 | export type DeleteProjectionThreadActivitiesInput = |
| 34 | 42 | typeof DeleteProjectionThreadActivitiesInput.Type; |
| 35 | 43 | |
| 44 | /** | |
| 45 | * ProjectionThreadActivityRepositoryShape - Service API for projected thread activity. | |
| 46 | */ | |
| 36 | 47 | export interface ProjectionThreadActivityRepositoryShape { |
| 48 | /** | |
| 49 | * Insert or replace a projected thread activity row. | |
| 50 | * | |
| 51 | * Upserts by `activityId` and JSON-encodes payload. | |
| 52 | */ | |
| 37 | 53 | readonly upsert: ( |
| 38 | 54 | row: ProjectionThreadActivity, |
| 39 | 55 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 40 | 56 | |
| 57 | /** | |
| 58 | * List projected thread activity rows for a thread. | |
| 59 | * | |
| 60 | * Returned in ascending creation order. | |
| 61 | */ | |
| 41 | 62 | readonly listByThreadId: ( |
| 42 | 63 | input: ListProjectionThreadActivitiesInput, |
| 43 | 64 | ) => Effect.Effect<ReadonlyArray<ProjectionThreadActivity>, ProjectionRepositoryError>; |
| 44 | 65 | |
| 66 | /** | |
| 67 | * Delete projected thread activity rows by thread. | |
| 68 | */ | |
| 45 | 69 | readonly deleteByThreadId: ( |
| 46 | 70 | input: DeleteProjectionThreadActivitiesInput, |
| 47 | 71 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 48 | 72 | } |
| 49 | 73 | |
| 74 | /** | |
| 75 | * ProjectionThreadActivityRepository - Service tag for thread activity persistence. | |
| 76 | */ | |
| 50 | 77 | export class ProjectionThreadActivityRepository extends ServiceMap.Service< |
| 51 | 78 | ProjectionThreadActivityRepository, |
| 52 | 79 | ProjectionThreadActivityRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreadMessages.ts+27| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProjectionThreadMessageRepository - Projection repository interface for messages. | |
| 3 | * | |
| 4 | * Owns persistence operations for projected thread messages rendered in the | |
| 5 | * orchestration read model. | |
| 6 | * | |
| 7 | * @module ProjectionThreadMessageRepository | |
| 8 | */ | |
| 1 | 9 | import { |
| 2 | 10 | OrchestrationMessageRole, |
| 3 | 11 | MessageId, |
| ... | ... | @@ -32,20 +40,39 @@ export const DeleteProjectionThreadMessagesInput = Schema.Struct({ |
| 32 | 40 | }); |
| 33 | 41 | export type DeleteProjectionThreadMessagesInput = typeof DeleteProjectionThreadMessagesInput.Type; |
| 34 | 42 | |
| 43 | /** | |
| 44 | * ProjectionThreadMessageRepositoryShape - Service API for projected thread messages. | |
| 45 | */ | |
| 35 | 46 | export interface ProjectionThreadMessageRepositoryShape { |
| 47 | /** | |
| 48 | * Insert or replace a projected thread message row. | |
| 49 | * | |
| 50 | * Upserts by `messageId`. | |
| 51 | */ | |
| 36 | 52 | readonly upsert: ( |
| 37 | 53 | message: ProjectionThreadMessage, |
| 38 | 54 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 39 | 55 | |
| 56 | /** | |
| 57 | * List projected thread messages for a thread. | |
| 58 | * | |
| 59 | * Returned in ascending creation order. | |
| 60 | */ | |
| 40 | 61 | readonly listByThreadId: ( |
| 41 | 62 | input: ListProjectionThreadMessagesInput, |
| 42 | 63 | ) => Effect.Effect<ReadonlyArray<ProjectionThreadMessage>, ProjectionRepositoryError>; |
| 43 | 64 | |
| 65 | /** | |
| 66 | * Delete projected thread messages by thread. | |
| 67 | */ | |
| 44 | 68 | readonly deleteByThreadId: ( |
| 45 | 69 | input: DeleteProjectionThreadMessagesInput, |
| 46 | 70 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 47 | 71 | } |
| 48 | 72 | |
| 73 | /** | |
| 74 | * ProjectionThreadMessageRepository - Service tag for message projection persistence. | |
| 75 | */ | |
| 49 | 76 | export class ProjectionThreadMessageRepository extends ServiceMap.Service< |
| 50 | 77 | ProjectionThreadMessageRepository, |
| 51 | 78 | ProjectionThreadMessageRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreadSessions.ts+25| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProjectionThreadSessionRepository - Repository interface for thread sessions. | |
| 3 | * | |
| 4 | * Owns persistence operations for projected provider-session linkage and | |
| 5 | * runtime status for each thread. | |
| 6 | * | |
| 7 | * @module ProjectionThreadSessionRepository | |
| 8 | */ | |
| 1 | 9 | import { |
| 2 | 10 | IsoDateTime, |
| 3 | 11 | OrchestrationSessionStatus, |
| ... | ... | @@ -33,18 +41,35 @@ export const DeleteProjectionThreadSessionInput = Schema.Struct({ |
| 33 | 41 | }); |
| 34 | 42 | export type DeleteProjectionThreadSessionInput = typeof DeleteProjectionThreadSessionInput.Type; |
| 35 | 43 | |
| 44 | /** | |
| 45 | * ProjectionThreadSessionRepositoryShape - Service API for projected thread sessions. | |
| 46 | */ | |
| 36 | 47 | export interface ProjectionThreadSessionRepositoryShape { |
| 48 | /** | |
| 49 | * Insert or replace a projected thread-session row. | |
| 50 | * | |
| 51 | * Upserts by `threadId`. | |
| 52 | */ | |
| 37 | 53 | readonly upsert: (row: ProjectionThreadSession) => Effect.Effect<void, ProjectionRepositoryError>; |
| 38 | 54 | |
| 55 | /** | |
| 56 | * Read projected thread-session state by thread id. | |
| 57 | */ | |
| 39 | 58 | readonly getByThreadId: ( |
| 40 | 59 | input: GetProjectionThreadSessionInput, |
| 41 | 60 | ) => Effect.Effect<Option.Option<ProjectionThreadSession>, ProjectionRepositoryError>; |
| 42 | 61 | |
| 62 | /** | |
| 63 | * Delete projected thread-session state by thread id. | |
| 64 | */ | |
| 43 | 65 | readonly deleteByThreadId: ( |
| 44 | 66 | input: DeleteProjectionThreadSessionInput, |
| 45 | 67 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 46 | 68 | } |
| 47 | 69 | |
| 70 | /** | |
| 71 | * ProjectionThreadSessionRepository - Service tag for thread-session persistence. | |
| 72 | */ | |
| 48 | 73 | export class ProjectionThreadSessionRepository extends ServiceMap.Service< |
| 49 | 74 | ProjectionThreadSessionRepository, |
| 50 | 75 | ProjectionThreadSessionRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreadTurns.ts+29| ... | ... | @@ -1,3 +1,10 @@ |
| 1 | /** | |
| 2 | * ProjectionThreadTurnRepository - Projection repository interface for turns. | |
| 3 | * | |
| 4 | * Owns persistence operations for projected turn records in thread timelines. | |
| 5 | * | |
| 6 | * @module ProjectionThreadTurnRepository | |
| 7 | */ | |
| 1 | 8 | import { |
| 2 | 9 | IsoDateTime, |
| 3 | 10 | MessageId, |
| ... | ... | @@ -38,22 +45,44 @@ export const DeleteProjectionThreadTurnsInput = Schema.Struct({ |
| 38 | 45 | }); |
| 39 | 46 | export type DeleteProjectionThreadTurnsInput = typeof DeleteProjectionThreadTurnsInput.Type; |
| 40 | 47 | |
| 48 | /** | |
| 49 | * ProjectionThreadTurnRepositoryShape - Service API for projected thread turns. | |
| 50 | */ | |
| 41 | 51 | export interface ProjectionThreadTurnRepositoryShape { |
| 52 | /** | |
| 53 | * Insert or replace a projected turn row. | |
| 54 | * | |
| 55 | * Upserts by `turnId`. | |
| 56 | */ | |
| 42 | 57 | readonly upsert: (turn: ProjectionThreadTurn) => Effect.Effect<void, ProjectionRepositoryError>; |
| 43 | 58 | |
| 59 | /** | |
| 60 | * List projected turns for a thread. | |
| 61 | * | |
| 62 | * Returned in ascending `turnCount` order. | |
| 63 | */ | |
| 44 | 64 | readonly listByThreadId: ( |
| 45 | 65 | input: ListProjectionThreadTurnsInput, |
| 46 | 66 | ) => Effect.Effect<ReadonlyArray<ProjectionThreadTurn>, ProjectionRepositoryError>; |
| 47 | 67 | |
| 68 | /** | |
| 69 | * Read a projected turn row by turn id. | |
| 70 | */ | |
| 48 | 71 | readonly getByTurnId: ( |
| 49 | 72 | input: GetProjectionThreadTurnInput, |
| 50 | 73 | ) => Effect.Effect<Option.Option<ProjectionThreadTurn>, ProjectionRepositoryError>; |
| 51 | 74 | |
| 75 | /** | |
| 76 | * Delete projected turn rows by thread. | |
| 77 | */ | |
| 52 | 78 | readonly deleteByThreadId: ( |
| 53 | 79 | input: DeleteProjectionThreadTurnsInput, |
| 54 | 80 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 55 | 81 | } |
| 56 | 82 | |
| 83 | /** | |
| 84 | * ProjectionThreadTurnRepository - Service tag for turn projection persistence. | |
| 85 | */ | |
| 57 | 86 | export class ProjectionThreadTurnRepository extends ServiceMap.Service< |
| 58 | 87 | ProjectionThreadTurnRepository, |
| 59 | 88 | ProjectionThreadTurnRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreads.ts+30| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProjectionThreadRepository - Projection repository interface for threads. | |
| 3 | * | |
| 4 | * Owns persistence operations for projected thread records in the | |
| 5 | * orchestration read model. | |
| 6 | * | |
| 7 | * @module ProjectionThreadRepository | |
| 8 | */ | |
| 1 | 9 | import { IsoDateTime, ProjectId, ThreadId, TurnId } from "@t3tools/contracts"; |
| 2 | 10 | import { Option, Schema, ServiceMap } from "effect"; |
| 3 | 11 | import type { Effect } from "effect"; |
| ... | ... | @@ -33,22 +41,44 @@ export const ListProjectionThreadsByProjectInput = Schema.Struct({ |
| 33 | 41 | }); |
| 34 | 42 | export type ListProjectionThreadsByProjectInput = typeof ListProjectionThreadsByProjectInput.Type; |
| 35 | 43 | |
| 44 | /** | |
| 45 | * ProjectionThreadRepositoryShape - Service API for projected thread records. | |
| 46 | */ | |
| 36 | 47 | export interface ProjectionThreadRepositoryShape { |
| 48 | /** | |
| 49 | * Insert or replace a projected thread row. | |
| 50 | * | |
| 51 | * Upserts by `threadId`. | |
| 52 | */ | |
| 37 | 53 | readonly upsert: (thread: ProjectionThread) => Effect.Effect<void, ProjectionRepositoryError>; |
| 38 | 54 | |
| 55 | /** | |
| 56 | * Read a projected thread row by id. | |
| 57 | */ | |
| 39 | 58 | readonly getById: ( |
| 40 | 59 | input: GetProjectionThreadInput, |
| 41 | 60 | ) => Effect.Effect<Option.Option<ProjectionThread>, ProjectionRepositoryError>; |
| 42 | 61 | |
| 62 | /** | |
| 63 | * List projected threads for a project. | |
| 64 | * | |
| 65 | * Returned in deterministic creation order. | |
| 66 | */ | |
| 43 | 67 | readonly listByProjectId: ( |
| 44 | 68 | input: ListProjectionThreadsByProjectInput, |
| 45 | 69 | ) => Effect.Effect<ReadonlyArray<ProjectionThread>, ProjectionRepositoryError>; |
| 46 | 70 | |
| 71 | /** | |
| 72 | * Soft-delete a projected thread row by id. | |
| 73 | */ | |
| 47 | 74 | readonly deleteById: ( |
| 48 | 75 | input: DeleteProjectionThreadInput, |
| 49 | 76 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 50 | 77 | } |
| 51 | 78 | |
| 79 | /** | |
| 80 | * ProjectionThreadRepository - Service tag for thread projection persistence. | |
| 81 | */ | |
| 52 | 82 | export class ProjectionThreadRepository extends ServiceMap.Service< |
| 53 | 83 | ProjectionThreadRepository, |
| 54 | 84 | ProjectionThreadRepositoryShape |
apps/server/src/persistence/Services/ProviderSessionRuntime.ts+29| ... | ... | @@ -1,3 +1,10 @@ |
| 1 | /** | |
| 2 | * ProviderSessionRuntimeRepository - Repository interface for provider runtime sessions. | |
| 3 | * | |
| 4 | * Owns persistence operations for provider runtime metadata and resume cursors. | |
| 5 | * | |
| 6 | * @module ProviderSessionRuntimeRepository | |
| 7 | */ | |
| 1 | 8 | import { |
| 2 | 9 | IsoDateTime, |
| 3 | 10 | ProviderSessionId, |
| ... | ... | @@ -33,25 +40,47 @@ export const DeleteProviderSessionRuntimeInput = Schema.Struct({ |
| 33 | 40 | }); |
| 34 | 41 | export type DeleteProviderSessionRuntimeInput = typeof DeleteProviderSessionRuntimeInput.Type; |
| 35 | 42 | |
| 43 | /** | |
| 44 | * ProviderSessionRuntimeRepositoryShape - Service API for provider runtime records. | |
| 45 | */ | |
| 36 | 46 | export interface ProviderSessionRuntimeRepositoryShape { |
| 47 | /** | |
| 48 | * Insert or replace a provider runtime row. | |
| 49 | * | |
| 50 | * Upserts by `providerSessionId`, including JSON payload/cursor fields. | |
| 51 | */ | |
| 37 | 52 | readonly upsert: ( |
| 38 | 53 | runtime: ProviderSessionRuntime, |
| 39 | 54 | ) => Effect.Effect<void, ProviderSessionRuntimeRepositoryError>; |
| 40 | 55 | |
| 56 | /** | |
| 57 | * Read provider runtime state by provider session id. | |
| 58 | */ | |
| 41 | 59 | readonly getBySessionId: ( |
| 42 | 60 | input: GetProviderSessionRuntimeInput, |
| 43 | 61 | ) => Effect.Effect<Option.Option<ProviderSessionRuntime>, ProviderSessionRuntimeRepositoryError>; |
| 44 | 62 | |
| 63 | /** | |
| 64 | * List all provider runtime rows. | |
| 65 | * | |
| 66 | * Returned in ascending last-seen order. | |
| 67 | */ | |
| 45 | 68 | readonly list: () => Effect.Effect< |
| 46 | 69 | ReadonlyArray<ProviderSessionRuntime>, |
| 47 | 70 | ProviderSessionRuntimeRepositoryError |
| 48 | 71 | >; |
| 49 | 72 | |
| 73 | /** | |
| 74 | * Delete provider runtime state by provider session id. | |
| 75 | */ | |
| 50 | 76 | readonly deleteBySessionId: ( |
| 51 | 77 | input: DeleteProviderSessionRuntimeInput, |
| 52 | 78 | ) => Effect.Effect<void, ProviderSessionRuntimeRepositoryError>; |
| 53 | 79 | } |
| 54 | 80 | |
| 81 | /** | |
| 82 | * ProviderSessionRuntimeRepository - Service tag for provider runtime persistence. | |
| 83 | */ | |
| 55 | 84 | export class ProviderSessionRuntimeRepository extends ServiceMap.Service< |
| 56 | 85 | ProviderSessionRuntimeRepository, |
| 57 | 86 | ProviderSessionRuntimeRepositoryShape |
apps/server/src/persistence/Services/ProviderSessions.ts+30| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * ProviderSessionRepository - Repository interface for provider session lookup. | |
| 3 | * | |
| 4 | * Owns persistence operations that map internal sessions to provider kinds and | |
| 5 | * optional thread ownership. | |
| 6 | * | |
| 7 | * @module ProviderSessionRepository | |
| 8 | */ | |
| 1 | 9 | import type { ProviderKind, ProviderSessionId, ThreadId } from "@t3tools/contracts"; |
| 2 | 10 | import { Option, ServiceMap } from "effect"; |
| 3 | 11 | import type { Effect } from "effect"; |
| ... | ... | @@ -26,25 +34,47 @@ export interface GetProviderSessionInput { |
| 26 | 34 | readonly sessionId: ProviderSessionId; |
| 27 | 35 | } |
| 28 | 36 | |
| 37 | /** | |
| 38 | * ProviderSessionRepositoryShape - Service API for provider-session records. | |
| 39 | */ | |
| 29 | 40 | export interface ProviderSessionRepositoryShape { |
| 41 | /** | |
| 42 | * Insert or replace a provider-session row. | |
| 43 | * | |
| 44 | * Upserts by `sessionId`. | |
| 45 | */ | |
| 30 | 46 | readonly upsertSession: ( |
| 31 | 47 | input: UpsertProviderSessionInput, |
| 32 | 48 | ) => Effect.Effect<void, ProviderSessionRepositoryError>; |
| 33 | 49 | |
| 50 | /** | |
| 51 | * Read a provider-session row by session id. | |
| 52 | */ | |
| 34 | 53 | readonly getSession: ( |
| 35 | 54 | input: GetProviderSessionInput, |
| 36 | 55 | ) => Effect.Effect<Option.Option<ProviderSessionEntry>, ProviderSessionRepositoryError>; |
| 37 | 56 | |
| 57 | /** | |
| 58 | * List all provider-session rows. | |
| 59 | * | |
| 60 | * Returned in deterministic creation order. | |
| 61 | */ | |
| 38 | 62 | readonly listSessions: () => Effect.Effect< |
| 39 | 63 | ReadonlyArray<ProviderSessionEntry>, |
| 40 | 64 | ProviderSessionRepositoryError |
| 41 | 65 | >; |
| 42 | 66 | |
| 67 | /** | |
| 68 | * Delete a provider-session row by session id. | |
| 69 | */ | |
| 43 | 70 | readonly deleteSession: ( |
| 44 | 71 | input: DeleteProviderSessionInput, |
| 45 | 72 | ) => Effect.Effect<void, ProviderSessionRepositoryError>; |
| 46 | 73 | } |
| 47 | 74 | |
| 75 | /** | |
| 76 | * ProviderSessionRepository - Service tag for provider-session persistence. | |
| 77 | */ | |
| 48 | 78 | export class ProviderSessionRepository extends ServiceMap.Service< |
| 49 | 79 | ProviderSessionRepository, |
| 50 | 80 | ProviderSessionRepositoryShape |
apps/server/src/provider/Errors.ts-71| ... | ... | @@ -51,23 +51,6 @@ export class ProviderAdapterSessionClosedError extends Schema.TaggedErrorClass<P |
| 51 | 51 | } |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | /** | |
| 55 | * ProviderAdapterProtocolError - Invalid/unexpected provider protocol payload. | |
| 56 | */ | |
| 57 | export class ProviderAdapterProtocolError extends Schema.TaggedErrorClass<ProviderAdapterProtocolError>()( | |
| 58 | "ProviderAdapterProtocolError", | |
| 59 | { | |
| 60 | provider: Schema.String, | |
| 61 | operation: Schema.String, | |
| 62 | detail: Schema.String, | |
| 63 | cause: Schema.optional(Schema.Defect), | |
| 64 | }, | |
| 65 | ) { | |
| 66 | override get message(): string { | |
| 67 | return `Provider adapter protocol error (${this.provider}) in ${this.operation}: ${this.detail}`; | |
| 68 | } | |
| 69 | } | |
| 70 | ||
| 71 | 54 | /** |
| 72 | 55 | * ProviderAdapterRequestError - Provider protocol request failed or timed out. |
| 73 | 56 | */ |
| ... | ... | @@ -164,61 +147,10 @@ export class ProviderSessionDirectoryPersistenceError extends Schema.TaggedError |
| 164 | 147 | } |
| 165 | 148 | } |
| 166 | 149 | |
| 167 | /** | |
| 168 | * ProviderCheckpointUnavailableError - Checkpointing unavailable for this session. | |
| 169 | */ | |
| 170 | export class ProviderCheckpointUnavailableError extends Schema.TaggedErrorClass<ProviderCheckpointUnavailableError>()( | |
| 171 | "ProviderCheckpointUnavailableError", | |
| 172 | { | |
| 173 | sessionId: Schema.String, | |
| 174 | detail: Schema.String, | |
| 175 | cause: Schema.optional(Schema.Defect), | |
| 176 | }, | |
| 177 | ) { | |
| 178 | override get message(): string { | |
| 179 | return `Provider checkpoint unavailable for session ${this.sessionId}: ${this.detail}`; | |
| 180 | } | |
| 181 | } | |
| 182 | ||
| 183 | /** | |
| 184 | * ProviderCheckpointRangeError - Requested checkpoint range is invalid. | |
| 185 | */ | |
| 186 | export class ProviderCheckpointRangeError extends Schema.TaggedErrorClass<ProviderCheckpointRangeError>()( | |
| 187 | "ProviderCheckpointRangeError", | |
| 188 | { | |
| 189 | sessionId: Schema.String, | |
| 190 | fromTurnCount: Schema.Number, | |
| 191 | toTurnCount: Schema.Number, | |
| 192 | detail: Schema.String, | |
| 193 | cause: Schema.optional(Schema.Defect), | |
| 194 | }, | |
| 195 | ) { | |
| 196 | override get message(): string { | |
| 197 | return `Provider checkpoint range error for session ${this.sessionId}: ${this.fromTurnCount}..${this.toTurnCount} (${this.detail})`; | |
| 198 | } | |
| 199 | } | |
| 200 | ||
| 201 | /** | |
| 202 | * ProviderFilesystemError - Filesystem checkpoint capture/restore failure. | |
| 203 | */ | |
| 204 | export class ProviderFilesystemError extends Schema.TaggedErrorClass<ProviderFilesystemError>()( | |
| 205 | "ProviderFilesystemError", | |
| 206 | { | |
| 207 | sessionId: Schema.String, | |
| 208 | detail: Schema.String, | |
| 209 | cause: Schema.optional(Schema.Defect), | |
| 210 | }, | |
| 211 | ) { | |
| 212 | override get message(): string { | |
| 213 | return `Provider filesystem checkpoint error for session ${this.sessionId}: ${this.detail}`; | |
| 214 | } | |
| 215 | } | |
| 216 | ||
| 217 | 150 | export type ProviderAdapterError = |
| 218 | 151 | | ProviderAdapterValidationError |
| 219 | 152 | | ProviderAdapterSessionNotFoundError |
| 220 | 153 | | ProviderAdapterSessionClosedError |
| 221 | | ProviderAdapterProtocolError | |
| 222 | 154 | | ProviderAdapterRequestError |
| 223 | 155 | | ProviderAdapterProcessError; |
| 224 | 156 | |
| ... | ... | @@ -227,8 +159,5 @@ export type ProviderServiceError = |
| 227 | 159 | | ProviderUnsupportedError |
| 228 | 160 | | ProviderSessionNotFoundError |
| 229 | 161 | | ProviderSessionDirectoryPersistenceError |
| 230 | | ProviderCheckpointUnavailableError | |
| 231 | | ProviderCheckpointRangeError | |
| 232 | | ProviderFilesystemError | |
| 233 | 162 | | ProviderAdapterError |
| 234 | 163 | | CheckpointServiceError; |
apps/server/src/provider/Services/CodexAdapter.ts+3| ... | ... | @@ -15,6 +15,9 @@ import { ServiceMap } from "effect"; |
| 15 | 15 | import type { ProviderAdapterError } from "../Errors.ts"; |
| 16 | 16 | import type { ProviderAdapterShape } from "./ProviderAdapter.ts"; |
| 17 | 17 | |
| 18 | /** | |
| 19 | * CodexAdapterShape - Service API for the Codex provider adapter. | |
| 20 | */ | |
| 18 | 21 | export interface CodexAdapterShape extends ProviderAdapterShape<ProviderAdapterError> { |
| 19 | 22 | readonly provider: "codex"; |
| 20 | 23 | } |
apps/server/src/provider/Services/ProviderAdapterRegistry.ts+3| ... | ... | @@ -14,6 +14,9 @@ import type { Effect } from "effect"; |
| 14 | 14 | import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts"; |
| 15 | 15 | import type { ProviderAdapterShape } from "./ProviderAdapter.ts"; |
| 16 | 16 | |
| 17 | /** | |
| 18 | * ProviderAdapterRegistryShape - Service API for adapter lookup by provider kind. | |
| 19 | */ | |
| 17 | 20 | export interface ProviderAdapterRegistryShape { |
| 18 | 21 | /** |
| 19 | 22 | * Resolve the adapter for a provider kind. |
apps/server/src/provider/Services/ProviderService.ts+7| ... | ... | @@ -28,6 +28,9 @@ import type { Effect, Stream } from "effect"; |
| 28 | 28 | |
| 29 | 29 | import type { ProviderServiceError } from "../Errors.ts"; |
| 30 | 30 | |
| 31 | /** | |
| 32 | * ProviderServiceShape - Service API for provider session and turn orchestration. | |
| 33 | */ | |
| 31 | 34 | export interface ProviderServiceShape { |
| 32 | 35 | /** |
| 33 | 36 | * Start a provider session. |
| ... | ... | @@ -67,6 +70,8 @@ export interface ProviderServiceShape { |
| 67 | 70 | |
| 68 | 71 | /** |
| 69 | 72 | * List active provider sessions. |
| 73 | * | |
| 74 | * Aggregates runtime session lists from all registered adapters. | |
| 70 | 75 | */ |
| 71 | 76 | readonly listSessions: () => Effect.Effect<ReadonlyArray<ProviderSession>>; |
| 72 | 77 | |
| ... | ... | @@ -83,6 +88,8 @@ export interface ProviderServiceShape { |
| 83 | 88 | * |
| 84 | 89 | * Persisted session bindings remain so stale sessions can be resumed after |
| 85 | 90 | * process restart. |
| 91 | * | |
| 92 | * Runtime alias mappings are cleared as part of shutdown. | |
| 86 | 93 | */ |
| 87 | 94 | readonly stopAll: () => Effect.Effect<void, ProviderServiceError>; |
| 88 | 95 |
apps/server/src/provider/Services/ProviderSessionDirectory.ts+6| ... | ... | @@ -42,9 +42,15 @@ export type ProviderSessionDirectoryWriteError = |
| 42 | 42 | | ProviderValidationError |
| 43 | 43 | | ProviderSessionDirectoryPersistenceError; |
| 44 | 44 | |
| 45 | /** | |
| 46 | * ProviderSessionDirectoryShape - Service API for provider session ownership metadata. | |
| 47 | */ | |
| 45 | 48 | export interface ProviderSessionDirectoryShape { |
| 46 | 49 | /** |
| 47 | 50 | * Record or update ownership for one provider session. |
| 51 | * | |
| 52 | * Preserves existing persisted fields when omitted and shallow-merges | |
| 53 | * runtime payload objects. | |
| 48 | 54 | */ |
| 49 | 55 | readonly upsert: ( |
| 50 | 56 | binding: ProviderSessionBinding, |
apps/server/src/terminal/Services/Manager.ts+52| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * TerminalManager - Terminal session orchestration service interface. | |
| 3 | * | |
| 4 | * Owns terminal lifecycle operations, output fanout, and session state | |
| 5 | * transitions for thread-scoped terminals. | |
| 6 | * | |
| 7 | * @module TerminalManager | |
| 8 | */ | |
| 1 | 9 | import { |
| 2 | 10 | TerminalClearInput, |
| 3 | 11 | TerminalCloseInput, |
| ... | ... | @@ -40,17 +48,61 @@ export interface TerminalStartInput extends TerminalOpenInput { |
| 40 | 48 | rows: number; |
| 41 | 49 | } |
| 42 | 50 | |
| 51 | /** | |
| 52 | * TerminalManagerShape - Service API for terminal session lifecycle operations. | |
| 53 | */ | |
| 43 | 54 | export interface TerminalManagerShape { |
| 55 | /** | |
| 56 | * Open or attach to a terminal session. | |
| 57 | * | |
| 58 | * Reuses an existing session for the same thread/terminal id and restores | |
| 59 | * persisted history on first open. | |
| 60 | */ | |
| 44 | 61 | readonly open: (input: TerminalOpenInput) => Effect.Effect<TerminalSessionSnapshot, unknown>; |
| 62 | ||
| 63 | /** | |
| 64 | * Write input bytes to a terminal session. | |
| 65 | */ | |
| 45 | 66 | readonly write: (input: TerminalWriteInput) => Effect.Effect<void, unknown>; |
| 67 | ||
| 68 | /** | |
| 69 | * Resize the PTY backing a terminal session. | |
| 70 | */ | |
| 46 | 71 | readonly resize: (input: TerminalResizeInput) => Effect.Effect<void, unknown>; |
| 72 | ||
| 73 | /** | |
| 74 | * Clear terminal output history. | |
| 75 | */ | |
| 47 | 76 | readonly clear: (input: TerminalClearInput) => Effect.Effect<void, unknown>; |
| 77 | ||
| 78 | /** | |
| 79 | * Restart a terminal session in place. | |
| 80 | * | |
| 81 | * Always resets history before spawning the new process. | |
| 82 | */ | |
| 48 | 83 | readonly restart: (input: TerminalOpenInput) => Effect.Effect<TerminalSessionSnapshot, unknown>; |
| 84 | ||
| 85 | /** | |
| 86 | * Close an active terminal session. | |
| 87 | * | |
| 88 | * When `terminalId` is omitted, closes all sessions for the thread. | |
| 89 | */ | |
| 49 | 90 | readonly close: (input: TerminalCloseInput) => Effect.Effect<void, unknown>; |
| 91 | ||
| 92 | /** | |
| 93 | * Subscribe to terminal runtime events. | |
| 94 | */ | |
| 50 | 95 | readonly subscribe: (listener: (event: TerminalEvent) => void) => Effect.Effect<() => void>; |
| 96 | ||
| 97 | /** | |
| 98 | * Dispose all managed terminal resources. | |
| 99 | */ | |
| 51 | 100 | readonly dispose: () => Effect.Effect<void>; |
| 52 | 101 | } |
| 53 | 102 | |
| 103 | /** | |
| 104 | * TerminalManager - Service tag for terminal session orchestration. | |
| 105 | */ | |
| 54 | 106 | export class TerminalManager extends ServiceMap.Service<TerminalManager, TerminalManagerShape>()( |
| 55 | 107 | "terminal/TerminalManager", |
| 56 | 108 | ) {} |
apps/server/src/terminal/Services/PTY.ts+17| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * PtyAdapter - Terminal PTY adapter service contract. | |
| 3 | * | |
| 4 | * Defines the process primitives required by terminal session management | |
| 5 | * without binding to a specific PTY implementation. | |
| 6 | * | |
| 7 | * @module PtyAdapter | |
| 8 | */ | |
| 1 | 9 | import { ServiceMap } from "effect"; |
| 2 | 10 | |
| 3 | 11 | export interface PtyExitEvent { |
| ... | ... | @@ -23,10 +31,19 @@ export interface PtySpawnInput { |
| 23 | 31 | env: NodeJS.ProcessEnv; |
| 24 | 32 | } |
| 25 | 33 | |
| 34 | /** | |
| 35 | * PtyAdapterShape - Service API for spawning and controlling PTY processes. | |
| 36 | */ | |
| 26 | 37 | export interface PtyAdapterShape { |
| 38 | /** | |
| 39 | * Spawn a PTY process for a terminal session. | |
| 40 | */ | |
| 27 | 41 | spawn(input: PtySpawnInput): PtyProcess; |
| 28 | 42 | } |
| 29 | 43 | |
| 44 | /** | |
| 45 | * PtyAdapter - Service tag for PTY process integration. | |
| 46 | */ | |
| 30 | 47 | export class PtyAdapter extends ServiceMap.Service<PtyAdapter, PtyAdapterShape>()( |
| 31 | 48 | "terminal/PtyAdapter", |
| 32 | 49 | ) {} |
apps/server/src/wsServer.ts+21| ... | ... | @@ -1,3 +1,11 @@ |
| 1 | /** | |
| 2 | * Server - HTTP/WebSocket server service interface. | |
| 3 | * | |
| 4 | * Owns startup and shutdown lifecycle of the HTTP server, static asset serving, | |
| 5 | * and WebSocket request routing. | |
| 6 | * | |
| 7 | * @module Server | |
| 8 | */ | |
| 1 | 9 | import fs from "node:fs"; |
| 2 | 10 | import http from "node:http"; |
| 3 | 11 | import path from "node:path"; |
| ... | ... | @@ -49,15 +57,28 @@ const MIME_TYPES: Record<string, string> = { |
| 49 | 57 | ".map": "application/json", |
| 50 | 58 | }; |
| 51 | 59 | |
| 60 | /** | |
| 61 | * ServerShape - Service API for server lifecycle control. | |
| 62 | */ | |
| 52 | 63 | export interface ServerShape { |
| 64 | /** | |
| 65 | * Start HTTP and WebSocket listeners. | |
| 66 | */ | |
| 53 | 67 | readonly start: Effect.Effect< |
| 54 | 68 | http.Server, |
| 55 | 69 | unknown, |
| 56 | 70 | Scope.Scope | ServerRuntimeServices | ServerConfig |
| 57 | 71 | >; |
| 72 | ||
| 73 | /** | |
| 74 | * Wait for process shutdown signals. | |
| 75 | */ | |
| 58 | 76 | readonly stopSignal: Effect.Effect<unknown, never>; |
| 59 | 77 | } |
| 60 | 78 | |
| 79 | /** | |
| 80 | * Server - Service tag for HTTP/WebSocket lifecycle management. | |
| 81 | */ | |
| 61 | 82 | export class Server extends ServiceMap.Service<Server, ServerShape>()("server/Server") {} |
| 62 | 83 | |
| 63 | 84 | const isServerNotRunningError = (error: unknown): boolean => { |