authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-26 00:18:11-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-26 13:20:36-08:00
log9fae3ee0e95b036388b597558ee58617b0035494
tree1c308d19081f13418191a0188d75f5186559e63a
parenta328f66bfa9f09f2748a5960ed65546dbd8da9c9

look over effect services


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_kwDORLtfbc5wiLhQ210 - 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.
212212
213- [ ] `C033` Three error classes defined but never instantiated anywhere213- [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_kwDORLtfbc5wlYgo219 - 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.
221221
222- [ ] `C034` Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type222- [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_kwDORLtfbc5wj5fn228 - Threads: PRRT_kwDORLtfbc5wj5fn
229 - Audit note: CheckpointInvariantError remains redundantly included in service union.229 - Audit note: CheckpointInvariantError remains redundantly included in service union.
230230
231- [ ] `C035` Redundant error type in CheckpointServiceError union definition231- [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}
2121
22/**
23 * CheckpointRepositoryError - Checkpointing unavailable for cwd/repository.
24 */
25export 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 */
41export 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
69export type CheckpointStoreError =38export type CheckpointStoreError =
70 | GitCommandError39 | GitCommandError
71 | CheckpointInvariantError40 | CheckpointInvariantError
72 | CheckpointUnavailableError41 | CheckpointUnavailableError;
73 | CheckpointRepositoryError;
7442
75export type CheckpointServiceError =43export 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 @@
1import {1import {
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 */
1import type {9import 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";
917
10import type { CheckpointServiceError } from "../Errors.ts";18import type { CheckpointServiceError } from "../Errors.ts";
1119
20/**
21 * CheckpointDiffQueryShape - Service API for checkpoint diff queries.
22 */
12export interface CheckpointDiffQueryShape {23export 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}
2042
43/**
44 * CheckpointDiffQuery - Service tag for checkpoint diff queries.
45 */
21export class CheckpointDiffQuery extends ServiceMap.Service<46export class CheckpointDiffQuery extends ServiceMap.Service<
22 CheckpointDiffQuery,47 CheckpointDiffQuery,
23 CheckpointDiffQueryShape48 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}
4141
42/**
43 * CheckpointStoreShape - Service API for checkpoint capture/restore and diff access.
44 */
42export interface CheckpointStoreShape {45export 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 {
4750
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 {
6166
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 {
6875
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 {
7584
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 */
1import * as Net from "node:net";9import * as Net from "node:net";
2import { Data, Effect, FileSystem, Layer, Path, ServiceMap } from "effect";10import { Data, Effect, FileSystem, Layer, Path, ServiceMap } from "effect";
311
...@@ -5,6 +13,9 @@ export const DEFAULT_PORT = 3773;...@@ -5,6 +13,9 @@ export const DEFAULT_PORT = 3773;
513
6export type RuntimeMode = "web" | "desktop";14export type RuntimeMode = "web" | "desktop";
715
16/**
17 * ServerConfigShape - Process/runtime configuration required by the server.
18 */
8export interface ServerConfigShape {19export 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}
2233
34/**
35 * ServerConfig - Service tag for server runtime configuration.
36 */
23export class ServerConfig extends ServiceMap.Service<ServerConfig, ServerConfigShape>()(37export 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}> {}
3347
48/**
49 * NetServiceShape - Networking helper operations used during startup.
50 */
34export interface NetServiceShape {51export 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}
3759
60/**
61 * NetService - Service tag for startup networking helpers.
62 */
38export class NetService extends ServiceMap.Service<NetService, NetServiceShape>()(63export 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}
5959
60/**
61 * GitCoreError - Errors emitted by low-level Git repository operations.
62 */
63export type GitCoreError = GitCommandError;
64
65/**
66 * GitHubCliServiceError - Errors emitted by GitHub CLI service helpers.
67 */
68export type GitHubCliServiceError = GitHubCliError;
69
70/**
71 * TextGenerationServiceError - Errors emitted by commit/PR text generation.
72 */
73export 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 */
78export type GitManagerServiceError =63export type GitManagerServiceError =
79 | GitManagerError64 | GitManagerError
80 | GitCoreError65 | GitCommandError
81 | GitHubCliServiceError66 | 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}
2929
30/**
31 * GitServiceShape - Service API for Git command execution.
32 */
30export interface GitServiceShape {33export 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 */
1import {9import {
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}
390398
399/**
400 * KeybindingsShape - Service API for keybinding configuration operations.
401 */
391export interface KeybindingsShape {402export 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 KeybindingsConfigError410 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}
400423
424/**
425 * Keybindings - Service tag for keybinding configuration operations.
426 */
401export class Keybindings extends ServiceMap.Service<Keybindings, KeybindingsShape>()(427export 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 */
1import { Config, Data, Effect, FileSystem, Layer, Option, Path, Schema, ServiceMap } from "effect";9import { Config, Data, Effect, FileSystem, Layer, Option, Path, Schema, ServiceMap } from "effect";
2import { Command, Flag } from "effect/unstable/cli";10import { Command, Flag } from "effect/unstable/cli";
311
...@@ -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}
3442
43/**
44 * CliConfigShape - Startup helpers required while building server layers.
45 */
35export interface CliConfigShape {46export 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}
4062
63/**
64 * CliConfig - Service tag for startup CLI/runtime helpers.
65 */
41export class CliConfig extends ServiceMap.Service<CliConfig, CliConfigShape>()("server/CliConfig") {66export 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 */
1import { spawn } from "node:child_process";9import { spawn } from "node:child_process";
210
3import { EDITORS, type EditorId } from "@t3tools/contracts";11import { 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}
2432
33/**
34 * OpenShape - Service API for browser and editor launch actions.
35 */
25export interface OpenShape {36export 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}
2949
50/**
51 * Open - Service tag for browser/editor launch operations.
52 */
30export class Open extends ServiceMap.Service<Open, OpenShape>()("server/Open") {}53export class Open extends ServiceMap.Service<Open, OpenShape>()("server/Open") {}
3154
32// ==============================55// ==============================
apps/server/src/orchestration/Errors.ts-4
...@@ -1,8 +1,6 @@...@@ -1,8 +1,6 @@
1import { SchemaIssue, Schema } from "effect";1import { SchemaIssue, Schema } from "effect";
22
3import type {3import type {
4 OrchestrationCommandReceiptRepositoryError,
5 OrchestrationEventStoreError,
6 ProjectionRepositoryError,4 ProjectionRepositoryError,
7} from "../persistence/Errors.ts";5} from "../persistence/Errors.ts";
86
...@@ -83,8 +81,6 @@ export class OrchestrationListenerCallbackError extends Schema.TaggedErrorClass<...@@ -83,8 +81,6 @@ export class OrchestrationListenerCallbackError extends Schema.TaggedErrorClass<
83}81}
8482
85export type OrchestrationDispatchError =83export type OrchestrationDispatchError =
86 | OrchestrationEventStoreError
87 | OrchestrationCommandReceiptRepositoryError
88 | ProjectionRepositoryError84 | ProjectionRepositoryError
89 | OrchestrationCommandInvariantError85 | OrchestrationCommandInvariantError
90 | OrchestrationCommandPreviouslyRejectedError86 | 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";
14import * as SqlSchema from "effect/unstable/sql/SqlSchema";14import * as SqlSchema from "effect/unstable/sql/SqlSchema";
1515
16import {16import {
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}
108106
109function 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
122const makeProjectionSnapshotQuery = Effect.gen(function* () {107const makeProjectionSnapshotQuery = Effect.gen(function* () {
123 const sql = yield* SqlClient.SqlClient;108 const sql = yield* SqlClient.SqlClient;
124109
...@@ -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 );
446436
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 */
1import { ServiceMap } from "effect";9import { ServiceMap } from "effect";
2import type { Effect, Scope } from "effect";10import type { Effect, Scope } from "effect";
311
12/**
13 * CheckpointReactorShape - Service API for checkpoint reactor lifecycle.
14 */
4export interface CheckpointReactorShape {15export 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 be19 * 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}
1327
28/**
29 * CheckpointReactor - Service tag for checkpoint reactor workers.
30 */
14export class CheckpointReactor extends ServiceMap.Service<31export class CheckpointReactor extends ServiceMap.Service<
15 CheckpointReactor,32 CheckpointReactor,
16 CheckpointReactorShape33 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";
21import type { OrchestrationDispatchError } from "../Errors.ts";21import type { OrchestrationDispatchError } from "../Errors.ts";
22import type { OrchestrationEventStoreError } from "../../persistence/Errors.ts";22import type { OrchestrationEventStoreError } from "../../persistence/Errors.ts";
2323
24/**
25 * OrchestrationEngineShape - Service API for orchestration command and event flow.
26 */
24export interface OrchestrationEngineShape {27export 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 {
5157
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 */
1import { ServiceMap } from "effect";9import { ServiceMap } from "effect";
2import type { Effect, Scope } from "effect";10import type { Effect, Scope } from "effect";
311
12/**
13 * OrchestrationReactorShape - Service API for orchestration reactor lifecycle.
14 */
4export interface OrchestrationReactorShape {15export 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}
1324
25/**
26 * OrchestrationReactor - Service tag for orchestration reactor coordination.
27 */
14export class OrchestrationReactor extends ServiceMap.Service<28export class OrchestrationReactor extends ServiceMap.Service<
15 OrchestrationReactor,29 OrchestrationReactor,
16 OrchestrationReactorShape30 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 */
1import type { OrchestrationEvent } from "@t3tools/contracts";9import type { OrchestrationEvent } from "@t3tools/contracts";
2import { ServiceMap } from "effect";10import { ServiceMap } from "effect";
3import type { Effect } from "effect";11import type { Effect } from "effect";
412
5import type {13import type { ProjectionRepositoryError } from "../../persistence/Errors.ts";
6 OrchestrationEventStoreError,
7 ProjectionRepositoryError,
8} from "../../persistence/Errors.ts";
9
10export type OrchestrationProjectionPipelineError =
11 | OrchestrationEventStoreError
12 | ProjectionRepositoryError;
1314
15/**
16 * OrchestrationProjectionPipelineShape - Service API for projection execution.
17 */
14export interface OrchestrationProjectionPipelineShape {18export 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>;
1625
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}
2135
36/**
37 * OrchestrationProjectionPipeline - Service tag for orchestration projections.
38 */
22export class OrchestrationProjectionPipeline extends ServiceMap.Service<39export class OrchestrationProjectionPipeline extends ServiceMap.Service<
23 OrchestrationProjectionPipeline,40 OrchestrationProjectionPipeline,
24 OrchestrationProjectionPipelineShape41 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 */
1import type { OrchestrationReadModel } from "@t3tools/contracts";9import type { OrchestrationReadModel } from "@t3tools/contracts";
2import { ServiceMap } from "effect";10import { ServiceMap } from "effect";
3import type { Effect } from "effect";11import type { Effect } from "effect";
412
5import type { ProjectionRepositoryError } from "../../persistence/Errors.ts";13import type { ProjectionRepositoryError } from "../../persistence/Errors.ts";
614
15/**
16 * ProjectionSnapshotQueryShape - Service API for read-model snapshots.
17 */
7export interface ProjectionSnapshotQueryShape {18export 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}
1027
28/**
29 * ProjectionSnapshotQuery - Service tag for projection snapshot queries.
30 */
11export class ProjectionSnapshotQuery extends ServiceMap.Service<31export class ProjectionSnapshotQuery extends ServiceMap.Service<
12 ProjectionSnapshotQuery,32 ProjectionSnapshotQuery,
13 ProjectionSnapshotQueryShape33 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 */
1import { ServiceMap } from "effect";9import { ServiceMap } from "effect";
2import type { Effect, Scope } from "effect";10import type { Effect, Scope } from "effect";
311
12/**
13 * ProviderCommandReactorShape - Service API for provider command reactors.
14 */
4export interface ProviderCommandReactorShape {15export 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 be19 * 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}
1327
28/**
29 * ProviderCommandReactor - Service tag for provider command reaction workers.
30 */
14export class ProviderCommandReactor extends ServiceMap.Service<31export class ProviderCommandReactor extends ServiceMap.Service<
15 ProviderCommandReactor,32 ProviderCommandReactor,
16 ProviderCommandReactorShape33 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 */
1import { ServiceMap } from "effect";9import { ServiceMap } from "effect";
2import type { Effect, Scope } from "effect";10import type { Effect, Scope } from "effect";
311
12/**
13 * ProviderRuntimeIngestionShape - Service API for runtime ingestion lifecycle.
14 */
4export interface ProviderRuntimeIngestionShape {15export 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 be19 * 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}
1327
28/**
29 * ProviderRuntimeIngestionService - Service tag for runtime ingestion workers.
30 */
14export class ProviderRuntimeIngestionService extends ServiceMap.Service<31export class ProviderRuntimeIngestionService extends ServiceMap.Service<
15 ProviderRuntimeIngestionService,32 ProviderRuntimeIngestionService,
16 ProviderRuntimeIngestionShape33 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}
3232
33export 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
46export function toPersistenceSqlError(operation: string) {33export 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}
7259
73export function toPersistenceSerializationError(operation: string) {60export 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
86export 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
99export 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
111export 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
127export 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
140export 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}
15262
153// ===============================63// ===============================
154// Provider Session Repository Errors64// 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}
18292
183export type ProjectRepositoryError =93export type OrchestrationEventStoreError = PersistenceSqlError | PersistenceDecodeError;
184 | PersistenceSqlError
185 | PersistenceDecodeError
186 | PersistenceSerializationError
187 | ProjectValidationError
188 | ProjectPathMissingError
189 | ProjectNotFoundError;
190
191export type OrchestrationEventStoreError =
192 | PersistenceSqlError
193 | PersistenceDecodeError
194 | PersistenceSerializationError;
195
196export type CheckpointRepositoryError =
197 | CheckpointRepositoryValidationError
198 | CheckpointRepositoryPersistenceError;
19994
200export type ProviderSessionRepositoryError =95export type ProviderSessionRepositoryError =
201 | ProviderSessionRepositoryValidationError96 | ProviderSessionRepositoryValidationError
...@@ -205,12 +100,6 @@ export type OrchestrationCommandReceiptRepositoryError =...@@ -205,12 +100,6 @@ export type OrchestrationCommandReceiptRepositoryError =
205 | PersistenceSqlError100 | PersistenceSqlError
206 | PersistenceDecodeError;101 | PersistenceDecodeError;
207102
208export type ProviderSessionRuntimeRepositoryError =103export type ProviderSessionRuntimeRepositoryError = PersistenceSqlError | PersistenceDecodeError;
209 | PersistenceSqlError
210 | PersistenceDecodeError
211 | PersistenceSerializationError;
212104
213export type ProjectionRepositoryError =105export 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 */
5import { DatabaseSync, type StatementSync } from "node:sqlite";7import { DatabaseSync, type StatementSync } from "node:sqlite";
68
...@@ -27,6 +29,9 @@ export const TypeId: TypeId = "~local/sqlite-node/SqliteClient";...@@ -27,6 +29,9 @@ export const TypeId: TypeId = "~local/sqlite-node/SqliteClient";
2729
28export type TypeId = "~local/sqlite-node/SqliteClient";30export type TypeId = "~local/sqlite-node/SqliteClient";
2931
32/**
33 * SqliteClient - Effect service tag for the sqlite SQL client.
34 */
30export const SqliteClient = ServiceMap.Service<Client.SqlClient>("@local/sqlite-node/SqliteClient");35export const SqliteClient = ServiceMap.Service<Client.SqlClient>("@local/sqlite-node/SqliteClient");
3136
32export interface SqliteClientConfig {37export 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 */
1import {9import {
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});
29export type GetByCommandIdInput = typeof GetByCommandIdInput.Type;37export type GetByCommandIdInput = typeof GetByCommandIdInput.Type;
3038
39/**
40 * OrchestrationCommandReceiptRepositoryShape - Service API for command receipts.
41 */
31export interface OrchestrationCommandReceiptRepositoryShape {42export 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>;
3551
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}
4362
63/**
64 * OrchestrationCommandReceiptRepository - Service tag for command receipt persistence.
65 */
44export class OrchestrationCommandReceiptRepository extends ServiceMap.Service<66export class OrchestrationCommandReceiptRepository extends ServiceMap.Service<
45 OrchestrationCommandReceiptRepository,67 OrchestrationCommandReceiptRepository,
46 OrchestrationCommandReceiptRepositoryShape68 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";
1515
16import type { OrchestrationEventStoreError } from "../Errors.ts";16import type { OrchestrationEventStoreError } from "../Errors.ts";
1717
18/**
19 * OrchestrationEventStoreShape - Service API for orchestration event persistence.
20 */
18export interface OrchestrationEventStoreShape {21export 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 */
1import {9import {
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});
42export type DeleteByThreadIdInput = typeof DeleteByThreadIdInput.Type;50export type DeleteByThreadIdInput = typeof DeleteByThreadIdInput.Type;
4351
52/**
53 * ProjectionCheckpointRepositoryShape - Service API for projected checkpoints.
54 */
44export interface ProjectionCheckpointRepositoryShape {55export 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>;
4662
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>;
5071
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>;
5478
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}
5986
87/**
88 * ProjectionCheckpointRepository - Service tag for checkpoint projection persistence.
89 */
60export class ProjectionCheckpointRepository extends ServiceMap.Service<90export class ProjectionCheckpointRepository extends ServiceMap.Service<
61 ProjectionCheckpointRepository,91 ProjectionCheckpointRepository,
62 ProjectionCheckpointRepositoryShape92 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 */
1import {9import {
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});
38export type DeleteProjectionPendingApprovalInput = typeof DeleteProjectionPendingApprovalInput.Type;46export type DeleteProjectionPendingApprovalInput = typeof DeleteProjectionPendingApprovalInput.Type;
3947
48/**
49 * ProjectionPendingApprovalRepositoryShape - Service API for pending approvals.
50 */
40export interface ProjectionPendingApprovalRepositoryShape {51export 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>;
4460
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>;
4869
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>;
5276
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}
5784
85/**
86 * ProjectionPendingApprovalRepository - Service tag for pending approval persistence.
87 */
58export class ProjectionPendingApprovalRepository extends ServiceMap.Service<88export class ProjectionPendingApprovalRepository extends ServiceMap.Service<
59 ProjectionPendingApprovalRepository,89 ProjectionPendingApprovalRepository,
60 ProjectionPendingApprovalRepositoryShape90 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 */
1import { IsoDateTime, MessageId, ThreadId } from "@t3tools/contracts";9import { IsoDateTime, MessageId, ThreadId } from "@t3tools/contracts";
2import { Option, Schema, ServiceMap } from "effect";10import { Option, Schema, ServiceMap } from "effect";
3import type { Effect } from "effect";11import type { Effect } from "effect";
...@@ -21,20 +29,37 @@ export const DeleteProjectionPendingTurnStartInput = Schema.Struct({...@@ -21,20 +29,37 @@ export const DeleteProjectionPendingTurnStartInput = Schema.Struct({
21});29});
22export type DeleteProjectionPendingTurnStartInput = typeof DeleteProjectionPendingTurnStartInput.Type;30export type DeleteProjectionPendingTurnStartInput = typeof DeleteProjectionPendingTurnStartInput.Type;
2331
32/**
33 * ProjectionPendingTurnStartRepositoryShape - Service API for pending turn-start markers.
34 */
24export interface ProjectionPendingTurnStartRepositoryShape {35export 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>;
2844
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>;
3251
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}
3759
60/**
61 * ProjectionPendingTurnStartRepository - Service tag for pending turn-start persistence.
62 */
38export class ProjectionPendingTurnStartRepository extends ServiceMap.Service<63export class ProjectionPendingTurnStartRepository extends ServiceMap.Service<
39 ProjectionPendingTurnStartRepository,64 ProjectionPendingTurnStartRepository,
40 ProjectionPendingTurnStartRepositoryShape65 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 */
1import { IsoDateTime, ProjectId, ProjectScript } from "@t3tools/contracts";9import { IsoDateTime, ProjectId, ProjectScript } from "@t3tools/contracts";
2import { Option, Schema, ServiceMap } from "effect";10import { Option, Schema, ServiceMap } from "effect";
3import type { Effect } from "effect";11import type { Effect } from "effect";
...@@ -26,23 +34,45 @@ export const DeleteProjectionProjectInput = Schema.Struct({...@@ -26,23 +34,45 @@ export const DeleteProjectionProjectInput = Schema.Struct({
26});34});
27export type DeleteProjectionProjectInput = typeof DeleteProjectionProjectInput.Type;35export type DeleteProjectionProjectInput = typeof DeleteProjectionProjectInput.Type;
2836
37/**
38 * ProjectionProjectRepositoryShape - Service API for projected project records.
39 */
29export interface ProjectionProjectRepositoryShape {40export 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>;
3147
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>;
3554
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 ProjectionRepositoryError62 ProjectionRepositoryError
39 >;63 >;
4064
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}
4572
73/**
74 * ProjectionProjectRepository - Service tag for project projection persistence.
75 */
46export class ProjectionProjectRepository extends ServiceMap.Service<76export class ProjectionProjectRepository extends ServiceMap.Service<
47 ProjectionProjectRepository,77 ProjectionProjectRepository,
48 ProjectionProjectRepositoryShape78 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 */
1import { IsoDateTime, NonNegativeInt } from "@t3tools/contracts";9import { IsoDateTime, NonNegativeInt } from "@t3tools/contracts";
2import { Option, Schema, ServiceMap } from "effect";10import { Option, Schema, ServiceMap } from "effect";
3import type { Effect } from "effect";11import type { Effect } from "effect";
...@@ -16,18 +24,40 @@ export const GetProjectionStateInput = Schema.Struct({...@@ -16,18 +24,40 @@ export const GetProjectionStateInput = Schema.Struct({
16});24});
17export type GetProjectionStateInput = typeof GetProjectionStateInput.Type;25export type GetProjectionStateInput = typeof GetProjectionStateInput.Type;
1826
27/**
28 * ProjectionStateRepositoryShape - Service API for projector state records.
29 */
19export interface ProjectionStateRepositoryShape {30export 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>;
2137
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>;
2544
45 /**
46 * List all projector cursor rows.
47 */
26 readonly listAll: () => Effect.Effect<ReadonlyArray<ProjectionState>, ProjectionRepositoryError>;48 readonly listAll: () => Effect.Effect<ReadonlyArray<ProjectionState>, ProjectionRepositoryError>;
2749
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}
3057
58/**
59 * ProjectionStateRepository - Service tag for projection cursor persistence.
60 */
31export class ProjectionStateRepository extends ServiceMap.Service<61export class ProjectionStateRepository extends ServiceMap.Service<
32 ProjectionStateRepository,62 ProjectionStateRepository,
33 ProjectionStateRepositoryShape63 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 */
1import {9import {
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({
33export type DeleteProjectionThreadActivitiesInput =41export type DeleteProjectionThreadActivitiesInput =
34 typeof DeleteProjectionThreadActivitiesInput.Type;42 typeof DeleteProjectionThreadActivitiesInput.Type;
3543
44/**
45 * ProjectionThreadActivityRepositoryShape - Service API for projected thread activity.
46 */
36export interface ProjectionThreadActivityRepositoryShape {47export 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>;
4056
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>;
4465
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}
4973
74/**
75 * ProjectionThreadActivityRepository - Service tag for thread activity persistence.
76 */
50export class ProjectionThreadActivityRepository extends ServiceMap.Service<77export class ProjectionThreadActivityRepository extends ServiceMap.Service<
51 ProjectionThreadActivityRepository,78 ProjectionThreadActivityRepository,
52 ProjectionThreadActivityRepositoryShape79 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 */
1import {9import {
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});
33export type DeleteProjectionThreadMessagesInput = typeof DeleteProjectionThreadMessagesInput.Type;41export type DeleteProjectionThreadMessagesInput = typeof DeleteProjectionThreadMessagesInput.Type;
3442
43/**
44 * ProjectionThreadMessageRepositoryShape - Service API for projected thread messages.
45 */
35export interface ProjectionThreadMessageRepositoryShape {46export 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>;
3955
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>;
4364
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}
4872
73/**
74 * ProjectionThreadMessageRepository - Service tag for message projection persistence.
75 */
49export class ProjectionThreadMessageRepository extends ServiceMap.Service<76export class ProjectionThreadMessageRepository extends ServiceMap.Service<
50 ProjectionThreadMessageRepository,77 ProjectionThreadMessageRepository,
51 ProjectionThreadMessageRepositoryShape78 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 */
1import {9import {
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});
34export type DeleteProjectionThreadSessionInput = typeof DeleteProjectionThreadSessionInput.Type;42export type DeleteProjectionThreadSessionInput = typeof DeleteProjectionThreadSessionInput.Type;
3543
44/**
45 * ProjectionThreadSessionRepositoryShape - Service API for projected thread sessions.
46 */
36export interface ProjectionThreadSessionRepositoryShape {47export 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>;
3854
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>;
4261
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}
4769
70/**
71 * ProjectionThreadSessionRepository - Service tag for thread-session persistence.
72 */
48export class ProjectionThreadSessionRepository extends ServiceMap.Service<73export class ProjectionThreadSessionRepository extends ServiceMap.Service<
49 ProjectionThreadSessionRepository,74 ProjectionThreadSessionRepository,
50 ProjectionThreadSessionRepositoryShape75 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 */
1import {8import {
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});
39export type DeleteProjectionThreadTurnsInput = typeof DeleteProjectionThreadTurnsInput.Type;46export type DeleteProjectionThreadTurnsInput = typeof DeleteProjectionThreadTurnsInput.Type;
4047
48/**
49 * ProjectionThreadTurnRepositoryShape - Service API for projected thread turns.
50 */
41export interface ProjectionThreadTurnRepositoryShape {51export 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>;
4358
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>;
4767
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>;
5174
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}
5682
83/**
84 * ProjectionThreadTurnRepository - Service tag for turn projection persistence.
85 */
57export class ProjectionThreadTurnRepository extends ServiceMap.Service<86export class ProjectionThreadTurnRepository extends ServiceMap.Service<
58 ProjectionThreadTurnRepository,87 ProjectionThreadTurnRepository,
59 ProjectionThreadTurnRepositoryShape88 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 */
1import { IsoDateTime, ProjectId, ThreadId, TurnId } from "@t3tools/contracts";9import { IsoDateTime, ProjectId, ThreadId, TurnId } from "@t3tools/contracts";
2import { Option, Schema, ServiceMap } from "effect";10import { Option, Schema, ServiceMap } from "effect";
3import type { Effect } from "effect";11import type { Effect } from "effect";
...@@ -33,22 +41,44 @@ export const ListProjectionThreadsByProjectInput = Schema.Struct({...@@ -33,22 +41,44 @@ export const ListProjectionThreadsByProjectInput = Schema.Struct({
33});41});
34export type ListProjectionThreadsByProjectInput = typeof ListProjectionThreadsByProjectInput.Type;42export type ListProjectionThreadsByProjectInput = typeof ListProjectionThreadsByProjectInput.Type;
3543
44/**
45 * ProjectionThreadRepositoryShape - Service API for projected thread records.
46 */
36export interface ProjectionThreadRepositoryShape {47export 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>;
3854
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>;
4261
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>;
4670
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}
5178
79/**
80 * ProjectionThreadRepository - Service tag for thread projection persistence.
81 */
52export class ProjectionThreadRepository extends ServiceMap.Service<82export class ProjectionThreadRepository extends ServiceMap.Service<
53 ProjectionThreadRepository,83 ProjectionThreadRepository,
54 ProjectionThreadRepositoryShape84 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 */
1import {8import {
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});
34export type DeleteProviderSessionRuntimeInput = typeof DeleteProviderSessionRuntimeInput.Type;41export type DeleteProviderSessionRuntimeInput = typeof DeleteProviderSessionRuntimeInput.Type;
3542
43/**
44 * ProviderSessionRuntimeRepositoryShape - Service API for provider runtime records.
45 */
36export interface ProviderSessionRuntimeRepositoryShape {46export 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>;
4055
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>;
4462
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 ProviderSessionRuntimeRepositoryError70 ProviderSessionRuntimeRepositoryError
48 >;71 >;
4972
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}
5480
81/**
82 * ProviderSessionRuntimeRepository - Service tag for provider runtime persistence.
83 */
55export class ProviderSessionRuntimeRepository extends ServiceMap.Service<84export class ProviderSessionRuntimeRepository extends ServiceMap.Service<
56 ProviderSessionRuntimeRepository,85 ProviderSessionRuntimeRepository,
57 ProviderSessionRuntimeRepositoryShape86 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 */
1import type { ProviderKind, ProviderSessionId, ThreadId } from "@t3tools/contracts";9import type { ProviderKind, ProviderSessionId, ThreadId } from "@t3tools/contracts";
2import { Option, ServiceMap } from "effect";10import { Option, ServiceMap } from "effect";
3import type { Effect } from "effect";11import 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}
2836
37/**
38 * ProviderSessionRepositoryShape - Service API for provider-session records.
39 */
29export interface ProviderSessionRepositoryShape {40export 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>;
3349
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>;
3756
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 ProviderSessionRepositoryError64 ProviderSessionRepositoryError
41 >;65 >;
4266
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}
4774
75/**
76 * ProviderSessionRepository - Service tag for provider-session persistence.
77 */
48export class ProviderSessionRepository extends ServiceMap.Service<78export class ProviderSessionRepository extends ServiceMap.Service<
49 ProviderSessionRepository,79 ProviderSessionRepository,
50 ProviderSessionRepositoryShape80 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}
5353
54/**
55 * ProviderAdapterProtocolError - Invalid/unexpected provider protocol payload.
56 */
57export 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}
166149
167/**
168 * ProviderCheckpointUnavailableError - Checkpointing unavailable for this session.
169 */
170export 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 */
186export 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 */
204export 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
217export type ProviderAdapterError =150export type ProviderAdapterError =
218 | ProviderAdapterValidationError151 | ProviderAdapterValidationError
219 | ProviderAdapterSessionNotFoundError152 | ProviderAdapterSessionNotFoundError
220 | ProviderAdapterSessionClosedError153 | ProviderAdapterSessionClosedError
221 | ProviderAdapterProtocolError
222 | ProviderAdapterRequestError154 | ProviderAdapterRequestError
223 | ProviderAdapterProcessError;155 | ProviderAdapterProcessError;
224156
...@@ -227,8 +159,5 @@ export type ProviderServiceError =...@@ -227,8 +159,5 @@ export type ProviderServiceError =
227 | ProviderUnsupportedError159 | ProviderUnsupportedError
228 | ProviderSessionNotFoundError160 | ProviderSessionNotFoundError
229 | ProviderSessionDirectoryPersistenceError161 | ProviderSessionDirectoryPersistenceError
230 | ProviderCheckpointUnavailableError
231 | ProviderCheckpointRangeError
232 | ProviderFilesystemError
233 | ProviderAdapterError162 | 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";
15import type { ProviderAdapterError } from "../Errors.ts";15import type { ProviderAdapterError } from "../Errors.ts";
16import type { ProviderAdapterShape } from "./ProviderAdapter.ts";16import type { ProviderAdapterShape } from "./ProviderAdapter.ts";
1717
18/**
19 * CodexAdapterShape - Service API for the Codex provider adapter.
20 */
18export interface CodexAdapterShape extends ProviderAdapterShape<ProviderAdapterError> {21export 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";
14import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts";14import type { ProviderAdapterError, ProviderUnsupportedError } from "../Errors.ts";
15import type { ProviderAdapterShape } from "./ProviderAdapter.ts";15import type { ProviderAdapterShape } from "./ProviderAdapter.ts";
1616
17/**
18 * ProviderAdapterRegistryShape - Service API for adapter lookup by provider kind.
19 */
17export interface ProviderAdapterRegistryShape {20export 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";
2828
29import type { ProviderServiceError } from "../Errors.ts";29import type { ProviderServiceError } from "../Errors.ts";
3030
31/**
32 * ProviderServiceShape - Service API for provider session and turn orchestration.
33 */
31export interface ProviderServiceShape {34export 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 {
6770
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>>;
7277
...@@ -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 after89 * 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>;
8895
apps/server/src/provider/Services/ProviderSessionDirectory.ts+6
...@@ -42,9 +42,15 @@ export type ProviderSessionDirectoryWriteError =...@@ -42,9 +42,15 @@ export type ProviderSessionDirectoryWriteError =
42 | ProviderValidationError42 | ProviderValidationError
43 | ProviderSessionDirectoryPersistenceError;43 | ProviderSessionDirectoryPersistenceError;
4444
45/**
46 * ProviderSessionDirectoryShape - Service API for provider session ownership metadata.
47 */
45export interface ProviderSessionDirectoryShape {48export 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 */
1import {9import {
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}
4250
51/**
52 * TerminalManagerShape - Service API for terminal session lifecycle operations.
53 */
43export interface TerminalManagerShape {54export 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}
53102
103/**
104 * TerminalManager - Service tag for terminal session orchestration.
105 */
54export class TerminalManager extends ServiceMap.Service<TerminalManager, TerminalManagerShape>()(106export 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 */
1import { ServiceMap } from "effect";9import { ServiceMap } from "effect";
210
3export interface PtyExitEvent {11export 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}
2533
34/**
35 * PtyAdapterShape - Service API for spawning and controlling PTY processes.
36 */
26export interface PtyAdapterShape {37export 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}
2943
44/**
45 * PtyAdapter - Service tag for PTY process integration.
46 */
30export class PtyAdapter extends ServiceMap.Service<PtyAdapter, PtyAdapterShape>()(47export 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 */
1import fs from "node:fs";9import fs from "node:fs";
2import http from "node:http";10import http from "node:http";
3import path from "node:path";11import 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};
5159
60/**
61 * ServerShape - Service API for server lifecycle control.
62 */
52export interface ServerShape {63export 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 | ServerConfig70 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}
6078
79/**
80 * Server - Service tag for HTTP/WebSocket lifecycle management.
81 */
61export class Server extends ServiceMap.Service<Server, ServerShape>()("server/Server") {}82export class Server extends ServiceMap.Service<Server, ServerShape>()("server/Server") {}
6283
63const isServerNotRunningError = (error: unknown): boolean => {84const isServerNotRunningError = (error: unknown): boolean => {