| 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,8 +210,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6` |
| 210 | - Threads: PRRT_kwDORLtfbc5wiLhQ | 210 | - Threads: PRRT_kwDORLtfbc5wiLhQ |
| 211 | - Audit note: Snapshot sequence can under-report due to extra projectors, but replay impact is lower now. | 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 | 213 | - [x] `C033` Three error classes defined but never instantiated anywhere |
| 214 | - Status: `TODO` | 214 | - Status: `DONE` |
| 215 | - Verdict: `partially-valid` | 215 | - Verdict: `partially-valid` |
| 216 | - Severity: `Low` | 216 | - Severity: `Low` |
| 217 | - Area: `Checkpointing correctness` | 217 | - Area: `Checkpointing correctness` |
| ... | @@ -219,8 +219,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6` | ... | @@ -219,8 +219,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6` |
| 219 | - Threads: PRRT_kwDORLtfbc5wlYgo | 219 | - Threads: PRRT_kwDORLtfbc5wlYgo |
| 220 | - Audit note: Original claim overstated; some errors used, others appear unused. | 220 | - Audit note: Original claim overstated; some errors used, others appear unused. |
| 221 | 221 | ||
| 222 | - [ ] `C034` Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type | 222 | - [x] `C034` Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type |
| 223 | - Status: `TODO` | 223 | - Status: `DONE` |
| 224 | - Verdict: `valid` | 224 | - Verdict: `valid` |
| 225 | - Severity: `Low` | 225 | - Severity: `Low` |
| 226 | - Area: `Checkpointing correctness` | 226 | - Area: `Checkpointing correctness` |
| ... | @@ -228,8 +228,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6` | ... | @@ -228,8 +228,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6` |
| 228 | - Threads: PRRT_kwDORLtfbc5wj5fn | 228 | - Threads: PRRT_kwDORLtfbc5wj5fn |
| 229 | - Audit note: CheckpointInvariantError remains redundantly included in service union. | 229 | - Audit note: CheckpointInvariantError remains redundantly included in service union. |
| 230 | 230 | ||
| 231 | - [ ] `C035` Redundant error type in CheckpointServiceError union definition | 231 | - [x] `C035` Redundant error type in CheckpointServiceError union definition |
| 232 | - Status: `TODO` | 232 | - Status: `DONE` |
| 233 | - Verdict: `valid` | 233 | - Verdict: `valid` |
| 234 | - Severity: `Low` | 234 | - Severity: `Low` |
| 235 | - Area: `Checkpointing correctness` | 235 | - Area: `Checkpointing correctness` |
apps/server/src/checkpointing/Errors.ts+2-38| ... | @@ -19,37 +19,6 @@ export class CheckpointUnavailableError extends Schema.TaggedErrorClass<Checkpoi | ... | @@ -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 | * CheckpointInvariantError - Inconsistent provider/filesystem/catalog state. | 23 | * CheckpointInvariantError - Inconsistent provider/filesystem/catalog state. |
| 55 | */ | 24 | */ |
| ... | @@ -69,11 +38,6 @@ export class CheckpointInvariantError extends Schema.TaggedErrorClass<Checkpoint | ... | @@ -69,11 +38,6 @@ export class CheckpointInvariantError extends Schema.TaggedErrorClass<Checkpoint |
| 69 | export type CheckpointStoreError = | 38 | export type CheckpointStoreError = |
| 70 | | GitCommandError | 39 | | GitCommandError |
| 71 | | CheckpointInvariantError | 40 | | CheckpointInvariantError |
| 72 | | CheckpointUnavailableError | 41 | | CheckpointUnavailableError; |
| 73 | | CheckpointRepositoryError; | ||
| 74 | 42 | ||
| 75 | export type CheckpointServiceError = | 43 | export type CheckpointServiceError = CheckpointStoreError | ProjectionRepositoryError; |
| 76 | | CheckpointStoreError | ||
| 77 | | ProjectionRepositoryError | ||
| 78 | | CheckpointSessionNotFoundError | ||
| 79 | | CheckpointInvariantError; |
apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts-1| ... | @@ -1,6 +1,5 @@ | ... | @@ -1,6 +1,5 @@ |
| 1 | import { | 1 | import { |
| 2 | OrchestrationGetTurnDiffResult, | 2 | OrchestrationGetTurnDiffResult, |
| 3 | type ProjectId, | ||
| 4 | type OrchestrationGetFullThreadDiffInput, | 3 | type OrchestrationGetFullThreadDiffInput, |
| 5 | type OrchestrationGetFullThreadDiffResult, | 4 | type OrchestrationGetFullThreadDiffResult, |
| 6 | type OrchestrationGetTurnDiffResult as OrchestrationGetTurnDiffResultType, | 5 | type OrchestrationGetTurnDiffResult as OrchestrationGetTurnDiffResultType, |
apps/server/src/checkpointing/Services/CheckpointDiffQuery.ts+25| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import type { | 9 | import type { |
| 2 | OrchestrationGetFullThreadDiffInput, | 10 | OrchestrationGetFullThreadDiffInput, |
| 3 | OrchestrationGetFullThreadDiffResult, | 11 | OrchestrationGetFullThreadDiffResult, |
| ... | @@ -9,15 +17,32 @@ import type { Effect } from "effect"; | ... | @@ -9,15 +17,32 @@ import type { Effect } from "effect"; |
| 9 | 17 | ||
| 10 | import type { CheckpointServiceError } from "../Errors.ts"; | 18 | import type { CheckpointServiceError } from "../Errors.ts"; |
| 11 | 19 | ||
| 20 | /** | ||
| 21 | * CheckpointDiffQueryShape - Service API for checkpoint diff queries. | ||
| 22 | */ | ||
| 12 | export interface CheckpointDiffQueryShape { | 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 | readonly getTurnDiff: ( | 29 | readonly getTurnDiff: ( |
| 14 | input: OrchestrationGetTurnDiffInput, | 30 | input: OrchestrationGetTurnDiffInput, |
| 15 | ) => Effect.Effect<OrchestrationGetTurnDiffResult, CheckpointServiceError>; | 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 | readonly getFullThreadDiff: ( | 38 | readonly getFullThreadDiff: ( |
| 17 | input: OrchestrationGetFullThreadDiffInput, | 39 | input: OrchestrationGetFullThreadDiffInput, |
| 18 | ) => Effect.Effect<OrchestrationGetFullThreadDiffResult, CheckpointServiceError>; | 40 | ) => Effect.Effect<OrchestrationGetFullThreadDiffResult, CheckpointServiceError>; |
| 19 | } | 41 | } |
| 20 | 42 | ||
| 43 | /** | ||
| 44 | * CheckpointDiffQuery - Service tag for checkpoint diff queries. | ||
| 45 | */ | ||
| 21 | export class CheckpointDiffQuery extends ServiceMap.Service< | 46 | export class CheckpointDiffQuery extends ServiceMap.Service< |
| 22 | CheckpointDiffQuery, | 47 | CheckpointDiffQuery, |
| 23 | CheckpointDiffQueryShape | 48 | CheckpointDiffQueryShape |
apps/server/src/checkpointing/Services/CheckpointStore.ts+11| ... | @@ -39,6 +39,9 @@ export interface DeleteCheckpointRefsInput { | ... | @@ -39,6 +39,9 @@ export interface DeleteCheckpointRefsInput { |
| 39 | readonly checkpointRefs: ReadonlyArray<CheckpointRef>; | 39 | readonly checkpointRefs: ReadonlyArray<CheckpointRef>; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | /** | ||
| 43 | * CheckpointStoreShape - Service API for checkpoint capture/restore and diff access. | ||
| 44 | */ | ||
| 42 | export interface CheckpointStoreShape { | 45 | export interface CheckpointStoreShape { |
| 43 | /** | 46 | /** |
| 44 | * Check whether cwd is inside a Git worktree. | 47 | * Check whether cwd is inside a Git worktree. |
| ... | @@ -47,6 +50,8 @@ export interface CheckpointStoreShape { | ... | @@ -47,6 +50,8 @@ export interface CheckpointStoreShape { |
| 47 | 50 | ||
| 48 | /** | 51 | /** |
| 49 | * Capture a checkpoint commit and store it at the provided checkpoint ref. | 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 | readonly captureCheckpoint: ( | 56 | readonly captureCheckpoint: ( |
| 52 | input: CaptureCheckpointInput, | 57 | input: CaptureCheckpointInput, |
| ... | @@ -61,6 +66,8 @@ export interface CheckpointStoreShape { | ... | @@ -61,6 +66,8 @@ export interface CheckpointStoreShape { |
| 61 | 66 | ||
| 62 | /** | 67 | /** |
| 63 | * Restore workspace/staging state to a checkpoint. | 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 | readonly restoreCheckpoint: ( | 72 | readonly restoreCheckpoint: ( |
| 66 | input: RestoreCheckpointInput, | 73 | input: RestoreCheckpointInput, |
| ... | @@ -68,6 +75,8 @@ export interface CheckpointStoreShape { | ... | @@ -68,6 +75,8 @@ export interface CheckpointStoreShape { |
| 68 | 75 | ||
| 69 | /** | 76 | /** |
| 70 | * Compute patch diff between two checkpoint refs. | 77 | * Compute patch diff between two checkpoint refs. |
| 78 | * | ||
| 79 | * Can optionally treat missing "from" ref as `HEAD`. | ||
| 71 | */ | 80 | */ |
| 72 | readonly diffCheckpoints: ( | 81 | readonly diffCheckpoints: ( |
| 73 | input: DiffCheckpointsInput, | 82 | input: DiffCheckpointsInput, |
| ... | @@ -75,6 +84,8 @@ export interface CheckpointStoreShape { | ... | @@ -75,6 +84,8 @@ export interface CheckpointStoreShape { |
| 75 | 84 | ||
| 76 | /** | 85 | /** |
| 77 | * Delete the provided checkpoint refs. | 86 | * Delete the provided checkpoint refs. |
| 87 | * | ||
| 88 | * Best-effort delete: missing refs are tolerated. | ||
| 78 | */ | 89 | */ |
| 79 | readonly deleteCheckpointRefs: ( | 90 | readonly deleteCheckpointRefs: ( |
| 80 | input: DeleteCheckpointRefsInput, | 91 | input: DeleteCheckpointRefsInput, |
apps/server/src/config.ts+25| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import * as Net from "node:net"; | 9 | import * as Net from "node:net"; |
| 2 | import { Data, Effect, FileSystem, Layer, Path, ServiceMap } from "effect"; | 10 | import { Data, Effect, FileSystem, Layer, Path, ServiceMap } from "effect"; |
| 3 | 11 | ||
| ... | @@ -5,6 +13,9 @@ export const DEFAULT_PORT = 3773; | ... | @@ -5,6 +13,9 @@ export const DEFAULT_PORT = 3773; |
| 5 | 13 | ||
| 6 | export type RuntimeMode = "web" | "desktop"; | 14 | export type RuntimeMode = "web" | "desktop"; |
| 7 | 15 | ||
| 16 | /** | ||
| 17 | * ServerConfigShape - Process/runtime configuration required by the server. | ||
| 18 | */ | ||
| 8 | export interface ServerConfigShape { | 19 | export interface ServerConfigShape { |
| 9 | readonly mode: RuntimeMode; | 20 | readonly mode: RuntimeMode; |
| 10 | readonly port: number; | 21 | readonly port: number; |
| ... | @@ -20,6 +31,9 @@ export interface ServerConfigShape { | ... | @@ -20,6 +31,9 @@ export interface ServerConfigShape { |
| 20 | readonly logWebSocketEvents: boolean; | 31 | readonly logWebSocketEvents: boolean; |
| 21 | } | 32 | } |
| 22 | 33 | ||
| 34 | /** | ||
| 35 | * ServerConfig - Service tag for server runtime configuration. | ||
| 36 | */ | ||
| 23 | export class ServerConfig extends ServiceMap.Service<ServerConfig, ServerConfigShape>()( | 37 | export class ServerConfig extends ServiceMap.Service<ServerConfig, ServerConfigShape>()( |
| 24 | "server/ServerConfig", | 38 | "server/ServerConfig", |
| 25 | ) {} | 39 | ) {} |
| ... | @@ -31,10 +45,21 @@ export class NetError extends Data.TaggedError("NetError")<{ | ... | @@ -31,10 +45,21 @@ export class NetError extends Data.TaggedError("NetError")<{ |
| 31 | readonly cause?: unknown; | 45 | readonly cause?: unknown; |
| 32 | }> {} | 46 | }> {} |
| 33 | 47 | ||
| 48 | /** | ||
| 49 | * NetServiceShape - Networking helper operations used during startup. | ||
| 50 | */ | ||
| 34 | export interface NetServiceShape { | 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 | readonly findAvailablePort: (preferred: number) => Effect.Effect<number, NetError>; | 57 | readonly findAvailablePort: (preferred: number) => Effect.Effect<number, NetError>; |
| 36 | } | 58 | } |
| 37 | 59 | ||
| 60 | /** | ||
| 61 | * NetService - Service tag for startup networking helpers. | ||
| 62 | */ | ||
| 38 | export class NetService extends ServiceMap.Service<NetService, NetServiceShape>()( | 63 | export class NetService extends ServiceMap.Service<NetService, NetServiceShape>()( |
| 39 | "server/NetService", | 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,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 | * GitManagerServiceError - Errors emitted by stacked Git workflow orchestration. | 61 | * GitManagerServiceError - Errors emitted by stacked Git workflow orchestration. |
| 77 | */ | 62 | */ |
| 78 | export type GitManagerServiceError = | 63 | export type GitManagerServiceError = |
| 79 | | GitManagerError | 64 | | GitManagerError |
| 80 | | GitCoreError | 65 | | GitCommandError |
| 81 | | GitHubCliServiceError | 66 | | GitHubCliError |
| 82 | | TextGenerationServiceError; | 67 | | TextGenerationError; |
apps/server/src/git/Services/GitService.ts+3| ... | @@ -27,6 +27,9 @@ export interface ExecuteGitResult { | ... | @@ -27,6 +27,9 @@ export interface ExecuteGitResult { |
| 27 | readonly stderr: string; | 27 | readonly stderr: string; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | /** | ||
| 31 | * GitServiceShape - Service API for Git command execution. | ||
| 32 | */ | ||
| 30 | export interface GitServiceShape { | 33 | export interface GitServiceShape { |
| 31 | /** | 34 | /** |
| 32 | * Execute a Git command. | 35 | * Execute a Git command. |
apps/server/src/keybindings.ts+26| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { | 9 | import { |
| 2 | KeybindingRule, | 10 | KeybindingRule, |
| 3 | KeybindingsConfig, | 11 | KeybindingsConfig, |
| ... | @@ -388,16 +396,34 @@ function mergeWithDefaultKeybindings(custom: ResolvedKeybindingsConfig): Resolve | ... | @@ -388,16 +396,34 @@ function mergeWithDefaultKeybindings(custom: ResolvedKeybindingsConfig): Resolve |
| 388 | return merged.slice(-MAX_KEYBINDINGS_COUNT); | 396 | return merged.slice(-MAX_KEYBINDINGS_COUNT); |
| 389 | } | 397 | } |
| 390 | 398 | ||
| 399 | /** | ||
| 400 | * KeybindingsShape - Service API for keybinding configuration operations. | ||
| 401 | */ | ||
| 391 | export interface KeybindingsShape { | 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 | readonly loadResolvedKeybindingsConfig: Effect.Effect< | 408 | readonly loadResolvedKeybindingsConfig: Effect.Effect< |
| 393 | readonly ResolvedKeybindingRule[], | 409 | readonly ResolvedKeybindingRule[], |
| 394 | KeybindingsConfigError | 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 | readonly upsertKeybindingRule: ( | 419 | readonly upsertKeybindingRule: ( |
| 397 | rule: KeybindingRule, | 420 | rule: KeybindingRule, |
| 398 | ) => Effect.Effect<ResolvedKeybindingsConfig, KeybindingsConfigError>; | 421 | ) => Effect.Effect<ResolvedKeybindingsConfig, KeybindingsConfigError>; |
| 399 | } | 422 | } |
| 400 | 423 | ||
| 424 | /** | ||
| 425 | * Keybindings - Service tag for keybinding configuration operations. | ||
| 426 | */ | ||
| 401 | export class Keybindings extends ServiceMap.Service<Keybindings, KeybindingsShape>()( | 427 | export class Keybindings extends ServiceMap.Service<Keybindings, KeybindingsShape>()( |
| 402 | "server/Keybindings", | 428 | "server/Keybindings", |
| 403 | ) {} | 429 | ) {} |
apps/server/src/main.ts+25| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { Config, Data, Effect, FileSystem, Layer, Option, Path, Schema, ServiceMap } from "effect"; | 9 | import { Config, Data, Effect, FileSystem, Layer, Option, Path, Schema, ServiceMap } from "effect"; |
| 2 | import { Command, Flag } from "effect/unstable/cli"; | 10 | import { Command, Flag } from "effect/unstable/cli"; |
| 3 | 11 | ||
| ... | @@ -32,12 +40,29 @@ interface CliInput { | ... | @@ -32,12 +40,29 @@ interface CliInput { |
| 32 | readonly logWebSocketEvents: Option.Option<boolean>; | 40 | readonly logWebSocketEvents: Option.Option<boolean>; |
| 33 | } | 41 | } |
| 34 | 42 | ||
| 43 | /** | ||
| 44 | * CliConfigShape - Startup helpers required while building server layers. | ||
| 45 | */ | ||
| 35 | export interface CliConfigShape { | 46 | export interface CliConfigShape { |
| 47 | /** | ||
| 48 | * Current process working directory. | ||
| 49 | */ | ||
| 36 | readonly cwd: string; | 50 | readonly cwd: string; |
| 51 | |||
| 52 | /** | ||
| 53 | * Apply OS-specific PATH normalization. | ||
| 54 | */ | ||
| 37 | readonly fixPath: Effect.Effect<void>; | 55 | readonly fixPath: Effect.Effect<void>; |
| 56 | |||
| 57 | /** | ||
| 58 | * Resolve static web asset directory for server mode. | ||
| 59 | */ | ||
| 38 | readonly resolveStaticDir: Effect.Effect<string | undefined>; | 60 | readonly resolveStaticDir: Effect.Effect<string | undefined>; |
| 39 | } | 61 | } |
| 40 | 62 | ||
| 63 | /** | ||
| 64 | * CliConfig - Service tag for startup CLI/runtime helpers. | ||
| 65 | */ | ||
| 41 | export class CliConfig extends ServiceMap.Service<CliConfig, CliConfigShape>()("server/CliConfig") { | 66 | export class CliConfig extends ServiceMap.Service<CliConfig, CliConfigShape>()("server/CliConfig") { |
| 42 | static readonly layer = Layer.effect( | 67 | static readonly layer = Layer.effect( |
| 43 | CliConfig, | 68 | CliConfig, |
apps/server/src/open.ts+23| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { spawn } from "node:child_process"; | 9 | import { spawn } from "node:child_process"; |
| 2 | 10 | ||
| 3 | import { EDITORS, type EditorId } from "@t3tools/contracts"; | 11 | import { EDITORS, type EditorId } from "@t3tools/contracts"; |
| ... | @@ -22,11 +30,26 @@ interface EditorLaunch { | ... | @@ -22,11 +30,26 @@ interface EditorLaunch { |
| 22 | readonly args: ReadonlyArray<string>; | 30 | readonly args: ReadonlyArray<string>; |
| 23 | } | 31 | } |
| 24 | 32 | ||
| 33 | /** | ||
| 34 | * OpenShape - Service API for browser and editor launch actions. | ||
| 35 | */ | ||
| 25 | export interface OpenShape { | 36 | export interface OpenShape { |
| 37 | /** | ||
| 38 | * Open a URL target in the default browser. | ||
| 39 | */ | ||
| 26 | readonly openBrowser: (target: string) => Effect.Effect<void, OpenError>; | 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 | readonly openInEditor: (input: OpenInEditorInput) => Effect.Effect<void, OpenError>; | 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 | export class Open extends ServiceMap.Service<Open, OpenShape>()("server/Open") {} | 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,8 +1,6 @@ |
| 1 | import { SchemaIssue, Schema } from "effect"; | 1 | import { SchemaIssue, Schema } from "effect"; |
| 2 | 2 | ||
| 3 | import type { | 3 | import type { |
| 4 | OrchestrationCommandReceiptRepositoryError, | ||
| 5 | OrchestrationEventStoreError, | ||
| 6 | ProjectionRepositoryError, | 4 | ProjectionRepositoryError, |
| 7 | } from "../persistence/Errors.ts"; | 5 | } from "../persistence/Errors.ts"; |
| 8 | 6 | ||
| ... | @@ -83,8 +81,6 @@ export class OrchestrationListenerCallbackError extends Schema.TaggedErrorClass< | ... | @@ -83,8 +81,6 @@ export class OrchestrationListenerCallbackError extends Schema.TaggedErrorClass< |
| 83 | } | 81 | } |
| 84 | 82 | ||
| 85 | export type OrchestrationDispatchError = | 83 | export type OrchestrationDispatchError = |
| 86 | | OrchestrationEventStoreError | ||
| 87 | | OrchestrationCommandReceiptRepositoryError | ||
| 88 | | ProjectionRepositoryError | 84 | | ProjectionRepositoryError |
| 89 | | OrchestrationCommandInvariantError | 85 | | OrchestrationCommandInvariantError |
| 90 | | OrchestrationCommandPreviouslyRejectedError | 86 | | OrchestrationCommandPreviouslyRejectedError |
apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts+7-17| ... | @@ -14,9 +14,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; | ... | @@ -14,9 +14,7 @@ import * as SqlClient from "effect/unstable/sql/SqlClient"; |
| 14 | import * as SqlSchema from "effect/unstable/sql/SqlSchema"; | 14 | import * as SqlSchema from "effect/unstable/sql/SqlSchema"; |
| 15 | 15 | ||
| 16 | import { | 16 | import { |
| 17 | PersistenceDecodeError, | 17 | isPersistenceError, |
| 18 | PersistenceSerializationError, | ||
| 19 | PersistenceSqlError, | ||
| 20 | toPersistenceDecodeError, | 18 | toPersistenceDecodeError, |
| 21 | toPersistenceSqlError, | 19 | toPersistenceSqlError, |
| 22 | type ProjectionRepositoryError, | 20 | type ProjectionRepositoryError, |
| ... | @@ -106,19 +104,6 @@ function toPersistenceSqlOrDecodeError(sqlOperation: string, decodeOperation: st | ... | @@ -106,19 +104,6 @@ function toPersistenceSqlOrDecodeError(sqlOperation: string, decodeOperation: st |
| 106 | : toPersistenceSqlError(sqlOperation)(cause); | 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 | const makeProjectionSnapshotQuery = Effect.gen(function* () { | 107 | const makeProjectionSnapshotQuery = Effect.gen(function* () { |
| 123 | const sql = yield* SqlClient.SqlClient; | 108 | const sql = yield* SqlClient.SqlClient; |
| 124 | 109 | ||
| ... | @@ -441,7 +426,12 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { | ... | @@ -441,7 +426,12 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { |
| 441 | }), | 426 | }), |
| 442 | ) | 427 | ) |
| 443 | .pipe( | 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 | return { | 437 | return { |
apps/server/src/orchestration/Services/CheckpointReactor.ts+17| ... | @@ -1,16 +1,33 @@ | ... | @@ -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 | import { ServiceMap } from "effect"; | 9 | import { ServiceMap } from "effect"; |
| 2 | import type { Effect, Scope } from "effect"; | 10 | import type { Effect, Scope } from "effect"; |
| 3 | 11 | ||
| 12 | /** | ||
| 13 | * CheckpointReactorShape - Service API for checkpoint reactor lifecycle. | ||
| 14 | */ | ||
| 4 | export interface CheckpointReactorShape { | 15 | export interface CheckpointReactorShape { |
| 5 | /** | 16 | /** |
| 6 | * Start the checkpoint reactor. | 17 | * Start the checkpoint reactor. |
| 7 | * | 18 | * |
| 8 | * The returned effect must be run in a scope so all worker fibers can be | 19 | * The returned effect must be run in a scope so all worker fibers can be |
| 9 | * finalized on shutdown. | 20 | * finalized on shutdown. |
| 21 | * | ||
| 22 | * Consumes both orchestration-domain and provider-runtime events via an | ||
| 23 | * internal queue. | ||
| 10 | */ | 24 | */ |
| 11 | readonly start: Effect.Effect<void, never, Scope.Scope>; | 25 | readonly start: Effect.Effect<void, never, Scope.Scope>; |
| 12 | } | 26 | } |
| 13 | 27 | ||
| 28 | /** | ||
| 29 | * CheckpointReactor - Service tag for checkpoint reactor workers. | ||
| 30 | */ | ||
| 14 | export class CheckpointReactor extends ServiceMap.Service< | 31 | export class CheckpointReactor extends ServiceMap.Service< |
| 15 | CheckpointReactor, | 32 | CheckpointReactor, |
| 16 | CheckpointReactorShape | 33 | CheckpointReactorShape |
apps/server/src/orchestration/Services/OrchestrationEngine.ts+8| ... | @@ -21,6 +21,9 @@ import type { Effect, Stream } from "effect"; | ... | @@ -21,6 +21,9 @@ import type { Effect, Stream } from "effect"; |
| 21 | import type { OrchestrationDispatchError } from "../Errors.ts"; | 21 | import type { OrchestrationDispatchError } from "../Errors.ts"; |
| 22 | import type { OrchestrationEventStoreError } from "../../persistence/Errors.ts"; | 22 | import type { OrchestrationEventStoreError } from "../../persistence/Errors.ts"; |
| 23 | 23 | ||
| 24 | /** | ||
| 25 | * OrchestrationEngineShape - Service API for orchestration command and event flow. | ||
| 26 | */ | ||
| 24 | export interface OrchestrationEngineShape { | 27 | export interface OrchestrationEngineShape { |
| 25 | /** | 28 | /** |
| 26 | * Read the current in-memory orchestration read model. | 29 | * Read the current in-memory orchestration read model. |
| ... | @@ -44,6 +47,9 @@ export interface OrchestrationEngineShape { | ... | @@ -44,6 +47,9 @@ export interface OrchestrationEngineShape { |
| 44 | * | 47 | * |
| 45 | * @param command - Valid orchestration command. | 48 | * @param command - Valid orchestration command. |
| 46 | * @returns Effect containing the sequence of the persisted event. | 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 | readonly dispatch: ( | 54 | readonly dispatch: ( |
| 49 | command: OrchestrationCommand, | 55 | command: OrchestrationCommand, |
| ... | @@ -51,6 +57,8 @@ export interface OrchestrationEngineShape { | ... | @@ -51,6 +57,8 @@ export interface OrchestrationEngineShape { |
| 51 | 57 | ||
| 52 | /** | 58 | /** |
| 53 | * Stream persisted domain events in dispatch order. | 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 | readonly streamDomainEvents: Stream.Stream<OrchestrationEvent>; | 63 | readonly streamDomainEvents: Stream.Stream<OrchestrationEvent>; |
| 56 | } | 64 | } |
apps/server/src/orchestration/Services/OrchestrationReactor.ts+14| ... | @@ -1,6 +1,17 @@ | ... | @@ -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 | import { ServiceMap } from "effect"; | 9 | import { ServiceMap } from "effect"; |
| 2 | import type { Effect, Scope } from "effect"; | 10 | import type { Effect, Scope } from "effect"; |
| 3 | 11 | ||
| 12 | /** | ||
| 13 | * OrchestrationReactorShape - Service API for orchestration reactor lifecycle. | ||
| 14 | */ | ||
| 4 | export interface OrchestrationReactorShape { | 15 | export interface OrchestrationReactorShape { |
| 5 | /** | 16 | /** |
| 6 | * Start orchestration-side reactors for provider/runtime/checkpoint flows. | 17 | * Start orchestration-side reactors for provider/runtime/checkpoint flows. |
| ... | @@ -11,6 +22,9 @@ export interface OrchestrationReactorShape { | ... | @@ -11,6 +22,9 @@ export interface OrchestrationReactorShape { |
| 11 | readonly start: Effect.Effect<void, never, Scope.Scope>; | 22 | readonly start: Effect.Effect<void, never, Scope.Scope>; |
| 12 | } | 23 | } |
| 13 | 24 | ||
| 25 | /** | ||
| 26 | * OrchestrationReactor - Service tag for orchestration reactor coordination. | ||
| 27 | */ | ||
| 14 | export class OrchestrationReactor extends ServiceMap.Service< | 28 | export class OrchestrationReactor extends ServiceMap.Service< |
| 15 | OrchestrationReactor, | 29 | OrchestrationReactor, |
| 16 | OrchestrationReactorShape | 30 | OrchestrationReactorShape |
apps/server/src/orchestration/Services/ProjectionPipeline.ts+26-9| ... | @@ -1,24 +1,41 @@ | ... | @@ -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 | import type { OrchestrationEvent } from "@t3tools/contracts"; | 9 | import type { OrchestrationEvent } from "@t3tools/contracts"; |
| 2 | import { ServiceMap } from "effect"; | 10 | import { ServiceMap } from "effect"; |
| 3 | import type { Effect } from "effect"; | 11 | import type { Effect } from "effect"; |
| 4 | 12 | ||
| 5 | import type { | 13 | import type { ProjectionRepositoryError } from "../../persistence/Errors.ts"; |
| 6 | OrchestrationEventStoreError, | ||
| 7 | ProjectionRepositoryError, | ||
| 8 | } from "../../persistence/Errors.ts"; | ||
| 9 | |||
| 10 | export type OrchestrationProjectionPipelineError = | ||
| 11 | | OrchestrationEventStoreError | ||
| 12 | | ProjectionRepositoryError; | ||
| 13 | 14 | ||
| 15 | /** | ||
| 16 | * OrchestrationProjectionPipelineShape - Service API for projection execution. | ||
| 17 | */ | ||
| 14 | export interface OrchestrationProjectionPipelineShape { | 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 | readonly projectEvent: ( | 31 | readonly projectEvent: ( |
| 18 | event: OrchestrationEvent, | 32 | event: OrchestrationEvent, |
| 19 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 33 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 20 | } | 34 | } |
| 21 | 35 | ||
| 36 | /** | ||
| 37 | * OrchestrationProjectionPipeline - Service tag for orchestration projections. | ||
| 38 | */ | ||
| 22 | export class OrchestrationProjectionPipeline extends ServiceMap.Service< | 39 | export class OrchestrationProjectionPipeline extends ServiceMap.Service< |
| 23 | OrchestrationProjectionPipeline, | 40 | OrchestrationProjectionPipeline, |
| 24 | OrchestrationProjectionPipelineShape | 41 | OrchestrationProjectionPipelineShape |
apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts+20| ... | @@ -1,13 +1,33 @@ | ... | @@ -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 | import type { OrchestrationReadModel } from "@t3tools/contracts"; | 9 | import type { OrchestrationReadModel } from "@t3tools/contracts"; |
| 2 | import { ServiceMap } from "effect"; | 10 | import { ServiceMap } from "effect"; |
| 3 | import type { Effect } from "effect"; | 11 | import type { Effect } from "effect"; |
| 4 | 12 | ||
| 5 | import type { ProjectionRepositoryError } from "../../persistence/Errors.ts"; | 13 | import type { ProjectionRepositoryError } from "../../persistence/Errors.ts"; |
| 6 | 14 | ||
| 15 | /** | ||
| 16 | * ProjectionSnapshotQueryShape - Service API for read-model snapshots. | ||
| 17 | */ | ||
| 7 | export interface ProjectionSnapshotQueryShape { | 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 | readonly getSnapshot: () => Effect.Effect<OrchestrationReadModel, ProjectionRepositoryError>; | 25 | readonly getSnapshot: () => Effect.Effect<OrchestrationReadModel, ProjectionRepositoryError>; |
| 9 | } | 26 | } |
| 10 | 27 | ||
| 28 | /** | ||
| 29 | * ProjectionSnapshotQuery - Service tag for projection snapshot queries. | ||
| 30 | */ | ||
| 11 | export class ProjectionSnapshotQuery extends ServiceMap.Service< | 31 | export class ProjectionSnapshotQuery extends ServiceMap.Service< |
| 12 | ProjectionSnapshotQuery, | 32 | ProjectionSnapshotQuery, |
| 13 | ProjectionSnapshotQueryShape | 33 | ProjectionSnapshotQueryShape |
apps/server/src/orchestration/Services/ProviderCommandReactor.ts+17| ... | @@ -1,16 +1,33 @@ | ... | @@ -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 | import { ServiceMap } from "effect"; | 9 | import { ServiceMap } from "effect"; |
| 2 | import type { Effect, Scope } from "effect"; | 10 | import type { Effect, Scope } from "effect"; |
| 3 | 11 | ||
| 12 | /** | ||
| 13 | * ProviderCommandReactorShape - Service API for provider command reactors. | ||
| 14 | */ | ||
| 4 | export interface ProviderCommandReactorShape { | 15 | export interface ProviderCommandReactorShape { |
| 5 | /** | 16 | /** |
| 6 | * Start reacting to provider-intent orchestration domain events. | 17 | * Start reacting to provider-intent orchestration domain events. |
| 7 | * | 18 | * |
| 8 | * The returned effect must be run in a scope so all worker fibers can be | 19 | * The returned effect must be run in a scope so all worker fibers can be |
| 9 | * finalized on shutdown. | 20 | * finalized on shutdown. |
| 21 | * | ||
| 22 | * Filters orchestration domain events to provider-intent types before | ||
| 23 | * processing. | ||
| 10 | */ | 24 | */ |
| 11 | readonly start: Effect.Effect<void, never, Scope.Scope>; | 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 | export class ProviderCommandReactor extends ServiceMap.Service< | 31 | export class ProviderCommandReactor extends ServiceMap.Service< |
| 15 | ProviderCommandReactor, | 32 | ProviderCommandReactor, |
| 16 | ProviderCommandReactorShape | 33 | ProviderCommandReactorShape |
apps/server/src/orchestration/Services/ProviderRuntimeIngestion.ts+17| ... | @@ -1,16 +1,33 @@ | ... | @@ -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 | import { ServiceMap } from "effect"; | 9 | import { ServiceMap } from "effect"; |
| 2 | import type { Effect, Scope } from "effect"; | 10 | import type { Effect, Scope } from "effect"; |
| 3 | 11 | ||
| 12 | /** | ||
| 13 | * ProviderRuntimeIngestionShape - Service API for runtime ingestion lifecycle. | ||
| 14 | */ | ||
| 4 | export interface ProviderRuntimeIngestionShape { | 15 | export interface ProviderRuntimeIngestionShape { |
| 5 | /** | 16 | /** |
| 6 | * Start ingesting provider runtime events into orchestration commands. | 17 | * Start ingesting provider runtime events into orchestration commands. |
| 7 | * | 18 | * |
| 8 | * The returned effect must be run in a scope so all worker fibers can be | 19 | * The returned effect must be run in a scope so all worker fibers can be |
| 9 | * finalized on shutdown. | 20 | * finalized on shutdown. |
| 21 | * | ||
| 22 | * Uses an internal queue and continues after non-interrupt failures by | ||
| 23 | * logging warnings. | ||
| 10 | */ | 24 | */ |
| 11 | readonly start: Effect.Effect<void, never, Scope.Scope>; | 25 | readonly start: Effect.Effect<void, never, Scope.Scope>; |
| 12 | } | 26 | } |
| 13 | 27 | ||
| 28 | /** | ||
| 29 | * ProviderRuntimeIngestionService - Service tag for runtime ingestion workers. | ||
| 30 | */ | ||
| 14 | export class ProviderRuntimeIngestionService extends ServiceMap.Service< | 31 | export class ProviderRuntimeIngestionService extends ServiceMap.Service< |
| 15 | ProviderRuntimeIngestionService, | 32 | ProviderRuntimeIngestionService, |
| 16 | ProviderRuntimeIngestionShape | 33 | ProviderRuntimeIngestionShape |
apps/server/src/persistence/Errors.ts+5-116| ... | @@ -30,19 +30,6 @@ export class PersistenceDecodeError extends Schema.TaggedErrorClass<PersistenceD | ... | @@ -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 | export function toPersistenceSqlError(operation: string) { | 33 | export function toPersistenceSqlError(operation: string) { |
| 47 | return (cause: unknown): PersistenceSqlError => | 34 | return (cause: unknown): PersistenceSqlError => |
| 48 | new PersistenceSqlError({ | 35 | new PersistenceSqlError({ |
| ... | @@ -70,85 +57,8 @@ export function toPersistenceDecodeCauseError(operation: string) { | ... | @@ -70,85 +57,8 @@ export function toPersistenceDecodeCauseError(operation: string) { |
| 70 | }); | 57 | }); |
| 71 | } | 58 | } |
| 72 | 59 | ||
| 73 | export function toPersistenceSerializationError(operation: string) { | 60 | export const isPersistenceError = (u: unknown) => |
| 74 | return (cause: unknown): PersistenceSerializationError => | 61 | Schema.is(PersistenceSqlError)(u) || Schema.is(PersistenceDecodeError)(u); |
| 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 | } | ||
| 152 | 62 | ||
| 153 | // =============================== | 63 | // =============================== |
| 154 | // Provider Session Repository Errors | 64 | // Provider Session Repository Errors |
| ... | @@ -180,22 +90,7 @@ export class ProviderSessionRepositoryPersistenceError extends Schema.TaggedErro | ... | @@ -180,22 +90,7 @@ export class ProviderSessionRepositoryPersistenceError extends Schema.TaggedErro |
| 180 | } | 90 | } |
| 181 | } | 91 | } |
| 182 | 92 | ||
| 183 | export type ProjectRepositoryError = | 93 | export type OrchestrationEventStoreError = PersistenceSqlError | PersistenceDecodeError; |
| 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; | ||
| 199 | 94 | ||
| 200 | export type ProviderSessionRepositoryError = | 95 | export type ProviderSessionRepositoryError = |
| 201 | | ProviderSessionRepositoryValidationError | 96 | | ProviderSessionRepositoryValidationError |
| ... | @@ -205,12 +100,6 @@ export type OrchestrationCommandReceiptRepositoryError = | ... | @@ -205,12 +100,6 @@ export type OrchestrationCommandReceiptRepositoryError = |
| 205 | | PersistenceSqlError | 100 | | PersistenceSqlError |
| 206 | | PersistenceDecodeError; | 101 | | PersistenceDecodeError; |
| 207 | 102 | ||
| 208 | export type ProviderSessionRuntimeRepositoryError = | 103 | export type ProviderSessionRuntimeRepositoryError = PersistenceSqlError | PersistenceDecodeError; |
| 209 | | PersistenceSqlError | ||
| 210 | | PersistenceDecodeError | ||
| 211 | | PersistenceSerializationError; | ||
| 212 | 104 | ||
| 213 | export type ProjectionRepositoryError = | 105 | export type ProjectionRepositoryError = PersistenceSqlError | PersistenceDecodeError; |
| 214 | | PersistenceSqlError | ||
| 215 | | PersistenceDecodeError | ||
| 216 | | PersistenceSerializationError; |
apps/server/src/persistence/NodeSqliteClient.ts+5| ... | @@ -1,6 +1,8 @@ | ... | @@ -1,6 +1,8 @@ |
| 1 | /** | 1 | /** |
| 2 | * Port of `@effect/sql-sqlite-node` that uses the native `node:sqlite` | 2 | * Port of `@effect/sql-sqlite-node` that uses the native `node:sqlite` |
| 3 | * bindings instead of `better-sqlite3`. | 3 | * bindings instead of `better-sqlite3`. |
| 4 | * | ||
| 5 | * @module SqliteClient | ||
| 4 | */ | 6 | */ |
| 5 | import { DatabaseSync, type StatementSync } from "node:sqlite"; | 7 | import { DatabaseSync, type StatementSync } from "node:sqlite"; |
| 6 | 8 | ||
| ... | @@ -27,6 +29,9 @@ export const TypeId: TypeId = "~local/sqlite-node/SqliteClient"; | ... | @@ -27,6 +29,9 @@ export const TypeId: TypeId = "~local/sqlite-node/SqliteClient"; |
| 27 | 29 | ||
| 28 | export type TypeId = "~local/sqlite-node/SqliteClient"; | 30 | export type TypeId = "~local/sqlite-node/SqliteClient"; |
| 29 | 31 | ||
| 32 | /** | ||
| 33 | * SqliteClient - Effect service tag for the sqlite SQL client. | ||
| 34 | */ | ||
| 30 | export const SqliteClient = ServiceMap.Service<Client.SqlClient>("@local/sqlite-node/SqliteClient"); | 35 | export const SqliteClient = ServiceMap.Service<Client.SqlClient>("@local/sqlite-node/SqliteClient"); |
| 31 | 36 | ||
| 32 | export interface SqliteClientConfig { | 37 | export interface SqliteClientConfig { |
apps/server/src/persistence/Services/OrchestrationCommandReceipts.ts+22| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { | 9 | import { |
| 2 | CommandId, | 10 | CommandId, |
| 3 | IsoDateTime, | 11 | IsoDateTime, |
| ... | @@ -28,11 +36,22 @@ export const GetByCommandIdInput = Schema.Struct({ | ... | @@ -28,11 +36,22 @@ export const GetByCommandIdInput = Schema.Struct({ |
| 28 | }); | 36 | }); |
| 29 | export type GetByCommandIdInput = typeof GetByCommandIdInput.Type; | 37 | export type GetByCommandIdInput = typeof GetByCommandIdInput.Type; |
| 30 | 38 | ||
| 39 | /** | ||
| 40 | * OrchestrationCommandReceiptRepositoryShape - Service API for command receipts. | ||
| 41 | */ | ||
| 31 | export interface OrchestrationCommandReceiptRepositoryShape { | 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 | readonly upsert: ( | 48 | readonly upsert: ( |
| 33 | receipt: OrchestrationCommandReceipt, | 49 | receipt: OrchestrationCommandReceipt, |
| 34 | ) => Effect.Effect<void, OrchestrationCommandReceiptRepositoryError>; | 50 | ) => Effect.Effect<void, OrchestrationCommandReceiptRepositoryError>; |
| 35 | 51 | ||
| 52 | /** | ||
| 53 | * Read a command receipt by command id. | ||
| 54 | */ | ||
| 36 | readonly getByCommandId: ( | 55 | readonly getByCommandId: ( |
| 37 | input: GetByCommandIdInput, | 56 | input: GetByCommandIdInput, |
| 38 | ) => Effect.Effect< | 57 | ) => Effect.Effect< |
| ... | @@ -41,6 +60,9 @@ export interface OrchestrationCommandReceiptRepositoryShape { | ... | @@ -41,6 +60,9 @@ export interface OrchestrationCommandReceiptRepositoryShape { |
| 41 | >; | 60 | >; |
| 42 | } | 61 | } |
| 43 | 62 | ||
| 63 | /** | ||
| 64 | * OrchestrationCommandReceiptRepository - Service tag for command receipt persistence. | ||
| 65 | */ | ||
| 44 | export class OrchestrationCommandReceiptRepository extends ServiceMap.Service< | 66 | export class OrchestrationCommandReceiptRepository extends ServiceMap.Service< |
| 45 | OrchestrationCommandReceiptRepository, | 67 | OrchestrationCommandReceiptRepository, |
| 46 | OrchestrationCommandReceiptRepositoryShape | 68 | OrchestrationCommandReceiptRepositoryShape |
apps/server/src/persistence/Services/OrchestrationEventStore.ts+7| ... | @@ -15,12 +15,17 @@ import type { Effect, Stream } from "effect"; | ... | @@ -15,12 +15,17 @@ import type { Effect, Stream } from "effect"; |
| 15 | 15 | ||
| 16 | import type { OrchestrationEventStoreError } from "../Errors.ts"; | 16 | import type { OrchestrationEventStoreError } from "../Errors.ts"; |
| 17 | 17 | ||
| 18 | /** | ||
| 19 | * OrchestrationEventStoreShape - Service API for orchestration event persistence. | ||
| 20 | */ | ||
| 18 | export interface OrchestrationEventStoreShape { | 21 | export interface OrchestrationEventStoreShape { |
| 19 | /** | 22 | /** |
| 20 | * Persist a new orchestration event. | 23 | * Persist a new orchestration event. |
| 21 | * | 24 | * |
| 22 | * @param event - Event payload without sequence (assigned by storage). | 25 | * @param event - Event payload without sequence (assigned by storage). |
| 23 | * @returns Effect containing the stored event with assigned sequence. | 26 | * @returns Effect containing the stored event with assigned sequence. |
| 27 | * | ||
| 28 | * Actor kind is inferred from command/metadata before persistence. | ||
| 24 | */ | 29 | */ |
| 25 | readonly append: ( | 30 | readonly append: ( |
| 26 | event: Omit<OrchestrationEvent, "sequence">, | 31 | event: Omit<OrchestrationEvent, "sequence">, |
| ... | @@ -32,6 +37,8 @@ export interface OrchestrationEventStoreShape { | ... | @@ -32,6 +37,8 @@ export interface OrchestrationEventStoreShape { |
| 32 | * @param sequenceExclusive - Sequence cursor (exclusive). | 37 | * @param sequenceExclusive - Sequence cursor (exclusive). |
| 33 | * @param limit - Maximum number of events to emit. | 38 | * @param limit - Maximum number of events to emit. |
| 34 | * @returns Stream containing ordered events. | 39 | * @returns Stream containing ordered events. |
| 40 | * | ||
| 41 | * Reads in fixed-size pages and normalizes non-integer/negative limits. | ||
| 35 | */ | 42 | */ |
| 36 | readonly readFromSequence: ( | 43 | readonly readFromSequence: ( |
| 37 | sequenceExclusive: number, | 44 | sequenceExclusive: number, |
apps/server/src/persistence/Services/ProjectionCheckpoints.ts+30| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { | 9 | import { |
| 2 | CheckpointRef, | 10 | CheckpointRef, |
| 3 | IsoDateTime, | 11 | IsoDateTime, |
| ... | @@ -41,22 +49,44 @@ export const DeleteByThreadIdInput = Schema.Struct({ | ... | @@ -41,22 +49,44 @@ export const DeleteByThreadIdInput = Schema.Struct({ |
| 41 | }); | 49 | }); |
| 42 | export type DeleteByThreadIdInput = typeof DeleteByThreadIdInput.Type; | 50 | export type DeleteByThreadIdInput = typeof DeleteByThreadIdInput.Type; |
| 43 | 51 | ||
| 52 | /** | ||
| 53 | * ProjectionCheckpointRepositoryShape - Service API for projected checkpoints. | ||
| 54 | */ | ||
| 44 | export interface ProjectionCheckpointRepositoryShape { | 55 | export interface ProjectionCheckpointRepositoryShape { |
| 56 | /** | ||
| 57 | * Insert or replace a projected checkpoint row. | ||
| 58 | * | ||
| 59 | * Upserts by composite key `(threadId, checkpointTurnCount)`. | ||
| 60 | */ | ||
| 45 | readonly upsert: (row: ProjectionCheckpoint) => Effect.Effect<void, ProjectionRepositoryError>; | 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 | readonly listByThreadId: ( | 68 | readonly listByThreadId: ( |
| 48 | input: ListByThreadIdInput, | 69 | input: ListByThreadIdInput, |
| 49 | ) => Effect.Effect<ReadonlyArray<ProjectionCheckpoint>, ProjectionRepositoryError>; | 70 | ) => Effect.Effect<ReadonlyArray<ProjectionCheckpoint>, ProjectionRepositoryError>; |
| 50 | 71 | ||
| 72 | /** | ||
| 73 | * Read a projected checkpoint by thread and turn-count key. | ||
| 74 | */ | ||
| 51 | readonly getByThreadAndTurnCount: ( | 75 | readonly getByThreadAndTurnCount: ( |
| 52 | input: GetByThreadAndTurnCountInput, | 76 | input: GetByThreadAndTurnCountInput, |
| 53 | ) => Effect.Effect<Option.Option<ProjectionCheckpoint>, ProjectionRepositoryError>; | 77 | ) => Effect.Effect<Option.Option<ProjectionCheckpoint>, ProjectionRepositoryError>; |
| 54 | 78 | ||
| 79 | /** | ||
| 80 | * Delete projected checkpoint rows by thread. | ||
| 81 | */ | ||
| 55 | readonly deleteByThreadId: ( | 82 | readonly deleteByThreadId: ( |
| 56 | input: DeleteByThreadIdInput, | 83 | input: DeleteByThreadIdInput, |
| 57 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 84 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 58 | } | 85 | } |
| 59 | 86 | ||
| 87 | /** | ||
| 88 | * ProjectionCheckpointRepository - Service tag for checkpoint projection persistence. | ||
| 89 | */ | ||
| 60 | export class ProjectionCheckpointRepository extends ServiceMap.Service< | 90 | export class ProjectionCheckpointRepository extends ServiceMap.Service< |
| 61 | ProjectionCheckpointRepository, | 91 | ProjectionCheckpointRepository, |
| 62 | ProjectionCheckpointRepositoryShape | 92 | ProjectionCheckpointRepositoryShape |
apps/server/src/persistence/Services/ProjectionPendingApprovals.ts+30| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { | 9 | import { |
| 2 | ApprovalRequestId, | 10 | ApprovalRequestId, |
| 3 | IsoDateTime, | 11 | IsoDateTime, |
| ... | @@ -37,24 +45,46 @@ export const DeleteProjectionPendingApprovalInput = Schema.Struct({ | ... | @@ -37,24 +45,46 @@ export const DeleteProjectionPendingApprovalInput = Schema.Struct({ |
| 37 | }); | 45 | }); |
| 38 | export type DeleteProjectionPendingApprovalInput = typeof DeleteProjectionPendingApprovalInput.Type; | 46 | export type DeleteProjectionPendingApprovalInput = typeof DeleteProjectionPendingApprovalInput.Type; |
| 39 | 47 | ||
| 48 | /** | ||
| 49 | * ProjectionPendingApprovalRepositoryShape - Service API for pending approvals. | ||
| 50 | */ | ||
| 40 | export interface ProjectionPendingApprovalRepositoryShape { | 51 | export interface ProjectionPendingApprovalRepositoryShape { |
| 52 | /** | ||
| 53 | * Insert or replace a projected pending approval row. | ||
| 54 | * | ||
| 55 | * Upserts by `requestId`. | ||
| 56 | */ | ||
| 41 | readonly upsert: ( | 57 | readonly upsert: ( |
| 42 | row: ProjectionPendingApproval, | 58 | row: ProjectionPendingApproval, |
| 43 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 59 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 44 | 60 | ||
| 61 | /** | ||
| 62 | * List pending approvals for a thread. | ||
| 63 | * | ||
| 64 | * Returned in ascending creation order. | ||
| 65 | */ | ||
| 45 | readonly listByThreadId: ( | 66 | readonly listByThreadId: ( |
| 46 | input: ListProjectionPendingApprovalsInput, | 67 | input: ListProjectionPendingApprovalsInput, |
| 47 | ) => Effect.Effect<ReadonlyArray<ProjectionPendingApproval>, ProjectionRepositoryError>; | 68 | ) => Effect.Effect<ReadonlyArray<ProjectionPendingApproval>, ProjectionRepositoryError>; |
| 48 | 69 | ||
| 70 | /** | ||
| 71 | * Read a pending approval row by request id. | ||
| 72 | */ | ||
| 49 | readonly getByRequestId: ( | 73 | readonly getByRequestId: ( |
| 50 | input: GetProjectionPendingApprovalInput, | 74 | input: GetProjectionPendingApprovalInput, |
| 51 | ) => Effect.Effect<Option.Option<ProjectionPendingApproval>, ProjectionRepositoryError>; | 75 | ) => Effect.Effect<Option.Option<ProjectionPendingApproval>, ProjectionRepositoryError>; |
| 52 | 76 | ||
| 77 | /** | ||
| 78 | * Delete a pending approval row by request id. | ||
| 79 | */ | ||
| 53 | readonly deleteByRequestId: ( | 80 | readonly deleteByRequestId: ( |
| 54 | input: DeleteProjectionPendingApprovalInput, | 81 | input: DeleteProjectionPendingApprovalInput, |
| 55 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 82 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 56 | } | 83 | } |
| 57 | 84 | ||
| 85 | /** | ||
| 86 | * ProjectionPendingApprovalRepository - Service tag for pending approval persistence. | ||
| 87 | */ | ||
| 58 | export class ProjectionPendingApprovalRepository extends ServiceMap.Service< | 88 | export class ProjectionPendingApprovalRepository extends ServiceMap.Service< |
| 59 | ProjectionPendingApprovalRepository, | 89 | ProjectionPendingApprovalRepository, |
| 60 | ProjectionPendingApprovalRepositoryShape | 90 | ProjectionPendingApprovalRepositoryShape |
apps/server/src/persistence/Services/ProjectionPendingTurnStarts.ts+25| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { IsoDateTime, MessageId, ThreadId } from "@t3tools/contracts"; | 9 | import { IsoDateTime, MessageId, ThreadId } from "@t3tools/contracts"; |
| 2 | import { Option, Schema, ServiceMap } from "effect"; | 10 | import { Option, Schema, ServiceMap } from "effect"; |
| 3 | import type { Effect } from "effect"; | 11 | import type { Effect } from "effect"; |
| ... | @@ -21,20 +29,37 @@ export const DeleteProjectionPendingTurnStartInput = Schema.Struct({ | ... | @@ -21,20 +29,37 @@ export const DeleteProjectionPendingTurnStartInput = Schema.Struct({ |
| 21 | }); | 29 | }); |
| 22 | export type DeleteProjectionPendingTurnStartInput = typeof DeleteProjectionPendingTurnStartInput.Type; | 30 | export type DeleteProjectionPendingTurnStartInput = typeof DeleteProjectionPendingTurnStartInput.Type; |
| 23 | 31 | ||
| 32 | /** | ||
| 33 | * ProjectionPendingTurnStartRepositoryShape - Service API for pending turn-start markers. | ||
| 34 | */ | ||
| 24 | export interface ProjectionPendingTurnStartRepositoryShape { | 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 | readonly upsert: ( | 41 | readonly upsert: ( |
| 26 | row: ProjectionPendingTurnStart, | 42 | row: ProjectionPendingTurnStart, |
| 27 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 43 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 28 | 44 | ||
| 45 | /** | ||
| 46 | * Read a pending turn-start marker by thread id. | ||
| 47 | */ | ||
| 29 | readonly getByThreadId: ( | 48 | readonly getByThreadId: ( |
| 30 | input: GetProjectionPendingTurnStartInput, | 49 | input: GetProjectionPendingTurnStartInput, |
| 31 | ) => Effect.Effect<Option.Option<ProjectionPendingTurnStart>, ProjectionRepositoryError>; | 50 | ) => Effect.Effect<Option.Option<ProjectionPendingTurnStart>, ProjectionRepositoryError>; |
| 32 | 51 | ||
| 52 | /** | ||
| 53 | * Delete a pending turn-start marker by thread id. | ||
| 54 | */ | ||
| 33 | readonly deleteByThreadId: ( | 55 | readonly deleteByThreadId: ( |
| 34 | input: DeleteProjectionPendingTurnStartInput, | 56 | input: DeleteProjectionPendingTurnStartInput, |
| 35 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 57 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 36 | } | 58 | } |
| 37 | 59 | ||
| 60 | /** | ||
| 61 | * ProjectionPendingTurnStartRepository - Service tag for pending turn-start persistence. | ||
| 62 | */ | ||
| 38 | export class ProjectionPendingTurnStartRepository extends ServiceMap.Service< | 63 | export class ProjectionPendingTurnStartRepository extends ServiceMap.Service< |
| 39 | ProjectionPendingTurnStartRepository, | 64 | ProjectionPendingTurnStartRepository, |
| 40 | ProjectionPendingTurnStartRepositoryShape | 65 | ProjectionPendingTurnStartRepositoryShape |
apps/server/src/persistence/Services/ProjectionProjects.ts+30| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { IsoDateTime, ProjectId, ProjectScript } from "@t3tools/contracts"; | 9 | import { IsoDateTime, ProjectId, ProjectScript } from "@t3tools/contracts"; |
| 2 | import { Option, Schema, ServiceMap } from "effect"; | 10 | import { Option, Schema, ServiceMap } from "effect"; |
| 3 | import type { Effect } from "effect"; | 11 | import type { Effect } from "effect"; |
| ... | @@ -26,23 +34,45 @@ export const DeleteProjectionProjectInput = Schema.Struct({ | ... | @@ -26,23 +34,45 @@ export const DeleteProjectionProjectInput = Schema.Struct({ |
| 26 | }); | 34 | }); |
| 27 | export type DeleteProjectionProjectInput = typeof DeleteProjectionProjectInput.Type; | 35 | export type DeleteProjectionProjectInput = typeof DeleteProjectionProjectInput.Type; |
| 28 | 36 | ||
| 37 | /** | ||
| 38 | * ProjectionProjectRepositoryShape - Service API for projected project records. | ||
| 39 | */ | ||
| 29 | export interface ProjectionProjectRepositoryShape { | 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 | readonly upsert: (row: ProjectionProject) => Effect.Effect<void, ProjectionRepositoryError>; | 46 | readonly upsert: (row: ProjectionProject) => Effect.Effect<void, ProjectionRepositoryError>; |
| 31 | 47 | ||
| 48 | /** | ||
| 49 | * Read a projected project row by id. | ||
| 50 | */ | ||
| 32 | readonly getById: ( | 51 | readonly getById: ( |
| 33 | input: GetProjectionProjectInput, | 52 | input: GetProjectionProjectInput, |
| 34 | ) => Effect.Effect<Option.Option<ProjectionProject>, ProjectionRepositoryError>; | 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 | readonly listAll: () => Effect.Effect< | 60 | readonly listAll: () => Effect.Effect< |
| 37 | ReadonlyArray<ProjectionProject>, | 61 | ReadonlyArray<ProjectionProject>, |
| 38 | ProjectionRepositoryError | 62 | ProjectionRepositoryError |
| 39 | >; | 63 | >; |
| 40 | 64 | ||
| 65 | /** | ||
| 66 | * Soft-delete a projected project row by id. | ||
| 67 | */ | ||
| 41 | readonly deleteById: ( | 68 | readonly deleteById: ( |
| 42 | input: DeleteProjectionProjectInput, | 69 | input: DeleteProjectionProjectInput, |
| 43 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 70 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 44 | } | 71 | } |
| 45 | 72 | ||
| 73 | /** | ||
| 74 | * ProjectionProjectRepository - Service tag for project projection persistence. | ||
| 75 | */ | ||
| 46 | export class ProjectionProjectRepository extends ServiceMap.Service< | 76 | export class ProjectionProjectRepository extends ServiceMap.Service< |
| 47 | ProjectionProjectRepository, | 77 | ProjectionProjectRepository, |
| 48 | ProjectionProjectRepositoryShape | 78 | ProjectionProjectRepositoryShape |
apps/server/src/persistence/Services/ProjectionState.ts+30| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { IsoDateTime, NonNegativeInt } from "@t3tools/contracts"; | 9 | import { IsoDateTime, NonNegativeInt } from "@t3tools/contracts"; |
| 2 | import { Option, Schema, ServiceMap } from "effect"; | 10 | import { Option, Schema, ServiceMap } from "effect"; |
| 3 | import type { Effect } from "effect"; | 11 | import type { Effect } from "effect"; |
| ... | @@ -16,18 +24,40 @@ export const GetProjectionStateInput = Schema.Struct({ | ... | @@ -16,18 +24,40 @@ export const GetProjectionStateInput = Schema.Struct({ |
| 16 | }); | 24 | }); |
| 17 | export type GetProjectionStateInput = typeof GetProjectionStateInput.Type; | 25 | export type GetProjectionStateInput = typeof GetProjectionStateInput.Type; |
| 18 | 26 | ||
| 27 | /** | ||
| 28 | * ProjectionStateRepositoryShape - Service API for projector state records. | ||
| 29 | */ | ||
| 19 | export interface ProjectionStateRepositoryShape { | 30 | export interface ProjectionStateRepositoryShape { |
| 31 | /** | ||
| 32 | * Insert or replace a projection cursor row. | ||
| 33 | * | ||
| 34 | * Upserts by projector name. | ||
| 35 | */ | ||
| 20 | readonly upsert: (row: ProjectionState) => Effect.Effect<void, ProjectionRepositoryError>; | 36 | readonly upsert: (row: ProjectionState) => Effect.Effect<void, ProjectionRepositoryError>; |
| 21 | 37 | ||
| 38 | /** | ||
| 39 | * Read projection cursor state for a projector key. | ||
| 40 | */ | ||
| 22 | readonly getByProjector: ( | 41 | readonly getByProjector: ( |
| 23 | input: GetProjectionStateInput, | 42 | input: GetProjectionStateInput, |
| 24 | ) => Effect.Effect<Option.Option<ProjectionState>, ProjectionRepositoryError>; | 43 | ) => Effect.Effect<Option.Option<ProjectionState>, ProjectionRepositoryError>; |
| 25 | 44 | ||
| 45 | /** | ||
| 46 | * List all projector cursor rows. | ||
| 47 | */ | ||
| 26 | readonly listAll: () => Effect.Effect<ReadonlyArray<ProjectionState>, ProjectionRepositoryError>; | 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 | readonly minLastAppliedSequence: () => Effect.Effect<number | null, ProjectionRepositoryError>; | 55 | readonly minLastAppliedSequence: () => Effect.Effect<number | null, ProjectionRepositoryError>; |
| 29 | } | 56 | } |
| 30 | 57 | ||
| 58 | /** | ||
| 59 | * ProjectionStateRepository - Service tag for projection cursor persistence. | ||
| 60 | */ | ||
| 31 | export class ProjectionStateRepository extends ServiceMap.Service< | 61 | export class ProjectionStateRepository extends ServiceMap.Service< |
| 32 | ProjectionStateRepository, | 62 | ProjectionStateRepository, |
| 33 | ProjectionStateRepositoryShape | 63 | ProjectionStateRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreadActivities.ts+27| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { | 9 | import { |
| 2 | EventId, | 10 | EventId, |
| 3 | IsoDateTime, | 11 | IsoDateTime, |
| ... | @@ -33,20 +41,39 @@ export const DeleteProjectionThreadActivitiesInput = Schema.Struct({ | ... | @@ -33,20 +41,39 @@ export const DeleteProjectionThreadActivitiesInput = Schema.Struct({ |
| 33 | export type DeleteProjectionThreadActivitiesInput = | 41 | export type DeleteProjectionThreadActivitiesInput = |
| 34 | typeof DeleteProjectionThreadActivitiesInput.Type; | 42 | typeof DeleteProjectionThreadActivitiesInput.Type; |
| 35 | 43 | ||
| 44 | /** | ||
| 45 | * ProjectionThreadActivityRepositoryShape - Service API for projected thread activity. | ||
| 46 | */ | ||
| 36 | export interface ProjectionThreadActivityRepositoryShape { | 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 | readonly upsert: ( | 53 | readonly upsert: ( |
| 38 | row: ProjectionThreadActivity, | 54 | row: ProjectionThreadActivity, |
| 39 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 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 | readonly listByThreadId: ( | 62 | readonly listByThreadId: ( |
| 42 | input: ListProjectionThreadActivitiesInput, | 63 | input: ListProjectionThreadActivitiesInput, |
| 43 | ) => Effect.Effect<ReadonlyArray<ProjectionThreadActivity>, ProjectionRepositoryError>; | 64 | ) => Effect.Effect<ReadonlyArray<ProjectionThreadActivity>, ProjectionRepositoryError>; |
| 44 | 65 | ||
| 66 | /** | ||
| 67 | * Delete projected thread activity rows by thread. | ||
| 68 | */ | ||
| 45 | readonly deleteByThreadId: ( | 69 | readonly deleteByThreadId: ( |
| 46 | input: DeleteProjectionThreadActivitiesInput, | 70 | input: DeleteProjectionThreadActivitiesInput, |
| 47 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 71 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 48 | } | 72 | } |
| 49 | 73 | ||
| 74 | /** | ||
| 75 | * ProjectionThreadActivityRepository - Service tag for thread activity persistence. | ||
| 76 | */ | ||
| 50 | export class ProjectionThreadActivityRepository extends ServiceMap.Service< | 77 | export class ProjectionThreadActivityRepository extends ServiceMap.Service< |
| 51 | ProjectionThreadActivityRepository, | 78 | ProjectionThreadActivityRepository, |
| 52 | ProjectionThreadActivityRepositoryShape | 79 | ProjectionThreadActivityRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreadMessages.ts+27| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { | 9 | import { |
| 2 | OrchestrationMessageRole, | 10 | OrchestrationMessageRole, |
| 3 | MessageId, | 11 | MessageId, |
| ... | @@ -32,20 +40,39 @@ export const DeleteProjectionThreadMessagesInput = Schema.Struct({ | ... | @@ -32,20 +40,39 @@ export const DeleteProjectionThreadMessagesInput = Schema.Struct({ |
| 32 | }); | 40 | }); |
| 33 | export type DeleteProjectionThreadMessagesInput = typeof DeleteProjectionThreadMessagesInput.Type; | 41 | export type DeleteProjectionThreadMessagesInput = typeof DeleteProjectionThreadMessagesInput.Type; |
| 34 | 42 | ||
| 43 | /** | ||
| 44 | * ProjectionThreadMessageRepositoryShape - Service API for projected thread messages. | ||
| 45 | */ | ||
| 35 | export interface ProjectionThreadMessageRepositoryShape { | 46 | export interface ProjectionThreadMessageRepositoryShape { |
| 47 | /** | ||
| 48 | * Insert or replace a projected thread message row. | ||
| 49 | * | ||
| 50 | * Upserts by `messageId`. | ||
| 51 | */ | ||
| 36 | readonly upsert: ( | 52 | readonly upsert: ( |
| 37 | message: ProjectionThreadMessage, | 53 | message: ProjectionThreadMessage, |
| 38 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 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 | readonly listByThreadId: ( | 61 | readonly listByThreadId: ( |
| 41 | input: ListProjectionThreadMessagesInput, | 62 | input: ListProjectionThreadMessagesInput, |
| 42 | ) => Effect.Effect<ReadonlyArray<ProjectionThreadMessage>, ProjectionRepositoryError>; | 63 | ) => Effect.Effect<ReadonlyArray<ProjectionThreadMessage>, ProjectionRepositoryError>; |
| 43 | 64 | ||
| 65 | /** | ||
| 66 | * Delete projected thread messages by thread. | ||
| 67 | */ | ||
| 44 | readonly deleteByThreadId: ( | 68 | readonly deleteByThreadId: ( |
| 45 | input: DeleteProjectionThreadMessagesInput, | 69 | input: DeleteProjectionThreadMessagesInput, |
| 46 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 70 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 47 | } | 71 | } |
| 48 | 72 | ||
| 73 | /** | ||
| 74 | * ProjectionThreadMessageRepository - Service tag for message projection persistence. | ||
| 75 | */ | ||
| 49 | export class ProjectionThreadMessageRepository extends ServiceMap.Service< | 76 | export class ProjectionThreadMessageRepository extends ServiceMap.Service< |
| 50 | ProjectionThreadMessageRepository, | 77 | ProjectionThreadMessageRepository, |
| 51 | ProjectionThreadMessageRepositoryShape | 78 | ProjectionThreadMessageRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreadSessions.ts+25| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { | 9 | import { |
| 2 | IsoDateTime, | 10 | IsoDateTime, |
| 3 | OrchestrationSessionStatus, | 11 | OrchestrationSessionStatus, |
| ... | @@ -33,18 +41,35 @@ export const DeleteProjectionThreadSessionInput = Schema.Struct({ | ... | @@ -33,18 +41,35 @@ export const DeleteProjectionThreadSessionInput = Schema.Struct({ |
| 33 | }); | 41 | }); |
| 34 | export type DeleteProjectionThreadSessionInput = typeof DeleteProjectionThreadSessionInput.Type; | 42 | export type DeleteProjectionThreadSessionInput = typeof DeleteProjectionThreadSessionInput.Type; |
| 35 | 43 | ||
| 44 | /** | ||
| 45 | * ProjectionThreadSessionRepositoryShape - Service API for projected thread sessions. | ||
| 46 | */ | ||
| 36 | export interface ProjectionThreadSessionRepositoryShape { | 47 | export interface ProjectionThreadSessionRepositoryShape { |
| 48 | /** | ||
| 49 | * Insert or replace a projected thread-session row. | ||
| 50 | * | ||
| 51 | * Upserts by `threadId`. | ||
| 52 | */ | ||
| 37 | readonly upsert: (row: ProjectionThreadSession) => Effect.Effect<void, ProjectionRepositoryError>; | 53 | readonly upsert: (row: ProjectionThreadSession) => Effect.Effect<void, ProjectionRepositoryError>; |
| 38 | 54 | ||
| 55 | /** | ||
| 56 | * Read projected thread-session state by thread id. | ||
| 57 | */ | ||
| 39 | readonly getByThreadId: ( | 58 | readonly getByThreadId: ( |
| 40 | input: GetProjectionThreadSessionInput, | 59 | input: GetProjectionThreadSessionInput, |
| 41 | ) => Effect.Effect<Option.Option<ProjectionThreadSession>, ProjectionRepositoryError>; | 60 | ) => Effect.Effect<Option.Option<ProjectionThreadSession>, ProjectionRepositoryError>; |
| 42 | 61 | ||
| 62 | /** | ||
| 63 | * Delete projected thread-session state by thread id. | ||
| 64 | */ | ||
| 43 | readonly deleteByThreadId: ( | 65 | readonly deleteByThreadId: ( |
| 44 | input: DeleteProjectionThreadSessionInput, | 66 | input: DeleteProjectionThreadSessionInput, |
| 45 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 67 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 46 | } | 68 | } |
| 47 | 69 | ||
| 70 | /** | ||
| 71 | * ProjectionThreadSessionRepository - Service tag for thread-session persistence. | ||
| 72 | */ | ||
| 48 | export class ProjectionThreadSessionRepository extends ServiceMap.Service< | 73 | export class ProjectionThreadSessionRepository extends ServiceMap.Service< |
| 49 | ProjectionThreadSessionRepository, | 74 | ProjectionThreadSessionRepository, |
| 50 | ProjectionThreadSessionRepositoryShape | 75 | ProjectionThreadSessionRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreadTurns.ts+29| ... | @@ -1,3 +1,10 @@ | ... | @@ -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 | import { | 8 | import { |
| 2 | IsoDateTime, | 9 | IsoDateTime, |
| 3 | MessageId, | 10 | MessageId, |
| ... | @@ -38,22 +45,44 @@ export const DeleteProjectionThreadTurnsInput = Schema.Struct({ | ... | @@ -38,22 +45,44 @@ export const DeleteProjectionThreadTurnsInput = Schema.Struct({ |
| 38 | }); | 45 | }); |
| 39 | export type DeleteProjectionThreadTurnsInput = typeof DeleteProjectionThreadTurnsInput.Type; | 46 | export type DeleteProjectionThreadTurnsInput = typeof DeleteProjectionThreadTurnsInput.Type; |
| 40 | 47 | ||
| 48 | /** | ||
| 49 | * ProjectionThreadTurnRepositoryShape - Service API for projected thread turns. | ||
| 50 | */ | ||
| 41 | export interface ProjectionThreadTurnRepositoryShape { | 51 | export interface ProjectionThreadTurnRepositoryShape { |
| 52 | /** | ||
| 53 | * Insert or replace a projected turn row. | ||
| 54 | * | ||
| 55 | * Upserts by `turnId`. | ||
| 56 | */ | ||
| 42 | readonly upsert: (turn: ProjectionThreadTurn) => Effect.Effect<void, ProjectionRepositoryError>; | 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 | readonly listByThreadId: ( | 64 | readonly listByThreadId: ( |
| 45 | input: ListProjectionThreadTurnsInput, | 65 | input: ListProjectionThreadTurnsInput, |
| 46 | ) => Effect.Effect<ReadonlyArray<ProjectionThreadTurn>, ProjectionRepositoryError>; | 66 | ) => Effect.Effect<ReadonlyArray<ProjectionThreadTurn>, ProjectionRepositoryError>; |
| 47 | 67 | ||
| 68 | /** | ||
| 69 | * Read a projected turn row by turn id. | ||
| 70 | */ | ||
| 48 | readonly getByTurnId: ( | 71 | readonly getByTurnId: ( |
| 49 | input: GetProjectionThreadTurnInput, | 72 | input: GetProjectionThreadTurnInput, |
| 50 | ) => Effect.Effect<Option.Option<ProjectionThreadTurn>, ProjectionRepositoryError>; | 73 | ) => Effect.Effect<Option.Option<ProjectionThreadTurn>, ProjectionRepositoryError>; |
| 51 | 74 | ||
| 75 | /** | ||
| 76 | * Delete projected turn rows by thread. | ||
| 77 | */ | ||
| 52 | readonly deleteByThreadId: ( | 78 | readonly deleteByThreadId: ( |
| 53 | input: DeleteProjectionThreadTurnsInput, | 79 | input: DeleteProjectionThreadTurnsInput, |
| 54 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 80 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 55 | } | 81 | } |
| 56 | 82 | ||
| 83 | /** | ||
| 84 | * ProjectionThreadTurnRepository - Service tag for turn projection persistence. | ||
| 85 | */ | ||
| 57 | export class ProjectionThreadTurnRepository extends ServiceMap.Service< | 86 | export class ProjectionThreadTurnRepository extends ServiceMap.Service< |
| 58 | ProjectionThreadTurnRepository, | 87 | ProjectionThreadTurnRepository, |
| 59 | ProjectionThreadTurnRepositoryShape | 88 | ProjectionThreadTurnRepositoryShape |
apps/server/src/persistence/Services/ProjectionThreads.ts+30| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { IsoDateTime, ProjectId, ThreadId, TurnId } from "@t3tools/contracts"; | 9 | import { IsoDateTime, ProjectId, ThreadId, TurnId } from "@t3tools/contracts"; |
| 2 | import { Option, Schema, ServiceMap } from "effect"; | 10 | import { Option, Schema, ServiceMap } from "effect"; |
| 3 | import type { Effect } from "effect"; | 11 | import type { Effect } from "effect"; |
| ... | @@ -33,22 +41,44 @@ export const ListProjectionThreadsByProjectInput = Schema.Struct({ | ... | @@ -33,22 +41,44 @@ export const ListProjectionThreadsByProjectInput = Schema.Struct({ |
| 33 | }); | 41 | }); |
| 34 | export type ListProjectionThreadsByProjectInput = typeof ListProjectionThreadsByProjectInput.Type; | 42 | export type ListProjectionThreadsByProjectInput = typeof ListProjectionThreadsByProjectInput.Type; |
| 35 | 43 | ||
| 44 | /** | ||
| 45 | * ProjectionThreadRepositoryShape - Service API for projected thread records. | ||
| 46 | */ | ||
| 36 | export interface ProjectionThreadRepositoryShape { | 47 | export interface ProjectionThreadRepositoryShape { |
| 48 | /** | ||
| 49 | * Insert or replace a projected thread row. | ||
| 50 | * | ||
| 51 | * Upserts by `threadId`. | ||
| 52 | */ | ||
| 37 | readonly upsert: (thread: ProjectionThread) => Effect.Effect<void, ProjectionRepositoryError>; | 53 | readonly upsert: (thread: ProjectionThread) => Effect.Effect<void, ProjectionRepositoryError>; |
| 38 | 54 | ||
| 55 | /** | ||
| 56 | * Read a projected thread row by id. | ||
| 57 | */ | ||
| 39 | readonly getById: ( | 58 | readonly getById: ( |
| 40 | input: GetProjectionThreadInput, | 59 | input: GetProjectionThreadInput, |
| 41 | ) => Effect.Effect<Option.Option<ProjectionThread>, ProjectionRepositoryError>; | 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 | readonly listByProjectId: ( | 67 | readonly listByProjectId: ( |
| 44 | input: ListProjectionThreadsByProjectInput, | 68 | input: ListProjectionThreadsByProjectInput, |
| 45 | ) => Effect.Effect<ReadonlyArray<ProjectionThread>, ProjectionRepositoryError>; | 69 | ) => Effect.Effect<ReadonlyArray<ProjectionThread>, ProjectionRepositoryError>; |
| 46 | 70 | ||
| 71 | /** | ||
| 72 | * Soft-delete a projected thread row by id. | ||
| 73 | */ | ||
| 47 | readonly deleteById: ( | 74 | readonly deleteById: ( |
| 48 | input: DeleteProjectionThreadInput, | 75 | input: DeleteProjectionThreadInput, |
| 49 | ) => Effect.Effect<void, ProjectionRepositoryError>; | 76 | ) => Effect.Effect<void, ProjectionRepositoryError>; |
| 50 | } | 77 | } |
| 51 | 78 | ||
| 79 | /** | ||
| 80 | * ProjectionThreadRepository - Service tag for thread projection persistence. | ||
| 81 | */ | ||
| 52 | export class ProjectionThreadRepository extends ServiceMap.Service< | 82 | export class ProjectionThreadRepository extends ServiceMap.Service< |
| 53 | ProjectionThreadRepository, | 83 | ProjectionThreadRepository, |
| 54 | ProjectionThreadRepositoryShape | 84 | ProjectionThreadRepositoryShape |
apps/server/src/persistence/Services/ProviderSessionRuntime.ts+29| ... | @@ -1,3 +1,10 @@ | ... | @@ -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 | import { | 8 | import { |
| 2 | IsoDateTime, | 9 | IsoDateTime, |
| 3 | ProviderSessionId, | 10 | ProviderSessionId, |
| ... | @@ -33,25 +40,47 @@ export const DeleteProviderSessionRuntimeInput = Schema.Struct({ | ... | @@ -33,25 +40,47 @@ export const DeleteProviderSessionRuntimeInput = Schema.Struct({ |
| 33 | }); | 40 | }); |
| 34 | export type DeleteProviderSessionRuntimeInput = typeof DeleteProviderSessionRuntimeInput.Type; | 41 | export type DeleteProviderSessionRuntimeInput = typeof DeleteProviderSessionRuntimeInput.Type; |
| 35 | 42 | ||
| 43 | /** | ||
| 44 | * ProviderSessionRuntimeRepositoryShape - Service API for provider runtime records. | ||
| 45 | */ | ||
| 36 | export interface ProviderSessionRuntimeRepositoryShape { | 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 | readonly upsert: ( | 52 | readonly upsert: ( |
| 38 | runtime: ProviderSessionRuntime, | 53 | runtime: ProviderSessionRuntime, |
| 39 | ) => Effect.Effect<void, ProviderSessionRuntimeRepositoryError>; | 54 | ) => Effect.Effect<void, ProviderSessionRuntimeRepositoryError>; |
| 40 | 55 | ||
| 56 | /** | ||
| 57 | * Read provider runtime state by provider session id. | ||
| 58 | */ | ||
| 41 | readonly getBySessionId: ( | 59 | readonly getBySessionId: ( |
| 42 | input: GetProviderSessionRuntimeInput, | 60 | input: GetProviderSessionRuntimeInput, |
| 43 | ) => Effect.Effect<Option.Option<ProviderSessionRuntime>, ProviderSessionRuntimeRepositoryError>; | 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 | readonly list: () => Effect.Effect< | 68 | readonly list: () => Effect.Effect< |
| 46 | ReadonlyArray<ProviderSessionRuntime>, | 69 | ReadonlyArray<ProviderSessionRuntime>, |
| 47 | ProviderSessionRuntimeRepositoryError | 70 | ProviderSessionRuntimeRepositoryError |
| 48 | >; | 71 | >; |
| 49 | 72 | ||
| 73 | /** | ||
| 74 | * Delete provider runtime state by provider session id. | ||
| 75 | */ | ||
| 50 | readonly deleteBySessionId: ( | 76 | readonly deleteBySessionId: ( |
| 51 | input: DeleteProviderSessionRuntimeInput, | 77 | input: DeleteProviderSessionRuntimeInput, |
| 52 | ) => Effect.Effect<void, ProviderSessionRuntimeRepositoryError>; | 78 | ) => Effect.Effect<void, ProviderSessionRuntimeRepositoryError>; |
| 53 | } | 79 | } |
| 54 | 80 | ||
| 81 | /** | ||
| 82 | * ProviderSessionRuntimeRepository - Service tag for provider runtime persistence. | ||
| 83 | */ | ||
| 55 | export class ProviderSessionRuntimeRepository extends ServiceMap.Service< | 84 | export class ProviderSessionRuntimeRepository extends ServiceMap.Service< |
| 56 | ProviderSessionRuntimeRepository, | 85 | ProviderSessionRuntimeRepository, |
| 57 | ProviderSessionRuntimeRepositoryShape | 86 | ProviderSessionRuntimeRepositoryShape |
apps/server/src/persistence/Services/ProviderSessions.ts+30| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import type { ProviderKind, ProviderSessionId, ThreadId } from "@t3tools/contracts"; | 9 | import type { ProviderKind, ProviderSessionId, ThreadId } from "@t3tools/contracts"; |
| 2 | import { Option, ServiceMap } from "effect"; | 10 | import { Option, ServiceMap } from "effect"; |
| 3 | import type { Effect } from "effect"; | 11 | import type { Effect } from "effect"; |
| ... | @@ -26,25 +34,47 @@ export interface GetProviderSessionInput { | ... | @@ -26,25 +34,47 @@ export interface GetProviderSessionInput { |
| 26 | readonly sessionId: ProviderSessionId; | 34 | readonly sessionId: ProviderSessionId; |
| 27 | } | 35 | } |
| 28 | 36 | ||
| 37 | /** | ||
| 38 | * ProviderSessionRepositoryShape - Service API for provider-session records. | ||
| 39 | */ | ||
| 29 | export interface ProviderSessionRepositoryShape { | 40 | export interface ProviderSessionRepositoryShape { |
| 41 | /** | ||
| 42 | * Insert or replace a provider-session row. | ||
| 43 | * | ||
| 44 | * Upserts by `sessionId`. | ||
| 45 | */ | ||
| 30 | readonly upsertSession: ( | 46 | readonly upsertSession: ( |
| 31 | input: UpsertProviderSessionInput, | 47 | input: UpsertProviderSessionInput, |
| 32 | ) => Effect.Effect<void, ProviderSessionRepositoryError>; | 48 | ) => Effect.Effect<void, ProviderSessionRepositoryError>; |
| 33 | 49 | ||
| 50 | /** | ||
| 51 | * Read a provider-session row by session id. | ||
| 52 | */ | ||
| 34 | readonly getSession: ( | 53 | readonly getSession: ( |
| 35 | input: GetProviderSessionInput, | 54 | input: GetProviderSessionInput, |
| 36 | ) => Effect.Effect<Option.Option<ProviderSessionEntry>, ProviderSessionRepositoryError>; | 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 | readonly listSessions: () => Effect.Effect< | 62 | readonly listSessions: () => Effect.Effect< |
| 39 | ReadonlyArray<ProviderSessionEntry>, | 63 | ReadonlyArray<ProviderSessionEntry>, |
| 40 | ProviderSessionRepositoryError | 64 | ProviderSessionRepositoryError |
| 41 | >; | 65 | >; |
| 42 | 66 | ||
| 67 | /** | ||
| 68 | * Delete a provider-session row by session id. | ||
| 69 | */ | ||
| 43 | readonly deleteSession: ( | 70 | readonly deleteSession: ( |
| 44 | input: DeleteProviderSessionInput, | 71 | input: DeleteProviderSessionInput, |
| 45 | ) => Effect.Effect<void, ProviderSessionRepositoryError>; | 72 | ) => Effect.Effect<void, ProviderSessionRepositoryError>; |
| 46 | } | 73 | } |
| 47 | 74 | ||
| 75 | /** | ||
| 76 | * ProviderSessionRepository - Service tag for provider-session persistence. | ||
| 77 | */ | ||
| 48 | export class ProviderSessionRepository extends ServiceMap.Service< | 78 | export class ProviderSessionRepository extends ServiceMap.Service< |
| 49 | ProviderSessionRepository, | 79 | ProviderSessionRepository, |
| 50 | ProviderSessionRepositoryShape | 80 | ProviderSessionRepositoryShape |
apps/server/src/provider/Errors.ts-71| ... | @@ -51,23 +51,6 @@ export class ProviderAdapterSessionClosedError extends Schema.TaggedErrorClass<P | ... | @@ -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 | * ProviderAdapterRequestError - Provider protocol request failed or timed out. | 55 | * ProviderAdapterRequestError - Provider protocol request failed or timed out. |
| 73 | */ | 56 | */ |
| ... | @@ -164,61 +147,10 @@ export class ProviderSessionDirectoryPersistenceError extends Schema.TaggedError | ... | @@ -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 | export type ProviderAdapterError = | 150 | export type ProviderAdapterError = |
| 218 | | ProviderAdapterValidationError | 151 | | ProviderAdapterValidationError |
| 219 | | ProviderAdapterSessionNotFoundError | 152 | | ProviderAdapterSessionNotFoundError |
| 220 | | ProviderAdapterSessionClosedError | 153 | | ProviderAdapterSessionClosedError |
| 221 | | ProviderAdapterProtocolError | ||
| 222 | | ProviderAdapterRequestError | 154 | | ProviderAdapterRequestError |
| 223 | | ProviderAdapterProcessError; | 155 | | ProviderAdapterProcessError; |
| 224 | 156 | ||
| ... | @@ -227,8 +159,5 @@ export type ProviderServiceError = | ... | @@ -227,8 +159,5 @@ export type ProviderServiceError = |
| 227 | | ProviderUnsupportedError | 159 | | ProviderUnsupportedError |
| 228 | | ProviderSessionNotFoundError | 160 | | ProviderSessionNotFoundError |
| 229 | | ProviderSessionDirectoryPersistenceError | 161 | | ProviderSessionDirectoryPersistenceError |
| 230 | | ProviderCheckpointUnavailableError | ||
| 231 | | ProviderCheckpointRangeError | ||
| 232 | | ProviderFilesystemError | ||
| 233 | | ProviderAdapterError | 162 | | ProviderAdapterError |
| 234 | | CheckpointServiceError; | 163 | | CheckpointServiceError; |
apps/server/src/provider/Services/CodexAdapter.ts+3| ... | @@ -15,6 +15,9 @@ import { ServiceMap } from "effect"; | ... | @@ -15,6 +15,9 @@ import { ServiceMap } from "effect"; |
| 15 | import type { ProviderAdapterError } from "../Errors.ts"; | 15 | import type { ProviderAdapterError } from "../Errors.ts"; |
| 16 | import type { ProviderAdapterShape } from "./ProviderAdapter.ts"; | 16 | import type { ProviderAdapterShape } from "./ProviderAdapter.ts"; |
| 17 | 17 | ||
| 18 | /** | ||
| 19 | * CodexAdapterShape - Service API for the Codex provider adapter. | ||
| 20 | */ | ||
| 18 | export interface CodexAdapterShape extends ProviderAdapterShape<ProviderAdapterError> { | 21 | export interface CodexAdapterShape extends ProviderAdapterShape<ProviderAdapterError> { |
| 19 | readonly provider: "codex"; | 22 | readonly provider: "codex"; |
| 20 | } | 23 | } |
apps/server/src/provider/Services/ProviderAdapterRegistry.ts+3| ... | @@ -14,6 +14,9 @@ import type { Effect } from "effect"; | ... | @@ -14,6 +14,9 @@ import type { Effect } from "effect"; |
| 14 | import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts"; | 14 | import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts"; |
| 15 | import type { ProviderAdapterShape } from "./ProviderAdapter.ts"; | 15 | import type { ProviderAdapterShape } from "./ProviderAdapter.ts"; |
| 16 | 16 | ||
| 17 | /** | ||
| 18 | * ProviderAdapterRegistryShape - Service API for adapter lookup by provider kind. | ||
| 19 | */ | ||
| 17 | export interface ProviderAdapterRegistryShape { | 20 | export interface ProviderAdapterRegistryShape { |
| 18 | /** | 21 | /** |
| 19 | * Resolve the adapter for a provider kind. | 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,6 +28,9 @@ import type { Effect, Stream } from "effect"; |
| 28 | 28 | ||
| 29 | import type { ProviderServiceError } from "../Errors.ts"; | 29 | import type { ProviderServiceError } from "../Errors.ts"; |
| 30 | 30 | ||
| 31 | /** | ||
| 32 | * ProviderServiceShape - Service API for provider session and turn orchestration. | ||
| 33 | */ | ||
| 31 | export interface ProviderServiceShape { | 34 | export interface ProviderServiceShape { |
| 32 | /** | 35 | /** |
| 33 | * Start a provider session. | 36 | * Start a provider session. |
| ... | @@ -67,6 +70,8 @@ export interface ProviderServiceShape { | ... | @@ -67,6 +70,8 @@ export interface ProviderServiceShape { |
| 67 | 70 | ||
| 68 | /** | 71 | /** |
| 69 | * List active provider sessions. | 72 | * List active provider sessions. |
| 73 | * | ||
| 74 | * Aggregates runtime session lists from all registered adapters. | ||
| 70 | */ | 75 | */ |
| 71 | readonly listSessions: () => Effect.Effect<ReadonlyArray<ProviderSession>>; | 76 | readonly listSessions: () => Effect.Effect<ReadonlyArray<ProviderSession>>; |
| 72 | 77 | ||
| ... | @@ -83,6 +88,8 @@ export interface ProviderServiceShape { | ... | @@ -83,6 +88,8 @@ export interface ProviderServiceShape { |
| 83 | * | 88 | * |
| 84 | * Persisted session bindings remain so stale sessions can be resumed after | 89 | * Persisted session bindings remain so stale sessions can be resumed after |
| 85 | * process restart. | 90 | * process restart. |
| 91 | * | ||
| 92 | * Runtime alias mappings are cleared as part of shutdown. | ||
| 86 | */ | 93 | */ |
| 87 | readonly stopAll: () => Effect.Effect<void, ProviderServiceError>; | 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,9 +42,15 @@ export type ProviderSessionDirectoryWriteError = |
| 42 | | ProviderValidationError | 42 | | ProviderValidationError |
| 43 | | ProviderSessionDirectoryPersistenceError; | 43 | | ProviderSessionDirectoryPersistenceError; |
| 44 | 44 | ||
| 45 | /** | ||
| 46 | * ProviderSessionDirectoryShape - Service API for provider session ownership metadata. | ||
| 47 | */ | ||
| 45 | export interface ProviderSessionDirectoryShape { | 48 | export interface ProviderSessionDirectoryShape { |
| 46 | /** | 49 | /** |
| 47 | * Record or update ownership for one provider session. | 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 | readonly upsert: ( | 55 | readonly upsert: ( |
| 50 | binding: ProviderSessionBinding, | 56 | binding: ProviderSessionBinding, |
apps/server/src/terminal/Services/Manager.ts+52| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { | 9 | import { |
| 2 | TerminalClearInput, | 10 | TerminalClearInput, |
| 3 | TerminalCloseInput, | 11 | TerminalCloseInput, |
| ... | @@ -40,17 +48,61 @@ export interface TerminalStartInput extends TerminalOpenInput { | ... | @@ -40,17 +48,61 @@ export interface TerminalStartInput extends TerminalOpenInput { |
| 40 | rows: number; | 48 | rows: number; |
| 41 | } | 49 | } |
| 42 | 50 | ||
| 51 | /** | ||
| 52 | * TerminalManagerShape - Service API for terminal session lifecycle operations. | ||
| 53 | */ | ||
| 43 | export interface TerminalManagerShape { | 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 | readonly open: (input: TerminalOpenInput) => Effect.Effect<TerminalSessionSnapshot, unknown>; | 61 | readonly open: (input: TerminalOpenInput) => Effect.Effect<TerminalSessionSnapshot, unknown>; |
| 62 | |||
| 63 | /** | ||
| 64 | * Write input bytes to a terminal session. | ||
| 65 | */ | ||
| 45 | readonly write: (input: TerminalWriteInput) => Effect.Effect<void, unknown>; | 66 | readonly write: (input: TerminalWriteInput) => Effect.Effect<void, unknown>; |
| 67 | |||
| 68 | /** | ||
| 69 | * Resize the PTY backing a terminal session. | ||
| 70 | */ | ||
| 46 | readonly resize: (input: TerminalResizeInput) => Effect.Effect<void, unknown>; | 71 | readonly resize: (input: TerminalResizeInput) => Effect.Effect<void, unknown>; |
| 72 | |||
| 73 | /** | ||
| 74 | * Clear terminal output history. | ||
| 75 | */ | ||
| 47 | readonly clear: (input: TerminalClearInput) => Effect.Effect<void, unknown>; | 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 | readonly restart: (input: TerminalOpenInput) => Effect.Effect<TerminalSessionSnapshot, unknown>; | 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 | readonly close: (input: TerminalCloseInput) => Effect.Effect<void, unknown>; | 90 | readonly close: (input: TerminalCloseInput) => Effect.Effect<void, unknown>; |
| 91 | |||
| 92 | /** | ||
| 93 | * Subscribe to terminal runtime events. | ||
| 94 | */ | ||
| 50 | readonly subscribe: (listener: (event: TerminalEvent) => void) => Effect.Effect<() => void>; | 95 | readonly subscribe: (listener: (event: TerminalEvent) => void) => Effect.Effect<() => void>; |
| 96 | |||
| 97 | /** | ||
| 98 | * Dispose all managed terminal resources. | ||
| 99 | */ | ||
| 51 | readonly dispose: () => Effect.Effect<void>; | 100 | readonly dispose: () => Effect.Effect<void>; |
| 52 | } | 101 | } |
| 53 | 102 | ||
| 103 | /** | ||
| 104 | * TerminalManager - Service tag for terminal session orchestration. | ||
| 105 | */ | ||
| 54 | export class TerminalManager extends ServiceMap.Service<TerminalManager, TerminalManagerShape>()( | 106 | export class TerminalManager extends ServiceMap.Service<TerminalManager, TerminalManagerShape>()( |
| 55 | "terminal/TerminalManager", | 107 | "terminal/TerminalManager", |
| 56 | ) {} | 108 | ) {} |
apps/server/src/terminal/Services/PTY.ts+17| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import { ServiceMap } from "effect"; | 9 | import { ServiceMap } from "effect"; |
| 2 | 10 | ||
| 3 | export interface PtyExitEvent { | 11 | export interface PtyExitEvent { |
| ... | @@ -23,10 +31,19 @@ export interface PtySpawnInput { | ... | @@ -23,10 +31,19 @@ export interface PtySpawnInput { |
| 23 | env: NodeJS.ProcessEnv; | 31 | env: NodeJS.ProcessEnv; |
| 24 | } | 32 | } |
| 25 | 33 | ||
| 34 | /** | ||
| 35 | * PtyAdapterShape - Service API for spawning and controlling PTY processes. | ||
| 36 | */ | ||
| 26 | export interface PtyAdapterShape { | 37 | export interface PtyAdapterShape { |
| 38 | /** | ||
| 39 | * Spawn a PTY process for a terminal session. | ||
| 40 | */ | ||
| 27 | spawn(input: PtySpawnInput): PtyProcess; | 41 | spawn(input: PtySpawnInput): PtyProcess; |
| 28 | } | 42 | } |
| 29 | 43 | ||
| 44 | /** | ||
| 45 | * PtyAdapter - Service tag for PTY process integration. | ||
| 46 | */ | ||
| 30 | export class PtyAdapter extends ServiceMap.Service<PtyAdapter, PtyAdapterShape>()( | 47 | export class PtyAdapter extends ServiceMap.Service<PtyAdapter, PtyAdapterShape>()( |
| 31 | "terminal/PtyAdapter", | 48 | "terminal/PtyAdapter", |
| 32 | ) {} | 49 | ) {} |
apps/server/src/wsServer.ts+21| ... | @@ -1,3 +1,11 @@ | ... | @@ -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 | import fs from "node:fs"; | 9 | import fs from "node:fs"; |
| 2 | import http from "node:http"; | 10 | import http from "node:http"; |
| 3 | import path from "node:path"; | 11 | import path from "node:path"; |
| ... | @@ -49,15 +57,28 @@ const MIME_TYPES: Record<string, string> = { | ... | @@ -49,15 +57,28 @@ const MIME_TYPES: Record<string, string> = { |
| 49 | ".map": "application/json", | 57 | ".map": "application/json", |
| 50 | }; | 58 | }; |
| 51 | 59 | ||
| 60 | /** | ||
| 61 | * ServerShape - Service API for server lifecycle control. | ||
| 62 | */ | ||
| 52 | export interface ServerShape { | 63 | export interface ServerShape { |
| 64 | /** | ||
| 65 | * Start HTTP and WebSocket listeners. | ||
| 66 | */ | ||
| 53 | readonly start: Effect.Effect< | 67 | readonly start: Effect.Effect< |
| 54 | http.Server, | 68 | http.Server, |
| 55 | unknown, | 69 | unknown, |
| 56 | Scope.Scope | ServerRuntimeServices | ServerConfig | 70 | Scope.Scope | ServerRuntimeServices | ServerConfig |
| 57 | >; | 71 | >; |
| 72 | |||
| 73 | /** | ||
| 74 | * Wait for process shutdown signals. | ||
| 75 | */ | ||
| 58 | readonly stopSignal: Effect.Effect<unknown, never>; | 76 | readonly stopSignal: Effect.Effect<unknown, never>; |
| 59 | } | 77 | } |
| 60 | 78 | ||
| 79 | /** | ||
| 80 | * Server - Service tag for HTTP/WebSocket lifecycle management. | ||
| 81 | */ | ||
| 61 | export class Server extends ServiceMap.Service<Server, ServerShape>()("server/Server") {} | 82 | export class Server extends ServiceMap.Service<Server, ServerShape>()("server/Server") {} |
| 62 | 83 | ||
| 63 | const isServerNotRunningError = (error: unknown): boolean => { | 84 | const isServerNotRunningError = (error: unknown): boolean => { |